Commit d3999a7
4
files
+108
added
-2
deleted
@@ -252,11 +252,12 @@ pub enum IconName {
252
252
User,
253
253
Info,
254
254
App,
255
+ Brightness,
255
256
}
256
257
257
258
impl IconName {
258
259
/// Wszystkie ikony z pakietu (kolejność = kolejność w galerii).
259
- pub const ALL: [IconName; 28] = [
260
+ pub const ALL: [IconName; 29] = [
260
261
IconName::Menu,
261
262
IconName::Search,
262
263
IconName::Files,
@@ -285,6 +286,7 @@ impl IconName {
285
286
IconName::User,
286
287
IconName::Info,
287
288
IconName::App,
289
+ IconName::Brightness,
288
290
];
289
291
290
292
/// Nazwa ikony (do galerii/diagnostyki).
@@ -318,6 +320,7 @@ impl IconName {
318
320
IconName::User => "User",
319
321
IconName::Info => "Info",
320
322
IconName::App => "App",
323
+ IconName::Brightness => "Brightness",
321
324
}
322
325
}
323
326
}
@@ -634,6 +637,19 @@ pub fn paint_icon(paint: &mut PaintCtx, name: IconName, rect: Rect, color: Color
634
637
color,
635
638
);
636
639
}
640
+ IconName::Brightness => {
641
+ scene.stroke_circle(g.p(12.0, 12.0), g.d(4.0), w, color);
642
+ for k in 0..8 {
643
+ let a = (k as f64 * 45.0).to_radians();
644
+ let (c, s) = (a.cos(), a.sin());
645
+ scene.stroke_line(
646
+ g.p(12.0 + 6.4 * c, 12.0 + 6.4 * s),
647
+ g.p(12.0 + 9.2 * c, 12.0 + 9.2 * s),
648
+ g.d(2.0),
649
+ color,
650
+ );
651
+ }
652
+ }
637
653
}
638
654
}
639
655
@@ -51,6 +51,8 @@ fn main() -> Result<(), Box<dyn Error>> {
51
51
// Daemon powiadomień D‑Bus (rejestrowany przy pierwszej klatce).
52
52
let mut bus: Option<NotificationsBus> = None;
53
53
let mut bus_tried = false;
54
+ // Bieżąca jasność (procent) — sterowana klikiem w pigułkę jasności.
55
+ let mut brightness_pct: u8 = 0;
54
56
// Lista okien z kompozytora (foreign-toplevel) — własne połączenie + wątek.
55
57
let toplevels = luna::toplevels::Toplevels::spawn();
56
58
// Przestrzenie robocze z kompozytora (ext-workspace) — własne połączenie + wątek.
@@ -98,6 +100,7 @@ fn main() -> Result<(), Box<dyn Error>> {
98
100
if let Ok(sys) = system.lock() {
99
101
state.user = sys.user.clone();
100
102
state.toggles = build_toggles(&sys);
103
+ brightness_pct = sys.brightness.unwrap_or(0);
101
104
}
102
105
// Przestrzenie robocze z kompozytora.
103
106
match &workspaces {
@@ -200,6 +203,13 @@ fn main() -> Result<(), Box<dyn Error>> {
200
203
luna::system::set_wifi(!state.toggles[i].active);
201
204
}
202
205
ToggleAction::Volume => luna::system::toggle_mute(),
206
+ ToggleAction::Brightness => {
207
+ let next = luna::system::next_brightness_level(brightness_pct);
208
+ luna::system::set_brightness(next);
209
+ brightness_pct = next;
210
+ osd.icon = IconName::Brightness;
211
+ osd.show("Jasność", next as f64 / 100.0);
212
+ }
203
213
ToggleAction::None => {}
204
214
}
205
215
} else if let Some(i) =
@@ -327,9 +337,13 @@ fn main() -> Result<(), Box<dyn Error>> {
327
337
.wrapping_add(u64::from(t.active) ^ t.title.len() as u64);
328
338
}
329
339
for tg in &state.toggles {
340
+ let mut lh: u64 = 0;
341
+ for b in tg.label.bytes() {
342
+ lh = lh.wrapping_mul(31).wrapping_add(u64::from(b));
343
+ }
330
344
th = th
331
345
.wrapping_mul(1_099_511_628_211)
332
- .wrapping_add(u64::from(tg.active) ^ tg.label.len() as u64);
346
+ .wrapping_add(u64::from(tg.active) ^ lh);
333
347
}
334
348
th = th
335
349
.wrapping_mul(1_099_511_628_211)
@@ -403,6 +417,14 @@ fn build_toggles(s: &luna::system::SystemStatus) -> Vec<Toggle> {
403
417
action: ToggleAction::None,
404
418
});
405
419
}
420
+ if let Some(pct) = s.brightness {
421
+ v.push(Toggle {
422
+ label: format!("{pct}%"),
423
+ icon: IconName::Brightness,
424
+ active: pct > 0,
425
+ action: ToggleAction::Brightness,
426
+ });
427
+ }
406
428
v
407
429
}
408
430
@@ -39,6 +39,8 @@ pub enum ToggleAction {
39
39
None,
40
40
Wifi,
41
41
Volume,
42
+ /// Jasność ekranu (klik cyklicznie zmienia poziom).
43
+ Brightness,
42
44
}
43
45
44
46
/// Przełącznik szybkiego dostępu (Wi-Fi, dźwięk, bateria…).
@@ -15,6 +15,8 @@ pub struct SystemStatus {
15
15
pub wifi: Option<(bool, Option<String>)>,
16
16
/// `(procent, ładuje)`.
17
17
pub battery: Option<(u8, bool)>,
18
+ /// Jasność ekranu (procent), gdy jest podświetlenie (`/sys/class/backlight`).
19
+ pub brightness: Option<u8>,
18
20
}
19
21
20
22
impl SystemStatus {
@@ -25,6 +27,7 @@ impl SystemStatus {
25
27
volume: read_volume(),
26
28
wifi: wifi(),
27
29
battery: battery(),
30
+ brightness: read_brightness(),
28
31
}
29
32
}
30
33
}
@@ -34,6 +37,60 @@ pub fn read_volume() -> Option<(u8, bool)> {
34
37
volume()
35
38
}
36
39
40
+/// Bieżąca jasność ekranu w procentach (z `/sys/class/backlight`), gdy dostępna.
41
+pub fn read_brightness() -> Option<u8> {
42
+ let dir = backlight_dir()?;
43
+ let cur: u32 = std::fs::read_to_string(dir.join("brightness"))
44
+ .ok()?
45
+ .trim()
46
+ .parse()
47
+ .ok()?;
48
+ let max: u32 = std::fs::read_to_string(dir.join("max_brightness"))
49
+ .ok()?
50
+ .trim()
51
+ .parse()
52
+ .ok()?;
53
+ if max == 0 {
54
+ return None;
55
+ }
56
+ Some(((cur * 100 + max / 2) / max) as u8)
57
+}
58
+
59
+/// Ustawia jasność ekranu (procent). Bez uprawnień do sysfs cicho nic nie robi.
60
+pub fn set_brightness(percent: u8) {
61
+ let Some(dir) = backlight_dir() else { return };
62
+ let Ok(text) = std::fs::read_to_string(dir.join("max_brightness")) else {
63
+ return;
64
+ };
65
+ let Ok(max) = text.trim().parse::<u32>() else {
66
+ return;
67
+ };
68
+ if max == 0 {
69
+ return;
70
+ }
71
+ let value = (percent as u32 * max + 50) / 100;
72
+ let _ = std::fs::write(dir.join("brightness"), format!("{value}\n"));
73
+}
74
+
75
+/// Katalog pierwszego podświetlenia (pierwszy wpis `/sys/class/backlight`).
76
+fn backlight_dir() -> Option<std::path::PathBuf> {
77
+ std::fs::read_dir("/sys/class/backlight")
78
+ .ok()?
79
+ .flatten()
80
+ .map(|e| e.path())
81
+ .next()
82
+}
83
+
84
+/// Kolejny poziom jasności po kliknięciu (cykl 25 → 50 → 75 → 100 → 25).
85
+pub fn next_brightness_level(current: u8) -> u8 {
86
+ for level in [25u8, 50, 75, 100] {
87
+ if current + 6 < level {
88
+ return level;
89
+ }
90
+ }
91
+ 25
92
+}
93
+
37
94
/// Współdzielony stan, aktualizowany w tle.
38
95
pub type Shared = Arc<Mutex<SystemStatus>>;
39
96
@@ -146,6 +203,15 @@ fn battery() -> Option<(u8, bool)> {
146
203
mod tests {
147
204
use super::*;
148
205
206
+ #[test]
207
+ fn brightness_cycles_levels() {
208
+ assert_eq!(next_brightness_level(0), 25);
209
+ assert_eq!(next_brightness_level(25), 50);
210
+ assert_eq!(next_brightness_level(50), 75);
211
+ assert_eq!(next_brightness_level(75), 100);
212
+ assert_eq!(next_brightness_level(100), 25);
213
+ }
214
+
149
215
#[test]
150
216
fn parses_volume_and_mute() {
151
217
assert_eq!(parse_volume("Volume: 0.40\n"), Some((40, false)));