# Fix Focus App High CPU Usage on macOS ## Problem The Focus app (com.ftustudio.focustime) - a Pomodoro timer - consumes ~50% CPU constantly, even when idle. ### Root Cause Stack sampling revealed the app redraws its menu bar icon at an excessive rate (~60 times/second) instead of once per second: ``` 414 samples: -[NSStatusItem _updateReplicantsUnlessMenuIsTracking:] 400 samples: -[NSStatusItem _updateReplicant:] 218 samples: -[NSStatusItem _redrawReplicantSnapshot:sourceView:] ``` Each tick triggers expensive bitmap capture, CALayer rendering, and compositing for a simple countdown timer that only needs 1 update/second. ## Solution Since the app is sandboxed with hardened runtime (no dylib injection possible) and the developer is unresponsive, we use a duty-cycle throttler that pauses/resumes the process. ### Install ```bash # Create throttle script mkdir -p ~/.local/bin cat > ~/.local/bin/throttle-focus << 'EOF' #!/usr/bin/env python3 """Throttle Focus app to reduce CPU usage using duty-cycle approach.""" import subprocess import signal import sys import time TARGET = "Focus" RUN_SEC = 0.05 # Run for 50ms PAUSE_SEC = 0.95 # Pause for 950ms (~5% duty cycle) def get_pid(): try: result = subprocess.run(["pgrep", "-x", TARGET], capture_output=True, text=True) pids = result.stdout.strip().split('\n') return int(pids[0]) if pids[0] else None except: return None def send_signal(pid, sig): try: subprocess.run(["kill", f"-{sig}", str(pid)], capture_output=True) except: pass current_pid = None def cleanup(signum=None, frame=None): if current_pid: send_signal(current_pid, "CONT") print("\nThrottling stopped.") sys.exit(0) signal.signal(signal.SIGINT, cleanup) signal.signal(signal.SIGTERM, cleanup) print(f"Throttling {TARGET} (run {RUN_SEC*1000:.0f}ms / pause {PAUSE_SEC*1000:.0f}ms)") print("Press Ctrl+C to stop") while True: pid = get_pid() if not pid: print(f"Waiting for {TARGET} to start...") time.sleep(2) continue current_pid = pid send_signal(pid, "CONT") time.sleep(RUN_SEC) send_signal(pid, "STOP") time.sleep(PAUSE_SEC) EOF chmod +x ~/.local/bin/throttle-focus # Create Launch Agent for auto-start cat > ~/Library/LaunchAgents/com.local.throttle-focus.plist << 'EOF' Label com.local.throttle-focus ProgramArguments /usr/bin/python3 /Users/hao/.local/bin/throttle-focus RunAtLoad KeepAlive StandardOutPath /tmp/throttle-focus.log StandardErrorPath /tmp/throttle-focus.log EOF # Load the agent launchctl load ~/Library/LaunchAgents/com.local.throttle-focus.plist ``` ### Management Commands ```bash # Stop throttling launchctl unload ~/Library/LaunchAgents/com.local.throttle-focus.plist # Resume throttling launchctl load ~/Library/LaunchAgents/com.local.throttle-focus.plist # Check status launchctl list | grep throttle # View logs cat /tmp/throttle-focus.log # Uninstall completely launchctl unload ~/Library/LaunchAgents/com.local.throttle-focus.plist rm ~/Library/LaunchAgents/com.local.throttle-focus.plist rm ~/.local/bin/throttle-focus ``` ## Results | Metric | Before | After | |--------|--------|-------| | CPU Usage | ~48% | ~2% | | Menu bar updates | ~60/sec | 1/sec | The timer still functions correctly - it just updates once per second instead of continuously.