← PaganDE

Commit 95eb18d

4
plików
+1914
dodanych
-3
usuniętych
@@ -27,7 +27,7 @@ use smithay::{
27 27 desktop::{
28 28 find_popup_root_surface, layer_map_for_output, LayerSurface as DesktopLayerSurface,
29 29 PopupKeyboardGrab, PopupKind, PopupManager, PopupPointerGrab, PopupUngrabStrategy, Space,
30 - Window,
30 + Window, WindowSurfaceType,
31 31 },
32 32 input::{
33 33 keyboard::XkbConfig,
@@ -77,8 +77,8 @@ use smithay::{
77 77 },
78 78 shell::{
79 79 wlr_layer::{
80 - Layer as WlrLayer, LayerSurface as WlrLayerSurface, WlrLayerShellHandler,
81 - WlrLayerShellState,
80 + Layer as WlrLayer, LayerSurface as WlrLayerSurface, LayerSurfaceData,
81 + WlrLayerShellHandler, WlrLayerShellState,
82 82 },
83 83 xdg::{
84 84 decoration::{XdgDecorationHandler, XdgDecorationState},
@@ -916,6 +916,39 @@ impl CompositorHandler for MyCompositor {
916 916 }
917 917 }
918 918
919 + // Warstwy (panele/paski): po commicie klienta przeliczamy strefy i — jeśli
920 + // to pierwszy commit — wysyłamy PIERWSZY `configure`. Smithay celowo nie
921 + // robi tego w `arrange()` (patrz komentarz w `layer.rs`), więc musi to
922 + // zrobić kompozytor. Bez tego panel nie wie, jak szeroki ma być.
923 + if let Some(output) = self
924 + .space
925 + .outputs()
926 + .find(|output| {
927 + layer_map_for_output(output)
928 + .layer_for_surface(surface, WindowSurfaceType::TOPLEVEL)
929 + .is_some()
930 + })
931 + .cloned()
932 + {
933 + let initial_configure_sent = with_states(surface, |states| {
934 + states
935 + .data_map
936 + .get::<LayerSurfaceData>()
937 + .map(|data| data.lock().unwrap().initial_configure_sent)
938 + .unwrap_or(false)
939 + });
940 +
941 + let mut map = layer_map_for_output(&output);
942 + // Najpierw przelicz (uwzględnij rozmiar/anchory podane przez klienta),
943 + // potem wyślij pierwszy configure.
944 + map.arrange();
945 + if !initial_configure_sent {
946 + if let Some(layer) = map.layer_for_surface(surface, WindowSurfaceType::TOPLEVEL) {
947 + layer.layer_surface().send_configure();
948 + }
949 + }
950 + }
951 +
919 952 self.popups.commit(surface);
920 953 }
921 954 }
922 955 new file mode 100644
@@ -0,0 +1,1061 @@
1 +# This file is automatically @generated by Cargo.
2 +# It is not intended for manual editing.
3 +version = 4
4 +
5 +[[package]]
6 +name = "aho-corasick"
7 +version = "1.1.5"
8 +source = "registry+https://github.com/rust-lang/crates.io-index"
9 +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
10 +dependencies = [
11 + "memchr",
12 +]
13 +
14 +[[package]]
15 +name = "android_system_properties"
16 +version = "0.1.6"
17 +source = "registry+https://github.com/rust-lang/crates.io-index"
18 +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
19 +dependencies = [
20 + "libc",
21 +]
22 +
23 +[[package]]
24 +name = "autocfg"
25 +version = "1.5.1"
26 +source = "registry+https://github.com/rust-lang/crates.io-index"
27 +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
28 +
29 +[[package]]
30 +name = "bitflags"
31 +version = "2.13.2"
32 +source = "registry+https://github.com/rust-lang/crates.io-index"
33 +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
34 +
35 +[[package]]
36 +name = "bumpalo"
37 +version = "3.20.3"
38 +source = "registry+https://github.com/rust-lang/crates.io-index"
39 +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
40 +
41 +[[package]]
42 +name = "cairo-rs"
43 +version = "0.22.9"
44 +source = "registry+https://github.com/rust-lang/crates.io-index"
45 +checksum = "df683f1d30b457964673a5541a4603208ef95ab6873d2a55871895cf310f3b56"
46 +dependencies = [
47 + "bitflags",
48 + "cairo-sys-rs",
49 + "glib",
50 + "libc",
51 +]
52 +
53 +[[package]]
54 +name = "cairo-sys-rs"
55 +version = "0.22.9"
56 +source = "registry+https://github.com/rust-lang/crates.io-index"
57 +checksum = "ee548131103ad8f698c6725669647b9c757b3b66992dd6a026037596417bee21"
58 +dependencies = [
59 + "glib-sys",
60 + "libc",
61 + "system-deps",
62 +]
63 +
64 +[[package]]
65 +name = "calloop"
66 +version = "0.13.0"
67 +source = "registry+https://github.com/rust-lang/crates.io-index"
68 +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
69 +dependencies = [
70 + "bitflags",
71 + "log",
72 + "polling",
73 + "rustix 0.38.44",
74 + "slab",
75 + "thiserror",
76 +]
77 +
78 +[[package]]
79 +name = "calloop-wayland-source"
80 +version = "0.3.0"
81 +source = "registry+https://github.com/rust-lang/crates.io-index"
82 +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
83 +dependencies = [
84 + "calloop",
85 + "rustix 0.38.44",
86 + "wayland-backend",
87 + "wayland-client",
88 +]
89 +
90 +[[package]]
91 +name = "cc"
92 +version = "1.4.6"
93 +source = "registry+https://github.com/rust-lang/crates.io-index"
94 +checksum = "a3eb0f42d6c360dc3f8a821f6bf2fdea7f72bfd36b3076eb0e6d1e9e0752fff4"
95 +dependencies = [
96 + "find-msvc-tools",
97 + "shlex",
98 +]
99 +
100 +[[package]]
101 +name = "cfg-expr"
102 +version = "0.20.9"
103 +source = "registry+https://github.com/rust-lang/crates.io-index"
104 +checksum = "fe4ece8474b5f766c63426647e7b4b316b67431ade1036a8313cee24a03ae917"
105 +dependencies = [
106 + "smallvec",
107 + "target-lexicon",
108 +]
109 +
110 +[[package]]
111 +name = "cfg-if"
112 +version = "1.0.4"
113 +source = "registry+https://github.com/rust-lang/crates.io-index"
114 +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
115 +
116 +[[package]]
117 +name = "chrono"
118 +version = "0.4.45"
119 +source = "registry+https://github.com/rust-lang/crates.io-index"
120 +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
121 +dependencies = [
122 + "iana-time-zone",
123 + "num-traits",
124 + "windows-link",
125 +]
126 +
127 +[[package]]
128 +name = "concurrent-queue"
129 +version = "2.5.0"
130 +source = "registry+https://github.com/rust-lang/crates.io-index"
131 +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
132 +dependencies = [
133 + "crossbeam-utils",
134 +]
135 +
136 +[[package]]
137 +name = "core-foundation-sys"
138 +version = "0.8.7"
139 +source = "registry+https://github.com/rust-lang/crates.io-index"
140 +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
141 +
142 +[[package]]
143 +name = "crossbeam-utils"
144 +version = "0.8.23"
145 +source = "registry+https://github.com/rust-lang/crates.io-index"
146 +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
147 +
148 +[[package]]
149 +name = "downcast-rs"
150 +version = "1.2.1"
151 +source = "registry+https://github.com/rust-lang/crates.io-index"
152 +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
153 +
154 +[[package]]
155 +name = "equivalent"
156 +version = "1.0.2"
157 +source = "registry+https://github.com/rust-lang/crates.io-index"
158 +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
159 +
160 +[[package]]
161 +name = "errno"
162 +version = "0.3.14"
163 +source = "registry+https://github.com/rust-lang/crates.io-index"
164 +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
165 +dependencies = [
166 + "libc",
167 + "windows-sys 0.61.2",
168 +]
169 +
170 +[[package]]
171 +name = "fastrand"
172 +version = "2.5.0"
173 +source = "registry+https://github.com/rust-lang/crates.io-index"
174 +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
175 +
176 +[[package]]
177 +name = "find-msvc-tools"
178 +version = "0.1.12"
179 +source = "registry+https://github.com/rust-lang/crates.io-index"
180 +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d"
181 +
182 +[[package]]
183 +name = "futures-core"
184 +version = "0.3.34"
185 +source = "registry+https://github.com/rust-lang/crates.io-index"
186 +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
187 +
188 +[[package]]
189 +name = "futures-task"
190 +version = "0.3.34"
191 +source = "registry+https://github.com/rust-lang/crates.io-index"
192 +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
193 +
194 +[[package]]
195 +name = "futures-util"
196 +version = "0.3.34"
197 +source = "registry+https://github.com/rust-lang/crates.io-index"
198 +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
199 +dependencies = [
200 + "futures-core",
201 + "futures-task",
202 + "pin-project-lite",
203 + "slab",
204 +]
205 +
206 +[[package]]
207 +name = "getrandom"
208 +version = "0.4.3"
209 +source = "registry+https://github.com/rust-lang/crates.io-index"
210 +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
211 +dependencies = [
212 + "cfg-if",
213 + "libc",
214 + "r-efi",
215 +]
216 +
217 +[[package]]
218 +name = "glib"
219 +version = "0.22.9"
220 +source = "registry+https://github.com/rust-lang/crates.io-index"
221 +checksum = "18b9b8d350db41f690ec1b87109f202782748bbd8ab2f2ede17cb40d29e2838c"
222 +dependencies = [
223 + "bitflags",
224 + "glib-macros",
225 + "glib-sys",
226 + "gobject-sys",
227 + "libc",
228 + "memchr",
229 + "smallvec",
230 +]
231 +
232 +[[package]]
233 +name = "glib-macros"
234 +version = "0.22.9"
235 +source = "registry+https://github.com/rust-lang/crates.io-index"
236 +checksum = "c9597c68fa7bdf4154a3079642cd21c4dccac0b0bdd2897d82861523bf91e7b9"
237 +dependencies = [
238 + "heck",
239 + "proc-macro2",
240 + "quote",
241 + "syn 3.0.5",
242 +]
243 +
244 +[[package]]
245 +name = "glib-sys"
246 +version = "0.22.9"
247 +source = "registry+https://github.com/rust-lang/crates.io-index"
248 +checksum = "99b38907e67e40dec9b60f858bcada952598719cb512a13eb13d6cdcd16f8295"
249 +dependencies = [
250 + "libc",
251 + "system-deps",
252 +]
253 +
254 +[[package]]
255 +name = "gobject-sys"
256 +version = "0.22.9"
257 +source = "registry+https://github.com/rust-lang/crates.io-index"
258 +checksum = "07595c9ba696bd9819cb6a8df39f08078f3dd679508fe052dd558492c2053834"
259 +dependencies = [
260 + "glib-sys",
261 + "libc",
262 + "system-deps",
263 +]
264 +
265 +[[package]]
266 +name = "hashbrown"
267 +version = "0.17.1"
268 +source = "registry+https://github.com/rust-lang/crates.io-index"
269 +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
270 +
271 +[[package]]
272 +name = "heck"
273 +version = "0.5.0"
274 +source = "registry+https://github.com/rust-lang/crates.io-index"
275 +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
276 +
277 +[[package]]
278 +name = "hermit-abi"
279 +version = "0.5.3"
280 +source = "registry+https://github.com/rust-lang/crates.io-index"
281 +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284"
282 +
283 +[[package]]
284 +name = "iana-time-zone"
285 +version = "0.1.65"
286 +source = "registry+https://github.com/rust-lang/crates.io-index"
287 +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
288 +dependencies = [
289 + "android_system_properties",
290 + "core-foundation-sys",
291 + "iana-time-zone-haiku",
292 + "js-sys",
293 + "log",
294 + "wasm-bindgen",
295 + "windows-core",
296 +]
297 +
298 +[[package]]
299 +name = "iana-time-zone-haiku"
300 +version = "0.1.2"
301 +source = "registry+https://github.com/rust-lang/crates.io-index"
302 +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
303 +dependencies = [
304 + "cc",
305 +]
306 +
307 +[[package]]
308 +name = "indexmap"
309 +version = "2.14.2"
310 +source = "registry+https://github.com/rust-lang/crates.io-index"
311 +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
312 +dependencies = [
313 + "equivalent",
314 + "hashbrown",
315 +]
316 +
317 +[[package]]
318 +name = "js-sys"
319 +version = "0.3.105"
320 +source = "registry+https://github.com/rust-lang/crates.io-index"
321 +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e"
322 +dependencies = [
323 + "cfg-if",
324 + "futures-util",
325 + "wasm-bindgen",
326 +]
327 +
328 +[[package]]
329 +name = "lazy_static"
330 +version = "1.5.0"
331 +source = "registry+https://github.com/rust-lang/crates.io-index"
332 +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
333 +
334 +[[package]]
335 +name = "libc"
336 +version = "0.2.189"
337 +source = "registry+https://github.com/rust-lang/crates.io-index"
338 +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
339 +
340 +[[package]]
341 +name = "linux-raw-sys"
342 +version = "0.4.15"
343 +source = "registry+https://github.com/rust-lang/crates.io-index"
344 +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
345 +
346 +[[package]]
347 +name = "linux-raw-sys"
348 +version = "0.12.1"
349 +source = "registry+https://github.com/rust-lang/crates.io-index"
350 +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
351 +
352 +[[package]]
353 +name = "log"
354 +version = "0.4.34"
355 +source = "registry+https://github.com/rust-lang/crates.io-index"
356 +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
357 +
358 +[[package]]
359 +name = "matchers"
360 +version = "0.2.0"
361 +source = "registry+https://github.com/rust-lang/crates.io-index"
362 +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
363 +dependencies = [
364 + "regex-automata",
365 +]
366 +
367 +[[package]]
368 +name = "memchr"
369 +version = "2.8.3"
370 +source = "registry+https://github.com/rust-lang/crates.io-index"
371 +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
372 +
373 +[[package]]
374 +name = "memmap2"
375 +version = "0.9.11"
376 +source = "registry+https://github.com/rust-lang/crates.io-index"
377 +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"
378 +dependencies = [
379 + "libc",
380 +]
381 +
382 +[[package]]
383 +name = "nu-ansi-term"
384 +version = "0.50.3"
385 +source = "registry+https://github.com/rust-lang/crates.io-index"
386 +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
387 +dependencies = [
388 + "windows-sys 0.61.2",
389 +]
390 +
391 +[[package]]
392 +name = "num-traits"
393 +version = "0.2.19"
394 +source = "registry+https://github.com/rust-lang/crates.io-index"
395 +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
396 +dependencies = [
397 + "autocfg",
398 +]
399 +
400 +[[package]]
401 +name = "once_cell"
402 +version = "1.21.4"
403 +source = "registry+https://github.com/rust-lang/crates.io-index"
404 +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
405 +
406 +[[package]]
407 +name = "pagan-panel"
408 +version = "0.1.0"
409 +dependencies = [
410 + "cairo-rs",
411 + "calloop",
412 + "calloop-wayland-source",
413 + "chrono",
414 + "memmap2",
415 + "pagan-toolkit",
416 + "tracing",
417 + "tracing-subscriber",
418 + "wayland-client",
419 + "wayland-protocols-wlr",
420 +]
421 +
422 +[[package]]
423 +name = "pagan-toolkit"
424 +version = "0.1.0"
425 +dependencies = [
426 + "cairo-rs",
427 + "memmap2",
428 + "tempfile",
429 + "tracing",
430 + "tracing-subscriber",
431 + "wayland-client",
432 + "wayland-protocols",
433 + "wayland-protocols-wlr",
434 +]
435 +
436 +[[package]]
437 +name = "pin-project-lite"
438 +version = "0.2.17"
439 +source = "registry+https://github.com/rust-lang/crates.io-index"
440 +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
441 +
442 +[[package]]
443 +name = "pkg-config"
444 +version = "0.3.34"
445 +source = "registry+https://github.com/rust-lang/crates.io-index"
446 +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
447 +
448 +[[package]]
449 +name = "polling"
450 +version = "3.11.0"
451 +source = "registry+https://github.com/rust-lang/crates.io-index"
452 +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
453 +dependencies = [
454 + "cfg-if",
455 + "concurrent-queue",
456 + "hermit-abi",
457 + "pin-project-lite",
458 + "rustix 1.1.4",
459 + "windows-sys 0.61.2",
460 +]
461 +
462 +[[package]]
463 +name = "proc-macro2"
464 +version = "1.0.107"
465 +source = "registry+https://github.com/rust-lang/crates.io-index"
466 +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
467 +dependencies = [
468 + "unicode-ident",
469 +]
470 +
471 +[[package]]
472 +name = "quick-xml"
473 +version = "0.41.0"
474 +source = "registry+https://github.com/rust-lang/crates.io-index"
475 +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
476 +dependencies = [
477 + "memchr",
478 +]
479 +
480 +[[package]]
481 +name = "quote"
482 +version = "1.0.47"
483 +source = "registry+https://github.com/rust-lang/crates.io-index"
484 +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
485 +dependencies = [
486 + "proc-macro2",
487 +]
488 +
489 +[[package]]
490 +name = "r-efi"
491 +version = "6.0.0"
492 +source = "registry+https://github.com/rust-lang/crates.io-index"
493 +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
494 +
495 +[[package]]
496 +name = "regex-automata"
497 +version = "0.4.18"
498 +source = "registry+https://github.com/rust-lang/crates.io-index"
499 +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
500 +dependencies = [
501 + "aho-corasick",
502 + "memchr",
503 + "regex-syntax",
504 +]
505 +
506 +[[package]]
507 +name = "regex-syntax"
508 +version = "0.8.11"
509 +source = "registry+https://github.com/rust-lang/crates.io-index"
510 +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
511 +
512 +[[package]]
513 +name = "rustix"
514 +version = "0.38.44"
515 +source = "registry+https://github.com/rust-lang/crates.io-index"
516 +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
517 +dependencies = [
518 + "bitflags",
519 + "errno",
520 + "libc",
521 + "linux-raw-sys 0.4.15",
522 + "windows-sys 0.59.0",
523 +]
524 +
525 +[[package]]
526 +name = "rustix"
527 +version = "1.1.4"
528 +source = "registry+https://github.com/rust-lang/crates.io-index"
529 +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
530 +dependencies = [
531 + "bitflags",
532 + "errno",
533 + "libc",
534 + "linux-raw-sys 0.12.1",
535 + "windows-sys 0.61.2",
536 +]
537 +
538 +[[package]]
539 +name = "rustversion"
540 +version = "1.0.23"
541 +source = "registry+https://github.com/rust-lang/crates.io-index"
542 +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
543 +
544 +[[package]]
545 +name = "serde_core"
546 +version = "1.0.229"
547 +source = "registry+https://github.com/rust-lang/crates.io-index"
548 +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
549 +dependencies = [
550 + "serde_derive",
551 +]
552 +
553 +[[package]]
554 +name = "serde_derive"
555 +version = "1.0.229"
556 +source = "registry+https://github.com/rust-lang/crates.io-index"
557 +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
558 +dependencies = [
559 + "proc-macro2",
560 + "quote",
561 + "syn 3.0.5",
562 +]
563 +
564 +[[package]]
565 +name = "serde_spanned"
566 +version = "1.1.1"
567 +source = "registry+https://github.com/rust-lang/crates.io-index"
568 +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
569 +dependencies = [
570 + "serde_core",
571 +]
572 +
573 +[[package]]
574 +name = "sharded-slab"
575 +version = "0.1.7"
576 +source = "registry+https://github.com/rust-lang/crates.io-index"
577 +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
578 +dependencies = [
579 + "lazy_static",
580 +]
581 +
582 +[[package]]
583 +name = "shlex"
584 +version = "2.0.1"
585 +source = "registry+https://github.com/rust-lang/crates.io-index"
586 +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
587 +
588 +[[package]]
589 +name = "slab"
590 +version = "0.4.12"
591 +source = "registry+https://github.com/rust-lang/crates.io-index"
592 +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
593 +
594 +[[package]]
595 +name = "smallvec"
596 +version = "1.16.1"
597 +source = "registry+https://github.com/rust-lang/crates.io-index"
598 +checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891"
599 +
600 +[[package]]
601 +name = "syn"
602 +version = "2.0.119"
603 +source = "registry+https://github.com/rust-lang/crates.io-index"
604 +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
605 +dependencies = [
606 + "proc-macro2",
607 + "quote",
608 + "unicode-ident",
609 +]
610 +
611 +[[package]]
612 +name = "syn"
613 +version = "3.0.5"
614 +source = "registry+https://github.com/rust-lang/crates.io-index"
615 +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
616 +dependencies = [
617 + "proc-macro2",
618 + "quote",
619 + "unicode-ident",
620 +]
621 +
622 +[[package]]
623 +name = "system-deps"
624 +version = "9.0.0"
625 +source = "registry+https://github.com/rust-lang/crates.io-index"
626 +checksum = "8a0dae2cbca1f0ff93795713cfe0a4c02f760e3c0b8ae2ab4ec4045808ff429f"
627 +dependencies = [
628 + "cfg-expr",
629 + "heck",
630 + "pkg-config",
631 + "toml",
632 + "version-compare",
633 +]
634 +
635 +[[package]]
636 +name = "target-lexicon"
637 +version = "0.13.5"
638 +source = "registry+https://github.com/rust-lang/crates.io-index"
639 +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
640 +
641 +[[package]]
642 +name = "tempfile"
643 +version = "3.27.0"
644 +source = "registry+https://github.com/rust-lang/crates.io-index"
645 +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
646 +dependencies = [
647 + "fastrand",
648 + "getrandom",
649 + "once_cell",
650 + "rustix 1.1.4",
651 + "windows-sys 0.61.2",
652 +]
653 +
654 +[[package]]
655 +name = "thiserror"
656 +version = "1.0.69"
657 +source = "registry+https://github.com/rust-lang/crates.io-index"
658 +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
659 +dependencies = [
660 + "thiserror-impl",
661 +]
662 +
663 +[[package]]
664 +name = "thiserror-impl"
665 +version = "1.0.69"
666 +source = "registry+https://github.com/rust-lang/crates.io-index"
667 +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
668 +dependencies = [
669 + "proc-macro2",
670 + "quote",
671 + "syn 2.0.119",
672 +]
673 +
674 +[[package]]
675 +name = "thread_local"
676 +version = "1.1.10"
677 +source = "registry+https://github.com/rust-lang/crates.io-index"
678 +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
679 +dependencies = [
680 + "cfg-if",
681 +]
682 +
683 +[[package]]
684 +name = "toml"
685 +version = "1.1.6+spec-1.1.0"
686 +source = "registry+https://github.com/rust-lang/crates.io-index"
687 +checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a"
688 +dependencies = [
689 + "indexmap",
690 + "serde_core",
691 + "serde_spanned",
692 + "toml_datetime",
693 + "toml_parser",
694 + "toml_writer",
695 + "winnow",
696 +]
697 +
698 +[[package]]
699 +name = "toml_datetime"
700 +version = "1.1.1+spec-1.1.0"
701 +source = "registry+https://github.com/rust-lang/crates.io-index"
702 +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
703 +dependencies = [
704 + "serde_core",
705 +]
706 +
707 +[[package]]
708 +name = "toml_parser"
709 +version = "1.1.3+spec-1.1.0"
710 +source = "registry+https://github.com/rust-lang/crates.io-index"
711 +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
712 +dependencies = [
713 + "winnow",
714 +]
715 +
716 +[[package]]
717 +name = "toml_writer"
718 +version = "1.1.2+spec-1.1.0"
719 +source = "registry+https://github.com/rust-lang/crates.io-index"
720 +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
721 +
722 +[[package]]
723 +name = "tracing"
724 +version = "0.1.44"
725 +source = "registry+https://github.com/rust-lang/crates.io-index"
726 +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
727 +dependencies = [
728 + "pin-project-lite",
729 + "tracing-attributes",
730 + "tracing-core",
731 +]
732 +
733 +[[package]]
734 +name = "tracing-attributes"
735 +version = "0.1.31"
736 +source = "registry+https://github.com/rust-lang/crates.io-index"
737 +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
738 +dependencies = [
739 + "proc-macro2",
740 + "quote",
741 + "syn 2.0.119",
742 +]
743 +
744 +[[package]]
745 +name = "tracing-core"
746 +version = "0.1.36"
747 +source = "registry+https://github.com/rust-lang/crates.io-index"
748 +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
749 +dependencies = [
750 + "once_cell",
751 + "valuable",
752 +]
753 +
754 +[[package]]
755 +name = "tracing-log"
756 +version = "0.2.0"
757 +source = "registry+https://github.com/rust-lang/crates.io-index"
758 +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
759 +dependencies = [
760 + "log",
761 + "once_cell",
762 + "tracing-core",
763 +]
764 +
765 +[[package]]
766 +name = "tracing-subscriber"
767 +version = "0.3.23"
768 +source = "registry+https://github.com/rust-lang/crates.io-index"
769 +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
770 +dependencies = [
771 + "matchers",
772 + "nu-ansi-term",
773 + "once_cell",
774 + "regex-automata",
775 + "sharded-slab",
776 + "smallvec",
777 + "thread_local",
778 + "tracing",
779 + "tracing-core",
780 + "tracing-log",
781 +]
782 +
783 +[[package]]
784 +name = "unicode-ident"
785 +version = "1.0.24"
786 +source = "registry+https://github.com/rust-lang/crates.io-index"
787 +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
788 +
789 +[[package]]
790 +name = "valuable"
791 +version = "0.1.1"
792 +source = "registry+https://github.com/rust-lang/crates.io-index"
793 +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
794 +
795 +[[package]]
796 +name = "version-compare"
797 +version = "0.2.1"
798 +source = "registry+https://github.com/rust-lang/crates.io-index"
799 +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e"
800 +
801 +[[package]]
802 +name = "wasm-bindgen"
803 +version = "0.2.128"
804 +source = "registry+https://github.com/rust-lang/crates.io-index"
805 +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf"
806 +dependencies = [
807 + "cfg-if",
808 + "once_cell",
809 + "rustversion",
810 + "wasm-bindgen-macro",
811 + "wasm-bindgen-shared",
812 +]
813 +
814 +[[package]]
815 +name = "wasm-bindgen-macro"
816 +version = "0.2.128"
817 +source = "registry+https://github.com/rust-lang/crates.io-index"
818 +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed"
819 +dependencies = [
820 + "quote",
821 + "wasm-bindgen-macro-support",
822 +]
823 +
824 +[[package]]
825 +name = "wasm-bindgen-macro-support"
826 +version = "0.2.128"
827 +source = "registry+https://github.com/rust-lang/crates.io-index"
828 +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a"
829 +dependencies = [
830 + "bumpalo",
831 + "proc-macro2",
832 + "quote",
833 + "syn 3.0.5",
834 + "wasm-bindgen-shared",
835 +]
836 +
837 +[[package]]
838 +name = "wasm-bindgen-shared"
839 +version = "0.2.128"
840 +source = "registry+https://github.com/rust-lang/crates.io-index"
841 +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e"
842 +dependencies = [
843 + "unicode-ident",
844 +]
845 +
846 +[[package]]
847 +name = "wayland-backend"
848 +version = "0.3.17"
849 +source = "registry+https://github.com/rust-lang/crates.io-index"
850 +checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078"
851 +dependencies = [
852 + "cc",
853 + "downcast-rs",
854 + "rustix 1.1.4",
855 + "smallvec",
856 + "wayland-sys",
857 +]
858 +
859 +[[package]]
860 +name = "wayland-client"
861 +version = "0.31.15"
862 +source = "registry+https://github.com/rust-lang/crates.io-index"
863 +checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073"
864 +dependencies = [
865 + "bitflags",
866 + "rustix 1.1.4",
867 + "wayland-backend",
868 + "wayland-scanner",
869 +]
870 +
871 +[[package]]
872 +name = "wayland-protocols"
873 +version = "0.32.13"
874 +source = "registry+https://github.com/rust-lang/crates.io-index"
875 +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
876 +dependencies = [
877 + "bitflags",
878 + "wayland-backend",
879 + "wayland-client",
880 + "wayland-scanner",
881 +]
882 +
883 +[[package]]
884 +name = "wayland-protocols-wlr"
885 +version = "0.3.12"
886 +source = "registry+https://github.com/rust-lang/crates.io-index"
887 +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
888 +dependencies = [
889 + "bitflags",
890 + "wayland-backend",
891 + "wayland-client",
892 + "wayland-protocols",
893 + "wayland-scanner",
894 +]
895 +
896 +[[package]]
897 +name = "wayland-scanner"
898 +version = "0.31.11"
899 +source = "registry+https://github.com/rust-lang/crates.io-index"
900 +checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0"
901 +dependencies = [
902 + "proc-macro2",
903 + "quick-xml",
904 + "quote",
905 +]
906 +
907 +[[package]]
908 +name = "wayland-sys"
909 +version = "0.31.11"
910 +source = "registry+https://github.com/rust-lang/crates.io-index"
911 +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
912 +dependencies = [
913 + "pkg-config",
914 +]
915 +
916 +[[package]]
917 +name = "windows-core"
918 +version = "0.62.2"
919 +source = "registry+https://github.com/rust-lang/crates.io-index"
920 +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
921 +dependencies = [
922 + "windows-implement",
923 + "windows-interface",
924 + "windows-link",
925 + "windows-result",
926 + "windows-strings",
927 +]
928 +
929 +[[package]]
930 +name = "windows-implement"
931 +version = "0.60.2"
932 +source = "registry+https://github.com/rust-lang/crates.io-index"
933 +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
934 +dependencies = [
935 + "proc-macro2",
936 + "quote",
937 + "syn 2.0.119",
938 +]
939 +
940 +[[package]]
941 +name = "windows-interface"
942 +version = "0.59.3"
943 +source = "registry+https://github.com/rust-lang/crates.io-index"
944 +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
945 +dependencies = [
946 + "proc-macro2",
947 + "quote",
948 + "syn 2.0.119",
949 +]
950 +
951 +[[package]]
952 +name = "windows-link"
953 +version = "0.2.1"
954 +source = "registry+https://github.com/rust-lang/crates.io-index"
955 +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
956 +
957 +[[package]]
958 +name = "windows-result"
959 +version = "0.4.1"
960 +source = "registry+https://github.com/rust-lang/crates.io-index"
961 +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
962 +dependencies = [
963 + "windows-link",
964 +]
965 +
966 +[[package]]
967 +name = "windows-strings"
968 +version = "0.5.1"
969 +source = "registry+https://github.com/rust-lang/crates.io-index"
970 +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
971 +dependencies = [
972 + "windows-link",
973 +]
974 +
975 +[[package]]
976 +name = "windows-sys"
977 +version = "0.59.0"
978 +source = "registry+https://github.com/rust-lang/crates.io-index"
979 +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
980 +dependencies = [
981 + "windows-targets",
982 +]
983 +
984 +[[package]]
985 +name = "windows-sys"
986 +version = "0.61.2"
987 +source = "registry+https://github.com/rust-lang/crates.io-index"
988 +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
989 +dependencies = [
990 + "windows-link",
991 +]
992 +
993 +[[package]]
994 +name = "windows-targets"
995 +version = "0.52.6"
996 +source = "registry+https://github.com/rust-lang/crates.io-index"
997 +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
998 +dependencies = [
999 + "windows_aarch64_gnullvm",
1000 + "windows_aarch64_msvc",
1001 + "windows_i686_gnu",
1002 + "windows_i686_gnullvm",
1003 + "windows_i686_msvc",
1004 + "windows_x86_64_gnu",
1005 + "windows_x86_64_gnullvm",
1006 + "windows_x86_64_msvc",
1007 +]
1008 +
1009 +[[package]]
1010 +name = "windows_aarch64_gnullvm"
1011 +version = "0.52.6"
1012 +source = "registry+https://github.com/rust-lang/crates.io-index"
1013 +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
1014 +
1015 +[[package]]
1016 +name = "windows_aarch64_msvc"
1017 +version = "0.52.6"
1018 +source = "registry+https://github.com/rust-lang/crates.io-index"
1019 +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
1020 +
1021 +[[package]]
1022 +name = "windows_i686_gnu"
1023 +version = "0.52.6"
1024 +source = "registry+https://github.com/rust-lang/crates.io-index"
1025 +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
1026 +
1027 +[[package]]
1028 +name = "windows_i686_gnullvm"
1029 +version = "0.52.6"
1030 +source = "registry+https://github.com/rust-lang/crates.io-index"
1031 +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
1032 +
1033 +[[package]]
1034 +name = "windows_i686_msvc"
1035 +version = "0.52.6"
1036 +source = "registry+https://github.com/rust-lang/crates.io-index"
1037 +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
1038 +
1039 +[[package]]
1040 +name = "windows_x86_64_gnu"
1041 +version = "0.52.6"
1042 +source = "registry+https://github.com/rust-lang/crates.io-index"
1043 +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
1044 +
1045 +[[package]]
1046 +name = "windows_x86_64_gnullvm"
1047 +version = "0.52.6"
1048 +source = "registry+https://github.com/rust-lang/crates.io-index"
1049 +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
1050 +
1051 +[[package]]
1052 +name = "windows_x86_64_msvc"
1053 +version = "0.52.6"
1054 +source = "registry+https://github.com/rust-lang/crates.io-index"
1055 +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
1056 +
1057 +[[package]]
1058 +name = "winnow"
1059 +version = "1.0.4"
1060 +source = "registry+https://github.com/rust-lang/crates.io-index"
1061 +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
0 1062 new file mode 100644
@@ -0,0 +1,26 @@
1 +[package]
2 +name = "pagan-panel"
3 +version = "0.1.0"
4 +edition = "2021"
5 +license = "MIT"
6 +description = "PaganDE: panel + pasek zadań (klient wlr-layer-shell) budowany na pagan-toolkit"
7 +publish = false
8 +
9 +[dependencies]
10 +# Rysowanie do SHM i prymitywy Cairo dzielimy z toolkit — jeden spójny wygląd.
11 +pagan-toolkit = { path = "../pagan-toolkit" }
12 +
13 +wayland-client = "0.31"
14 +# Protokoły WLR po stronie klienta: warstwy (panel) i lista okien (pasek zadań).
15 +wayland-protocols-wlr = { version = "0.3", features = ["client"] }
16 +
17 +# Pętla zdarzeń jak w kompozytorze: calloop + źródło Waylanda. Daje też timer
18 +# na zegar, bez pisania własnego polla.
19 +calloop = "0.13"
20 +calloop-wayland-source = "0.3"
21 +
22 +memmap2 = "0.9"
23 +cairo-rs = "0.22"
24 +chrono = { version = "0.4", default-features = false, features = ["clock"] }
25 +tracing = "0.1"
26 +tracing-subscriber = { version = "0.3", features = ["env-filter"] }
0 27 new file mode 100644
@@ -0,0 +1,791 @@
1 +//! PaganDE: panel + pasek zadań.
2 +//!
3 +//! PO CO osobny program: panel to zwykły klient Waylanda, ale **nie** okno —
4 +//! to powierzchnia warstwy (`wlr-layer-shell`), która rezerwuje pas ekranu
5 +//! (`exclusive_zone`), więc okna nie wchodzą pod pasek.
6 +//!
7 +//! Co robi:
8 +//! * lista okien z kompozytora (`wlr-foreign-toplevel-management`) → kafelki,
9 +//! * kliknięcie kafelka aktywuje okno (i przywraca zminimalizowane),
10 +//! środkowy przycisk zamyka okno,
11 +//! * przycisk „Pagan” uruchamia terminal (prosty launcher),
12 +//! * zegar po prawej (odświeżany timerem calloop).
13 +//!
14 +//! Wygląd: ciemny, półprzezroczysty pasek z zaokrąglonymi DOLNYMI rogami i akcentem
15 +//! `#4ea1ff` — spójny z motywem kompozytora. Rysujemy Cairo do bufora SHM, dzieląc
16 +//! `ShmBuffer` z `pagan-toolkit`.
17 +
18 +use std::{process::Command, time::Duration};
19 +
20 +use cairo::{Context, FontSlant, FontWeight, Format, ImageSurface};
21 +use calloop::{timer::TimeoutAction, EventLoop, LoopHandle};
22 +use calloop_wayland_source::WaylandSource;
23 +use chrono::Local;
24 +use memmap2::MmapMut;
25 +use tracing::{error, info, warn};
26 +
27 +use wayland_client::{
28 + globals::{registry_queue_init, GlobalListContents},
29 + protocol::{
30 + wl_buffer, wl_callback, wl_compositor, wl_output, wl_pointer, wl_registry, wl_seat, wl_shm,
31 + wl_shm_pool, wl_surface,
32 + },
33 + Connection, Dispatch, Proxy, QueueHandle,
34 +};
35 +use wayland_protocols_wlr::{
36 + foreign_toplevel::v1::client::{
37 + zwlr_foreign_toplevel_handle_v1 as ft_handle,
38 + zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1,
39 + zwlr_foreign_toplevel_manager_v1 as ft_manager,
40 + zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1,
41 + },
42 + layer_shell::v1::client::{
43 + zwlr_layer_shell_v1::{self as layer_shell, Layer, ZwlrLayerShellV1},
44 + zwlr_layer_surface_v1::{self as layer_surface, Anchor, ZwlrLayerSurfaceV1},
45 + },
46 +};
47 +
48 +use pagan_toolkit::shm::ShmBuffer;
49 +
50 +// --- Geometria paska ---------------------------------------------------------
51 +
52 +/// Wysokość panelu (px logiczne) — zarazem `exclusive_zone`.
53 +const BAR_HEIGHT: i32 = 36;
54 +/// Promień zaokrąglenia dolnych rogów.
55 +const BAR_RADIUS: f64 = 12.0;
56 +/// Margines boczny zawartości.
57 +const PAD: f64 = 10.0;
58 +/// Odstęp między elementami.
59 +const GAP: f64 = 8.0;
60 +/// Wysokość „pigułek” (kafelków/przycisków).
61 +const PILL_H: f64 = 24.0;
62 +/// Szerokość przycisku launchera.
63 +const LAUNCHER_W: f64 = 92.0;
64 +/// Maksymalna szerokość kafelka okna.
65 +const TASK_MAX_W: f64 = 190.0;
66 +/// Minimalna szerokość kafelka okna.
67 +const TASK_MIN_W: f64 = 96.0;
68 +
69 +// --- Kolory (spójne z motywem kompozytora) -----------------------------------
70 +
71 +const BG: (f64, f64, f64, f64) = (0.090, 0.098, 0.125, 0.94);
72 +const ACCENT: (f64, f64, f64) = (0.306, 0.631, 1.0);
73 +const TEXT: (f64, f64, f64) = (0.87, 0.89, 0.93);
74 +const TEXT_DIM: (f64, f64, f64) = (0.66, 0.69, 0.75);
75 +const TASK_BG: (f64, f64, f64, f64) = (1.0, 1.0, 1.0, 0.07);
76 +const TASK_HOVER_BG: (f64, f64, f64, f64) = (1.0, 1.0, 1.0, 0.13);
77 +const TASK_ACTIVE_BG: (f64, f64, f64, f64) = (0.306, 0.631, 1.0, 0.22);
78 +
79 +/// Jedno okno widziane przez protokół listy okien.
80 +struct Task {
81 + handle: ZwlrForeignToplevelHandleV1,
82 + title: String,
83 + activated: bool,
84 + minimized: bool,
85 + /// Położenie i szerokość kafelka (liczone przy rysowaniu, używane do trafień).
86 + layout: (f64, f64),
87 +}
88 +
89 +/// Stan panelu.
90 +struct Panel {
91 + connection: Connection,
92 + qh: QueueHandle<Panel>,
93 +
94 + // Trzymane, by obiekty protokołu żyły tak długo jak panel (upuszczenie
95 + // zniszczyłoby `wl_surface`/warstwę). Nie czytamy ich pól.
96 + #[allow(dead_code)]
97 + compositor: wl_compositor::WlCompositor,
98 + #[allow(dead_code)]
99 + layer_shell: ZwlrLayerShellV1,
100 + #[allow(dead_code)]
101 + manager: ZwlrForeignToplevelManagerV1,
102 + shm: wl_shm::WlShm,
103 + #[allow(dead_code)]
104 + layer_surface: ZwlrLayerSurfaceV1,
105 + surface: wl_surface::WlSurface,
106 + seat: wl_seat::WlSeat,
107 + pointer_handle: Option<wl_pointer::WlPointer>,
108 + /// Callback klatki — trzymany, by kompozytor nie wysyłał zdarzeń do „martwego” obiektu.
109 + frame_cb: Option<wl_callback::WlCallback>,
110 +
111 + /// Rozmiar powierzchni z ostatniego `configure` (px logiczne).
112 + size: (i32, i32),
113 + tasks: Vec<Task>,
114 + /// Indeks kafelka pod kursorem.
115 + hover: Option<usize>,
116 + /// Czy kursor jest nad przyciskiem launchera.
117 + hover_launcher: bool,
118 + /// Ostatnia pozycja kursora w układzie powierzchni.
119 + pointer_pos: (f64, f64),
120 + launcher_rect: (f64, f64, f64, f64),
121 + clock: String,
122 + dirty: bool,
123 + closed: bool,
124 +
125 + /// Bufory czekające na zwolnienie (podmieniane przy każdej klatce).
126 + current: Option<ShmBuffer>,
127 + stale: Vec<ShmBuffer>,
128 +}
129 +
130 +impl Panel {
131 + fn update_clock(&mut self) {
132 + self.clock = Local::now().format("%H:%M").to_string();
133 + }
134 +
135 + /// Rysuje pasek do bufora SHM i wysyła go kompozytorowi.
136 + fn redraw(&mut self) {
137 + let (width, height) = self.size;
138 + if width <= 0 || height <= 0 || !self.dirty {
139 + return;
140 + }
141 +
142 + let mut buffer = match ShmBuffer::new(&self.shm, &self.qh, width, height) {
143 + Ok(buffer) => buffer,
144 + Err(err) => {
145 + error!(%err, "nie udało się zaalokować bufora panelu");
146 + return;
147 + }
148 + };
149 +
150 + self.paint(&mut buffer.mmap, width, height);
151 +
152 + self.surface.attach(Some(&buffer.buffer), 0, 0);
153 + self.surface.damage(0, 0, width, height);
154 + self.surface.commit();
155 + self.frame_cb = Some(self.surface.frame(&self.qh, ()));
156 + let _ = self.connection.flush();
157 +
158 + if let Some(previous) = self.current.replace(buffer) {
159 + self.stale.push(previous);
160 + }
161 + self.dirty = false;
162 + }
163 +
164 + /// Cała grafika paska (Cairo).
165 + fn paint(&mut self, mmap: &mut MmapMut, width: i32, height: i32) {
166 + let stride = width * 4;
167 + // Powierzchnia Cairo wskazuje wprost na pamięć bufora SHM (jak w toolkicie);
168 + // `flush()` na końcu wypycha piksele.
169 + let surface = unsafe {
170 + ImageSurface::create_for_data_unsafe(
171 + mmap.as_mut_ptr(),
172 + Format::ARgb32,
173 + width,
174 + height,
175 + stride,
176 + )
177 + }
178 + .expect("powierzchnia Cairo na buforze SHM");
179 + let ctx = Context::new(&surface).expect("kontekst Cairo");
180 +
181 + // Tło z zaokrąglonymi dolnymi rogami.
182 + ctx.set_source_rgba(BG.0, BG.1, BG.2, BG.3);
183 + bar_path(&ctx, width as f64, height as f64, BAR_RADIUS);
184 + ctx.fill().ok();
185 +
186 + // Akcentowa linia pod paskiem.
187 + ctx.set_source_rgba(ACCENT.0, ACCENT.1, ACCENT.2, 0.55);
188 + ctx.set_line_width(1.0);
189 + ctx.move_to(0.0, height as f64 - 0.5);
190 + ctx.line_to(width as f64, height as f64 - 0.5);
191 + ctx.stroke().ok();
192 +
193 + ctx.select_font_face("Sans", FontSlant::Normal, FontWeight::Normal);
194 + ctx.set_font_size(13.0);
195 +
196 + let pill_y = (height as f64 - PILL_H) / 2.0;
197 +
198 + // 1) Launcher.
199 + let launcher_bg = if self.hover_launcher {
200 + TASK_HOVER_BG
201 + } else {
202 + TASK_BG
203 + };
204 + draw_pill(&ctx, PAD, pill_y, LAUNCHER_W, PILL_H, launcher_bg);
205 + ctx.set_source_rgb(ACCENT.0, ACCENT.1, ACCENT.2);
206 + ctx.set_font_size(14.0);
207 + draw_centered_text(&ctx, "Pagan", PAD, pill_y, LAUNCHER_W, PILL_H);
208 + ctx.set_font_size(13.0);
209 + self.launcher_rect = (PAD, pill_y, LAUNCHER_W, PILL_H);
210 +
211 + // 2) Kafelki okien.
212 + let clock_w = 64.0;
213 + let right_limit = width as f64 - PAD - clock_w - GAP * 2.0;
214 + let mut x = PAD + LAUNCHER_W + GAP * 2.0;
215 +
216 + for (index, task) in self.tasks.iter_mut().enumerate() {
217 + let label = truncate(&ctx, &task.title, TASK_MAX_W - 16.0);
218 + let pill_w = (text_width(&ctx, &label) + 18.0).clamp(TASK_MIN_W, TASK_MAX_W);
219 + if x + pill_w > right_limit {
220 + break; // brak miejsca — dalsze okna nie mieszczą się na pasku
221 + }
222 + task.layout = (x, pill_w);
223 +
224 + let hovered = self.hover == Some(index);
225 + let background = if task.activated {
226 + TASK_ACTIVE_BG
227 + } else if hovered {
228 + TASK_HOVER_BG
229 + } else {
230 + TASK_BG
231 + };
232 + draw_pill(&ctx, x, pill_y, pill_w, PILL_H, background);
233 +
234 + // Kropka stanu.
235 + let dot = if task.activated {
236 + (ACCENT.0, ACCENT.1, ACCENT.2, 1.0)
237 + } else if task.minimized {
238 + (0.60, 0.60, 0.65, 0.70)
239 + } else {
240 + (0.45, 0.47, 0.52, 1.0)
241 + };
242 + ctx.set_source_rgba(dot.0, dot.1, dot.2, dot.3);
243 + ctx.arc(
244 + x + 9.0,
245 + pill_y + PILL_H / 2.0,
246 + 2.6,
247 + 0.0,
248 + std::f64::consts::TAU,
249 + );
250 + ctx.fill().ok();
251 +
252 + if task.activated {
253 + ctx.set_source_rgb(TEXT.0, TEXT.1, TEXT.2);
254 + } else {
255 + ctx.set_source_rgb(TEXT_DIM.0, TEXT_DIM.1, TEXT_DIM.2);
256 + }
257 + draw_text(&ctx, &label, x + 16.0, pill_y + PILL_H / 2.0);
258 +
259 + x += pill_w + GAP;
260 + }
261 +
262 + // 3) Zegar.
263 + ctx.set_source_rgb(TEXT.0, TEXT.1, TEXT.2);
264 + let clock = self.clock.clone();
265 + let clock_x = width as f64 - PAD - text_width(&ctx, &clock);
266 + draw_text(&ctx, &clock, clock_x, pill_y + PILL_H / 2.0);
267 +
268 + surface.flush();
269 + }
270 +
271 + fn launcher_contains(&self, (px, py): (f64, f64)) -> bool {
272 + let (x, y, w, h) = self.launcher_rect;
273 + px >= x && px <= x + w && py >= y && py <= y + h
274 + }
275 +
276 + /// Kafelek pod danym punktem.
277 + fn task_at(&self, (px, py): (f64, f64)) -> Option<usize> {
278 + let pill_y = (self.size.1 as f64 - PILL_H) / 2.0;
279 + self.tasks.iter().position(|task| {
280 + let (x, w) = task.layout;
281 + px >= x && px <= x + w && py >= pill_y && py <= pill_y + PILL_H
282 + })
283 + }
284 +
285 + /// Przelicza podświetlenia i — jeśli się zmieniły — przerysowuje pasek.
286 + fn refresh_hover(&mut self) {
287 + let point = self.pointer_pos;
288 + let new_task = self.task_at(point);
289 + let new_launcher = self.launcher_contains(point);
290 + if new_task != self.hover || new_launcher != self.hover_launcher {
291 + self.hover = new_task;
292 + self.hover_launcher = new_launcher;
293 + self.dirty = true;
294 + self.redraw();
295 + }
296 + }
297 +
298 + fn activate_task(&self, index: usize) {
299 + if let Some(task) = self.tasks.get(index) {
300 + task.handle.activate(&self.seat);
301 + }
302 + }
303 +
304 + fn close_task(&self, index: usize) {
305 + if let Some(task) = self.tasks.get(index) {
306 + task.handle.close();
307 + }
308 + }
309 +
310 + /// Prosty launcher: uruchamia pierwszy dostępny terminal.
311 + fn launch_terminal(&self) {
312 + for candidate in [
313 + "foot",
314 + "pagan-terminal",
315 + "xfce4-terminal",
316 + "kitty",
317 + "alacritty",
318 + "xterm",
319 + ] {
320 + if which(candidate) {
321 + match Command::new(candidate).spawn() {
322 + Ok(_) => {
323 + info!(terminal = candidate, "uruchomiono terminal");
324 + return;
325 + }
326 + Err(err) => warn!(terminal = candidate, %err, "nie udało się uruchomić"),
327 + }
328 + }
329 + }
330 + warn!("nie znalazłem terminala (foot/kitty/xterm/…) — launcher nic nie zrobił");
331 + }
332 +}
333 +
334 +// --- Pomocnicze rysowanie ----------------------------------------------------
335 +
336 +/// Ścieżka paska: pełna szerokość u góry, zaokrąglone DOLNE rogi.
337 +fn bar_path(ctx: &Context, width: f64, height: f64, radius: f64) {
338 + let r = radius.min(height);
339 + ctx.new_path();
340 + ctx.move_to(0.0, 0.0);
341 + ctx.line_to(width, 0.0);
342 + ctx.line_to(width, height - r);
343 + ctx.arc(width - r, height - r, r, 0.0, std::f64::consts::FRAC_PI_2);
344 + ctx.arc(
345 + r,
346 + height - r,
347 + r,
348 + std::f64::consts::FRAC_PI_2,
349 + std::f64::consts::PI,
350 + );
351 + ctx.close_path();
352 +}
353 +
354 +/// „Pigułka” (zaokrąglony prostokąt) wypełniona kolorem.
355 +fn draw_pill(ctx: &Context, x: f64, y: f64, w: f64, h: f64, color: (f64, f64, f64, f64)) {
356 + let r = h / 2.0;
357 + ctx.set_source_rgba(color.0, color.1, color.2, color.3);
358 + ctx.new_path();
359 + ctx.move_to(x + r, y);
360 + ctx.line_to(x + w - r, y);
361 + ctx.arc(x + w - r, y + r, r, -std::f64::consts::FRAC_PI_2, 0.0);
362 + ctx.arc(x + w - r, y + h - r, r, 0.0, std::f64::consts::FRAC_PI_2);
363 + ctx.line_to(x + r, y + h);
364 + ctx.arc(
365 + x + r,
366 + y + h - r,
367 + r,
368 + std::f64::consts::FRAC_PI_2,
369 + std::f64::consts::PI,
370 + );
371 + ctx.arc(
372 + x + r,
373 + y + r,
374 + r,
375 + std::f64::consts::PI,
376 + std::f64::consts::PI * 1.5,
377 + );
378 + ctx.close_path();
379 + ctx.fill().ok();
380 +}
381 +
382 +/// Tekst wyrównany do bazowej linii tak, by środek liter był na `center_y`.
383 +fn draw_text(ctx: &Context, text: &str, x: f64, center_y: f64) {
384 + let Ok(extents) = ctx.text_extents(text) else {
385 + return;
386 + };
387 + ctx.move_to(x, center_y - extents.y_bearing() / 2.0);
388 + ctx.show_text(text).ok();
389 +}
390 +
391 +/// Tekst wyśrodkowany w prostokącie.
392 +fn draw_centered_text(ctx: &Context, text: &str, x: f64, y: f64, w: f64, h: f64) {
393 + let Ok(extents) = ctx.text_extents(text) else {
394 + return;
395 + };
396 + ctx.move_to(
397 + x + (w - extents.x_advance()) / 2.0,
398 + y + h / 2.0 - extents.y_bearing() / 2.0,
399 + );
400 + ctx.show_text(text).ok();
401 +}
402 +
403 +fn text_width(ctx: &Context, text: &str) -> f64 {
404 + ctx.text_extents(text).map(|e| e.x_advance()).unwrap_or(0.0)
405 +}
406 +
407 +/// Skraca tytuł wielokropkiem, aż zmieści się w `max_width`.
408 +fn truncate(ctx: &Context, text: &str, max_width: f64) -> String {
409 + if text_width(ctx, text) <= max_width {
410 + return text.to_string();
411 + }
412 + let mut result = String::new();
413 + for character in text.chars() {
414 + let candidate = format!("{result}{character}…");
415 + if text_width(ctx, &candidate) > max_width {
416 + break;
417 + }
418 + result.push(character);
419 + }
420 + format!("{result}…")
421 +}
422 +
423 +/// Czy program jest w `PATH` (prosty launcher bez dodatkowych zależności).
424 +fn which(name: &str) -> bool {
425 + std::env::var_os("PATH")
426 + .map(|paths| std::env::split_paths(&paths).any(|dir| dir.join(name).is_file()))
427 + .unwrap_or(false)
428 +}
429 +
430 +// --- Dispatch Waylanda -------------------------------------------------------
431 +
432 +impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for Panel {
433 + fn event(
434 + _state: &mut Self,
435 + _proxy: &wl_registry::WlRegistry,
436 + _event: wl_registry::Event,
437 + _data: &GlobalListContents,
438 + _conn: &Connection,
439 + _qh: &QueueHandle<Self>,
440 + ) {
441 + }
442 +}
443 +
444 +/// Puste implementacje dla obiektów, których zdarzeń nie potrzebujemy.
445 +macro_rules! ignore_events {
446 + ($($iface:ty),* $(,)?) => {
447 + $(
448 + impl Dispatch<$iface, ()> for Panel {
449 + fn event(
450 + _state: &mut Self,
451 + _proxy: &$iface,
452 + _event: <$iface as Proxy>::Event,
453 + _data: &(),
454 + _conn: &Connection,
455 + _qh: &QueueHandle<Self>,
456 + ) {
457 + }
458 + }
459 + )*
460 + };
461 +}
462 +
463 +ignore_events!(
464 + wl_compositor::WlCompositor,
465 + wl_shm::WlShm,
466 + wl_shm_pool::WlShmPool,
467 + wl_buffer::WlBuffer,
468 + wl_output::WlOutput,
469 +);
470 +
471 +impl Dispatch<wl_callback::WlCallback, ()> for Panel {
472 + fn event(
473 + state: &mut Self,
474 + _proxy: &wl_callback::WlCallback,
475 + event: wl_callback::Event,
476 + _data: &(),
477 + _conn: &Connection,
478 + _qh: &QueueHandle<Self>,
479 + ) {
480 + if let wl_callback::Event::Done { .. } = event {
481 + // Kompozytor oddał klatkę — bufory z poprzednich klatek są wolne.
482 + state.stale.clear();
483 + state.frame_cb = None;
484 + }
485 + }
486 +}
487 +
488 +impl Dispatch<wl_surface::WlSurface, ()> for Panel {
489 + fn event(
490 + _state: &mut Self,
491 + _proxy: &wl_surface::WlSurface,
492 + _event: wl_surface::Event,
493 + _data: &(),
494 + _conn: &Connection,
495 + _qh: &QueueHandle<Self>,
496 + ) {
497 + }
498 +}
499 +
500 +impl Dispatch<wl_seat::WlSeat, ()> for Panel {
501 + fn event(
502 + state: &mut Self,
503 + seat: &wl_seat::WlSeat,
504 + event: wl_seat::Event,
505 + _data: &(),
506 + _conn: &Connection,
507 + qh: &QueueHandle<Self>,
508 + ) {
509 + if let wl_seat::Event::Capabilities { capabilities: caps } = event {
510 + // `caps` to `WEnum` (może nieść nieznaną wartość od nowszego serwera).
511 + let has_pointer = caps
512 + .into_result()
513 + .map(|caps| caps.contains(wl_seat::Capability::Pointer))
514 + .unwrap_or(false);
515 + if has_pointer && state.pointer_handle.is_none() {
516 + state.pointer_handle = Some(seat.get_pointer(qh, ()));
517 + }
518 + }
519 + }
520 +}
521 +
522 +impl Dispatch<wl_pointer::WlPointer, ()> for Panel {
523 + fn event(
524 + state: &mut Self,
525 + _pointer: &wl_pointer::WlPointer,
526 + event: wl_pointer::Event,
527 + _data: &(),
528 + _conn: &Connection,
529 + _qh: &QueueHandle<Self>,
530 + ) {
531 + match event {
532 + wl_pointer::Event::Enter {
533 + surface_x,
534 + surface_y,
535 + ..
536 + } => {
537 + state.pointer_pos = (surface_x, surface_y);
538 + state.refresh_hover();
539 + }
540 + wl_pointer::Event::Leave { .. } => {
541 + if state.hover.take().is_some() || state.hover_launcher {
542 + state.hover_launcher = false;
543 + state.dirty = true;
544 + state.redraw();
545 + }
546 + }
547 + wl_pointer::Event::Motion {
548 + surface_x,
549 + surface_y,
550 + ..
551 + } => {
552 + state.pointer_pos = (surface_x, surface_y);
553 + state.refresh_hover();
554 + }
555 + wl_pointer::Event::Button {
556 + button,
557 + state: button_state,
558 + ..
559 + } => {
560 + // BTN_LEFT = 0x110, BTN_MIDDLE = 0x112 (linux/input-event-codes.h)
561 + let pressed = button_state
562 + .into_result()
563 + .map(|state| state == wl_pointer::ButtonState::Pressed)
564 + .unwrap_or(false);
565 + if pressed {
566 + let point = state.pointer_pos;
567 + if state.launcher_contains(point) {
568 + state.launch_terminal();
569 + } else if let Some(index) = state.task_at(point) {
570 + match button {
571 + 0x112 => state.close_task(index),
572 + _ => state.activate_task(index),
573 + }
574 + }
575 + }
576 + }
577 + _ => {}
578 + }
579 + }
580 +}
581 +
582 +impl Dispatch<ZwlrLayerShellV1, ()> for Panel {
583 + fn event(
584 + _state: &mut Self,
585 + _proxy: &ZwlrLayerShellV1,
586 + _event: layer_shell::Event,
587 + _data: &(),
588 + _conn: &Connection,
589 + _qh: &QueueHandle<Self>,
590 + ) {
591 + }
592 +}
593 +
594 +impl Dispatch<ZwlrLayerSurfaceV1, ()> for Panel {
595 + fn event(
596 + state: &mut Self,
597 + layer_surface: &ZwlrLayerSurfaceV1,
598 + event: layer_surface::Event,
599 + _data: &(),
600 + _conn: &Connection,
601 + _qh: &QueueHandle<Self>,
602 + ) {
603 + match event {
604 + layer_surface::Event::Configure {
605 + serial,
606 + width,
607 + height,
608 + } => {
609 + // Kompozytor mówi, jak duża ma być powierzchnia — trzeba potwierdzić.
610 + layer_surface.ack_configure(serial);
611 + state.size = (width as i32, height as i32);
612 + info!(width, height, "panel skonfigurowany");
613 + state.dirty = true;
614 + state.redraw();
615 + }
616 + layer_surface::Event::Closed => {
617 + info!("kompozytor zamknął powierzchnię panelu — kończę");
618 + state.closed = true;
619 + }
620 + _ => {}
621 + }
622 + }
623 +}
624 +
625 +impl Dispatch<ZwlrForeignToplevelManagerV1, ()> for Panel {
626 + // Zdarzenie `toplevel` tworzy u klienta nowy obiekt uchwytu — trzeba wskazać
627 + // jego typ, inaczej `wayland-client` panikuje.
628 + wayland_client::event_created_child!(Panel, ZwlrForeignToplevelManagerV1, [
629 + ft_manager::EVT_TOPLEVEL_OPCODE => (ZwlrForeignToplevelHandleV1, ())
630 + ]);
631 +
632 + fn event(
633 + state: &mut Self,
634 + _manager: &ZwlrForeignToplevelManagerV1,
635 + event: ft_manager::Event,
636 + _data: &(),
637 + _conn: &Connection,
638 + _qh: &QueueHandle<Self>,
639 + ) {
640 + match event {
641 + ft_manager::Event::Toplevel { toplevel } => {
642 + state.tasks.push(Task {
643 + handle: toplevel,
644 + title: String::new(),
645 + activated: false,
646 + minimized: false,
647 + layout: (0.0, 0.0),
648 + });
649 + }
650 + ft_manager::Event::Finished => info!("kompozytor zakończył listę okien"),
651 + _ => {}
652 + }
653 + }
654 +}
655 +
656 +impl Dispatch<ZwlrForeignToplevelHandleV1, ()> for Panel {
657 + fn event(
658 + state: &mut Self,
659 + handle: &ZwlrForeignToplevelHandleV1,
660 + event: ft_handle::Event,
661 + _data: &(),
662 + _conn: &Connection,
663 + _qh: &QueueHandle<Self>,
664 + ) {
665 + let Some(index) = state
666 + .tasks
667 + .iter()
668 + .position(|task| task.handle.id() == handle.id())
669 + else {
670 + return;
671 + };
672 +
673 + match event {
674 + ft_handle::Event::Title { title } => state.tasks[index].title = title,
675 + ft_handle::Event::State { state: bytes } => {
676 + let mut activated = false;
677 + let mut minimized = false;
678 + for chunk in bytes.chunks_exact(4) {
679 + match u32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]) {
680 + 1 => minimized = true,
681 + 2 => activated = true,
682 + _ => {}
683 + }
684 + }
685 + state.tasks[index].activated = activated;
686 + state.tasks[index].minimized = minimized;
687 + }
688 + ft_handle::Event::Done => {
689 + // `done` zamyka porcję zmian — teraz odświeżamy pasek.
690 + state.dirty = true;
691 + state.redraw();
692 + }
693 + ft_handle::Event::Closed => {
694 + state.tasks.retain(|task| task.handle.id() != handle.id());
695 + state.dirty = true;
696 + state.redraw();
697 + }
698 + _ => {}
699 + }
700 + }
701 +}
702 +
703 +// --- Start -------------------------------------------------------------------
704 +
705 +fn main() -> Result<(), Box<dyn std::error::Error>> {
706 + tracing_subscriber::fmt()
707 + .with_env_filter(
708 + tracing_subscriber::EnvFilter::try_from_default_env()
709 + .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
710 + )
711 + .compact()
712 + .init();
713 +
714 + let connection = Connection::connect_to_env()?;
715 + let (globals, queue) = registry_queue_init::<Panel>(&connection)?;
716 + let qh = queue.handle();
717 + info!("panel PaganDE startuje");
718 +
719 + let compositor = globals.bind::<wl_compositor::WlCompositor, Panel, ()>(&qh, 4..=6, ())?;
720 + let shm = globals.bind::<wl_shm::WlShm, Panel, ()>(&qh, 1..=1, ())?;
721 + let layer_shell = globals.bind::<ZwlrLayerShellV1, Panel, ()>(&qh, 1..=4, ())?;
722 + let manager = globals.bind::<ZwlrForeignToplevelManagerV1, Panel, ()>(&qh, 1..=3, ())?;
723 + let seat = globals.bind::<wl_seat::WlSeat, Panel, ()>(&qh, 1..=7, ())?;
724 +
725 + let surface = compositor.create_surface(&qh, ());
726 + let layer_surface = layer_shell.get_layer_surface(
727 + &surface,
728 + None, // wyjście wybiera kompozytor
729 + Layer::Top,
730 + "pagan-panel".to_string(),
731 + &qh,
732 + (),
733 + );
734 +
735 + // Rezerwujemy pas u góry ekranu — okna nie będą pod niego wchodzić.
736 + layer_surface.set_anchor(Anchor::Top | Anchor::Left | Anchor::Right);
737 + layer_surface.set_exclusive_zone(BAR_HEIGHT);
738 + // Szerokość 0 = „rozciągnij” (bo zakotwiczone z lewej i prawej), wysokość stała.
739 + layer_surface.set_size(0, BAR_HEIGHT as u32);
740 + surface.commit();
741 +
742 + let mut panel = Panel {
743 + connection: connection.clone(),
744 + qh: qh.clone(),
745 + compositor,
746 + layer_shell,
747 + manager,
748 + shm,
749 + layer_surface,
750 + surface,
751 + seat,
752 + pointer_handle: None,
753 + frame_cb: None,
754 + size: (0, 0),
755 + tasks: Vec::new(),
756 + hover: None,
757 + hover_launcher: false,
758 + pointer_pos: (0.0, 0.0),
759 + launcher_rect: (0.0, 0.0, 0.0, 0.0),
760 + clock: String::new(),
761 + dirty: false,
762 + closed: false,
763 + current: None,
764 + stale: Vec::new(),
765 + };
766 + panel.update_clock();
767 +
768 + let mut event_loop: EventLoop<Panel> = EventLoop::try_new()?;
769 + let loop_handle: LoopHandle<'_, Panel> = event_loop.handle();
770 + WaylandSource::new(connection, queue).insert(loop_handle.clone())?;
771 +
772 + // Zegar: odświeżamy co 30 s (i tak zmienia się raz na minutę).
773 + loop_handle.insert_source(
774 + calloop::timer::Timer::from_duration(Duration::from_secs(30)),
775 + |_, _, data: &mut Panel| {
776 + data.update_clock();
777 + data.dirty = true;
778 + data.redraw();
779 + TimeoutAction::ToDuration(Duration::from_secs(30))
780 + },
781 + )?;
782 +
783 + info!("panel gotowy (warstwa górna, {BAR_HEIGHT} px)");
784 + while !panel.closed {
785 + if let Err(err) = event_loop.dispatch(Some(Duration::from_millis(250)), &mut panel) {
786 + error!(%err, "błąd pętli zdarzeń panelu");
787 + break;
788 + }
789 + }
790 + Ok(())
791 +}