Skip to content

Commit aca45f4

Browse files
committed
fix(Backlight): add support for 'brightnessctl'
1 parent 941a7be commit aca45f4

File tree

1 file changed

+246
-78
lines changed

1 file changed

+246
-78
lines changed

core/global/display_manager.gd

Lines changed: 246 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,16 @@ enum VALUE {
1010
RELATIVE,
1111
}
1212

13-
enum BRIGHTNESS_PROVIDER {
14-
NONE,
15-
STEAMOS,
16-
}
17-
1813
signal brightness_changed
1914

20-
const backlight_path := "/sys/class/backlight"
21-
const steamos_write_bin := "/usr/bin/steamos-polkit-helpers/steamos-priv-write"
22-
2315
var logger := Log.get_logger("DisplayManager", Log.LEVEL.INFO)
16+
var brightness_provider := _get_backlight_provider()
2417
var backlights := get_backlight_paths()
25-
var brightness_provider := _get_brightness_provider()
2618

2719

2820
## Returns true if OpenGamepadUI has access to adjust brightness
2921
func supports_brightness() -> bool:
30-
brightness_provider = _get_brightness_provider()
31-
if brightness_provider == BRIGHTNESS_PROVIDER.NONE:
22+
if brightness_provider == null:
3223
logger.debug("No brightness setter found")
3324
return false
3425
backlights = get_backlight_paths()
@@ -40,97 +31,274 @@ func supports_brightness() -> bool:
4031

4132
## Sets the brightness on all discovered backlights to the given value as a
4233
## percentage (e.g. 1.0 is 100% brightness)
43-
func set_brightness(value: float, type: VALUE = VALUE.ABSOLUTE) -> int:
34+
func set_brightness(value: float, type: VALUE = VALUE.ABSOLUTE, backlight: String = "") -> int:
4435
if not supports_brightness():
4536
logger.debug("Failed to set brightness, no providers found")
4637
return FAILED
47-
value = minf(value, 1.0)
48-
value = maxf(value, 0.0)
49-
backlights = get_backlight_paths()
50-
51-
# Set the brightness for all backlights
52-
for backlight_path in backlights:
53-
# Calculate the value based on whether this is an absolute or relative change
54-
var abs_value := value
55-
if type == VALUE.RELATIVE:
56-
abs_value = get_brightness(backlight_path) + value
57-
abs_value = minf(abs_value, 1.0)
58-
abs_value = maxf(abs_value, 0.0)
59-
var real_value := int(abs_value * get_max_brightness_value(backlight_path))
60-
61-
# Use the preferred method of setting brightness
62-
var brightness_file := "/".join([backlight_path, "brightness"])
63-
logger.debug("Setting brightness on " + brightness_file + " to " + str(real_value))
64-
if brightness_provider == BRIGHTNESS_PROVIDER.STEAMOS:
65-
if _steamos_priv_write(brightness_file, real_value) != OK:
66-
return FAILED
67-
continue
68-
69-
return FAILED
70-
71-
brightness_changed.emit()
72-
73-
return OK
38+
39+
var err := brightness_provider.set_brightness(value, type, backlight)
40+
if err == OK:
41+
brightness_changed.emit()
42+
43+
return err
7444

7545

7646
## Returns the current brightness level for the given backlight as a percent
7747
func get_brightness(backlight_path: String) -> float:
78-
var cur_brightness := get_brightness_value(backlight_path)
79-
var max_brightness := get_max_brightness_value(backlight_path)
80-
if cur_brightness == -1 or max_brightness == -1:
81-
return -1
82-
return float(cur_brightness) / max_brightness
48+
if brightness_provider == null:
49+
return -1.0
50+
return brightness_provider.get_brightness(backlight_path)
8351

8452

8553
## Returns the current brightness value for the given backlight
8654
func get_brightness_value(backlight_path: String) -> int:
87-
var brightness_file := "/".join([backlight_path, "brightness"])
88-
var output := []
89-
var code := OS.execute("cat", [brightness_file], output)
90-
if code != OK:
91-
logger.debug("Unable to get current brightness: " + output[0])
55+
if brightness_provider == null:
9256
return -1
93-
var value := (output[0] as String).strip_edges()
94-
if value.is_valid_int():
95-
return value.to_int()
96-
logger.debug("Brightness is not a valid number: " + value)
97-
return -1
57+
return brightness_provider.get_brightness_value(backlight_path)
9858

9959

10060
## Returns the maximum brightness for the given backlight
10161
func get_max_brightness_value(backlight_path: String) -> int:
102-
var max_brightness_file := "/".join([backlight_path, "max_brightness"])
103-
var output := []
104-
var code := OS.execute("cat", [max_brightness_file], output)
105-
if code != OK:
106-
logger.debug("Unable to get max brightness: " + output[0])
62+
if brightness_provider == null:
10763
return -1
108-
var value := (output[0] as String).strip_edges()
109-
if value.is_valid_int():
110-
return value.to_int()
111-
logger.debug("Max brightness is not a valid number: " + value)
112-
return -1
64+
return brightness_provider.get_max_brightness_value(backlight_path)
11365

11466

11567
## Returns a list of all detected backlight devices
11668
func get_backlight_paths() -> PackedStringArray:
117-
var backlights := PackedStringArray()
118-
var backlight_dir := DirAccess.open(backlight_path)
119-
var devices := backlight_dir.get_directories()
120-
for device in devices:
121-
backlights.append("/".join([backlight_path, device]))
122-
logger.debug("Found backlights: " + str(backlights))
123-
return backlights
69+
if brightness_provider == null:
70+
return PackedStringArray()
71+
return brightness_provider.get_backlights()
12472

12573

126-
func _get_brightness_provider() -> BRIGHTNESS_PROVIDER:
127-
if FileAccess.file_exists(steamos_write_bin):
74+
func _get_backlight_provider() -> BacklightProvider:
75+
if OS.execute("which", ["brightnessctl"]) == OK:
76+
logger.debug("Using brightnessctl backlight provider")
77+
return BrightnessctlBacklight.new()
78+
if FileAccess.file_exists("/usr/bin/steamos-polkit-helpers/steamos-priv-write"):
12879
logger.debug("Using SteamOS backlight writer")
129-
return BRIGHTNESS_PROVIDER.STEAMOS
80+
return SteamOsBacklight.new()
13081
logger.debug("No backlight writer found")
131-
return BRIGHTNESS_PROVIDER.NONE
82+
return null
83+
84+
85+
## Interface for controlling backlights (e.g. screen brightness)
86+
class BacklightProvider:
87+
## Returns all available backlights
88+
func get_backlights() -> PackedStringArray:
89+
return PackedStringArray()
90+
91+
## Returns the maximum raw brightness value of the given backlight. If
92+
## No backlight is passed, then this should return the value for the "main"
93+
## display. Returns -1 if there is an error fetching the value.
94+
func get_max_brightness_value(_backlight: String = "") -> int:
95+
return -1
96+
97+
## Returns the current raw brightness value of the given backlight. If no
98+
## backlight is passed, then this should return the value for the "main"
99+
## display. Returns -1 if there is an error fetching the value.
100+
func get_brightness_value(_backlight: String = "") -> int:
101+
return -1
102+
103+
## Returns the current brightness level for the given backlight as a percent.
104+
## Returns -1 if there is an error fetching the value
105+
func get_brightness(_backlight: String = "") -> float:
106+
return -1.0
107+
108+
## Sets the brightness for the given backlight to the given value as a
109+
## percentage (e.g. 1.0 is 100% brightness). If no backlight is specified,
110+
## this should set the value on _all_ discovered backlights. Returns OK
111+
## if set successfully.
112+
func set_brightness(_value: float, _type: VALUE = VALUE.ABSOLUTE, _backlight: String = "") -> int:
113+
return ERR_METHOD_NOT_FOUND
114+
115+
116+
## SteamOS implementation of backlight control
117+
class SteamOsBacklight extends BacklightProvider:
118+
const backlight_path_base := "/sys/class/backlight"
119+
const steamos_write_bin := "/usr/bin/steamos-polkit-helpers/steamos-priv-write"
120+
var logger := Log.get_logger("SteamOsBacklight", Log.LEVEL.INFO)
121+
122+
func get_backlights() -> PackedStringArray:
123+
var backlights := PackedStringArray()
124+
var backlight_dir := DirAccess.open(backlight_path_base)
125+
var devices := backlight_dir.get_directories()
126+
for device in devices:
127+
backlights.append("/".join([backlight_path_base, device]))
128+
self.logger.debug("Found backlights: " + str(backlights))
129+
return backlights
130+
131+
func set_brightness(value: float, type: VALUE = VALUE.ABSOLUTE, backlight: String = "") -> int:
132+
value = minf(value, 1.0)
133+
value = maxf(value, 0.0)
134+
var backlights := PackedStringArray()
135+
if backlight.is_empty():
136+
backlights = self.get_backlights()
137+
else:
138+
backlights.push_back(backlight)
139+
140+
# Set the brightness for all backlights
141+
for backlight_path in backlights:
142+
# Calculate the value based on whether this is an absolute or relative change
143+
var abs_value := value
144+
if type == VALUE.RELATIVE:
145+
abs_value = self.get_brightness(backlight_path) + value
146+
abs_value = minf(abs_value, 1.0)
147+
abs_value = maxf(abs_value, 0.0)
148+
var real_value := int(abs_value * self.get_max_brightness_value(backlight_path))
149+
150+
# Use the preferred method of setting brightness
151+
var brightness_file := "/".join([backlight_path, "brightness"])
152+
self.logger.debug("Setting brightness on " + brightness_file + " to " + str(real_value))
153+
if self._steamos_priv_write(brightness_file, real_value) != OK:
154+
return FAILED
155+
156+
return OK
157+
158+
func get_brightness(backlight_path: String = "") -> float:
159+
var cur_brightness := self.get_brightness_value(backlight_path)
160+
var max_brightness := self.get_max_brightness_value(backlight_path)
161+
if cur_brightness == -1 or max_brightness == -1:
162+
return -1
163+
return float(cur_brightness) / max_brightness
164+
165+
func get_brightness_value(backlight_path: String = "") -> int:
166+
var brightness_file := "/".join([backlight_path, "brightness"])
167+
var output := []
168+
var code := OS.execute("cat", [brightness_file], output)
169+
if code != OK:
170+
logger.debug("Unable to get current brightness: " + output[0])
171+
return -1
172+
var value := (output[0] as String).strip_edges()
173+
if value.is_valid_int():
174+
return value.to_int()
175+
self.logger.debug("Brightness is not a valid number: " + value)
176+
return -1
177+
178+
func get_max_brightness_value(backlight_path: String = "") -> int:
179+
var max_brightness_file := "/".join([backlight_path, "max_brightness"])
180+
var output := []
181+
var code := OS.execute("cat", [max_brightness_file], output)
182+
if code != OK:
183+
self.logger.debug("Unable to get max brightness: " + output[0])
184+
return -1
185+
var value := (output[0] as String).strip_edges()
186+
if value.is_valid_int():
187+
return value.to_int()
188+
self.logger.debug("Max brightness is not a valid number: " + value)
189+
return -1
190+
191+
## Write a value using steamos polkit helper
192+
func _steamos_priv_write(path: String, value: int) -> int:
193+
return OS.execute(steamos_write_bin, [path, str(value)])
194+
195+
196+
## Brightnessctl implementation of backlight control
197+
class BrightnessctlBacklight extends BacklightProvider:
198+
var logger := Log.get_logger("BrightnessctlBacklight", Log.LEVEL.INFO)
199+
200+
func _exec(args: Array[String]) -> Command:
201+
self.logger.debug("Executing command:", "brightnessctl", args)
202+
var cmd := Command.create("brightnessctl", args)
203+
cmd.execute_blocking()
204+
self.logger.debug("Output:", cmd.stdout, cmd.stderr)
205+
return cmd
206+
207+
func _get_info(backlight: String = "") -> PackedStringArray:
208+
var args: Array[String] = ["--machine", "--class", "backlight", "info"]
209+
if not backlight.is_empty():
210+
args.push_front(backlight)
211+
args.push_front("--device")
212+
var cmd := _exec(args)
213+
if cmd.code != OK:
214+
return PackedStringArray()
215+
216+
var lines := cmd.stdout.split("\n", false)
217+
if lines.is_empty():
218+
return PackedStringArray()
219+
220+
# Example Output: amdgpu_bl1,backlight,128,50%,255
221+
var parts := lines[0].split(",", false)
222+
return parts
223+
224+
func get_backlights() -> PackedStringArray:
225+
# brightnessctl -l -c backlight -m
226+
# Output: amdgpu_bl1,backlight,128,50%,255
227+
var backlights := PackedStringArray()
228+
var cmd := self._exec(["--list", "--class", "backlight", "--machine"])
229+
if cmd.code != OK:
230+
return backlights
231+
232+
var lines := cmd.stdout.split("\n", false)
233+
for line in lines:
234+
var parts := line.split(",")
235+
if parts.is_empty():
236+
continue
237+
backlights.push_back(parts[0])
238+
239+
self.logger.debug("Found backlights: " + str(backlights))
240+
return backlights
241+
242+
func set_brightness(value: float, type: VALUE = VALUE.ABSOLUTE, backlight: String = "") -> int:
243+
value = minf(value, 1.0)
244+
value = maxf(value, -1.0)
245+
value = value * 100.0
246+
var backlights := PackedStringArray()
247+
if backlight.is_empty():
248+
backlights = self.get_backlights()
249+
else:
250+
backlights.push_back(backlight)
251+
252+
# Set the brightness for all backlights
253+
for backlight_name in backlights:
254+
var value_str := str(value) + "%"
255+
if type == VALUE.RELATIVE:
256+
if value > 0.0:
257+
value_str = "+" + value_str
258+
else:
259+
value_str = value_str + "-"
260+
261+
self.logger.debug("Setting brightness on " + backlight_name + " to " + value_str)
262+
var cmd := _exec(["--device", backlight_name, "set", value_str])
263+
if cmd.code != OK:
264+
return FAILED
265+
266+
return OK
267+
268+
func get_brightness(backlight: String = "") -> float:
269+
var info := self._get_info(backlight)
270+
if info.size() < 4:
271+
return -1.0
272+
273+
# Example Output: amdgpu_bl1,backlight,128,50%,255
274+
var cur_brightness := info[3]
275+
cur_brightness = cur_brightness.replace("%", "")
276+
if not cur_brightness.is_valid_int():
277+
return -1.0
278+
279+
return cur_brightness.to_float() / 100.0
280+
281+
282+
func get_brightness_value(backlight: String = "") -> int:
283+
var info := self._get_info(backlight)
284+
if info.size() < 4:
285+
return -1
286+
287+
# Example Output: amdgpu_bl1,backlight,128,50%,255
288+
var cur_brightness := info[2]
289+
if not cur_brightness.is_valid_int():
290+
return -1
291+
292+
return cur_brightness.to_int()
293+
294+
func get_max_brightness_value(backlight: String = "") -> int:
295+
var info := self._get_info(backlight)
296+
if info.size() < 5:
297+
return -1
132298

299+
# Example Output: amdgpu_bl1,backlight,128,50%,255
300+
var max_brightness := info[4]
301+
if not max_brightness.is_valid_int():
302+
return -1
133303

134-
## Write a value using steamos polkit helper
135-
func _steamos_priv_write(path: String, value: int) -> int:
136-
return OS.execute(steamos_write_bin, [path, str(value)])
304+
return max_brightness.to_int()

0 commit comments

Comments
 (0)