Commit ee4f8d4
8
files
+445
added
-37
deleted
@@ -454,6 +454,7 @@ dependencies = [
454
454
"libluna-render",
455
455
"rustix 0.38.44",
456
456
"wayland-protocols",
457
+ "wayland-protocols-wlr",
457
458
"wayland-server",
458
459
]
459
460
@@ -669,6 +670,7 @@ dependencies = [
669
670
"wayland-client",
670
671
"wayland-protocols",
671
672
"wayland-scanner",
673
+ "wayland-server",
672
674
]
673
675
674
676
[[package]]
@@ -19,4 +19,5 @@ libluna-platform.workspace = true
19
19
20
20
wayland-server = "0.31"
21
21
wayland-protocols = { version = "0.32", features = ["server"] }
22
+wayland-protocols-wlr = { version = "0.3", features = ["server"] }
22
23
rustix = { version = "0.38", features = ["event", "fs", "time", "mm"] }
@@ -4,11 +4,13 @@ use std::sync::Mutex;
4
4
5
5
use libluna_render::ImageData;
6
6
use wayland_protocols::xdg::shell::server::{xdg_popup, xdg_surface, xdg_toplevel};
7
+use wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_surface_v1;
7
8
use wayland_server::{
8
9
protocol::{wl_buffer, wl_callback, wl_compositor, wl_region, wl_surface},
9
10
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
10
11
};
11
12
13
+use crate::layer::LayerData;
12
14
use crate::shell::{XdgSurfaceData, XdgToplevelData};
13
15
use crate::shm::BufferData;
14
16
use crate::state::{State, Window};
@@ -23,6 +25,8 @@ pub struct SurfaceData {
23
25
pub buffer: Mutex<Option<wl_buffer::WlBuffer>>,
24
26
/// Slot okna w modelu (przydzielany przy pierwszym commicie).
25
27
pub slot: Mutex<Option<usize>>,
28
+ /// Powiązana powierzchnia warstwowa (`wlr-layer-shell`), jeśli klient ją utworzył.
29
+ pub layer: Mutex<Option<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1>>,
26
30
}
27
31
28
32
impl SurfaceData {
@@ -32,6 +36,7 @@ impl SurfaceData {
32
36
xdg_surface: Mutex::new(None),
33
37
buffer: Mutex::new(None),
34
38
slot: Mutex::new(None),
39
+ layer: Mutex::new(None),
35
40
}
36
41
}
37
42
}
@@ -138,8 +143,13 @@ impl Dispatch<wl_surface::WlSurface, SurfaceData> for State {
138
143
}
139
144
}
140
145
141
-/// Skomponuj powierzchnię do modelu okien (toplevel lub popup z buforem `wl_shm`).
146
+/// Skomponuj powierzchnię do modelu okien (toplevel, popup lub powierzchnia warstwowa).
142
147
fn composite(state: &mut State, surface: &wl_surface::WlSurface, data: &SurfaceData) {
148
+ // Powierzchnia warstwowa (panel/dock) — własna ścieżka (bez xdg_surface).
149
+ if let Some(ls) = data.layer.lock().unwrap().clone() {
150
+ composite_layer(state, surface, data, &ls);
151
+ return;
152
+ }
143
153
let Some(xdg_surface) = data.xdg_surface.lock().unwrap().clone() else {
144
154
return;
145
155
};
@@ -185,51 +195,144 @@ fn composite(state: &mut State, surface: &wl_surface::WlSurface, data: &SurfaceD
185
195
String::new()
186
196
};
187
197
198
+ let count = state.windows.lock().unwrap().len();
199
+ let (px, py) = popup_xy.unwrap_or((60.0 + count as f64 * 36.0, 60.0 + count as f64 * 36.0));
200
+ let (_, created) = upsert_window(
201
+ state, surface, data, px, py, w, h, z, &title, is_popup, false, false, image,
202
+ );
203
+ // Popup: wyślij konfigurację (rozmiar + pozycja).
204
+ if is_popup && created {
205
+ send_popup_configure(state, &xdg_surface, popup_xy.unwrap_or((0.0, 0.0)), w, h);
206
+ }
207
+ // Pozwól klientowi ponownie użyć bufora.
208
+ let _ = buffer.send_event(wl_buffer::Event::Release);
209
+}
210
+
211
+/// Skomponuj powierzchnię warstwową (`wlr-layer-shell`): konfiguracja + kotwiczenie.
212
+fn composite_layer(
213
+ state: &mut State,
214
+ surface: &wl_surface::WlSurface,
215
+ data: &SurfaceData,
216
+ ls: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
217
+) {
218
+ let Some(ld) = ls.data::<LayerData>() else {
219
+ return;
220
+ };
221
+ let (out_w, out_h) = output_size(state);
222
+ let buffer = data.buffer.lock().unwrap().clone();
223
+ // Pierwszy `commit` (zwykle bez bufora) → wyślij `configure` z rozmiarem.
224
+ if !*ld.configured.lock().unwrap() {
225
+ let st = *ld.state.lock().unwrap();
226
+ let (cw, ch) = st.configure_size(out_w, out_h);
227
+ let serial = state.next_serial();
228
+ let _ = ls.send_event(zwlr_layer_surface_v1::Event::Configure {
229
+ serial,
230
+ width: cw.max(0) as u32,
231
+ height: ch.max(0) as u32,
232
+ });
233
+ *ld.configured.lock().unwrap() = true;
234
+ if buffer.is_none() {
235
+ return;
236
+ }
237
+ }
238
+ let Some(buffer) = buffer else {
239
+ return;
240
+ };
241
+ let Some(buf_data) = buffer.data::<BufferData>() else {
242
+ return;
243
+ };
244
+ let (w, h) = (buf_data.width, buf_data.height);
245
+ let Some(rgba) = buf_data.to_rgba() else {
246
+ return;
247
+ };
248
+ let image = std::sync::Arc::new(ImageData::new(w as u32, h as u32, rgba));
249
+ let st = *ld.state.lock().unwrap();
250
+ let (x, y) = st.position(out_w, out_h, w as f64, h as f64);
251
+ let z = crate::layer::layer_z(st.layer);
252
+ upsert_window(
253
+ state, surface, data, x, y, w, h, z, "", false, true, true, image,
254
+ );
255
+ let _ = buffer.send_event(wl_buffer::Event::Release);
256
+}
257
+
258
+/// Dodaje nowe okno lub aktualizuje istniejące (slot trzymany w [`SurfaceData`]).
259
+#[allow(clippy::too_many_arguments)]
260
+fn upsert_window(
261
+ state: &mut State,
262
+ surface: &wl_surface::WlSurface,
263
+ data: &SurfaceData,
264
+ x: f64,
265
+ y: f64,
266
+ w: i32,
267
+ h: i32,
268
+ z: i32,
269
+ title: &str,
270
+ popup: bool,
271
+ layer: bool,
272
+ reposition: bool,
273
+ image: std::sync::Arc<ImageData>,
274
+) -> (usize, bool) {
188
275
let mut slot = data.slot.lock().unwrap();
189
276
let mut windows = state.windows.lock().unwrap();
190
277
let mut created = false;
191
278
let idx = match *slot {
192
- Some(i) => i,
279
+ Some(i) => {
280
+ if let Some(win) = windows.get_mut(i) {
281
+ // Pozycję stałą okna zmieniamy tylko dla powierzchni kotwiczonych.
282
+ if reposition {
283
+ win.x = x;
284
+ win.y = y;
285
+ win.z = z;
286
+ }
287
+ win.width = w;
288
+ win.height = h;
289
+ win.mapped = true;
290
+ win.popup = popup;
291
+ win.layer = layer;
292
+ if !title.is_empty() {
293
+ win.title = title.to_owned();
294
+ }
295
+ win.data = image;
296
+ }
297
+ i
298
+ }
193
299
None => {
194
300
let i = windows.len();
195
- let (px, py) = popup_xy.unwrap_or((60.0 + i as f64 * 36.0, 60.0 + i as f64 * 36.0));
196
301
windows.push(Window {
197
- x: px,
198
- y: py,
302
+ x,
303
+ y,
199
304
width: w,
200
305
height: h,
201
306
z,
202
- title: title.clone(),
203
- popup: is_popup,
307
+ title: title.to_owned(),
308
+ popup,
309
+ layer,
204
310
mapped: true,
205
- data: image.clone(),
311
+ data: image,
206
312
});
207
313
*slot = Some(i);
208
314
created = true;
209
315
i
210
316
}
211
317
};
212
- if let Some(window) = windows.get_mut(idx) {
213
- window.width = w;
214
- window.height = h;
215
- window.mapped = true;
216
- if !title.is_empty() {
217
- window.title = title;
218
- }
219
- window.data = image;
220
- }
221
318
drop(windows);
222
319
drop(slot);
223
320
// Zarejestruj powierzchnię okna (indeks = slot) do trasowania wejścia.
224
321
if created && state.slots.len() == idx {
225
322
state.slots.push(surface.clone());
226
323
}
227
- // Popup: wyślij konfigurację (rozmiar + pozycja).
228
- if is_popup && created {
229
- send_popup_configure(state, &xdg_surface, popup_xy.unwrap_or((0.0, 0.0)), w, h);
230
- }
231
- // Pozwól klientowi ponownie użyć bufora.
232
- let _ = buffer.send_event(wl_buffer::Event::Release);
324
+ (idx, created)
325
+}
326
+
327
+/// Rozmiar monitora (na razie pierwszego; model gotowy na wiele).
328
+fn output_size(state: &State) -> (f64, f64) {
329
+ state
330
+ .outputs
331
+ .lock()
332
+ .unwrap()
333
+ .first()
334
+ .map(|o| (o.width as f64, o.height as f64))
335
+ .unwrap_or((1280.0, 800.0))
233
336
}
234
337
235
338
/// Pozycja popupu względem treści rodzica (prostokąt kotwicy + offset).
236
339
new file mode 100644
@@ -0,0 +1,291 @@
1
+//! `zwlr_layer_shell_v1` / `zwlr_layer_surface_v1` — powierzchnie warstwowe (panele, docki).
2
+//!
3
+//! To fundament przyszłego pulpitu Luna: pasek górny, dock i tło to powierzchnie
4
+//! kotwiczone do krawędzi monitora, niezależne od zwykłych okien `xdg_toplevel`.
5
+
6
+use std::sync::Mutex;
7
+
8
+use wayland_protocols_wlr::layer_shell::v1::server::{
9
+ zwlr_layer_shell_v1 as layer_shell, zwlr_layer_surface_v1 as layer_surface,
10
+};
11
+use wayland_server::protocol::wl_surface::WlSurface;
12
+use wayland_server::{
13
+ Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum,
14
+};
15
+
16
+use crate::compositor::SurfaceData;
17
+use crate::state::State;
18
+
19
+/// Kotwice krawędzi (`zwlr_layer_surface_v1.anchor`).
20
+pub const ANCHOR_TOP: u32 = 1;
21
+pub const ANCHOR_BOTTOM: u32 = 2;
22
+pub const ANCHOR_LEFT: u32 = 4;
23
+pub const ANCHOR_RIGHT: u32 = 8;
24
+
25
+/// Zebrane właściwości powierzchni warstwowej.
26
+#[derive(Debug, Clone, Copy, Default)]
27
+pub struct LayerState {
28
+ /// Warstwa (`background`/`bottom`/`top`/`overlay`).
29
+ pub layer: u32,
30
+ /// Bitmaska kotwic.
31
+ pub anchor: u32,
32
+ /// Żądana szerokość (0 = wybiera kompozytor/klient).
33
+ pub width: i32,
34
+ /// Żądana wysokość (0 = wybiera kompozytor/klient).
35
+ pub height: i32,
36
+ /// Strefa wyłączna (zarezerwowane miejsce na krawędzi).
37
+ pub exclusive_zone: i32,
38
+ pub margin_top: i32,
39
+ pub margin_right: i32,
40
+ pub margin_bottom: i32,
41
+ pub margin_left: i32,
42
+ /// `none`/`exclusive`/`on_demand`.
43
+ pub keyboard_interactivity: u32,
44
+}
45
+
46
+impl LayerState {
47
+ fn anchors(&self) -> (bool, bool, bool, bool) {
48
+ (
49
+ self.anchor & ANCHOR_LEFT != 0,
50
+ self.anchor & ANCHOR_RIGHT != 0,
51
+ self.anchor & ANCHOR_TOP != 0,
52
+ self.anchor & ANCHOR_BOTTOM != 0,
53
+ )
54
+ }
55
+
56
+ /// Rozmiar, jaki kompozytor proponuje klientowi w `configure`.
57
+ pub fn configure_size(&self, out_w: f64, out_h: f64) -> (i32, i32) {
58
+ let (left, right, top, bottom) = self.anchors();
59
+ let w = if left && right && self.width == 0 {
60
+ (out_w - self.margin_left as f64 - self.margin_right as f64).max(0.0) as i32
61
+ } else {
62
+ self.width
63
+ };
64
+ let h = if top && bottom && self.height == 0 {
65
+ (out_h - self.margin_top as f64 - self.margin_bottom as f64).max(0.0) as i32
66
+ } else {
67
+ self.height
68
+ };
69
+ (w, h)
70
+ }
71
+
72
+ /// Pozycja lewego-górnego narożnika na monitorze (z uwzględnieniem kotwic i marginesów).
73
+ pub fn position(&self, out_w: f64, out_h: f64, w: f64, h: f64) -> (f64, f64) {
74
+ let (left, right, top, bottom) = self.anchors();
75
+ let x = if left {
76
+ self.margin_left as f64
77
+ } else if right {
78
+ out_w - self.margin_right as f64 - w
79
+ } else {
80
+ ((out_w - w) / 2.0).max(0.0)
81
+ };
82
+ let y = if top {
83
+ self.margin_top as f64
84
+ } else if bottom {
85
+ out_h - self.margin_bottom as f64 - h
86
+ } else {
87
+ ((out_h - h) / 2.0).max(0.0)
88
+ };
89
+ (x, y)
90
+ }
91
+}
92
+
93
+/// Dane powierzchni warstwowej.
94
+pub struct LayerData {
95
+ /// Powiązana powierzchnia `wl_surface`.
96
+ pub surface: WlSurface,
97
+ /// Właściwości (aktualizowane przez `set_*`).
98
+ pub state: Mutex<LayerState>,
99
+ /// Czy wysłano już `configure`.
100
+ pub configured: Mutex<bool>,
101
+}
102
+
103
+impl LayerData {
104
+ pub fn new(surface: WlSurface, layer: u32) -> Self {
105
+ Self {
106
+ surface,
107
+ state: Mutex::new(LayerState {
108
+ layer,
109
+ ..LayerState::default()
110
+ }),
111
+ configured: Mutex::new(false),
112
+ }
113
+ }
114
+}
115
+
116
+/// Kolejność rysowania wynikająca z warstwy (panele nad oknami, tło pod nimi).
117
+pub fn layer_z(layer: u32) -> i32 {
118
+ match layer {
119
+ 0 => -200, // background
120
+ 1 => -100, // bottom
121
+ 2 => 1_000_000, // top
122
+ _ => 2_000_000, // overlay
123
+ }
124
+}
125
+
126
+fn anchor_bits(a: WEnum<layer_surface::Anchor>) -> u32 {
127
+ match a {
128
+ WEnum::Value(v) => v.bits(),
129
+ WEnum::Unknown(v) => v,
130
+ }
131
+}
132
+
133
+impl GlobalDispatch<layer_shell::ZwlrLayerShellV1, ()> for State {
134
+ fn bind(
135
+ _state: &mut Self,
136
+ _handle: &DisplayHandle,
137
+ _client: &Client,
138
+ resource: New<layer_shell::ZwlrLayerShellV1>,
139
+ _data: &(),
140
+ data_init: &mut DataInit<'_, Self>,
141
+ ) {
142
+ data_init.init(resource, ());
143
+ }
144
+}
145
+
146
+impl Dispatch<layer_shell::ZwlrLayerShellV1, ()> for State {
147
+ fn request(
148
+ _state: &mut Self,
149
+ _client: &Client,
150
+ _resource: &layer_shell::ZwlrLayerShellV1,
151
+ request: layer_shell::Request,
152
+ _data: &(),
153
+ _handle: &DisplayHandle,
154
+ data_init: &mut DataInit<'_, Self>,
155
+ ) {
156
+ if let layer_shell::Request::GetLayerSurface {
157
+ id, surface, layer, ..
158
+ } = request
159
+ {
160
+ let layer = match layer {
161
+ WEnum::Value(v) => v as u32,
162
+ WEnum::Unknown(v) => v,
163
+ };
164
+ let ls = data_init.init(id, LayerData::new(surface.clone(), layer));
165
+ // Zapamiętaj powiązanie po stronie `wl_surface` (potrzebne przy `commit`).
166
+ if let Some(sd) = surface.data::<SurfaceData>() {
167
+ *sd.layer.lock().unwrap() = Some(ls);
168
+ }
169
+ }
170
+ }
171
+}
172
+
173
+impl Dispatch<layer_surface::ZwlrLayerSurfaceV1, LayerData> for State {
174
+ fn request(
175
+ state: &mut Self,
176
+ _client: &Client,
177
+ resource: &layer_surface::ZwlrLayerSurfaceV1,
178
+ request: layer_surface::Request,
179
+ data: &LayerData,
180
+ _handle: &DisplayHandle,
181
+ _data_init: &mut DataInit<'_, Self>,
182
+ ) {
183
+ let mut st = data.state.lock().unwrap();
184
+ match request {
185
+ layer_surface::Request::SetSize { width, height } => {
186
+ st.width = width as i32;
187
+ st.height = height as i32;
188
+ }
189
+ layer_surface::Request::SetAnchor { anchor } => {
190
+ st.anchor = anchor_bits(anchor);
191
+ }
192
+ layer_surface::Request::SetExclusiveZone { zone } => {
193
+ st.exclusive_zone = zone;
194
+ }
195
+ layer_surface::Request::SetMargin {
196
+ top,
197
+ right,
198
+ bottom,
199
+ left,
200
+ } => {
201
+ st.margin_top = top;
202
+ st.margin_right = right;
203
+ st.margin_bottom = bottom;
204
+ st.margin_left = left;
205
+ }
206
+ layer_surface::Request::SetKeyboardInteractivity {
207
+ keyboard_interactivity,
208
+ } => {
209
+ st.keyboard_interactivity = match keyboard_interactivity {
210
+ WEnum::Value(v) => v as u32,
211
+ WEnum::Unknown(v) => v,
212
+ };
213
+ }
214
+ layer_surface::Request::SetLayer { layer } => {
215
+ st.layer = match layer {
216
+ WEnum::Value(v) => v as u32,
217
+ WEnum::Unknown(v) => v,
218
+ };
219
+ }
220
+ layer_surface::Request::Destroy => {
221
+ // Zdejmij powierzchnię z widoku.
222
+ drop(st);
223
+ if let Some(sd) = data.surface.data::<SurfaceData>() {
224
+ if let Some(slot) = *sd.slot.lock().unwrap() {
225
+ if let Some(w) = state.windows.lock().unwrap().get_mut(slot) {
226
+ w.mapped = false;
227
+ }
228
+ }
229
+ }
230
+ let _ = resource;
231
+ }
232
+ // `get_popup` i pozostałe: poza zakresem MVP.
233
+ _ => {}
234
+ }
235
+ }
236
+}
237
+
238
+#[cfg(test)]
239
+mod tests {
240
+ use super::*;
241
+
242
+ fn st(anchor: u32, width: i32, height: i32) -> LayerState {
243
+ LayerState {
244
+ anchor,
245
+ width,
246
+ height,
247
+ ..LayerState::default()
248
+ }
249
+ }
250
+
251
+ #[test]
252
+ fn top_panel_stretches_full_width() {
253
+ // Górny pasek: TOP|LEFT|RIGHT, wysokość 32, szerokość 0 → pełna szerokość.
254
+ let s = st(ANCHOR_TOP | ANCHOR_LEFT | ANCHOR_RIGHT, 0, 32);
255
+ assert_eq!(s.configure_size(1280.0, 800.0), (1280, 32));
256
+ assert_eq!(s.position(1280.0, 800.0, 1280.0, 32.0), (0.0, 0.0));
257
+ }
258
+
259
+ #[test]
260
+ fn bottom_right_dock_is_placed_at_corner() {
261
+ // Dock przy prawym-dolnym narożniku, marginesy 8.
262
+ let mut s = st(ANCHOR_BOTTOM | ANCHOR_RIGHT, 200, 400);
263
+ s.margin_right = 8;
264
+ s.margin_bottom = 8;
265
+ assert_eq!(s.configure_size(1280.0, 800.0), (200, 400));
266
+ assert_eq!(s.position(1280.0, 800.0, 200.0, 400.0), (1072.0, 392.0));
267
+ }
268
+
269
+ #[test]
270
+ fn unanchored_surface_is_centered() {
271
+ let s = st(0, 400, 200);
272
+ assert_eq!(s.position(1280.0, 800.0, 400.0, 200.0), (440.0, 300.0));
273
+ }
274
+
275
+ #[test]
276
+ fn exclusive_zone_does_not_resize_below_zero() {
277
+ let mut s = st(ANCHOR_TOP | ANCHOR_LEFT | ANCHOR_RIGHT, 0, 0);
278
+ // Marginesy większe niż ekran nie mogą dać ujemnego rozmiaru.
279
+ s.margin_left = 5000;
280
+ assert_eq!(s.configure_size(1280.0, 800.0).0, 0);
281
+ }
282
+
283
+ #[test]
284
+ fn layer_order_puts_top_above_windows_and_background_below() {
285
+ assert!(layer_z(2) > 0);
286
+ assert!(layer_z(3) > layer_z(2));
287
+ assert!(layer_z(0) < 0);
288
+ // `bottom` leży nad `background`.
289
+ assert!(layer_z(1) > layer_z(0));
290
+ }
291
+}
@@ -7,6 +7,7 @@
7
7
8
8
mod compositor;
9
9
mod input;
10
+mod layer;
10
11
mod monitors;
11
12
mod output;
12
13
mod seat;
@@ -20,6 +21,7 @@ use std::sync::Arc;
20
21
21
22
use libluna_core::Event;
22
23
use wayland_protocols::xdg::shell::server::xdg_wm_base;
24
+use wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_shell_v1;
23
25
use wayland_server::protocol::{wl_compositor, wl_output, wl_seat, wl_shm};
24
26
use wayland_server::{Display, ListeningSocket};
25
27
@@ -38,6 +40,7 @@ fn main() -> Result<(), Box<dyn Error>> {
38
40
handle.create_global::<State, wl_shm::WlShm, ()>(1, ());
39
41
handle.create_global::<State, wl_output::WlOutput, ()>(4, ());
40
42
handle.create_global::<State, xdg_wm_base::XdgWmBase, ()>(1, ());
43
+ handle.create_global::<State, zwlr_layer_shell_v1::ZwlrLayerShellV1, ()>(4, ());
41
44
handle.create_global::<State, wl_seat::WlSeat, ()>(1, ());
42
45
43
46
// Gniazdo nasłuchu: `wayland-0`, `wayland-1`, …
@@ -69,8 +69,8 @@ fn draw(frame: &mut Frame, model: &WindowModel) {
69
69
.fill_rect(w.content(), Color::from_rgba8(0x00, 0x00, 0x00, 0xff));
70
70
// Treść okna (bufor klienta).
71
71
frame.scene.image(w.content(), w.data.clone());
72
- // Dekoracja tylko dla topleveli (popupy jej nie mają).
73
- if !w.popup {
72
+ // Dekoracja tylko dla topleveli (popupy i panele jej nie mają).
73
+ if !w.popup && !w.layer {
74
74
frame
75
75
.scene
76
76
.fill_rect(w.titlebar(), Color::from_rgba8(0x2c, 0x2f, 0x38, 0xff));
@@ -104,13 +104,15 @@ fn draw(frame: &mut Frame, model: &WindowModel) {
104
104
);
105
105
}
106
106
}
107
- // Obramowanie całego okna.
108
- frame.scene.stroke_rounded_rect(
109
- w.outer(),
110
- 6.0,
111
- 1.0,
112
- Color::from_rgba8(0x50, 0x54, 0x60, 0xff),
113
- );
107
+ // Obramowanie całego okna (tylko zwykłe okna).
108
+ if !w.layer {
109
+ frame.scene.stroke_rounded_rect(
110
+ w.outer(),
111
+ 6.0,
112
+ 1.0,
113
+ Color::from_rgba8(0x50, 0x54, 0x60, 0xff),
114
+ );
115
+ }
114
116
}
115
117
}
116
118
@@ -463,7 +463,10 @@ fn raise_and_focus(state: &mut State, slot: usize) {
463
463
{
464
464
let mut windows = state.windows.lock().unwrap();
465
465
if let Some(w) = windows.get_mut(slot) {
466
- w.z = z;
466
+ // Powierzchnie warstwowe (panele/docki) trzymają stałą kolejność.
467
+ if !w.layer {
468
+ w.z = z;
469
+ }
467
470
}
468
471
}
469
472
set_keyboard_focus(state, slot);
@@ -702,6 +705,7 @@ mod tests {
702
705
z: 1,
703
706
title: "Test".to_owned(),
704
707
popup: false,
708
+ layer: false,
705
709
mapped: true,
706
710
data: Arc::new(ImageData::empty(200, 120)),
707
711
}
@@ -29,6 +29,8 @@ pub struct Window {
29
29
pub title: String,
30
30
/// Czy to `xdg_popup` (bez dekoracji, zamykany kliknięciem poza).
31
31
pub popup: bool,
32
+ /// Czy to powierzchnia `wlr-layer-shell` (panel/dock; bez dekoracji, kotwiczona).
33
+ pub layer: bool,
32
34
/// Czy powierzchnia jest widoczna (popup znika po `popup_done`).
33
35
pub mapped: bool,
34
36
/// Piksele (premultiplied RGBA8) ostatnio zacommitowanego bufora.
@@ -36,9 +38,9 @@ pub struct Window {
36
38
}
37
39
38
40
impl Window {
39
- /// Wysokość dekoracji (zero dla popupów).
41
+ /// Wysokość dekoracji (zero dla popupów i powierzchni warstwowych).
40
42
fn bar(&self) -> f64 {
41
- if self.popup {
43
+ if self.popup || self.layer {
42
44
0.0
43
45
} else {
44
46
TITLEBAR
@@ -72,7 +74,7 @@ impl Window {
72
74
73
75
/// Kwadratowy przycisk zamknięcia (tylko dla topleveli).
74
76
pub fn close_rect(&self) -> Rect {
75
- if self.popup {
77
+ if self.popup || self.layer {
76
78
return Rect::ZERO;
77
79
}
78
80
Rect::from_xywh(
@@ -85,7 +87,7 @@ impl Window {
85
87
86
88
/// Uchwyt przeskalowania (prawy-dolny narożnik treści; tylko dla topleveli).
87
89
pub fn grip_rect(&self) -> Rect {
88
- if self.popup {
90
+ if self.popup || self.layer {
89
91
return Rect::ZERO;
90
92
}
91
93
let c = self.content();