ohmu
·
2026-10-18
1/*
2 * Attempt to consolidate unavoidable suck into one file, away from leeloo.c. This
3 * file is not meant to be pretty. We use a .h file with static inline
4 * functions instead of a separate .c module, or function pointers like sway, so
5 * that they will simply compile out if the chosen #defines leave them unused.
6 */
7
8/* Leave these functions first; they're used in the others */
9static inline int
10client_is_x11(Client *c)
11{
12#ifdef XWAYLAND
13 return c->type == X11;
14#endif
15 return 0;
16}
17
18static inline struct wlr_surface *
19client_surface(Client *c)
20{
21#ifdef XWAYLAND
22 if (client_is_x11(c))
23 return c->surface.xwayland->surface;
24#endif
25 return c->surface.xdg->surface;
26}
27
28static inline int
29toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl)
30{
31 struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface;
32 struct wlr_surface *root_surface;
33 struct wlr_layer_surface_v1 *layer_surface;
34 Client *c = NULL;
35 LayerSurface *l = NULL;
36 int type = -1;
37#ifdef XWAYLAND
38 struct wlr_xwayland_surface *xsurface;
39#endif
40
41 if (!s)
42 return -1;
43 root_surface = wlr_surface_get_root_surface(s);
44
45#ifdef XWAYLAND
46 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) {
47 c = xsurface->data;
48 type = c->type;
49 goto end;
50 }
51#endif
52
53 if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) {
54 l = layer_surface->data;
55 type = LayerShell;
56 goto end;
57 }
58
59 xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface);
60 while (xdg_surface) {
61 tmp_xdg_surface = NULL;
62 switch (xdg_surface->role) {
63 case WLR_XDG_SURFACE_ROLE_POPUP:
64 if (!xdg_surface->popup || !xdg_surface->popup->parent)
65 return -1;
66
67 tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent);
68
69 if (!tmp_xdg_surface)
70 return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl);
71
72 xdg_surface = tmp_xdg_surface;
73 break;
74 case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
75 c = xdg_surface->data;
76 type = c->type;
77 goto end;
78 case WLR_XDG_SURFACE_ROLE_NONE:
79 return -1;
80 }
81 }
82
83end:
84 if (pl)
85 *pl = l;
86 if (pc)
87 *pc = c;
88 return type;
89}
90
91/* The others */
92static inline void
93client_activate_surface(struct wlr_surface *s, int activated)
94{
95 struct wlr_xdg_toplevel *toplevel;
96#ifdef XWAYLAND
97 struct wlr_xwayland_surface *xsurface;
98 if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) {
99 wlr_xwayland_surface_activate(xsurface, activated);
100 return;
101 }
102#endif
103 if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s)))
104 wlr_xdg_toplevel_set_activated(toplevel, activated);
105}
106
107static inline uint32_t
108client_set_bounds(Client *c, int32_t width, int32_t height)
109{
110#ifdef XWAYLAND
111 if (client_is_x11(c))
112 return 0;
113#endif
114 if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
115 XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0
116 && (c->bounds.width != width || c->bounds.height != height)) {
117 c->bounds.width = width;
118 c->bounds.height = height;
119 return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height);
120 }
121 return 0;
122}
123
124static inline const char *
125client_get_appid(Client *c)
126{
127#ifdef XWAYLAND
128 if (client_is_x11(c))
129 return c->surface.xwayland->class ? c->surface.xwayland->class : "broken";
130#endif
131 return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken";
132}
133
134static inline void
135client_get_clip(Client *c, struct wlr_box *clip)
136{
137 *clip = (struct wlr_box){
138 .x = 0,
139 .y = 0,
140 .width = c->geom.width - c->bw,
141 .height = c->geom.height - c->bw,
142 };
143
144#ifdef XWAYLAND
145 if (client_is_x11(c))
146 return;
147#endif
148
149 clip->x = c->surface.xdg->geometry.x;
150 clip->y = c->surface.xdg->geometry.y;
151}
152
153static inline void
154client_get_geometry(Client *c, struct wlr_box *geom)
155{
156#ifdef XWAYLAND
157 if (client_is_x11(c)) {
158 geom->x = c->surface.xwayland->x;
159 geom->y = c->surface.xwayland->y;
160 geom->width = c->surface.xwayland->width;
161 geom->height = c->surface.xwayland->height;
162 return;
163 }
164#endif
165 *geom = c->surface.xdg->geometry;
166}
167
168static inline Client *
169client_get_parent(Client *c)
170{
171 Client *p = NULL;
172#ifdef XWAYLAND
173 if (client_is_x11(c)) {
174 if (c->surface.xwayland->parent)
175 toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL);
176 return p;
177 }
178#endif
179 if (c->surface.xdg->toplevel->parent)
180 toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL);
181 return p;
182}
183
184static inline int
185client_has_children(Client *c)
186{
187#ifdef XWAYLAND
188 if (client_is_x11(c))
189 return !wl_list_empty(&c->surface.xwayland->children);
190#endif
191 /* surface.xdg->link is never empty because it always contains at least the
192 * surface itself. */
193 return wl_list_length(&c->surface.xdg->link) > 1;
194}
195
196static inline const char *
197client_get_title(Client *c)
198{
199#ifdef XWAYLAND
200 if (client_is_x11(c))
201 return c->surface.xwayland->title ? c->surface.xwayland->title : "broken";
202#endif
203 return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title : "broken";
204}
205
206static inline int
207client_is_float_type(Client *c)
208{
209 struct wlr_xdg_toplevel *toplevel;
210 struct wlr_xdg_toplevel_state state;
211
212#ifdef XWAYLAND
213 if (client_is_x11(c)) {
214 struct wlr_xwayland_surface *surface = c->surface.xwayland;
215 xcb_size_hints_t *size_hints = surface->size_hints;
216 if (surface->modal)
217 return 1;
218
219 if (wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG)
220 || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH)
221 || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR)
222 || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY)) {
223 return 1;
224 }
225
226 return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0
227 && (size_hints->max_width == size_hints->min_width
228 || size_hints->max_height == size_hints->min_height);
229 }
230#endif
231
232 toplevel = c->surface.xdg->toplevel;
233 state = toplevel->current;
234 return toplevel->parent || (state.min_width != 0 && state.min_height != 0
235 && (state.min_width == state.max_width
236 || state.min_height == state.max_height));
237}
238
239static inline int
240client_is_rendered_on_mon(Client *c, Monitor *m)
241{
242 /* This is needed for when you don't want to check formal assignment,
243 * but rather actual displaying of the pixels.
244 * Usually VISIBLEON suffices and is also faster. */
245 struct wlr_surface_output *s;
246 int unused_lx, unused_ly;
247 if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly))
248 return 0;
249 wl_list_for_each(s, &client_surface(c)->current_outputs, link)
250 if (s->output == m->wlr_output)
251 return 1;
252 return 0;
253}
254
255static inline int
256client_is_stopped(Client *c)
257{
258 int pid;
259 siginfo_t in = {0};
260#ifdef XWAYLAND
261 if (client_is_x11(c))
262 return 0;
263#endif
264
265 wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL);
266 if (waitid(P_PID, pid, &in, WNOHANG|WCONTINUED|WSTOPPED|WNOWAIT) < 0) {
267 /* This process is not our child process, while is very unlikely that
268 * it is stopped, in order to do not skip frames, assume that it is. */
269 if (errno == ECHILD)
270 return 1;
271 } else if (in.si_pid) {
272 if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED)
273 return 1;
274 if (in.si_code == CLD_CONTINUED)
275 return 0;
276 }
277
278 return 0;
279}
280
281static inline int
282client_is_unmanaged(Client *c)
283{
284#ifdef XWAYLAND
285 if (client_is_x11(c))
286 return c->surface.xwayland->override_redirect;
287#endif
288 return 0;
289}
290
291static inline void
292client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb)
293{
294 if (kb)
295 wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes,
296 kb->num_keycodes, &kb->modifiers);
297 else
298 wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL);
299}
300
301static inline void
302client_send_close(Client *c)
303{
304#ifdef XWAYLAND
305 if (client_is_x11(c)) {
306 wlr_xwayland_surface_close(c->surface.xwayland);
307 return;
308 }
309#endif
310 wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel);
311}
312
313static inline void
314client_set_border_color(Client *c, const float inner[static 4], const float outer[static 4])
315{
316 int i;
317 for (i = 0; i < 4; i++) {
318 wlr_scene_rect_set_color(c->inner_border[i], inner);
319 wlr_scene_rect_set_color(c->outer_border[i], outer);
320 }
321}
322
323static inline void
324client_set_fullscreen(Client *c, int fullscreen)
325{
326#ifdef XWAYLAND
327 if (client_is_x11(c)) {
328 wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen);
329 return;
330 }
331#endif
332 wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen);
333}
334
335static inline void
336client_set_scale(struct wlr_surface *s, float scale)
337{
338 wlr_fractional_scale_v1_notify_scale(s, scale);
339 wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale));
340}
341
342static inline uint32_t
343client_set_size(Client *c, uint32_t width, uint32_t height)
344{
345#ifdef XWAYLAND
346 if (client_is_x11(c)) {
347 wlr_xwayland_surface_configure(c->surface.xwayland,
348 c->geom.x + c->bw, c->geom.y + c->bw, width, height);
349 return 0;
350 }
351#endif
352 if ((int32_t)width == c->surface.xdg->toplevel->current.width
353 && (int32_t)height == c->surface.xdg->toplevel->current.height)
354 return 0;
355 return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height);
356}
357
358static inline void
359client_set_tiled(Client *c, uint32_t edges)
360{
361#ifdef XWAYLAND
362 if (client_is_x11(c)) {
363 wlr_xwayland_surface_set_maximized(c->surface.xwayland,
364 edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE);
365 return;
366 }
367#endif
368 if (wl_resource_get_version(c->surface.xdg->toplevel->resource)
369 >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) {
370 wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges);
371 } else {
372 wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE);
373 }
374}
375
376static inline void
377client_set_suspended(Client *c, int suspended)
378{
379#ifdef XWAYLAND
380 if (client_is_x11(c))
381 return;
382#endif
383
384 wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended);
385}
386
387static inline int
388client_wants_focus(Client *c)
389{
390#ifdef XWAYLAND
391 return client_is_unmanaged(c)
392 && wlr_xwayland_surface_override_redirect_wants_focus(c->surface.xwayland)
393 && wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE;
394#endif
395 return 0;
396}
397
398static inline int
399client_wants_fullscreen(Client *c)
400{
401#ifdef XWAYLAND
402 if (client_is_x11(c))
403 return c->surface.xwayland->fullscreen;
404#endif
405 return c->surface.xdg->toplevel->requested.fullscreen;
406}