[GH-ISSUE #1803] 🚀 MonitorControl 2.0 Vision: Ultra-Light Menu Agent + Pro-Level Features to Outshine Lunar & BetterDisplay #994

Closed
opened 2026-05-05 07:00:49 -06:00 by gitea-mirror · 1 comment
Owner

Originally created by @Yodoma on GitHub (Sep 18, 2025).
Original GitHub issue: https://github.com/MonitorControl/MonitorControl/issues/1803

Before opening the issue, have you...?

  • Searched for existing issues

Discussion

TL;DR — biggest opportunities

  1. Make MonitorControl event-driven and split into a tiny resident agent + an on-demand worker so the heavy DDC/monitor logic only runs while a display is connected. (Technical details and Swift pattern below.) ([Apple Developer]1)
  2. Add the high-value features people pay for in competitors: ambient-sensor / adaptive brightness sync, per-display presets & input switching, HDR/XDR and HiDPI/virtual-display support, and robust per-monitor compatibility quirks database. (These are what Lunar, BetterDisplay and DisplayBuddy emphasize.) ([Lunar]2)
  3. Improve trust & distribution: signed/notarized builds, Homebrew cask + Mac App Store/paid Pro option (or Patreon/licensing), clear hardware compatibility docs and reporting UI. ([monitorcontrol.macupdate.com]3)

Quick feature comparison (what users want)

  • MonitorControl (current) — simple, free, menu bar brightness/volume/contrast via DDC when supported; shows native macOS OSD. Great baseline but perceived by some as “lightweight” with room for richer automation/features. ([GitHub]4)
  • Lunar — focuses on syncing brightness using ambient-light sensor, adaptive sync, and advanced automation (sync mode, per-monitor sync). Users choose it when they want automatic/adaptive behavior. ([Lunar]2)
  • DisplayBuddy — focuses on simple reliable controls, presets and time-based/automation flows (easy UX). Good for users who want “set-and-forget” presets. ([DisplayBuddy]5)
  • BetterDisplay (BetterDummy) — targets power users: HiDPI scaling, virtual screens, XDR/HDR extras and deep display hacks. Good example of advanced capabilities MonitorControl could add or integrate with. ([GitHub]6)

Concrete improvements to tremendously outcompete others

Product / UX (what to build)

  1. Adaptive sync & ambient sensor mode — mirror macOS adaptive-brightness to externals (Lunar’s key selling point). Add “Sync to built-in” and “Ambient sensor follower.” ([Lunar]2)
  2. Per-display profiles & presets + input switching — named presets (movie / night / coding), per-input defaults, quick menu keyboard shortcuts and scheduling. (DisplayBuddy emphasizes presets.) ([DisplayBuddy]5)
  3. Compatibility database + per-model quirks — crowdsource (toggle software dim, DDC quirks, m1/HDMI limits) and surface “works / partial / software-only” in the UI. This reduces support noise.
  4. HDR/XDR & HiDPI support — integrate or interoperate with BetterDisplay-style virtual-display to support XDR extra brightness and scaling features for creative users. ([GitHub]6)
  5. Remote control + automation — Apple Shortcuts / iOS companion or a small REST/XPC API so you can change profiles from iPhone, scripts, or MDM.
  6. Accessibility & keyboard flow — expose Apple Keyboard keys, custom hotkeys, AppleScript + Shortcuts hooks, and Control Center integration where possible. MonitorControl already shows macOS OSD — preserve and polish that. ([GitHub]4)

Engineering / performance (how to make it efficient)

  • Do not poll. Replace any polling loop with event callbacks from the OS (display add/remove) and DDC-notifications where available. Polling is the main CPU killer when many apps try to “refresh” monitor state. Use CoreGraphics display callbacks and IOKit for DDC when needed. ([Apple Developer]1)

  • Two-process architecture (recommended):

    • A tiny menu agent (few MB RSS) that: registers for display reconfiguration events, shows the menu icon, displays saved presets and “connect” status, and spawns the worker when external displays become available. This agent should not load heavy display libraries or open DDC/I2C until needed.
    • A worker process (full feature set) spawned only when at least one compatible external display is present. When all displays disconnect, gracefully shutdown the worker to free memory/threads.
  • Lazy load modules — keep frameworks (DDC/IOKit, color management, UI heavy code) in dynamic frameworks and only dlopen/instantiate when needed.

Security & packaging

  • Ship signed and notarized builds (users trust this). Provide a Homebrew cask, and offer a paid Pro channel (or donation/Patreon) for advanced features while keeping core free. Provide enterprise/MDM packaging for orgs.

How to implement the “menu but no CPU/RAM until a display is connected” (practical pattern)

Important note up front: a visible menu bar icon requires a running process — you cannot magically show a UI element with zero process memory. But you can make that resident process extremely tiny and event-driven, and only start the heavy work when a display is present. Below is a safe, practical pattern used by macOS apps.

Minimal strategy (implementation steps)

  1. Create a tiny agent (LSBackgroundOnly or small GUIless status item) that:

    • Registers a display reconfiguration callback (CoreGraphics CGDisplayRegisterReconfigurationCallback) to get add/remove events. ([Apple Developer]1)
    • Does not open IOKit/DDC or start polling threads.
    • Shows a simple status icon/menu and “No external displays” caption when nothing is connected.
  2. When the callback indicates a compatible display is present, the agent spawns the worker (Process or XPC service) which loads the DDC / IOKit code and UI.

  3. Worker does DDC/CI via IOKit APIs like IOI2CSendRequest (DDC over I2C) or platform-specific helpers (note: some connections/hubs block DDC — fall back to software dim). ([Stack Overflow]7)

  4. When displays disconnect, the worker gracefully exits — agent returns to idle.

Example Swift-ish sketch (display callback + spawn)

import Cocoa
import CoreGraphics

// tiny agent app
func displayReconfigCallback(_ display: CGDirectDisplayID,
                             _ flags: CGDisplayChangeSummaryFlags,
                             _ userInfo: UnsafeMutableRawPointer?) {
    // check flags for add/remove/online
    if flags.contains(.addFlag) || flags.contains(.beginConfigurationFlag) {
        spawnWorkerIfNeeded()
    }
    if flags.contains(.removeFlag) {
        checkAndStopWorkerIfNoDisplays()
    }
}

func startAgent() {
    // register callback (Apple doc API)
    CGDisplayRegisterReconfigurationCallback(displayReconfigCallback, nil)
    // create tiny NSStatusItem and run runloop — do NOT load heavy libs
}

// spawn a full worker process (or connect to an XPC service)
func spawnWorkerIfNeeded() {
    if workerIsRunning { return }
    let worker = Process()
    worker.executableURL = URL(fileURLWithPath: "/Library/Application Support/MonitorControl/monitor-worker")
    try? worker.run()
}

(Cite Apple docs for the callback API.) ([Apple Developer]1)

Why this is low-cost

  • The agent only responds to OS events; no polling loops => near-zero CPU when idle.
  • Memory: a tiny agent process will still consume a few MB of RAM, but that’s much smaller than keeping the whole feature set resident. This is how you get practically zero impact for end users.

DDC/CI and platform notes (real-world quirks)

  • Many monitors support DDC/CI over the display link, but certain Mac HDMI ports, DisplayLink docks, or some USB-C adapters block DDC — in those cases you must fall back to software dimming (i.e., gamma tables) rather than DDC. Make this explicit in the UI and compatibility DB. ([Ask Different]8)
  • On Apple Silicon some low-level DDC tools needed changes (there are community efforts like m1ddc), so test on M1/M2 and document any known limitations. ([Ask Different]8)

Roadmap (prioritized)

  1. Quick wins (weeks):

    • Replace any polling with CGDisplay callbacks; implement two-process spawn/kill pattern. (High ROI: immediate battery/CPU wins.) ([Apple Developer]1)
    • Add “software dim” fallback and show compatibility in the UI.
  2. Mid (1–3 months):

    • Add presets, per-display profiles, hotkeys and save/export profiles.
    • Build a simple compatibility DB + “Report issue” flow.
  3. Bigger (3–6 months):

    • Ambient sensor sync + per-app/per-time automation (Lunar parity). (Lunar)
    • HDR/XDR/HiDPI integrations or clear integration docs with BetterDisplay features. ([GitHub]6)
  4. Business & trust (ongoing):

    • Signed, notarized releases, Homebrew cask, paid Pro channel or donor model, clearer docs and screenshots for trust (MacUpdate/Softpedia users value that). ([monitorcontrol.macupdate.com]3)

Final note (realistic expectations)

  • You can’t show a menu icon with literally zero memory because something must run to draw it. The practical and user-visible goal is to make that resident process tiny and event-driven, and only run heavyweight code while a compatible external display is connected — this is straightforward and will produce a dramatic reduction in CPU/RAM when the user isn’t using monitors. The CoreGraphics display callback + on-demand worker is the cleanest pattern. ([Apple Developer]1)

Originally created by @Yodoma on GitHub (Sep 18, 2025). Original GitHub issue: https://github.com/MonitorControl/MonitorControl/issues/1803 ### Before opening the issue, have you...? - [x] Searched for existing issues ### Discussion # TL;DR — biggest opportunities 1. Make MonitorControl event-driven and split into a tiny resident *agent* + an on-demand *worker* so the heavy DDC/monitor logic only runs while a display is connected. (Technical details and Swift pattern below.) ([[Apple Developer](https://developer.apple.com/documentation/coregraphics/cgdisplayregisterreconfigurationcallback%28_%3A_%3A%29?utm_source=chatgpt.com)][1]) 2. Add the high-value features people pay for in competitors: ambient-sensor / adaptive brightness sync, per-display presets & input switching, HDR/XDR and HiDPI/virtual-display support, and robust per-monitor compatibility quirks database. (These are what Lunar, BetterDisplay and DisplayBuddy emphasize.) ([[Lunar](https://lunar.fyi/?utm_source=chatgpt.com)][2]) 3. Improve trust & distribution: signed/notarized builds, Homebrew cask + Mac App Store/paid Pro option (or Patreon/licensing), clear hardware compatibility docs and reporting UI. ([[monitorcontrol.macupdate.com](https://monitorcontrol.macupdate.com/?utm_source=chatgpt.com)][3]) --- # Quick feature comparison (what users want) * **MonitorControl (current)** — simple, free, menu bar brightness/volume/contrast via DDC when supported; shows native macOS OSD. Great baseline but perceived by some as “lightweight” with room for richer automation/features. ([[GitHub](https://github.com/MonitorControl/MonitorControl?utm_source=chatgpt.com)][4]) * **Lunar** — focuses on syncing brightness using ambient-light sensor, adaptive sync, and advanced automation (sync mode, per-monitor sync). Users choose it when they want automatic/adaptive behavior. ([[Lunar](https://lunar.fyi/?utm_source=chatgpt.com)][2]) * **DisplayBuddy** — focuses on simple reliable controls, presets and time-based/automation flows (easy UX). Good for users who want “set-and-forget” presets. ([[DisplayBuddy](https://displaybuddy.app/?utm_source=chatgpt.com)][5]) * **BetterDisplay (BetterDummy)** — targets power users: HiDPI scaling, virtual screens, XDR/HDR extras and deep display hacks. Good example of advanced capabilities MonitorControl could add or integrate with. ([[GitHub](https://github.com/waydabber/BetterDisplay?utm_source=chatgpt.com)][6]) --- # Concrete improvements to **tremendously** outcompete others ## Product / UX (what to build) 1. **Adaptive sync & ambient sensor mode** — mirror macOS adaptive-brightness to externals (Lunar’s key selling point). Add “Sync to built-in” and “Ambient sensor follower.” ([[Lunar](https://lunar.fyi/?utm_source=chatgpt.com)][2]) 2. **Per-display profiles & presets + input switching** — named presets (movie / night / coding), per-input defaults, quick menu keyboard shortcuts and scheduling. (DisplayBuddy emphasizes presets.) ([[DisplayBuddy](https://displaybuddy.app/?utm_source=chatgpt.com)][5]) 3. **Compatibility database + per-model quirks** — crowdsource (toggle software dim, DDC quirks, m1/HDMI limits) and surface “works / partial / software-only” in the UI. This reduces support noise. 4. **HDR/XDR & HiDPI support** — integrate or interoperate with BetterDisplay-style virtual-display to support XDR extra brightness and scaling features for creative users. ([[GitHub](https://github.com/waydabber/BetterDisplay?utm_source=chatgpt.com)][6]) 5. **Remote control + automation** — Apple Shortcuts / iOS companion or a small REST/XPC API so you can change profiles from iPhone, scripts, or MDM. 6. **Accessibility & keyboard flow** — expose Apple Keyboard keys, custom hotkeys, AppleScript + Shortcuts hooks, and Control Center integration where possible. MonitorControl already shows macOS OSD — preserve and polish that. ([[GitHub](https://github.com/MonitorControl/MonitorControl?utm_source=chatgpt.com)][4]) ## Engineering / performance (how to make it efficient) * **Do not poll.** Replace any polling loop with event callbacks from the OS (display add/remove) and DDC-notifications where available. Polling is the main CPU killer when many apps try to “refresh” monitor state. Use CoreGraphics display callbacks and IOKit for DDC when needed. ([[Apple Developer](https://developer.apple.com/documentation/coregraphics/cgdisplayregisterreconfigurationcallback%28_%3A_%3A%29?utm_source=chatgpt.com)][1]) * **Two-process architecture (recommended):** * A *tiny menu agent* (few MB RSS) that: registers for display reconfiguration events, shows the menu icon, displays saved presets and “connect” status, and spawns the worker when external displays become available. This agent should *not* load heavy display libraries or open DDC/I2C until needed. * A *worker process* (full feature set) spawned only when at least one compatible external display is present. When all displays disconnect, gracefully shutdown the worker to free memory/threads. * **Lazy load modules** — keep frameworks (DDC/IOKit, color management, UI heavy code) in dynamic frameworks and only `dlopen`/instantiate when needed. ## Security & packaging * Ship signed and notarized builds (users trust this). Provide a Homebrew cask, and offer a paid Pro channel (or donation/Patreon) for advanced features while keeping core free. Provide enterprise/MDM packaging for orgs. --- # How to implement the “menu but no CPU/RAM until a display is connected” (practical pattern) Important note up front: a visible menu bar icon requires a running process — you cannot magically show a UI element with *zero* process memory. But you **can** make that resident process extremely tiny and *event-driven*, and only start the heavy work when a display is present. Below is a safe, practical pattern used by macOS apps. ### Minimal strategy (implementation steps) 1. Create a tiny **agent** (LSBackgroundOnly or small GUIless status item) that: * Registers a display reconfiguration callback (CoreGraphics `CGDisplayRegisterReconfigurationCallback`) to get add/remove events. ([[Apple Developer](https://developer.apple.com/documentation/coregraphics/cgdisplayregisterreconfigurationcallback%28_%3A_%3A%29?utm_source=chatgpt.com)][1]) * Does *not* open IOKit/DDC or start polling threads. * Shows a simple status icon/menu and “No external displays” caption when nothing is connected. 2. When the callback indicates a compatible display is present, the agent spawns the **worker** (Process or XPC service) which loads the DDC / IOKit code and UI. 3. Worker does DDC/CI via IOKit APIs like `IOI2CSendRequest` (DDC over I2C) or platform-specific helpers (note: some connections/hubs block DDC — fall back to software dim). ([[Stack Overflow](https://stackoverflow.com/questions/11561623/i2c-communication-in-iokit-on-mac-os-x?utm_source=chatgpt.com)][7]) 4. When displays disconnect, the worker gracefully exits — agent returns to idle. ### Example Swift-ish sketch (display callback + spawn) ```swift import Cocoa import CoreGraphics // tiny agent app func displayReconfigCallback(_ display: CGDirectDisplayID, _ flags: CGDisplayChangeSummaryFlags, _ userInfo: UnsafeMutableRawPointer?) { // check flags for add/remove/online if flags.contains(.addFlag) || flags.contains(.beginConfigurationFlag) { spawnWorkerIfNeeded() } if flags.contains(.removeFlag) { checkAndStopWorkerIfNoDisplays() } } func startAgent() { // register callback (Apple doc API) CGDisplayRegisterReconfigurationCallback(displayReconfigCallback, nil) // create tiny NSStatusItem and run runloop — do NOT load heavy libs } // spawn a full worker process (or connect to an XPC service) func spawnWorkerIfNeeded() { if workerIsRunning { return } let worker = Process() worker.executableURL = URL(fileURLWithPath: "/Library/Application Support/MonitorControl/monitor-worker") try? worker.run() } ``` (Cite Apple docs for the callback API.) ([[Apple Developer](https://developer.apple.com/documentation/coregraphics/cgdisplayregisterreconfigurationcallback%28_%3A_%3A%29?utm_source=chatgpt.com)][1]) ### Why this is low-cost * The agent only responds to OS events; no polling loops => near-zero CPU when idle. * Memory: a tiny agent process will still consume a few MB of RAM, but that’s much smaller than keeping the whole feature set resident. This is how you get *practically* zero impact for end users. --- # DDC/CI and platform notes (real-world quirks) * Many monitors support DDC/CI over the display link, but certain Mac HDMI ports, DisplayLink docks, or some USB-C adapters block DDC — in those cases you must fall back to **software dimming** (i.e., gamma tables) rather than DDC. Make this explicit in the UI and compatibility DB. ([[Ask Different](https://apple.stackexchange.com/questions/453496/how-do-use-ddc-ci-to-change-input-to-my-mac-when-a-usb-device-is-connected-to-it?utm_source=chatgpt.com)][8]) * On Apple Silicon some low-level DDC tools needed changes (there are community efforts like `m1ddc`), so test on M1/M2 and document any known limitations. ([[Ask Different](https://apple.stackexchange.com/questions/453496/how-do-use-ddc-ci-to-change-input-to-my-mac-when-a-usb-device-is-connected-to-it?utm_source=chatgpt.com)][8]) --- # Roadmap (prioritized) 1. **Quick wins (weeks):** * Replace any polling with CGDisplay callbacks; implement two-process spawn/kill pattern. (High ROI: immediate battery/CPU wins.) ([[Apple Developer](https://developer.apple.com/documentation/coregraphics/cgdisplayregisterreconfigurationcallback%28_%3A_%3A%29?utm_source=chatgpt.com)][1]) * Add “software dim” fallback and show compatibility in the UI. 2. **Mid (1–3 months):** * Add presets, per-display profiles, hotkeys and save/export profiles. * Build a simple compatibility DB + “Report issue” flow. 3. **Bigger (3–6 months):** * Ambient sensor sync + per-app/per-time automation ([Lunar](https://lunar.fyi/?utm_source=chatgpt.com) parity). ([Lunar][2]) * HDR/XDR/HiDPI integrations or clear integration docs with BetterDisplay features. ([[GitHub](https://github.com/waydabber/BetterDisplay?utm_source=chatgpt.com)][6]) 4. **Business & trust (ongoing):** * Signed, notarized releases, Homebrew cask, paid Pro channel or donor model, clearer docs and screenshots for trust (MacUpdate/Softpedia users value that). ([[monitorcontrol.macupdate.com](https://monitorcontrol.macupdate.com/?utm_source=chatgpt.com)][3]) --- # Final note (realistic expectations) * You **can’t** show a menu icon with literally zero memory because something must run to draw it. The practical and user-visible goal is to make that resident process *tiny and event-driven*, and only run heavyweight code while a compatible external display is connected — this is straightforward and will produce a dramatic reduction in CPU/RAM when the user isn’t using monitors. The CoreGraphics display callback + on-demand worker is the cleanest pattern. ([[Apple Developer](https://developer.apple.com/documentation/coregraphics/cgdisplayregisterreconfigurationcallback%28_%3A_%3A%29?utm_source=chatgpt.com)][1]) --- [1]: https://developer.apple.com/documentation/coregraphics/cgdisplayregisterreconfigurationcallback%28_%3A_%3A%29?utm_source=chatgpt.com "CGDisplayRegisterReconfigurati..." [2]: https://lunar.fyi/?utm_source=chatgpt.com "Lunar - The defacto app for controlling monitor brightness" [3]: https://monitorcontrol.macupdate.com/?utm_source=chatgpt.com "Download MonitorControl for Mac | MacUpdate" [4]: https://github.com/MonitorControl/MonitorControl?utm_source=chatgpt.com "Control your display's brightness & volume ..." [5]: https://displaybuddy.app/?utm_source=chatgpt.com "DisplayBuddy - Control monitor brightness directly from your ..." [6]: https://github.com/waydabber/BetterDisplay?utm_source=chatgpt.com "waydabber/BetterDisplay" [7]: https://stackoverflow.com/questions/11561623/i2c-communication-in-iokit-on-mac-os-x?utm_source=chatgpt.com "i2c communication in IOKit on Mac OS X" [8]: https://apple.stackexchange.com/questions/453496/how-do-use-ddc-ci-to-change-input-to-my-mac-when-a-usb-device-is-connected-to-it?utm_source=chatgpt.com "How do use DDC/CI to change input to my Mac when a ..."
Author
Owner

@amelnikoff commented on GitHub (Sep 23, 2025):

Ex Lunar user here:

Add the high-value features people pay for in competitors: ambient-sensor / adaptive brightness sync, per-display presets & input switching, HDR/XDR and HiDPI/virtual-display support, and robust per-monitor compatibility quirks database. (These are what Lunar, BetterDisplay and DisplayBuddy emphasize.) ([Lunar]2)

I switched off it almost immediately because it's hard to configure same brightness levels on Macbook with non Apple monitor. You always see diffenet ambience, colors.
Much easier to calibrate once color profiles between monitors, and use after hotkeys to adjust brightness.

<!-- gh-comment-id:3322619045 --> @amelnikoff commented on GitHub (Sep 23, 2025): Ex Lunar user here: > Add the high-value features people pay for in competitors: ambient-sensor / adaptive brightness sync, per-display presets & input switching, HDR/XDR and HiDPI/virtual-display support, and robust per-monitor compatibility quirks database. (These are what Lunar, BetterDisplay and DisplayBuddy emphasize.) ([[Lunar](https://lunar.fyi/?utm_source=chatgpt.com)][2](https://lunar.fyi/?utm_source=chatgpt.com)) I switched off it almost immediately because it's hard to configure same brightness levels on Macbook with non Apple monitor. You always see diffenet ambience, colors. Much easier to calibrate once color profiles between monitors, and use after hotkeys to adjust brightness.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: github-starred/MonitorControl#994
No description provided.