-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathstack.c
More file actions
306 lines (264 loc) · 8.31 KB
/
Copy pathstack.c
File metadata and controls
306 lines (264 loc) · 8.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*
* stack.c - client stack management for somewm
*
* Manages Z-order (stacking) of windows using wlroots scene graph layers.
* Ported from AwesomeWM's stack.c, adapted for Wayland.
*
* Key differences from AwesomeWM:
* - Uses wlroots scene graph layers instead of XCB stacking
*/
#include "stack.h"
#include "ewmh.h"
#include "somewm_types.h"
#include "objects/client.h" /* For complete client_t definition */
#include "objects/drawin.h" /* For drawin stacking */
#include "globalconf.h" /* For globalconf.stack and globalconf.drawins */
#include "somewm_api.h"
#include <stdbool.h>
#include <wlr/types/wlr_scene.h>
#ifdef XWAYLAND
#include <wlr/xwayland.h>
#endif
/* Flag to mark stack as needing refresh */
static bool need_stack_refresh = false;
/* External references */
extern struct wlr_scene_tree *layers[NUM_LAYERS];
/*
* Stack management - uses globalconf.stack (matches AwesomeWM)
*/
void
stack_client_remove(Client *c)
{
foreach(client, globalconf.stack)
if (*client == c)
{
client_array_remove(&globalconf.stack, client);
break;
}
stack_windows();
}
/** Push the client at the beginning of the client stack.
* \param c The client to push.
*/
void
stack_client_push(Client *c)
{
stack_client_remove(c);
client_array_push(&globalconf.stack, c);
stack_windows();
}
/** Push the client at the end of the client stack.
* \param c The client to push.
*/
void
stack_client_append(Client *c)
{
stack_client_remove(c);
client_array_append(&globalconf.stack, c);
stack_windows();
}
void
stack_windows(void)
{
need_stack_refresh = true;
}
/*
* Layer classification
*/
/** Get the real layer of a client according to its attributes
* Matches AwesomeWM stack.c:client_layer_translator (lines 129-156)
* \param c The client
* \return The layer this client belongs in
*/
static window_layer_t
client_layer_translator(Client *c)
{
Client *focused;
if (!c)
return WINDOW_LAYER_NORMAL;
/* First deal with user-set attributes */
if (c->ontop)
return WINDOW_LAYER_ONTOP;
/* Fullscreen windows only get their own layer when they have focus.
* On Wayland, we also keep the fullscreen layer when the focused client
* is on a different screen, since the scene graph uses separate layers
* (unlike X11's flat stacking model where wibars are below all clients). */
focused = some_get_focused_client();
if (c->fullscreen && (focused == c || !focused || focused->screen != c->screen))
return WINDOW_LAYER_FULLSCREEN;
if (c->above)
return WINDOW_LAYER_ABOVE;
if (c->below)
return WINDOW_LAYER_BELOW;
/* Check for transient attribute */
if (c->transient_for)
return WINDOW_LAYER_IGNORE;
/* Then deal with window type */
switch (c->type) {
case WINDOW_TYPE_DESKTOP:
return WINDOW_LAYER_DESKTOP;
default:
break;
}
return WINDOW_LAYER_NORMAL;
}
/*
* Scene graph integration
*/
/** Get the wlroots scene layer for a window layer
* Maps our logical layers to wlroots scene graph layers
* \param layer Window layer
* \return Scene graph layer index
*/
static int
get_scene_layer(window_layer_t layer)
{
switch (layer) {
case WINDOW_LAYER_DESKTOP:
return LyrBg;
case WINDOW_LAYER_BELOW:
return LyrBottom;
case WINDOW_LAYER_NORMAL:
return LyrTile;
case WINDOW_LAYER_ABOVE:
return LyrTop;
case WINDOW_LAYER_FULLSCREEN:
return LyrFS;
case WINDOW_LAYER_ONTOP:
return LyrOverlay;
case WINDOW_LAYER_IGNORE:
case WINDOW_LAYER_COUNT:
default:
return LyrTile;
}
}
/** Stack a client relative to another within the same scene layer
* \param c Client to stack
* \param previous Previous client (stack above this one), or NULL
*/
static void
stack_client_relative(Client *c, Client *previous)
{
if (!c || !c->scene)
return;
if (previous && previous->scene) {
/* Ensure both nodes share the same scene parent.
* In X11, stacking is flat (xcb_configure_window works on any two windows).
* In wlroots, wlr_scene_node_place_above requires shared parents.
* Transients should visually stack with their parent regardless of
* which layer they were initially placed in. */
if (c->scene->node.parent != previous->scene->node.parent) {
wlr_scene_node_reparent(&c->scene->node, previous->scene->node.parent);
}
wlr_scene_node_place_above(&c->scene->node, &previous->scene->node);
} else {
/* No previous client, raise to top of layer */
wlr_scene_node_raise_to_top(&c->scene->node);
}
}
/** Stack transient windows above their parent
* Recursively stacks all transients above c
* \param c Parent client
* \param previous Last stacked window
* \return Last stacked transient (or c if no transients)
*/
static Client *
stack_transients_above(Client *c, Client *previous)
{
if (!c)
return previous;
/* Stack this client first */
stack_client_relative(c, previous);
previous = c;
/* Then stack all transients above it */
foreach(node, globalconf.stack) {
if ((*node)->transient_for == c) {
/* Recursively stack this transient and its transients */
previous = stack_transients_above(*node, previous);
}
}
return previous;
}
/** Refresh stacking order
* Ported from AwesomeWM stack.c:stack_refresh (lines 162-199)
* Applies computed stack order to wlroots scene graph
*/
void
stack_refresh(void)
{
window_layer_t layer;
Client *prev_in_layer[WINDOW_LAYER_COUNT];
int scene_layer;
if (!need_stack_refresh)
return;
/* Initialize previous pointers for each layer */
for (layer = 0; layer < WINDOW_LAYER_COUNT; layer++) {
prev_in_layer[layer] = NULL;
}
/* Process stack from bottom to top, organizing by layer */
foreach(node, globalconf.stack) {
if (!(*node) || !(*node)->scene)
continue;
/* Unmanaged (override_redirect) X11 clients bypass the window
* manager; they have no stacking attributes, so running them
* through client_layer_translator() returns LyrTile and drops
* Wine/Qt popups below their floating parents. mapnotify()
* placed them in LyrOverlay; skip them here so the placement
* survives. */
#ifdef XWAYLAND
if ((*node)->client_type == X11 &&
(*node)->surface.xwayland->override_redirect)
continue;
#endif
layer = client_layer_translator(*node);
/* Skip IGNORE layer (transients are handled with their parents) */
if (layer == WINDOW_LAYER_IGNORE)
continue;
/* Move client to correct scene graph layer if needed */
scene_layer = get_scene_layer(layer);
if ((void *)(*node)->scene->node.parent != (void *)layers[scene_layer]) {
wlr_scene_node_reparent(&(*node)->scene->node, layers[scene_layer]);
}
/* Stack client and its transients */
prev_in_layer[layer] = stack_transients_above(*node, prev_in_layer[layer]);
}
/* Stack drawins (wiboxes) - AwesomeWM stacks these after clients
* Layer is determined by: ontop property AND type property (AwesomeWM compat)
* - type="desktop" → LyrBg (below everything, like wallpaper)
* - type="dock" → LyrTop (above normal windows, like panels)
* - ontop=true → LyrOverlay (above everything except fullscreen)
* - otherwise → LyrTile (same as normal clients) */
foreach(drawin, globalconf.drawins) {
if (!(*drawin)->scene_tree)
continue;
/* Lock drawins are managed by some_activate_lua_lock() /
* some_promote_lock_cover() and must stay in LyrBlock while the
* session is locked. Without this skip, the normal ontop/type
* logic below would reparent them out of LyrBlock. */
if (session_is_locked() && some_is_lock_drawin(*drawin))
continue;
/* Determine layer based on type and ontop (AwesomeWM compatibility) */
if ((*drawin)->type == WINDOW_TYPE_DESKTOP ||
(*drawin)->type == WINDOW_TYPE_SPLASH) {
/* Desktop/splash type goes to background layer (below clients) */
scene_layer = LyrBg;
} else if ((*drawin)->ontop) {
/* ontop drawins go to overlay layer */
scene_layer = LyrOverlay;
} else if ((*drawin)->type == WINDOW_TYPE_DOCK) {
/* Dock type goes above normal windows */
scene_layer = LyrTop;
} else {
/* Normal drawins go to wibox layer (above clients but below ontop) */
scene_layer = LyrWibox;
}
/* Reparent to correct layer if needed */
if ((void *)(*drawin)->scene_tree->node.parent != (void *)layers[scene_layer]) {
wlr_scene_node_reparent(&(*drawin)->scene_tree->node, layers[scene_layer]);
}
/* Raise to top of its layer (drawins stack above clients in same layer) */
wlr_scene_node_raise_to_top(&(*drawin)->scene_tree->node);
}
ewmh_update_net_client_list_stacking();
need_stack_refresh = false;
}