{"id":50998753,"url":"https://github.com/gingerbeardman/didact","last_synced_at":"2026-06-20T12:03:04.822Z","repository":{"id":365337259,"uuid":"1268735875","full_name":"gingerbeardman/Didact","owner":"gingerbeardman","description":"Control your BenQ RD280UG from the command line","archived":false,"fork":false,"pushed_at":"2026-06-16T22:31:55.000Z","size":277,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-17T00:19:01.253Z","etag":null,"topics":["benq","ddc","display-control","macos-app","menu-bar-app","rd280"],"latest_commit_sha":null,"homepage":"https://www.gingerbeardman.com/apps/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gingerbeardman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-13T21:58:09.000Z","updated_at":"2026-06-16T22:31:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gingerbeardman/Didact","commit_stats":null,"previous_names":["gingerbeardman/btnq"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/gingerbeardman/Didact","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gingerbeardman%2FDidact","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gingerbeardman%2FDidact/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gingerbeardman%2FDidact/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gingerbeardman%2FDidact/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gingerbeardman","download_url":"https://codeload.github.com/gingerbeardman/Didact/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gingerbeardman%2FDidact/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34568743,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-20T02:00:06.407Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["benq","ddc","display-control","macos-app","menu-bar-app","rd280"],"created_at":"2026-06-20T12:03:01.025Z","updated_at":"2026-06-20T12:03:04.813Z","avatar_url":"https://github.com/gingerbeardman.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Didact (previously BtnQ)\n\nA tiny macOS menu-bar app to control an external monitor over DDC/CI — no settings\nwindow, no ~1 GB control panel, just standard macOS menus.\n\nInitially built for the **BenQ RD280UG** (which it ships with a profile for),\nDidact now works toward supporting any DDC/CI monitor: a built-in wizard detects\nthe standard controls and learns the rest, and every monitor-specific detail lives\nin a shareable JSON profile — so new monitors need **no code changes**.\n\n\u003cimg width=\"472\" height=\"536\" alt=\"Didact Light Mode\" src=\"https://github.com/user-attachments/assets/6e3e6b7a-fd4c-4bd3-bb48-cf05da2d98e3\" /\u003e \u003cimg width=\"472\" height=\"536\" alt=\"Didact Dark Mode\" src=\"https://github.com/user-attachments/assets/6df68a1a-b997-4fb8-a16e-4915e1dd0b81\" /\u003e\n\n## Didact vs BenQ Display Pilot 2\n\n|           | Didact       | Display Pilot 2 |\n|-----------|:----------:|:---------------:|\n| Download  | **517 KB** | 404 MB          |\n| Installed | **578 KB** | 936 MB          |\n\n…about **800× smaller to download** and **over 1,600× smaller installed**, for the\ncontrols you actually use.\n\n## Requirements\n\n- Apple Silicon Mac (DDC is done via `IOAVService`, Apple-Silicon only).\n- A monitor connected over DisplayPort / USB-C / HDMI with DDC/CI enabled.\n- macOS 13.0 or later.\n\n## How it works\n\n- **DDC transport** is a vendored copy of [AppleSiliconDDC](https://github.com/waydabber/AppleSiliconDDC)\n  (MIT). The private CoreDisplay/IOKit symbols it needs are declared in\n  `AppleSiliconDDCBridge.swift` via `@_silgen_name`, so the whole library is just\n  two Swift files — no bridging header, no SPM dependency. `CoreDisplay.framework`\n  is linked via `OTHER_LDFLAGS = -framework CoreDisplay`.\n- **The app is not sandboxed** (`ENABLE_APP_SANDBOX = NO`): DDC needs raw IOKit\n  access, which the App Sandbox forbids.\n- The DDC/VCP map was ported from [bebenqli](https://github.com/iurev/bebenqli),\n  a Linux TUI for the same panel.\n\n## Adding support for another monitor\n\nDrop a JSON file into either:\n\n- the app bundle (`Didact/Monitors/`), or\n- `~/Library/Application Support/Didact/Monitors/` (no rebuild — use\n  **Didact ▸ Open Monitors Folder**, then **Reload Configs**).\n\n### Config format\n\n```jsonc\n{\n  \"name\": \"BenQ RD280UG\",          // shown in the menu\n  \"match\": [\"RD280U\", \"RD280UG\"],  // case-insensitive substrings of the display's product name\n  \"controls\": [\n    { \"kind\": \"group\",   \"label\": \"Image\" },\n    { \"kind\": \"section\", \"label\": \"Night Protection\" },\n\n    // Slider. min/max/step are decimal.\n    { \"kind\": \"range\", \"label\": \"Brightness\", \"vcp\": \"10\", \"min\": 0, \"max\": 100 },\n\n    // Pick-one. Each option value mirrors the DDC docs (hex string) or a decimal number.\n    { \"kind\": \"cycle\", \"label\": \"Source\", \"vcp\": \"60\",\n      \"options\": [\n        { \"value\": \"0x0f\", \"label\": \"DisplayPort\" },\n        { \"value\": \"0x11\", \"label\": \"HDMI\" },\n        { \"value\": \"0x13\", \"label\": \"USB-C\" }\n      ] },\n\n    // On/off.\n    { \"kind\": \"toggle\", \"label\": \"Auto Brightness\", \"vcp\": \"e2\", \"onValue\": 255, \"offValue\": 0 }\n  ]\n}\n```\n\n**Field reference**\n\n| Field | Applies to | Meaning |\n|-------|-----------|---------|\n| `kind` | all | `group`, `section`, `range`, `cycle`, or `toggle` |\n| `label` | all | Menu text |\n| `vcp` | range/cycle/toggle | VCP feature code, **hexadecimal** (e.g. `\"60\"`, `\"d9\"`) |\n| `min`/`max`/`step` | range | Slider bounds (decimal); `step` defaults to 1 |\n| `options` | cycle | List of `{ value, label }` |\n| `onValue`/`offValue` | toggle | Values written for on/off |\n| `channel` | range/cycle/toggle | High byte **written** for 16-bit multiplexed registers (e.g. Moon Halo on `d9`) |\n| `readChannels` | range/cycle/toggle | High byte(s) that identify this control's value on **read** (a multiplexed read returns only the last-touched channel). Defaults to `channel`. |\n| `noRead` | any control | Value can't be read back; Didact remembers the last value you set |\n| `noVerify` | any control | Monitor reports a bogus value after a write |\n\n**Values**: a JSON **number** is decimal; a JSON **string** is hexadecimal\n(`\"0x30\"` or `\"30\"`). `vcp` is always hex.\n\n**Multiplexed registers**: some BenQ features share one VCP code, selected by the\nhigh byte. Moon Halo brightness and colour temperature both live on `d9`\n(`channel: \"0x01\"` and `channel: \"0x07\"`); Didact writes `(channel \u003c\u003c 8) | value`.\n\n## Debugging — dump DDC values\n\n`Tools/dump.sh` compiles a small CLI from the app's own DDC code and prints the\ncurrent value of every candidate VCP code, decoded against the config:\n\n```\n./Tools/dump.sh                       # uses Didact/Monitors/BenQ-RD280UG.json\n./Tools/dump.sh path/to/other.json    # decode against another config\n```\n\n## Thanks\n\nDidact is built directly on these two projects:\n\n- **[AppleSiliconDDC](https://github.com/waydabber/AppleSiliconDDC)** by [@waydabber](https://github.com/waydabber) (MIT) — the DDC/CI transport, vendored into Didact.\n- **[bebenqli](https://github.com/iurev/bebenqli)** by [@iurev](https://github.com/iurev) (MIT) — the BenQ RD280UG DDC/VCP map and the baseline-sweep discovery behind Listen mode.\n\n## License\n\nDidact is released under the [MIT License](LICENSE). Both vendored/ported\ncomponents above are also MIT-licensed; their notices are reproduced in the\n[`LICENSE`](LICENSE) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgingerbeardman%2Fdidact","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgingerbeardman%2Fdidact","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgingerbeardman%2Fdidact/lists"}