brightctrl 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +135 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12,13 +12,28 @@ import { useCallback, useEffect, useRef, useState } from "react";
12
12
  import { execFile } from "node:child_process";
13
13
  import { promisify } from "node:util";
14
14
  var execFileAsync = promisify(execFile);
15
+ var isWindows = process.platform === "win32";
16
+ var isMac = process.platform === "darwin";
15
17
  function ddcutil(args, timeout = 8000) {
16
18
  return execFileAsync("ddcutil", args, {
17
19
  timeout,
18
20
  encoding: "utf-8"
19
21
  }).then((r) => r.stdout);
20
22
  }
23
+ function powerShell(script, timeout = 8000) {
24
+ return execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], { timeout, encoding: "utf-8" }).then((r) => r.stdout.trim());
25
+ }
21
26
  async function checkDdcutil() {
27
+ if (isWindows) {
28
+ try {
29
+ const out = await powerShell("Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightness | ConvertTo-Json -Depth 3", 5000);
30
+ return out.length > 0 && out !== "null";
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+ if (isMac)
36
+ return false;
22
37
  try {
23
38
  await execFileAsync("which", ["ddcutil"]);
24
39
  return true;
@@ -26,7 +41,7 @@ async function checkDdcutil() {
26
41
  return false;
27
42
  }
28
43
  }
29
- async function detectMonitors() {
44
+ async function detectMonitorsLinux() {
30
45
  const stdout = await ddcutil(["detect", "--brief"], 15000);
31
46
  const monitors = [];
32
47
  let current = null;
@@ -37,8 +52,9 @@ async function detectMonitors() {
37
52
  if (current?.index != null)
38
53
  monitors.push(current);
39
54
  const m = t.match(/Display\s+(\d+)/);
55
+ const idxStr = m?.[1];
40
56
  current = {
41
- index: m ? Number.parseInt(m[1], 10) : monitors.length + 1,
57
+ index: idxStr ? Number.parseInt(idxStr, 10) : monitors.length + 1,
42
58
  name: "Unknown Monitor",
43
59
  bus: ""
44
60
  };
@@ -56,16 +72,64 @@ async function detectMonitors() {
56
72
  monitors.push(current);
57
73
  return monitors;
58
74
  }
59
- async function getBrightness(displayIndex) {
75
+ async function detectMonitorsWindows() {
76
+ const ps = `$b=Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightness; $r=@(); $i=0; foreach($m in $b){$i++; $r+=[PSCustomObject]@{index=$i;name=[string]($m.InstanceName -replace '.*\\\\([^\\\\]+)\\\\.*','$1');bus=$m.InstanceName}}; $r | ConvertTo-Json -Depth 3`;
77
+ const stdout = await powerShell(ps, 1e4);
78
+ if (!stdout || stdout === "null")
79
+ return [];
80
+ const data = JSON.parse(stdout);
81
+ const arr = Array.isArray(data) ? data : [data];
82
+ return arr.map((d, i) => ({
83
+ index: i + 1,
84
+ name: typeof d.name === "string" && d.name ? d.name : "Monitor",
85
+ bus: typeof d.bus === "string" ? d.bus : ""
86
+ }));
87
+ }
88
+ async function detectMonitorsMac() {
89
+ return [];
90
+ }
91
+ async function getBrightnessMac(_displayIndex) {
92
+ return null;
93
+ }
94
+ async function setBrightnessMac(_displayIndex, _value) {
95
+ return false;
96
+ }
97
+ async function detectMonitors() {
98
+ if (isWindows)
99
+ return detectMonitorsWindows();
100
+ if (isMac)
101
+ return detectMonitorsMac();
102
+ return detectMonitorsLinux();
103
+ }
104
+ async function getBrightnessLinux(displayIndex) {
60
105
  try {
61
106
  const stdout = await ddcutil(["getvcp", "10", `--display=${displayIndex}`], 5000);
62
107
  const m = stdout.match(/current value\s*=\s*(\d+)/);
63
- return m ? Number.parseInt(m[1], 10) : null;
108
+ const val = m?.[1];
109
+ return val ? Number.parseInt(val, 10) : null;
64
110
  } catch {
65
111
  return null;
66
112
  }
67
113
  }
68
- async function setBrightness(displayIndex, value) {
114
+ async function getBrightnessWindows(displayIndex) {
115
+ try {
116
+ const idx = Math.max(0, displayIndex - 1);
117
+ const ps = `$b=Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightness; if($b -and $b.Count -ge ${idx + 1}){$b[${idx}].CurrentBrightness}else{-1}`;
118
+ const stdout = await powerShell(ps, 5000);
119
+ const v = Number.parseInt(stdout, 10);
120
+ return Number.isNaN(v) || v < 0 ? null : v;
121
+ } catch {
122
+ return null;
123
+ }
124
+ }
125
+ async function getBrightness(displayIndex) {
126
+ if (isWindows)
127
+ return getBrightnessWindows(displayIndex);
128
+ if (isMac)
129
+ return getBrightnessMac(displayIndex);
130
+ return getBrightnessLinux(displayIndex);
131
+ }
132
+ async function setBrightnessLinux(displayIndex, value) {
69
133
  try {
70
134
  await ddcutil(["setvcp", "10", String(Math.round(value)), `--display=${displayIndex}`], 5000);
71
135
  return true;
@@ -73,9 +137,26 @@ async function setBrightness(displayIndex, value) {
73
137
  return false;
74
138
  }
75
139
  }
140
+ async function setBrightnessWindows(displayIndex, value) {
141
+ try {
142
+ const idx = Math.max(0, displayIndex - 1);
143
+ const ps = `$m=Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightnessMethods; if($m -and $m.Count -ge ${idx + 1}){$m[${idx}].WmiSetBrightness(1,${Math.round(value)})}`;
144
+ await powerShell(ps, 5000);
145
+ return true;
146
+ } catch {
147
+ return false;
148
+ }
149
+ }
150
+ async function setBrightness(displayIndex, value) {
151
+ if (isWindows)
152
+ return setBrightnessWindows(displayIndex, value);
153
+ if (isMac)
154
+ return setBrightnessMac(displayIndex, value);
155
+ return setBrightnessLinux(displayIndex, value);
156
+ }
76
157
 
77
158
  // src/app.tsx
78
- import { jsxDEV } from "react/jsx-dev-runtime";
159
+ import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
79
160
  function BrightnessBar({
80
161
  value,
81
162
  width = 25
@@ -152,6 +233,9 @@ function MonitorCard({
152
233
  }, undefined, true, undefined, this);
153
234
  }
154
235
  function ErrorPanel() {
236
+ const plat = process.platform;
237
+ if (plat === "darwin")
238
+ return null;
155
239
  return /* @__PURE__ */ jsxDEV(Box, {
156
240
  flexDirection: "column",
157
241
  marginTop: 1,
@@ -160,26 +244,41 @@ function ErrorPanel() {
160
244
  color: "yellow",
161
245
  children: "Troubleshooting:"
162
246
  }, undefined, false, undefined, this),
163
- /* @__PURE__ */ jsxDEV(Text, {
164
- color: "gray",
165
- children: " sudo pacman -S ddcutil"
166
- }, undefined, false, undefined, this),
167
- /* @__PURE__ */ jsxDEV(Text, {
168
- color: "gray",
169
- children: " sudo usermod -aG i2c $USER"
170
- }, undefined, false, undefined, this),
171
- /* @__PURE__ */ jsxDEV(Text, {
172
- color: "gray",
173
- children: " sudo modprobe i2c-dev"
174
- }, undefined, false, undefined, this),
175
- /* @__PURE__ */ jsxDEV(Text, {
176
- color: "gray",
177
- children: "echo 'i2c-dev' | sudo tee /etc/modules-load.d/i2c.conf"
178
- }, undefined, false, undefined, this),
179
- /* @__PURE__ */ jsxDEV(Text, {
180
- color: "gray",
181
- children: " (log out and in for group change)"
182
- }, undefined, false, undefined, this)
247
+ plat === "win32" ? /* @__PURE__ */ jsxDEV(Fragment, {
248
+ children: [
249
+ /* @__PURE__ */ jsxDEV(Text, {
250
+ color: "gray",
251
+ children: "Some GPUs/monitors don't expose brightness via WMI."
252
+ }, undefined, false, undefined, this),
253
+ /* @__PURE__ */ jsxDEV(Text, {
254
+ color: "gray",
255
+ children: "Try installing MonitorController from Windows Store."
256
+ }, undefined, false, undefined, this)
257
+ ]
258
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV(Fragment, {
259
+ children: [
260
+ /* @__PURE__ */ jsxDEV(Text, {
261
+ color: "gray",
262
+ children: " sudo pacman -S ddcutil"
263
+ }, undefined, false, undefined, this),
264
+ /* @__PURE__ */ jsxDEV(Text, {
265
+ color: "gray",
266
+ children: " sudo usermod -aG i2c $USER"
267
+ }, undefined, false, undefined, this),
268
+ /* @__PURE__ */ jsxDEV(Text, {
269
+ color: "gray",
270
+ children: " sudo modprobe i2c-dev"
271
+ }, undefined, false, undefined, this),
272
+ /* @__PURE__ */ jsxDEV(Text, {
273
+ color: "gray",
274
+ children: "echo 'i2c-dev' | sudo tee /etc/modules-load.d/i2c.conf"
275
+ }, undefined, false, undefined, this),
276
+ /* @__PURE__ */ jsxDEV(Text, {
277
+ color: "gray",
278
+ children: " (log out and in for group change)"
279
+ }, undefined, false, undefined, this)
280
+ ]
281
+ }, undefined, true, undefined, this)
183
282
  ]
184
283
  }, undefined, true, undefined, this);
185
284
  }
@@ -211,10 +310,16 @@ function App() {
211
310
  setLoading(true);
212
311
  setError(null);
213
312
  setStatus("Checking ddcutil...");
214
- const hasDdcutil = await checkDdcutil();
215
- if (!hasDdcutil) {
216
- setError("ddcutil not found. Install: sudo pacman -S ddcutil");
217
- setStatus("ddcutil missing");
313
+ const hasBackend = await checkDdcutil();
314
+ if (!hasBackend) {
315
+ if (process.platform === "win32") {
316
+ setError("No monitors with WMI brightness control found");
317
+ } else if (process.platform === "darwin") {
318
+ setError("macOS is not supported at this moment");
319
+ } else {
320
+ setError("ddcutil not found. Install: sudo pacman -S ddcutil");
321
+ }
322
+ setStatus("backend unavailable");
218
323
  setLoading(false);
219
324
  return;
220
325
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brightctrl",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Terminal UI for DDC/CI monitor brightness control",
5
5
  "type": "module",
6
6
  "bin": {