1 // SPDX-License-Identifier: MIT
2 // Copyright (c) 2011-2013, Christopher Jeffrey
3 // Copyright (c) 2013 Richard Grenville <pyxlcy@gmail.com>
4 
5 #include <X11/Xlib.h>
6 #include <X11/Xutil.h>
7 #include <math.h>
8 #include <stdbool.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <xcb/composite.h>
12 #include <xcb/damage.h>
13 #include <xcb/render.h>
14 #include <xcb/xcb.h>
15 #include <xcb/xcb_renderutil.h>
16 #include <xcb/xinerama.h>
17 
18 #include "atom.h"
19 #include "backend/backend.h"
20 #include "c2.h"
21 #include "common.h"
22 #include "compiler.h"
23 #include "config.h"
24 #include "list.h"
25 #include "log.h"
26 #include "picom.h"
27 #include "region.h"
28 #include "render.h"
29 #include "string_utils.h"
30 #include "types.h"
31 #include "uthash_extra.h"
32 #include "utils.h"
33 #include "x.h"
34 
35 #ifdef CONFIG_DBUS
36 #include "dbus.h"
37 #endif
38 
39 #ifdef CONFIG_OPENGL
40 // TODO remove this include
41 #include "opengl.h"
42 #endif
43 
44 #include "win.h"
45 
46 // TODO Make more window states internal
47 struct managed_win_internal {
48 	struct managed_win base;
49 };
50 
51 #define OPAQUE (0xffffffff)
52 static const int WIN_GET_LEADER_MAX_RECURSION = 20;
53 static const int ROUNDED_PIXELS = 1;
54 static const double ROUNDED_PERCENT = 0.05;
55 
56 /// Generate a "return by value" function, from a function that returns the
57 /// region via a region_t pointer argument.
58 /// Function signature has to be (win *, region_t *)
59 #define gen_by_val(fun)                                                                  \
60 	region_t fun##_by_val(const struct managed_win *w) {                             \
61 		region_t ret;                                                            \
62 		pixman_region32_init(&ret);                                              \
63 		fun(w, &ret);                                                            \
64 		return ret;                                                              \
65 	}
66 
67 /**
68  * Clear leader cache of all windows.
69  */
clear_cache_win_leaders(session_t * ps)70 static inline void clear_cache_win_leaders(session_t *ps) {
71 	win_stack_foreach_managed(w, &ps->window_stack) {
72 		w->cache_leader = XCB_NONE;
73 	}
74 }
75 
76 static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int recursions);
77 
78 /**
79  * Get the leader of a window.
80  *
81  * This function updates w->cache_leader if necessary.
82  */
win_get_leader(session_t * ps,struct managed_win * w)83 static inline xcb_window_t win_get_leader(session_t *ps, struct managed_win *w) {
84 	return win_get_leader_raw(ps, w, 0);
85 }
86 
87 /**
88  * Update focused state of a window.
89  */
win_update_focused(session_t * ps,struct managed_win * w)90 static void win_update_focused(session_t *ps, struct managed_win *w) {
91 	if (UNSET != w->focused_force) {
92 		w->focused = w->focused_force;
93 	} else {
94 		w->focused = win_is_focused_raw(ps, w);
95 
96 		// Use wintype_focus, and treat WM windows and override-redirected
97 		// windows specially
98 		if (ps->o.wintype_option[w->window_type].focus ||
99 		    (ps->o.mark_wmwin_focused && w->wmwin) ||
100 		    (ps->o.mark_ovredir_focused && w->base.id == w->client_win && !w->wmwin) ||
101 		    (w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
102 		     c2_match(ps, w, ps->o.focus_blacklist, NULL)))
103 			w->focused = true;
104 
105 		// If window grouping detection is enabled, mark the window active if
106 		// its group is
107 		if (ps->o.track_leader && ps->active_leader &&
108 		    win_get_leader(ps, w) == ps->active_leader) {
109 			w->focused = true;
110 		}
111 	}
112 }
113 
114 /**
115  * Run win_on_factor_change() on all windows with the same leader window.
116  *
117  * @param leader leader window ID
118  */
group_on_factor_change(session_t * ps,xcb_window_t leader)119 static inline void group_on_factor_change(session_t *ps, xcb_window_t leader) {
120 	if (!leader)
121 		return;
122 
123 	HASH_ITER2(ps->windows, w) {
124 		assert(!w->destroyed);
125 		if (!w->managed) {
126 			continue;
127 		}
128 		auto mw = (struct managed_win *)w;
129 		if (win_get_leader(ps, mw) == leader) {
130 			win_on_factor_change(ps, mw);
131 		}
132 	}
133 
134 	return;
135 }
136 
win_get_name_if_managed(const struct win * w)137 static inline const char *win_get_name_if_managed(const struct win *w) {
138 	if (!w->managed) {
139 		return "(unmanaged)";
140 	}
141 	auto mw = (struct managed_win *)w;
142 	return mw->name;
143 }
144 
145 /**
146  * Return whether a window group is really focused.
147  *
148  * @param leader leader window ID
149  * @return true if the window group is focused, false otherwise
150  */
group_is_focused(session_t * ps,xcb_window_t leader)151 static inline bool group_is_focused(session_t *ps, xcb_window_t leader) {
152 	if (!leader) {
153 		return false;
154 	}
155 
156 	HASH_ITER2(ps->windows, w) {
157 		assert(!w->destroyed);
158 		if (!w->managed) {
159 			continue;
160 		}
161 		auto mw = (struct managed_win *)w;
162 		if (win_get_leader(ps, mw) == leader && win_is_focused_raw(ps, mw)) {
163 			return true;
164 		}
165 	}
166 
167 	return false;
168 }
169 
170 /**
171  * Get a rectangular region a window occupies, excluding shadow.
172  */
win_get_region_local(const struct managed_win * w,region_t * res)173 static void win_get_region_local(const struct managed_win *w, region_t *res) {
174 	assert(w->widthb >= 0 && w->heightb >= 0);
175 	pixman_region32_fini(res);
176 	pixman_region32_init_rect(res, 0, 0, (uint)w->widthb, (uint)w->heightb);
177 }
178 
179 /**
180  * Get a rectangular region a window occupies, excluding frame and shadow.
181  */
win_get_region_noframe_local(const struct managed_win * w,region_t * res)182 void win_get_region_noframe_local(const struct managed_win *w, region_t *res) {
183 	const margin_t extents = win_calc_frame_extents(w);
184 
185 	int x = extents.left;
186 	int y = extents.top;
187 	int width = max2(w->g.width - (extents.left + extents.right), 0);
188 	int height = max2(w->g.height - (extents.top + extents.bottom), 0);
189 
190 	pixman_region32_fini(res);
191 	if (width > 0 && height > 0) {
192 		pixman_region32_init_rect(res, x, y, (uint)width, (uint)height);
193 	}
194 }
195 
win_get_region_frame_local(const struct managed_win * w,region_t * res)196 void win_get_region_frame_local(const struct managed_win *w, region_t *res) {
197 	const margin_t extents = win_calc_frame_extents(w);
198 	auto outer_width = extents.left + extents.right + w->g.width;
199 	auto outer_height = extents.top + extents.bottom + w->g.height;
200 	pixman_region32_fini(res);
201 	pixman_region32_init_rects(
202 	    res,
203 	    (rect_t[]){
204 	        // top
205 	        {.x1 = 0, .y1 = 0, .x2 = outer_width, .y2 = extents.top},
206 	        // bottom
207 	        {.x1 = 0, .y1 = outer_height - extents.bottom, .x2 = outer_width, .y2 = outer_height},
208 	        // left
209 	        {.x1 = 0, .y1 = 0, .x2 = extents.left, .y2 = outer_height},
210 	        // right
211 	        {.x1 = outer_width - extents.right, .y1 = 0, .x2 = outer_width, .y2 = outer_height},
212 	    },
213 	    4);
214 
215 	// limit the frame region to inside the window
216 	region_t reg_win;
217 	pixman_region32_init_rects(&reg_win, (rect_t[]){0, 0, outer_width, outer_height}, 1);
218 	pixman_region32_intersect(res, &reg_win, res);
219 	pixman_region32_fini(&reg_win);
220 }
221 
222 gen_by_val(win_get_region_frame_local);
223 
224 /**
225  * Add a window to damaged area.
226  *
227  * @param ps current session
228  * @param w struct _win element representing the window
229  */
add_damage_from_win(session_t * ps,const struct managed_win * w)230 void add_damage_from_win(session_t *ps, const struct managed_win *w) {
231 	// XXX there was a cached extents region, investigate
232 	//     if that's better
233 	region_t extents;
234 	pixman_region32_init(&extents);
235 	win_extents(w, &extents);
236 	add_damage(ps, &extents);
237 	pixman_region32_fini(&extents);
238 }
239 
240 /// Release the images attached to this window
win_release_pixmap(backend_t * base,struct managed_win * w)241 static inline void win_release_pixmap(backend_t *base, struct managed_win *w) {
242 	log_debug("Releasing pixmap of window %#010x (%s)", w->base.id, w->name);
243 	assert(w->win_image);
244 	if (w->win_image) {
245 		base->ops->release_image(base, w->win_image);
246 		w->win_image = NULL;
247 		// Bypassing win_set_flags, because `w` might have been destroyed
248 		w->flags |= WIN_FLAGS_PIXMAP_NONE;
249 	}
250 }
win_release_shadow(backend_t * base,struct managed_win * w)251 static inline void win_release_shadow(backend_t *base, struct managed_win *w) {
252 	log_debug("Releasing shadow of window %#010x (%s)", w->base.id, w->name);
253 	assert(w->shadow_image);
254 	if (w->shadow_image) {
255 		base->ops->release_image(base, w->shadow_image);
256 		w->shadow_image = NULL;
257 		// Bypassing win_set_flags, because `w` might have been destroyed
258 		w->flags |= WIN_FLAGS_SHADOW_NONE;
259 	}
260 }
261 
win_bind_pixmap(struct backend_base * b,struct managed_win * w)262 static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w) {
263 	assert(!w->win_image);
264 	auto pixmap = x_new_id(b->c);
265 	auto e = xcb_request_check(
266 	    b->c, xcb_composite_name_window_pixmap_checked(b->c, w->base.id, pixmap));
267 	if (e) {
268 		log_error("Failed to get named pixmap for window %#010x(%s)", w->base.id,
269 		          w->name);
270 		free(e);
271 		return false;
272 	}
273 	log_debug("New named pixmap for %#010x (%s) : %#010x", w->base.id, w->name, pixmap);
274 	w->win_image =
275 	    b->ops->bind_pixmap(b, pixmap, x_get_visual_info(b->c, w->a.visual), true);
276 	if (!w->win_image) {
277 		log_error("Failed to bind pixmap");
278 		win_set_flags(w, WIN_FLAGS_IMAGE_ERROR);
279 		return false;
280 	}
281 
282 	win_clear_flags(w, WIN_FLAGS_PIXMAP_NONE);
283 	return true;
284 }
285 
win_bind_shadow(struct backend_base * b,struct managed_win * w,struct color c,struct conv * kernel)286 bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
287                      struct conv *kernel) {
288 	assert(!w->shadow_image);
289 	assert(w->shadow);
290 	w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, kernel, c.red,
291 	                                        c.green, c.blue, c.alpha);
292 	if (!w->shadow_image) {
293 		log_error("Failed to bind shadow image, shadow will be disabled for "
294 		          "%#010x (%s)",
295 		          w->base.id, w->name);
296 		win_set_flags(w, WIN_FLAGS_SHADOW_NONE);
297 		w->shadow = false;
298 		return false;
299 	}
300 
301 	log_debug("New shadow for %#010x (%s)", w->base.id, w->name);
302 	win_clear_flags(w, WIN_FLAGS_SHADOW_NONE);
303 	return true;
304 }
305 
win_release_images(struct backend_base * backend,struct managed_win * w)306 void win_release_images(struct backend_base *backend, struct managed_win *w) {
307 	// We don't want to decide what we should do if the image we want to release is
308 	// stale (do we clear the stale flags or not?)
309 	// But if we are not releasing any images anyway, we don't care about the stale
310 	// flags.
311 
312 	if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
313 		assert(!win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE));
314 		win_release_pixmap(backend, w);
315 	}
316 
317 	if (!win_check_flags_all(w, WIN_FLAGS_SHADOW_NONE)) {
318 		assert(!win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE));
319 		win_release_shadow(backend, w);
320 	}
321 }
322 
win_process_update_flags(session_t * ps,struct managed_win * w)323 void win_process_update_flags(session_t *ps, struct managed_win *w) {
324 	if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
325 		map_win_start(ps, w);
326 		win_clear_flags(w, WIN_FLAGS_MAPPED);
327 	}
328 
329 	if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
330 		win_recheck_client(ps, w);
331 		win_clear_flags(w, WIN_FLAGS_CLIENT_STALE);
332 	}
333 }
334 
win_process_image_flags(session_t * ps,struct managed_win * w)335 void win_process_image_flags(session_t *ps, struct managed_win *w) {
336 	assert(!win_check_flags_all(w, WIN_FLAGS_MAPPED));
337 
338 	// Not a loop
339 	while (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) &&
340 	       !win_check_flags_all(w, WIN_FLAGS_IMAGE_ERROR)) {
341 		// Image needs to be updated, update it.
342 		if (!ps->backend_data) {
343 			// We are using legacy backend, nothing to do here.
344 			break;
345 		}
346 
347 		if (win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE)) {
348 			// Check to make sure the window is still mapped, otherwise we
349 			// won't be able to rebind pixmap after releasing it, yet we might
350 			// still need the pixmap for rendering.
351 			assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
352 			if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
353 				// Must release images first, otherwise breaks
354 				// NVIDIA driver
355 				win_release_pixmap(ps->backend_data, w);
356 			}
357 			win_bind_pixmap(ps->backend_data, w);
358 		}
359 
360 		if (win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE)) {
361 			if (!win_check_flags_all(w, WIN_FLAGS_SHADOW_NONE)) {
362 				win_release_shadow(ps->backend_data, w);
363 			}
364 			if (w->shadow) {
365 				win_bind_shadow(ps->backend_data, w,
366 				                (struct color){.red = ps->o.shadow_red,
367 				                               .green = ps->o.shadow_green,
368 				                               .blue = ps->o.shadow_blue,
369 				                               .alpha = ps->o.shadow_opacity},
370 				                ps->gaussian_map);
371 			}
372 		}
373 
374 		// break here, loop always run only once
375 		break;
376 	}
377 
378 	// Clear stale image flags
379 	if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE)) {
380 		win_clear_flags(w, WIN_FLAGS_IMAGES_STALE);
381 	}
382 }
383 
384 /**
385  * Check if a window has rounded corners.
386  * XXX This is really dumb
387  */
win_has_rounded_corners(const struct managed_win * w)388 static bool attr_pure win_has_rounded_corners(const struct managed_win *w) {
389 	if (!w->bounding_shaped) {
390 		return false;
391 	}
392 
393 	// Quit if border_size() returns XCB_NONE
394 	if (!pixman_region32_not_empty((region_t *)&w->bounding_shape)) {
395 		return false;
396 	}
397 
398 	// Determine the minimum width/height of a rectangle that could mark
399 	// a window as having rounded corners
400 	auto minwidth =
401 	    (uint16_t)max2(w->widthb * (1 - ROUNDED_PERCENT), w->widthb - ROUNDED_PIXELS);
402 	auto minheight =
403 	    (uint16_t)max2(w->heightb * (1 - ROUNDED_PERCENT), w->heightb - ROUNDED_PIXELS);
404 
405 	// Get the rectangles in the bounding region
406 	int nrects = 0;
407 	const rect_t *rects =
408 	    pixman_region32_rectangles((region_t *)&w->bounding_shape, &nrects);
409 
410 	// Look for a rectangle large enough for this window be considered
411 	// having rounded corners
412 	for (int i = 0; i < nrects; ++i) {
413 		if (rects[i].x2 - rects[i].x1 >= minwidth &&
414 		    rects[i].y2 - rects[i].y1 >= minheight) {
415 			return true;
416 		}
417 	}
418 	return false;
419 }
420 
win_update_name(session_t * ps,struct managed_win * w)421 int win_update_name(session_t *ps, struct managed_win *w) {
422 	XTextProperty text_prop = {NULL, XCB_NONE, 0, 0};
423 	char **strlst = NULL;
424 	int nstr = 0;
425 
426 	if (!w->client_win)
427 		return 0;
428 
429 	if (!(wid_get_text_prop(ps, w->client_win, ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) {
430 		log_trace("(%#010x): _NET_WM_NAME unset, falling back to WM_NAME.",
431 		          w->client_win);
432 
433 		if (!(XGetWMName(ps->dpy, w->client_win, &text_prop) && text_prop.value)) {
434 			return -1;
435 		}
436 		if (Success != XmbTextPropertyToTextList(ps->dpy, &text_prop, &strlst, &nstr) ||
437 		    !nstr || !strlst) {
438 			if (strlst)
439 				XFreeStringList(strlst);
440 			XFree(text_prop.value);
441 			return -1;
442 		}
443 		XFree(text_prop.value);
444 	}
445 
446 	int ret = 0;
447 	if (!w->name || strcmp(w->name, strlst[0]) != 0) {
448 		ret = 1;
449 		free(w->name);
450 		w->name = strdup(strlst[0]);
451 	}
452 
453 	XFreeStringList(strlst);
454 
455 	log_trace("(%#010x): client = %#010x, name = \"%s\", "
456 	          "ret = %d",
457 	          w->base.id, w->client_win, w->name, ret);
458 	return ret;
459 }
460 
win_get_role(session_t * ps,struct managed_win * w)461 int win_get_role(session_t *ps, struct managed_win *w) {
462 	char **strlst = NULL;
463 	int nstr = 0;
464 
465 	if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr))
466 		return -1;
467 
468 	int ret = 0;
469 	if (!w->role || strcmp(w->role, strlst[0]) != 0) {
470 		ret = 1;
471 		free(w->role);
472 		w->role = strdup(strlst[0]);
473 	}
474 
475 	XFreeStringList(strlst);
476 
477 	log_trace("(%#010x): client = %#010x, role = \"%s\", "
478 	          "ret = %d",
479 	          w->base.id, w->client_win, w->role, ret);
480 	return ret;
481 }
482 
483 /**
484  * Check if a window is bounding-shaped.
485  */
win_bounding_shaped(const session_t * ps,xcb_window_t wid)486 static inline bool win_bounding_shaped(const session_t *ps, xcb_window_t wid) {
487 	if (ps->shape_exists) {
488 		xcb_shape_query_extents_reply_t *reply;
489 		Bool bounding_shaped;
490 
491 		reply = xcb_shape_query_extents_reply(
492 		    ps->c, xcb_shape_query_extents(ps->c, wid), NULL);
493 		bounding_shaped = reply && reply->bounding_shaped;
494 		free(reply);
495 
496 		return bounding_shaped;
497 	}
498 
499 	return false;
500 }
501 
wid_get_prop_wintype(session_t * ps,xcb_window_t wid)502 static wintype_t wid_get_prop_wintype(session_t *ps, xcb_window_t wid) {
503 	winprop_t prop =
504 	    x_get_prop(ps, wid, ps->atoms->a_NET_WM_WINDOW_TYPE, 32L, XCB_ATOM_ATOM, 32);
505 
506 	for (unsigned i = 0; i < prop.nitems; ++i) {
507 		for (wintype_t j = 1; j < NUM_WINTYPES; ++j) {
508 			if (ps->atoms_wintypes[j] == (xcb_atom_t)prop.p32[i]) {
509 				free_winprop(&prop);
510 				return j;
511 			}
512 		}
513 	}
514 
515 	free_winprop(&prop);
516 
517 	return WINTYPE_UNKNOWN;
518 }
519 
520 static bool
wid_get_opacity_prop(session_t * ps,xcb_window_t wid,opacity_t def,opacity_t * out)521 wid_get_opacity_prop(session_t *ps, xcb_window_t wid, opacity_t def, opacity_t *out) {
522 	bool ret = false;
523 	*out = def;
524 
525 	winprop_t prop = x_get_prop(ps, wid, ps->atoms->a_NET_WM_WINDOW_OPACITY, 1L,
526 	                            XCB_ATOM_CARDINAL, 32);
527 
528 	if (prop.nitems) {
529 		*out = *prop.c32;
530 		ret = true;
531 	}
532 
533 	free_winprop(&prop);
534 
535 	return ret;
536 }
537 
538 // XXX should distinguish between frame has alpha and window body has alpha
win_has_alpha(const struct managed_win * w)539 bool win_has_alpha(const struct managed_win *w) {
540 	return w->pictfmt && w->pictfmt->type == XCB_RENDER_PICT_TYPE_DIRECT &&
541 	       w->pictfmt->direct.alpha_mask;
542 }
543 
win_client_has_alpha(const struct managed_win * w)544 bool win_client_has_alpha(const struct managed_win *w) {
545 	return w->client_pictfmt && w->client_pictfmt->type == XCB_RENDER_PICT_TYPE_DIRECT &&
546 	       w->client_pictfmt->direct.alpha_mask;
547 }
548 
win_calc_mode(const struct managed_win * w)549 winmode_t win_calc_mode(const struct managed_win *w) {
550 	if (w->opacity < 1.0) {
551 		return WMODE_TRANS;
552 	}
553 
554 	if (win_has_alpha(w)) {
555 		if (w->client_win == XCB_NONE) {
556 			// This is a window not managed by the WM, and it has alpha,
557 			// so it's transparent. No need to check WM frame.
558 			return WMODE_TRANS;
559 		}
560 		// The WM window has alpha
561 		if (win_client_has_alpha(w)) {
562 			// The client window also has alpha, the entire window is
563 			// transparent
564 			return WMODE_TRANS;
565 		}
566 		if (win_has_frame(w)) {
567 			// The client window doesn't have alpha, but we have a WM frame
568 			// window, which has alpha.
569 			return WMODE_FRAME_TRANS;
570 		}
571 		// Although the WM window has alpha, the frame window has 0 size, so
572 		// consider the window solid
573 	}
574 
575 	if (w->frame_opacity != 1.0 && win_has_frame(w)) {
576 		return WMODE_FRAME_TRANS;
577 	}
578 
579 	// log_trace("Window %#010x(%s) is solid", w->client_win, w->name);
580 	return WMODE_SOLID;
581 }
582 
583 /**
584  * Calculate and return the opacity target of a window.
585  *
586  * The priority of opacity settings are:
587  *
588  * inactive_opacity_override (if set, and unfocused) > _NET_WM_WINDOW_OPACITY (if set) >
589  * opacity-rules (if matched) > window type default opacity > active/inactive opacity
590  *
591  * @param ps           current session
592  * @param w            struct _win object representing the window
593  *
594  * @return target opacity
595  */
win_calc_opacity_target(session_t * ps,const struct managed_win * w)596 double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
597 	double opacity = 1;
598 
599 	if (w->state == WSTATE_UNMAPPED) {
600 		// be consistent
601 		return 0;
602 	}
603 	if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) {
604 		return 0;
605 	}
606 	// Try obeying opacity property and window type opacity firstly
607 	if (w->has_opacity_prop) {
608 		opacity = ((double)w->opacity_prop) / OPAQUE;
609 	} else if (w->opacity_is_set) {
610 		opacity = w->opacity_set;
611 	} else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity)) {
612 		opacity = ps->o.wintype_option[w->window_type].opacity;
613 	} else {
614 		// Respect active_opacity only when the window is physically focused
615 		if (win_is_focused_raw(ps, w))
616 			opacity = ps->o.active_opacity;
617 		else if (!w->focused)
618 			// Respect inactive_opacity in some cases
619 			opacity = ps->o.inactive_opacity;
620 	}
621 
622 	// respect inactive override
623 	if (ps->o.inactive_opacity_override && !w->focused) {
624 		opacity = ps->o.inactive_opacity;
625 	}
626 
627 	return opacity;
628 }
629 
630 /**
631  * Determine whether a window is to be dimmed.
632  */
win_should_dim(session_t * ps,const struct managed_win * w)633 bool win_should_dim(session_t *ps, const struct managed_win *w) {
634 	// Make sure we do nothing if the window is unmapped / being destroyed
635 	if (w->state == WSTATE_UNMAPPED) {
636 		return false;
637 	}
638 
639 	if (ps->o.inactive_dim > 0 && !(w->focused)) {
640 		return true;
641 	} else {
642 		return false;
643 	}
644 }
645 
646 /**
647  * Determine if a window should fade on opacity change.
648  */
win_should_fade(session_t * ps,const struct managed_win * w)649 bool win_should_fade(session_t *ps, const struct managed_win *w) {
650 	// To prevent it from being overwritten by last-paint value if the window is
651 	if (w->fade_force != UNSET) {
652 		return w->fade_force;
653 	}
654 	if (ps->o.no_fading_openclose && w->in_openclose) {
655 		return false;
656 	}
657 	if (ps->o.no_fading_destroyed_argb && w->state == WSTATE_DESTROYING &&
658 	    win_has_alpha(w) && w->client_win && w->client_win != w->base.id) {
659 		// deprecated
660 		return false;
661 	}
662 	// Ignore other possible causes of fading state changes after window
663 	// gets unmapped
664 	// if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
665 	//}
666 	if (c2_match(ps, w, ps->o.fade_blacklist, NULL)) {
667 		return false;
668 	}
669 	return ps->o.wintype_option[w->window_type].fade;
670 }
671 
672 /**
673  * Reread _COMPTON_SHADOW property from a window.
674  *
675  * The property must be set on the outermost window, usually the WM frame.
676  */
win_update_prop_shadow_raw(session_t * ps,struct managed_win * w)677 void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w) {
678 	winprop_t prop = x_get_prop(ps, w->base.id, ps->atoms->a_COMPTON_SHADOW, 1,
679 	                            XCB_ATOM_CARDINAL, 32);
680 
681 	if (!prop.nitems) {
682 		w->prop_shadow = -1;
683 	} else {
684 		w->prop_shadow = *prop.c32;
685 	}
686 
687 	free_winprop(&prop);
688 }
689 
win_set_shadow(session_t * ps,struct managed_win * w,bool shadow_new)690 static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new) {
691 	if (w->shadow == shadow_new) {
692 		return;
693 	}
694 
695 	log_debug("Updating shadow property of window %#010x (%s) to %d", w->base.id,
696 	          w->name, shadow_new);
697 
698 	if (w->state == WSTATE_UNMAPPED) {
699 		// No need to add damage or update shadow
700 		// Unmapped window shouldn't have any images
701 		w->shadow = shadow_new;
702 		assert(!w->shadow_image);
703 		assert(!w->win_image);
704 		assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_NONE));
705 		return;
706 	}
707 
708 	// Keep a copy of window extent before the shadow change. Will be used for
709 	// calculation of damaged region
710 	region_t extents;
711 	pixman_region32_init(&extents);
712 	win_extents(w, &extents);
713 
714 	// Apply the shadow change
715 	w->shadow = shadow_new;
716 
717 	// Add damage for shadow change
718 
719 	// Window extents need update on shadow state change
720 	// Shadow geometry currently doesn't change on shadow state change
721 	// calc_shadow_geometry(ps, w);
722 
723 	// Note: because the release and creation of the shadow images are delayed. When
724 	// multiple shadow changes happen in a row, without rendering phase between them,
725 	// there could be a stale shadow image attached to the window even if w->shadow
726 	// was previously false. And vice versa. So we check the STALE flag before
727 	// asserting the existence of the shadow image.
728 	if (w->shadow) {
729 		// Mark the new extents as damaged if the shadow is added
730 		assert(!w->shadow_image || win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE) ||
731 		       !ps->o.experimental_backends);
732 		pixman_region32_clear(&extents);
733 		win_extents(w, &extents);
734 		add_damage_from_win(ps, w);
735 	} else {
736 		// Mark the old extents as damaged if the shadow is removed
737 		assert(w->shadow_image || win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE) ||
738 		       !ps->o.experimental_backends);
739 		add_damage(ps, &extents);
740 	}
741 
742 	pixman_region32_fini(&extents);
743 
744 	// Delayed update of shadow image
745 	// By setting WIN_FLAGS_SHADOW_STALE, we ask win_process_flags to re-create or
746 	// release the shaodw in based on whether w->shadow is set.
747 	win_set_flags(w, WIN_FLAGS_SHADOW_STALE);
748 	ps->pending_updates = true;
749 }
750 
751 /**
752  * Determine if a window should have shadow, and update things depending
753  * on shadow state.
754  */
win_determine_shadow(session_t * ps,struct managed_win * w)755 static void win_determine_shadow(session_t *ps, struct managed_win *w) {
756 	log_debug("Determining shadow of window %#010x (%s)", w->base.id, w->name);
757 	bool shadow_new = w->shadow;
758 
759 	if (w->shadow_force != UNSET) {
760 		shadow_new = w->shadow_force;
761 	} else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
762 		shadow_new = true;
763 		if (!ps->o.wintype_option[w->window_type].shadow) {
764 			log_debug("Shadow disabled by wintypes");
765 			shadow_new = false;
766 		} else if (c2_match(ps, w, ps->o.shadow_blacklist, NULL)) {
767 			log_debug("Shadow disabled by shadow-exclude");
768 			shadow_new = false;
769 		} else if (ps->o.shadow_ignore_shaped && w->bounding_shaped &&
770 		           !w->rounded_corners) {
771 			log_debug("Shadow disabled by shadow-ignore-shaped");
772 			shadow_new = false;
773 		} else if (w->prop_shadow == 0) {
774 			log_debug("Shadow disabled by shadow property");
775 			shadow_new = false;
776 		}
777 	}
778 
779 	win_set_shadow(ps, w, shadow_new);
780 }
781 
782 /**
783  * Reread _COMPTON_SHADOW property from a window and update related
784  * things.
785  */
win_update_prop_shadow(session_t * ps,struct managed_win * w)786 void win_update_prop_shadow(session_t *ps, struct managed_win *w) {
787 	long attr_shadow_old = w->prop_shadow;
788 
789 	win_update_prop_shadow_raw(ps, w);
790 
791 	if (w->prop_shadow != attr_shadow_old)
792 		win_determine_shadow(ps, w);
793 }
794 
win_set_invert_color(session_t * ps,struct managed_win * w,bool invert_color_new)795 static void win_set_invert_color(session_t *ps, struct managed_win *w, bool invert_color_new) {
796 	if (w->invert_color == invert_color_new)
797 		return;
798 
799 	w->invert_color = invert_color_new;
800 
801 	add_damage_from_win(ps, w);
802 }
803 
804 /**
805  * Determine if a window should have color inverted.
806  */
win_determine_invert_color(session_t * ps,struct managed_win * w)807 static void win_determine_invert_color(session_t *ps, struct managed_win *w) {
808 	bool invert_color_new = w->invert_color;
809 
810 	if (UNSET != w->invert_color_force)
811 		invert_color_new = w->invert_color_force;
812 	else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
813 		invert_color_new = c2_match(ps, w, ps->o.invert_color_list, NULL);
814 
815 	win_set_invert_color(ps, w, invert_color_new);
816 }
817 
818 /**
819  * Set w->invert_color_force of a window.
820  */
win_set_invert_color_force(session_t * ps,struct managed_win * w,switch_t val)821 void win_set_invert_color_force(session_t *ps, struct managed_win *w, switch_t val) {
822 	if (val != w->invert_color_force) {
823 		w->invert_color_force = val;
824 		win_determine_invert_color(ps, w);
825 		queue_redraw(ps);
826 	}
827 }
828 
829 /**
830  * Set w->fade_force of a window.
831  *
832  * Doesn't affect fading already in progress
833  */
win_set_fade_force(struct managed_win * w,switch_t val)834 void win_set_fade_force(struct managed_win *w, switch_t val) {
835 	w->fade_force = val;
836 }
837 
838 /**
839  * Set w->focused_force of a window.
840  */
win_set_focused_force(session_t * ps,struct managed_win * w,switch_t val)841 void win_set_focused_force(session_t *ps, struct managed_win *w, switch_t val) {
842 	if (val != w->focused_force) {
843 		w->focused_force = val;
844 		win_on_factor_change(ps, w);
845 		queue_redraw(ps);
846 	}
847 }
848 
849 /**
850  * Set w->shadow_force of a window.
851  */
win_set_shadow_force(session_t * ps,struct managed_win * w,switch_t val)852 void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val) {
853 	if (val != w->shadow_force) {
854 		w->shadow_force = val;
855 		win_determine_shadow(ps, w);
856 		queue_redraw(ps);
857 	}
858 }
859 
860 static void
win_set_blur_background(session_t * ps,struct managed_win * w,bool blur_background_new)861 win_set_blur_background(session_t *ps, struct managed_win *w, bool blur_background_new) {
862 	if (w->blur_background == blur_background_new)
863 		return;
864 
865 	w->blur_background = blur_background_new;
866 
867 	// This damage might not be absolutely necessary (e.g. when the window is opaque),
868 	// but blur_background changes should be rare, so this should be fine.
869 	add_damage_from_win(ps, w);
870 }
871 
872 /**
873  * Determine if a window should have background blurred.
874  */
win_determine_blur_background(session_t * ps,struct managed_win * w)875 static void win_determine_blur_background(session_t *ps, struct managed_win *w) {
876 	if (w->a.map_state != XCB_MAP_STATE_VIEWABLE)
877 		return;
878 
879 	bool blur_background_new =
880 	    ps->o.blur_method && !c2_match(ps, w, ps->o.blur_background_blacklist, NULL);
881 
882 	win_set_blur_background(ps, w, blur_background_new);
883 }
884 
885 /**
886  * Update window opacity according to opacity rules.
887  */
win_update_opacity_rule(session_t * ps,struct managed_win * w)888 void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
889 	if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
890 		return;
891 	}
892 
893 	double opacity = 1.0;
894 	bool is_set = false;
895 	void *val = NULL;
896 	if (c2_match(ps, w, ps->o.opacity_rules, &val)) {
897 		opacity = ((double)(long)val) / 100.0;
898 		is_set = true;
899 	}
900 
901 	w->opacity_set = opacity;
902 	w->opacity_is_set = is_set;
903 }
904 
905 /**
906  * Function to be called on window data changes.
907  *
908  * TODO need better name
909  */
win_on_factor_change(session_t * ps,struct managed_win * w)910 void win_on_factor_change(session_t *ps, struct managed_win *w) {
911 	log_debug("Window %#010x (%s) factor change", w->base.id, w->name);
912 	// Focus needs to be updated first, as other rules might depend on the focused
913 	// state of the window
914 	win_update_focused(ps, w);
915 
916 	win_determine_shadow(ps, w);
917 	win_determine_invert_color(ps, w);
918 	win_determine_blur_background(ps, w);
919 	w->mode = win_calc_mode(w);
920 	log_debug("Window mode changed to %d", w->mode);
921 	win_update_opacity_rule(ps, w);
922 	if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
923 		w->paint_excluded = c2_match(ps, w, ps->o.paint_blacklist, NULL);
924 	if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
925 		w->unredir_if_possible_excluded =
926 		    c2_match(ps, w, ps->o.unredir_if_possible_blacklist, NULL);
927 
928 	win_update_opacity_target(ps, w);
929 
930 	w->reg_ignore_valid = false;
931 }
932 
933 /**
934  * Update cache data in struct _win that depends on window size.
935  */
win_on_win_size_change(session_t * ps,struct managed_win * w)936 void win_on_win_size_change(session_t *ps, struct managed_win *w) {
937 	w->widthb = w->g.width + w->g.border_width * 2;
938 	w->heightb = w->g.height + w->g.border_width * 2;
939 	w->shadow_dx = ps->o.shadow_offset_x;
940 	w->shadow_dy = ps->o.shadow_offset_y;
941 	w->shadow_width = w->widthb + ps->o.shadow_radius * 2;
942 	w->shadow_height = w->heightb + ps->o.shadow_radius * 2;
943 
944 	// Invalidate the shadow we built
945 	if (w->state == WSTATE_MAPPED || w->state == WSTATE_MAPPING ||
946 	    w->state == WSTATE_FADING) {
947 		win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
948 		ps->pending_updates = true;
949 	} else {
950 		assert(w->state == WSTATE_UNMAPPED);
951 	}
952 	free_paint(ps, &w->shadow_paint);
953 }
954 
955 /**
956  * Update window type.
957  */
win_update_wintype(session_t * ps,struct managed_win * w)958 void win_update_wintype(session_t *ps, struct managed_win *w) {
959 	const wintype_t wtype_old = w->window_type;
960 
961 	// Detect window type here
962 	w->window_type = wid_get_prop_wintype(ps, w->client_win);
963 
964 	// Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take
965 	// override-redirect windows or windows without WM_TRANSIENT_FOR as
966 	// _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG.
967 	if (WINTYPE_UNKNOWN == w->window_type) {
968 		if (w->a.override_redirect ||
969 		    !wid_has_prop(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR))
970 			w->window_type = WINTYPE_NORMAL;
971 		else
972 			w->window_type = WINTYPE_DIALOG;
973 	}
974 
975 	if (w->window_type != wtype_old)
976 		win_on_factor_change(ps, w);
977 }
978 
979 /**
980  * Mark a window as the client window of another.
981  *
982  * @param ps current session
983  * @param w struct _win of the parent window
984  * @param client window ID of the client window
985  */
win_mark_client(session_t * ps,struct managed_win * w,xcb_window_t client)986 void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client) {
987 	w->client_win = client;
988 
989 	// If the window isn't mapped yet, stop here, as the function will be
990 	// called in map_win()
991 	if (w->a.map_state != XCB_MAP_STATE_VIEWABLE)
992 		return;
993 
994 	auto e = xcb_request_check(
995 	    ps->c, xcb_change_window_attributes(
996 	               ps->c, client, XCB_CW_EVENT_MASK,
997 	               (const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_CLIENT)}));
998 	if (e) {
999 		log_error("Failed to change event mask of window %#010x", client);
1000 		free(e);
1001 	}
1002 
1003 	win_update_wintype(ps, w);
1004 
1005 	// Get frame widths. The window is in damaged area already.
1006 	win_update_frame_extents(ps, w, client);
1007 
1008 	// Get window group
1009 	if (ps->o.track_leader)
1010 		win_update_leader(ps, w);
1011 
1012 	// Get window name and class if we are tracking them
1013 	win_update_name(ps, w);
1014 	win_get_class(ps, w);
1015 	win_get_role(ps, w);
1016 
1017 	// Update everything related to conditions
1018 	win_on_factor_change(ps, w);
1019 
1020 	auto r = xcb_get_window_attributes_reply(
1021 	    ps->c, xcb_get_window_attributes(ps->c, w->client_win), &e);
1022 	if (!r) {
1023 		log_error_x_error(e, "Failed to get client window attributes");
1024 		return;
1025 	}
1026 
1027 	w->client_pictfmt = x_get_pictform_for_visual(ps->c, r->visual);
1028 	free(r);
1029 }
1030 
1031 /**
1032  * Unmark current client window of a window.
1033  *
1034  * @param ps current session
1035  * @param w struct _win of the parent window
1036  */
win_unmark_client(session_t * ps,struct managed_win * w)1037 void win_unmark_client(session_t *ps, struct managed_win *w) {
1038 	xcb_window_t client = w->client_win;
1039 	log_debug("Detaching client window %#010x from frame %#010x (%s)", client,
1040 	          w->base.id, w->name);
1041 
1042 	w->client_win = XCB_NONE;
1043 
1044 	// Recheck event mask
1045 	xcb_change_window_attributes(
1046 	    ps->c, client, XCB_CW_EVENT_MASK,
1047 	    (const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)});
1048 }
1049 
1050 /**
1051  * Look for the client window of a particular window.
1052  */
find_client_win(session_t * ps,xcb_window_t w)1053 static xcb_window_t find_client_win(session_t *ps, xcb_window_t w) {
1054 	if (wid_has_prop(ps, w, ps->atoms->aWM_STATE)) {
1055 		return w;
1056 	}
1057 
1058 	xcb_query_tree_reply_t *reply =
1059 	    xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, w), NULL);
1060 	if (!reply)
1061 		return 0;
1062 
1063 	xcb_window_t *children = xcb_query_tree_children(reply);
1064 	int nchildren = xcb_query_tree_children_length(reply);
1065 	int i;
1066 	xcb_window_t ret = 0;
1067 
1068 	for (i = 0; i < nchildren; ++i) {
1069 		if ((ret = find_client_win(ps, children[i])))
1070 			break;
1071 	}
1072 
1073 	free(reply);
1074 
1075 	return ret;
1076 }
1077 
1078 /**
1079  * Recheck client window of a window.
1080  *
1081  * @param ps current session
1082  * @param w struct _win of the parent window
1083  */
win_recheck_client(session_t * ps,struct managed_win * w)1084 void win_recheck_client(session_t *ps, struct managed_win *w) {
1085 	assert(ps->server_grabbed);
1086 	// Initialize wmwin to false
1087 	w->wmwin = false;
1088 
1089 	// Look for the client window
1090 
1091 	// Always recursively look for a window with WM_STATE, as Fluxbox
1092 	// sets override-redirect flags on all frame windows.
1093 	xcb_window_t cw = find_client_win(ps, w->base.id);
1094 	if (cw) {
1095 		log_debug("(%#010x): client %#010x", w->base.id, cw);
1096 	}
1097 	// Set a window's client window to itself if we couldn't find a
1098 	// client window
1099 	if (!cw) {
1100 		cw = w->base.id;
1101 		w->wmwin = !w->a.override_redirect;
1102 		log_debug("(%#010x): client self (%s)", w->base.id,
1103 		          (w->wmwin ? "wmwin" : "override-redirected"));
1104 	}
1105 
1106 	// Unmark the old one
1107 	if (w->client_win && w->client_win != cw)
1108 		win_unmark_client(ps, w);
1109 
1110 	// Mark the new one
1111 	win_mark_client(ps, w, cw);
1112 }
1113 
1114 /**
1115  * Free all resources in a <code>struct _win</code>.
1116  */
free_win_res(session_t * ps,struct managed_win * w)1117 void free_win_res(session_t *ps, struct managed_win *w) {
1118 	// No need to call backend release_image here because
1119 	// finish_unmap_win should've done that for us.
1120 	// XXX unless we are called by session_destroy
1121 	// assert(w->win_data == NULL);
1122 	free_win_res_glx(ps, w);
1123 	free_paint(ps, &w->paint);
1124 	free_paint(ps, &w->shadow_paint);
1125 	// Above should be done during unmapping
1126 	// Except when we are called by session_destroy
1127 
1128 	pixman_region32_fini(&w->bounding_shape);
1129 	// BadDamage may be thrown if the window is destroyed
1130 	set_ignore_cookie(ps, xcb_damage_destroy(ps->c, w->damage));
1131 	rc_region_unref(&w->reg_ignore);
1132 	free(w->name);
1133 	free(w->class_instance);
1134 	free(w->class_general);
1135 	free(w->role);
1136 }
1137 
1138 /// Insert a new window after list_node `prev`
1139 /// New window will be in unmapped state
add_win(session_t * ps,xcb_window_t id,struct list_node * prev)1140 static struct win *add_win(session_t *ps, xcb_window_t id, struct list_node *prev) {
1141 	log_debug("Adding window %#010x", id);
1142 	struct win *old_w = NULL;
1143 	HASH_FIND_INT(ps->windows, &id, old_w);
1144 	assert(old_w == NULL);
1145 
1146 	auto new_w = cmalloc(struct win);
1147 	list_insert_after(prev, &new_w->stack_neighbour);
1148 	new_w->id = id;
1149 	new_w->managed = false;
1150 	new_w->is_new = true;
1151 	new_w->destroyed = false;
1152 
1153 	HASH_ADD_INT(ps->windows, id, new_w);
1154 	ps->pending_updates = true;
1155 	return new_w;
1156 }
1157 
1158 /// Insert a new win entry at the top of the stack
add_win_top(session_t * ps,xcb_window_t id)1159 struct win *add_win_top(session_t *ps, xcb_window_t id) {
1160 	return add_win(ps, id, &ps->window_stack);
1161 }
1162 
1163 /// Insert a new window above window with id `below`, if there is no window, add to top
1164 /// New window will be in unmapped state
add_win_above(session_t * ps,xcb_window_t id,xcb_window_t below)1165 struct win *add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below) {
1166 	struct win *w = NULL;
1167 	HASH_FIND_INT(ps->windows, &below, w);
1168 	if (!w) {
1169 		if (!list_is_empty(&ps->window_stack)) {
1170 			// `below` window is not found even if the window stack is not
1171 			// empty
1172 			return NULL;
1173 		}
1174 		return add_win_top(ps, id);
1175 	} else {
1176 		// we found something from the hash table, so if the stack is empty,
1177 		// we are in an inconsistent state.
1178 		assert(!list_is_empty(&ps->window_stack));
1179 		return add_win(ps, id, w->stack_neighbour.prev);
1180 	}
1181 }
1182 
1183 /// Query the Xorg for information about window `win`
1184 /// `win` pointer might become invalid after this function returns
1185 /// Returns the pointer to the window, might be different from `w`
fill_win(session_t * ps,struct win * w)1186 struct win *fill_win(session_t *ps, struct win *w) {
1187 	static const struct managed_win win_def = {
1188 	    // No need to initialize. (or, you can think that
1189 	    // they are initialized right here).
1190 	    // The following ones are updated during paint or paint preprocess
1191 	    .shadow_opacity = 0.0,
1192 	    .to_paint = false,
1193 	    .frame_opacity = 1.0,
1194 	    .dim = false,
1195 	    .invert_color = false,
1196 	    .blur_background = false,
1197 	    .reg_ignore = NULL,
1198 	    // The following ones are updated for other reasons
1199 	    .pixmap_damaged = false,          // updated by damage events
1200 	    .state = WSTATE_UNMAPPED,         // updated by window state changes
1201 	    .in_openclose = true,             // set to false after first map is done,
1202 	                                      // true here because window is just created
1203 	    .reg_ignore_valid = false,        // set to true when damaged
1204 	    .flags = WIN_FLAGS_IMAGES_NONE,        // updated by property/attributes/etc
1205 	                                           // change
1206 
1207 	    // Runtime variables, updated by dbus
1208 	    .fade_force = UNSET,
1209 	    .shadow_force = UNSET,
1210 	    .focused_force = UNSET,
1211 	    .invert_color_force = UNSET,
1212 
1213 	    // Initialized in this function
1214 	    .a = {0},
1215 	    .pictfmt = NULL,
1216 	    .client_pictfmt = NULL,
1217 	    .widthb = 0,
1218 	    .heightb = 0,
1219 	    .shadow_dx = 0,
1220 	    .shadow_dy = 0,
1221 	    .shadow_width = 0,
1222 	    .shadow_height = 0,
1223 	    .damage = XCB_NONE,
1224 
1225 	    // Not initialized until mapped, this variables
1226 	    // have no meaning or have no use until the window
1227 	    // is mapped
1228 	    .win_image = NULL,
1229 	    .shadow_image = NULL,
1230 	    .prev_trans = NULL,
1231 	    .shadow = false,
1232 	    .xinerama_scr = -1,
1233 	    .mode = WMODE_TRANS,
1234 	    .ever_damaged = false,
1235 	    .client_win = XCB_NONE,
1236 	    .leader = XCB_NONE,
1237 	    .cache_leader = XCB_NONE,
1238 	    .window_type = WINTYPE_UNKNOWN,
1239 	    .wmwin = false,
1240 	    .focused = false,
1241 	    .opacity = 0,
1242 	    .opacity_target = 0,
1243 	    .has_opacity_prop = false,
1244 	    .opacity_prop = OPAQUE,
1245 	    .opacity_is_set = false,
1246 	    .opacity_set = 1,
1247 	    .frame_extents = MARGIN_INIT,        // in win_mark_client
1248 	    .bounding_shaped = false,
1249 	    .bounding_shape = {0},
1250 	    .rounded_corners = false,
1251 	    .paint_excluded = false,
1252 	    .unredir_if_possible_excluded = false,
1253 	    .prop_shadow = -1,
1254 	    // following 4 are set in win_mark_client
1255 	    .name = NULL,
1256 	    .class_instance = NULL,
1257 	    .class_general = NULL,
1258 	    .role = NULL,
1259 
1260 	    // Initialized during paint
1261 	    .paint = PAINT_INIT,
1262 	    .shadow_paint = PAINT_INIT,
1263 	};
1264 
1265 	assert(!w->destroyed);
1266 	assert(w->is_new);
1267 
1268 	w->is_new = false;
1269 
1270 	// Reject overlay window and already added windows
1271 	if (w->id == ps->overlay) {
1272 		return w;
1273 	}
1274 
1275 	auto duplicated_win = find_managed_win(ps, w->id);
1276 	if (duplicated_win) {
1277 		log_debug("Window %#010x (recorded name: %s) added multiple times", w->id,
1278 		          duplicated_win->name);
1279 		return &duplicated_win->base;
1280 	}
1281 
1282 	log_debug("Managing window %#010x", w->id);
1283 	xcb_get_window_attributes_cookie_t acookie = xcb_get_window_attributes(ps->c, w->id);
1284 	xcb_get_window_attributes_reply_t *a =
1285 	    xcb_get_window_attributes_reply(ps->c, acookie, NULL);
1286 	if (!a || a->map_state == XCB_MAP_STATE_UNVIEWABLE) {
1287 		// Failed to get window attributes or geometry probably means
1288 		// the window is gone already. Unviewable means the window is
1289 		// already reparented elsewhere.
1290 		// BTW, we don't care about Input Only windows, except for stacking
1291 		// proposes, so we need to keep track of them still.
1292 		free(a);
1293 		return w;
1294 	}
1295 
1296 	if (a->_class == XCB_WINDOW_CLASS_INPUT_ONLY) {
1297 		// No need to manage this window, but we still keep it on the window stack
1298 		w->managed = false;
1299 		free(a);
1300 		return w;
1301 	}
1302 
1303 	// Allocate and initialize the new win structure
1304 	auto new_internal = cmalloc(struct managed_win_internal);
1305 	auto new = (struct managed_win *)new_internal;
1306 
1307 	// Fill structure
1308 	// We only need to initialize the part that are not initialized
1309 	// by map_win
1310 	*new = win_def;
1311 	new->base = *w;
1312 	new->base.managed = true;
1313 	new->a = *a;
1314 	pixman_region32_init(&new->bounding_shape);
1315 
1316 	free(a);
1317 
1318 	// Create Damage for window (if not Input Only)
1319 	new->damage = x_new_id(ps->c);
1320 	xcb_generic_error_t *e = xcb_request_check(
1321 	    ps->c, xcb_damage_create_checked(ps->c, new->damage, w->id,
1322 	                                     XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY));
1323 	if (e) {
1324 		free(e);
1325 		free(new);
1326 		return w;
1327 	}
1328 
1329 	new->pictfmt = x_get_pictform_for_visual(ps->c, new->a.visual);
1330 	new->client_pictfmt = NULL;
1331 
1332 	list_replace(&w->stack_neighbour, &new->base.stack_neighbour);
1333 	struct win *replaced = NULL;
1334 	HASH_REPLACE_INT(ps->windows, id, &new->base, replaced);
1335 	assert(replaced == w);
1336 	free(w);
1337 
1338 #ifdef CONFIG_DBUS
1339 	// Send D-Bus signal
1340 	if (ps->o.dbus) {
1341 		cdbus_ev_win_added(ps, &new->base);
1342 	}
1343 #endif
1344 	return &new->base;
1345 }
1346 
1347 /**
1348  * Set leader of a window.
1349  */
win_set_leader(session_t * ps,struct managed_win * w,xcb_window_t nleader)1350 static inline void win_set_leader(session_t *ps, struct managed_win *w, xcb_window_t nleader) {
1351 	// If the leader changes
1352 	if (w->leader != nleader) {
1353 		xcb_window_t cache_leader_old = win_get_leader(ps, w);
1354 
1355 		w->leader = nleader;
1356 
1357 		// Forcefully do this to deal with the case when a child window
1358 		// gets mapped before parent, or when the window is a waypoint
1359 		clear_cache_win_leaders(ps);
1360 
1361 		// Update the old and new window group and active_leader if the window
1362 		// could affect their state.
1363 		xcb_window_t cache_leader = win_get_leader(ps, w);
1364 		if (win_is_focused_raw(ps, w) && cache_leader_old != cache_leader) {
1365 			ps->active_leader = cache_leader;
1366 
1367 			group_on_factor_change(ps, cache_leader_old);
1368 			group_on_factor_change(ps, cache_leader);
1369 		}
1370 
1371 		// Update everything related to conditions
1372 		win_on_factor_change(ps, w);
1373 	}
1374 }
1375 
1376 /**
1377  * Update leader of a window.
1378  */
win_update_leader(session_t * ps,struct managed_win * w)1379 void win_update_leader(session_t *ps, struct managed_win *w) {
1380 	xcb_window_t leader = XCB_NONE;
1381 
1382 	// Read the leader properties
1383 	if (ps->o.detect_transient && !leader)
1384 		leader = wid_get_prop_window(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR);
1385 
1386 	if (ps->o.detect_client_leader && !leader)
1387 		leader = wid_get_prop_window(ps, w->client_win, ps->atoms->aWM_CLIENT_LEADER);
1388 
1389 	win_set_leader(ps, w, leader);
1390 
1391 	log_trace("(%#010x): client %#010x, leader %#010x, cache %#010x", w->base.id,
1392 	          w->client_win, w->leader, win_get_leader(ps, w));
1393 }
1394 
1395 /**
1396  * Internal function of win_get_leader().
1397  */
win_get_leader_raw(session_t * ps,struct managed_win * w,int recursions)1398 static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int recursions) {
1399 	// Rebuild the cache if needed
1400 	if (!w->cache_leader && (w->client_win || w->leader)) {
1401 		// Leader defaults to client window
1402 		if (!(w->cache_leader = w->leader))
1403 			w->cache_leader = w->client_win;
1404 
1405 		// If the leader of this window isn't itself, look for its ancestors
1406 		if (w->cache_leader && w->cache_leader != w->client_win) {
1407 			auto wp = find_toplevel(ps, w->cache_leader);
1408 			if (wp) {
1409 				// Dead loop?
1410 				if (recursions > WIN_GET_LEADER_MAX_RECURSION)
1411 					return XCB_NONE;
1412 
1413 				w->cache_leader = win_get_leader_raw(ps, wp, recursions + 1);
1414 			}
1415 		}
1416 	}
1417 
1418 	return w->cache_leader;
1419 }
1420 
1421 /**
1422  * Retrieve the <code>WM_CLASS</code> of a window and update its
1423  * <code>win</code> structure.
1424  */
win_get_class(session_t * ps,struct managed_win * w)1425 bool win_get_class(session_t *ps, struct managed_win *w) {
1426 	char **strlst = NULL;
1427 	int nstr = 0;
1428 
1429 	// Can't do anything if there's no client window
1430 	if (!w->client_win)
1431 		return false;
1432 
1433 	// Free and reset old strings
1434 	free(w->class_instance);
1435 	free(w->class_general);
1436 	w->class_instance = NULL;
1437 	w->class_general = NULL;
1438 
1439 	// Retrieve the property string list
1440 	if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_CLASS, &strlst, &nstr))
1441 		return false;
1442 
1443 	// Copy the strings if successful
1444 	w->class_instance = strdup(strlst[0]);
1445 
1446 	if (nstr > 1)
1447 		w->class_general = strdup(strlst[1]);
1448 
1449 	XFreeStringList(strlst);
1450 
1451 	log_trace("(%#010x): client = %#010x, "
1452 	          "instance = \"%s\", general = \"%s\"",
1453 	          w->base.id, w->client_win, w->class_instance, w->class_general);
1454 
1455 	return true;
1456 }
1457 
1458 /**
1459  * Handle window focus change.
1460  */
win_on_focus_change(session_t * ps,struct managed_win * w)1461 static void win_on_focus_change(session_t *ps, struct managed_win *w) {
1462 	// If window grouping detection is enabled
1463 	if (ps->o.track_leader) {
1464 		xcb_window_t leader = win_get_leader(ps, w);
1465 
1466 		// If the window gets focused, replace the old active_leader
1467 		if (win_is_focused_raw(ps, w) && leader != ps->active_leader) {
1468 			xcb_window_t active_leader_old = ps->active_leader;
1469 
1470 			ps->active_leader = leader;
1471 
1472 			group_on_factor_change(ps, active_leader_old);
1473 			group_on_factor_change(ps, leader);
1474 		}
1475 		// If the group get unfocused, remove it from active_leader
1476 		else if (!win_is_focused_raw(ps, w) && leader &&
1477 		         leader == ps->active_leader && !group_is_focused(ps, leader)) {
1478 			ps->active_leader = XCB_NONE;
1479 			group_on_factor_change(ps, leader);
1480 		}
1481 	}
1482 
1483 	// Update everything related to conditions
1484 	win_on_factor_change(ps, w);
1485 
1486 #ifdef CONFIG_DBUS
1487 	// Send D-Bus signal
1488 	if (ps->o.dbus) {
1489 		if (win_is_focused_raw(ps, w))
1490 			cdbus_ev_win_focusin(ps, &w->base);
1491 		else
1492 			cdbus_ev_win_focusout(ps, &w->base);
1493 	}
1494 #endif
1495 }
1496 
1497 /**
1498  * Set real focused state of a window.
1499  */
win_set_focused(session_t * ps,struct managed_win * w)1500 void win_set_focused(session_t *ps, struct managed_win *w) {
1501 	// Unmapped windows will have their focused state reset on map
1502 	if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
1503 		return;
1504 	}
1505 
1506 	if (win_is_focused_raw(ps, w)) {
1507 		return;
1508 	}
1509 
1510 	auto old_active_win = ps->active_win;
1511 	ps->active_win = w;
1512 	assert(win_is_focused_raw(ps, w));
1513 
1514 	if (old_active_win) {
1515 		win_on_focus_change(ps, old_active_win);
1516 	}
1517 	win_on_focus_change(ps, w);
1518 }
1519 
1520 /**
1521  * Get a rectangular region a window (and possibly its shadow) occupies.
1522  *
1523  * Note w->shadow and shadow geometry must be correct before calling this
1524  * function.
1525  */
win_extents(const struct managed_win * w,region_t * res)1526 void win_extents(const struct managed_win *w, region_t *res) {
1527 	pixman_region32_clear(res);
1528 	pixman_region32_union_rect(res, res, w->g.x, w->g.y, (uint)w->widthb, (uint)w->heightb);
1529 
1530 	if (w->shadow) {
1531 		assert(w->shadow_width >= 0 && w->shadow_height >= 0);
1532 		pixman_region32_union_rect(res, res, w->g.x + w->shadow_dx,
1533 		                           w->g.y + w->shadow_dy, (uint)w->shadow_width,
1534 		                           (uint)w->shadow_height);
1535 	}
1536 }
1537 
1538 gen_by_val(win_extents);
1539 
1540 /**
1541  * Update the out-dated bounding shape of a window.
1542  *
1543  * Mark the window shape as updated
1544  */
win_update_bounding_shape(session_t * ps,struct managed_win * w)1545 void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
1546 	if (ps->shape_exists)
1547 		w->bounding_shaped = win_bounding_shaped(ps, w->base.id);
1548 
1549 	pixman_region32_clear(&w->bounding_shape);
1550 	// Start with the window rectangular region
1551 	win_get_region_local(w, &w->bounding_shape);
1552 
1553 	// Only request for a bounding region if the window is shaped
1554 	// (while loop is used to avoid goto, not an actual loop)
1555 	while (w->bounding_shaped) {
1556 		/*
1557 		 * if window doesn't exist anymore,  this will generate an error
1558 		 * as well as not generate a region.
1559 		 */
1560 
1561 		xcb_shape_get_rectangles_reply_t *r = xcb_shape_get_rectangles_reply(
1562 		    ps->c,
1563 		    xcb_shape_get_rectangles(ps->c, w->base.id, XCB_SHAPE_SK_BOUNDING), NULL);
1564 
1565 		if (!r)
1566 			break;
1567 
1568 		xcb_rectangle_t *xrects = xcb_shape_get_rectangles_rectangles(r);
1569 		int nrects = xcb_shape_get_rectangles_rectangles_length(r);
1570 		rect_t *rects = from_x_rects(nrects, xrects);
1571 		free(r);
1572 
1573 		region_t br;
1574 		pixman_region32_init_rects(&br, rects, nrects);
1575 		free(rects);
1576 
1577 		// Add border width because we are using a different origin.
1578 		// X thinks the top left of the inner window is the origin
1579 		// (for the bounding shape, althought xcb_get_geometry thinks
1580 		//  the outer top left (outer means outside of the window
1581 		//  border) is the origin),
1582 		// We think the top left of the border is the origin
1583 		pixman_region32_translate(&br, w->g.border_width, w->g.border_width);
1584 
1585 		// Intersect the bounding region we got with the window rectangle, to
1586 		// make sure the bounding region is not bigger than the window
1587 		// rectangle
1588 		pixman_region32_intersect(&w->bounding_shape, &w->bounding_shape, &br);
1589 		pixman_region32_fini(&br);
1590 		break;
1591 	}
1592 
1593 	if (w->bounding_shaped && ps->o.detect_rounded_corners) {
1594 		w->rounded_corners = win_has_rounded_corners(w);
1595 	}
1596 
1597 	// Window shape changed, we should free old wpaint and shadow pict
1598 	// log_trace("free out dated pict");
1599 	if (w->state != WSTATE_UNMAPPED) {
1600 		// Note we only do this when screen is redirected, because
1601 		// otherwise win_data is not valid
1602 		assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
1603 		win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
1604 		ps->pending_updates = true;
1605 	}
1606 	free_paint(ps, &w->paint);
1607 	free_paint(ps, &w->shadow_paint);
1608 
1609 	win_on_factor_change(ps, w);
1610 }
1611 
1612 /**
1613  * Reread opacity property of a window.
1614  */
win_update_opacity_prop(session_t * ps,struct managed_win * w)1615 void win_update_opacity_prop(session_t *ps, struct managed_win *w) {
1616 	// get frame opacity first
1617 	w->has_opacity_prop = wid_get_opacity_prop(ps, w->base.id, OPAQUE, &w->opacity_prop);
1618 
1619 	if (w->has_opacity_prop)
1620 		// opacity found
1621 		return;
1622 
1623 	if (ps->o.detect_client_opacity && w->client_win && w->base.id == w->client_win)
1624 		// checking client opacity not allowed
1625 		return;
1626 
1627 	// get client opacity
1628 	w->has_opacity_prop =
1629 	    wid_get_opacity_prop(ps, w->client_win, OPAQUE, &w->opacity_prop);
1630 }
1631 
1632 /**
1633  * Retrieve frame extents from a window.
1634  */
win_update_frame_extents(session_t * ps,struct managed_win * w,xcb_window_t client)1635 void win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client) {
1636 	winprop_t prop = x_get_prop(ps, client, ps->atoms->a_NET_FRAME_EXTENTS, 4L,
1637 	                            XCB_ATOM_CARDINAL, 32);
1638 
1639 	if (prop.nitems == 4) {
1640 		const int32_t extents[4] = {
1641 		    to_int_checked(prop.c32[0]),
1642 		    to_int_checked(prop.c32[1]),
1643 		    to_int_checked(prop.c32[2]),
1644 		    to_int_checked(prop.c32[3]),
1645 		};
1646 		const bool changed = w->frame_extents.left != extents[0] ||
1647 		                     w->frame_extents.right != extents[1] ||
1648 		                     w->frame_extents.top != extents[2] ||
1649 		                     w->frame_extents.bottom != extents[3];
1650 		w->frame_extents.left = extents[0];
1651 		w->frame_extents.right = extents[1];
1652 		w->frame_extents.top = extents[2];
1653 		w->frame_extents.bottom = extents[3];
1654 
1655 		// If frame_opacity != 1, then frame of this window
1656 		// is not included in reg_ignore of underneath windows
1657 		if (ps->o.frame_opacity == 1 && changed)
1658 			w->reg_ignore_valid = false;
1659 	}
1660 
1661 	log_trace("(%#010x): %d, %d, %d, %d", w->base.id, w->frame_extents.left,
1662 	          w->frame_extents.right, w->frame_extents.top, w->frame_extents.bottom);
1663 
1664 	free_winprop(&prop);
1665 }
1666 
win_is_region_ignore_valid(session_t * ps,const struct managed_win * w)1667 bool win_is_region_ignore_valid(session_t *ps, const struct managed_win *w) {
1668 	win_stack_foreach_managed(i, &ps->window_stack) {
1669 		if (i == w)
1670 			break;
1671 		if (!i->reg_ignore_valid)
1672 			return false;
1673 	}
1674 	return true;
1675 }
1676 
1677 /**
1678  * Stop listening for events on a particular window.
1679  */
win_ev_stop(session_t * ps,const struct win * w)1680 void win_ev_stop(session_t *ps, const struct win *w) {
1681 	xcb_change_window_attributes(ps->c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]){0});
1682 
1683 	if (!w->managed) {
1684 		return;
1685 	}
1686 
1687 	auto mw = (struct managed_win *)w;
1688 	if (mw->client_win) {
1689 		xcb_change_window_attributes(ps->c, mw->client_win, XCB_CW_EVENT_MASK,
1690 		                             (const uint32_t[]){0});
1691 	}
1692 
1693 	if (ps->shape_exists) {
1694 		xcb_shape_select_input(ps->c, w->id, 0);
1695 	}
1696 }
1697 
1698 /// Finish the unmapping of a window (e.g. after fading has finished).
1699 /// Doesn't free `w`
unmap_win_finish(session_t * ps,struct managed_win * w)1700 static void unmap_win_finish(session_t *ps, struct managed_win *w) {
1701 	w->ever_damaged = false;
1702 	w->reg_ignore_valid = false;
1703 	w->state = WSTATE_UNMAPPED;
1704 
1705 	// We are in unmap_win, this window definitely was viewable
1706 	if (ps->backend_data) {
1707 		win_release_images(ps->backend_data, w);
1708 	} else {
1709 		assert(!w->win_image);
1710 		assert(!w->shadow_image);
1711 	}
1712 
1713 	free_paint(ps, &w->paint);
1714 	free_paint(ps, &w->shadow_paint);
1715 
1716 	// Try again at binding images when the window is mapped next time
1717 	win_clear_flags(w, WIN_FLAGS_IMAGE_ERROR);
1718 }
1719 
1720 /// Finish the destruction of a window (e.g. after fading has finished).
1721 /// Frees `w`
destroy_win_finish(session_t * ps,struct win * w)1722 static void destroy_win_finish(session_t *ps, struct win *w) {
1723 	log_trace("Trying to finish destroying (%#010x)", w->id);
1724 
1725 	auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour);
1726 	list_remove(&w->stack_neighbour);
1727 
1728 	if (w->managed) {
1729 		auto mw = (struct managed_win *)w;
1730 
1731 		if (mw->state != WSTATE_UNMAPPED) {
1732 			// Only UNMAPPED state has window resources freed, otherwise
1733 			// we need to call unmap_win_finish to free them.
1734 			// XXX actually we unmap_win_finish only frees the rendering
1735 			//     resources, we still need to call free_win_res. will fix
1736 			//     later.
1737 			unmap_win_finish(ps, mw);
1738 		}
1739 
1740 		// Invalidate reg_ignore of windows below this one
1741 		// TODO what if next_w is not mapped??
1742 		// TODO seriously figure out how reg_ignore behaves.
1743 		//      I think if `w` is unmapped, and destroyed after
1744 		//      paint happened at least once, w->reg_ignore_valid would
1745 		//      be true, and there is no need to invalid w->next->reg_ignore
1746 		//      when w is destroyed.
1747 		if (next_w) {
1748 			rc_region_unref(&next_w->reg_ignore);
1749 			next_w->reg_ignore_valid = false;
1750 		}
1751 
1752 		if (mw == ps->active_win) {
1753 			// Usually, the window cannot be the focused at destruction.
1754 			// FocusOut should be generated before the window is destroyed. We
1755 			// do this check just to be completely sure we don't have dangling
1756 			// references.
1757 			log_debug("window %#010x (%s) is destroyed while being focused",
1758 			          w->id, mw->name);
1759 			ps->active_win = NULL;
1760 		}
1761 
1762 		free_win_res(ps, mw);
1763 
1764 		// Drop w from all prev_trans to avoid accessing freed memory in
1765 		// repair_win()
1766 		// TODO there can only be one prev_trans pointing to w
1767 		win_stack_foreach_managed(w2, &ps->window_stack) {
1768 			if (mw == w2->prev_trans) {
1769 				w2->prev_trans = NULL;
1770 			}
1771 		}
1772 	}
1773 
1774 	free(w);
1775 }
1776 
map_win_finish(struct managed_win * w)1777 static void map_win_finish(struct managed_win *w) {
1778 	w->in_openclose = false;
1779 	w->state = WSTATE_MAPPED;
1780 }
1781 
1782 /// Move window `w` so it's before `next` in the list
restack_win(session_t * ps,struct win * w,struct list_node * next)1783 static inline void restack_win(session_t *ps, struct win *w, struct list_node *next) {
1784 	struct managed_win *mw = NULL;
1785 	if (w->managed) {
1786 		mw = (struct managed_win *)w;
1787 	}
1788 
1789 	if (mw) {
1790 		// This invalidates all reg_ignore below the new stack position of `w`
1791 		mw->reg_ignore_valid = false;
1792 		rc_region_unref(&mw->reg_ignore);
1793 
1794 		// This invalidates all reg_ignore below the old stack position of `w`
1795 		auto next_w = win_stack_find_next_managed(ps, &w->stack_neighbour);
1796 		if (next_w) {
1797 			next_w->reg_ignore_valid = false;
1798 			rc_region_unref(&next_w->reg_ignore);
1799 		}
1800 	}
1801 
1802 	list_move_before(&w->stack_neighbour, next);
1803 
1804 	// add damage for this window
1805 	if (mw) {
1806 		add_damage_from_win(ps, mw);
1807 	}
1808 
1809 #ifdef DEBUG_RESTACK
1810 	log_trace("Window stack modified. Current stack:");
1811 	for (auto c = ps->list; c; c = c->next) {
1812 		const char *desc = "";
1813 		if (c->state == WSTATE_DESTROYING) {
1814 			desc = "(D) ";
1815 		}
1816 		log_trace("%#010x \"%s\" %s", c->id, c->name, desc);
1817 	}
1818 #endif
1819 }
1820 
1821 /// Move window `w` so it's right above `below`
restack_above(session_t * ps,struct win * w,xcb_window_t below)1822 void restack_above(session_t *ps, struct win *w, xcb_window_t below) {
1823 	xcb_window_t old_below;
1824 
1825 	if (!list_node_is_last(&ps->window_stack, &w->stack_neighbour)) {
1826 		old_below = list_next_entry(w, stack_neighbour)->id;
1827 	} else {
1828 		old_below = XCB_NONE;
1829 	}
1830 	log_debug("Restack %#010x (%s), old_below: %#010x, new_below: %#010x", w->id,
1831 	          win_get_name_if_managed(w), old_below, below);
1832 
1833 	if (old_below != below) {
1834 		struct list_node *new_next;
1835 		if (!below) {
1836 			new_next = &ps->window_stack;
1837 		} else {
1838 			struct win *tmp_w = NULL;
1839 			HASH_FIND_INT(ps->windows, &below, tmp_w);
1840 
1841 			if (!tmp_w) {
1842 				log_error("Failed to found new below window %#010x.", below);
1843 				return;
1844 			}
1845 
1846 			new_next = &tmp_w->stack_neighbour;
1847 		}
1848 		restack_win(ps, w, new_next);
1849 	}
1850 }
1851 
restack_bottom(session_t * ps,struct win * w)1852 void restack_bottom(session_t *ps, struct win *w) {
1853 	restack_above(ps, w, 0);
1854 }
1855 
restack_top(session_t * ps,struct win * w)1856 void restack_top(session_t *ps, struct win *w) {
1857 	log_debug("Restack %#010x (%s) to top", w->id, win_get_name_if_managed(w));
1858 	if (&w->stack_neighbour == ps->window_stack.next) {
1859 		// already at top
1860 		return;
1861 	}
1862 	restack_win(ps, w, ps->window_stack.next);
1863 }
1864 
1865 /// Start destroying a window. Windows cannot always be destroyed immediately
1866 /// because of fading and such.
1867 ///
1868 /// @return whether the window has finished destroying and is freed
destroy_win_start(session_t * ps,struct win * w)1869 bool destroy_win_start(session_t *ps, struct win *w) {
1870 	auto mw = (struct managed_win *)w;
1871 	assert(w);
1872 
1873 	log_debug("Destroying %#010x \"%s\", managed = %d", w->id,
1874 	          (w->managed ? mw->name : NULL), w->managed);
1875 
1876 	// Delete destroyed window from the hash table, even though the window might still
1877 	// be rendered for a while. We need to make sure future window with the same
1878 	// window id won't confuse us. Keep the window in the window stack if it's managed
1879 	// and mapped, since we might still need to render it (e.g. fading out). Window
1880 	// will be removed from the stack when it finishes destroying.
1881 	HASH_DEL(ps->windows, w);
1882 
1883 	if (!w->managed || mw->state == WSTATE_UNMAPPED) {
1884 		// Window is already unmapped, or is an unmanged window, just destroy it
1885 		destroy_win_finish(ps, w);
1886 		return true;
1887 	}
1888 
1889 	if (w->managed) {
1890 		// Clear PIXMAP_STALE flag, since the window is destroyed there is no
1891 		// pixmap available so STALE doesn't make sense.
1892 		// Do this before changing the window state to destroying
1893 		win_clear_flags(mw, WIN_FLAGS_IMAGES_STALE);
1894 
1895 		// Update state flags of a managed window
1896 		mw->state = WSTATE_DESTROYING;
1897 		mw->a.map_state = XCB_MAP_STATE_UNMAPPED;
1898 		mw->in_openclose = true;
1899 	}
1900 
1901 	// don't need win_ev_stop because the window is gone anyway
1902 #ifdef CONFIG_DBUS
1903 	// Send D-Bus signal
1904 	if (ps->o.dbus) {
1905 		cdbus_ev_win_destroyed(ps, w);
1906 	}
1907 #endif
1908 
1909 	if (!ps->redirected) {
1910 		// Skip transition if we are not rendering
1911 		return win_skip_fading(ps, mw);
1912 	}
1913 
1914 	return false;
1915 }
1916 
unmap_win_start(session_t * ps,struct managed_win * w)1917 void unmap_win_start(session_t *ps, struct managed_win *w) {
1918 	assert(w);
1919 	assert(w->base.managed);
1920 	assert(w->a._class != XCB_WINDOW_CLASS_INPUT_ONLY);
1921 
1922 	log_debug("Unmapping %#010x \"%s\"", w->base.id, w->name);
1923 
1924 	if (unlikely(w->state == WSTATE_DESTROYING)) {
1925 		log_warn("Trying to undestroy a window?");
1926 		assert(false);
1927 	}
1928 
1929 	if (unlikely(w->state == WSTATE_UNMAPPING || w->state == WSTATE_UNMAPPED)) {
1930 		if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
1931 			// Clear the pending map as this window is now unmapped
1932 			win_clear_flags(w, WIN_FLAGS_MAPPED);
1933 		} else {
1934 			log_warn("Trying to unmapping an already unmapped window %#010x "
1935 			         "\"%s\"",
1936 			         w->base.id, w->name);
1937 			assert(false);
1938 		}
1939 		return;
1940 	}
1941 
1942 	// Note we don't update focused window here. This will either be
1943 	// triggered by subsequence Focus{In, Out} event, or by recheck_focus
1944 
1945 	w->a.map_state = XCB_MAP_STATE_UNMAPPED;
1946 	w->state = WSTATE_UNMAPPING;
1947 	w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old);
1948 	w->opacity_target = win_calc_opacity_target(ps, w);
1949 
1950 	// Clear PIXMAP_STALE flag, since the window is unmapped there is no pixmap
1951 	// available so STALE doesn't make sense.
1952 	win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE);
1953 
1954 	// don't care about properties anymore
1955 	win_ev_stop(ps, &w->base);
1956 
1957 #ifdef CONFIG_DBUS
1958 	// Send D-Bus signal
1959 	if (ps->o.dbus) {
1960 		cdbus_ev_win_unmapped(ps, &w->base);
1961 	}
1962 #endif
1963 
1964 	if (!ps->redirected) {
1965 		CHECK(!win_skip_fading(ps, w));
1966 	}
1967 }
1968 
1969 /**
1970  * Execute fade callback of a window if fading finished.
1971  *
1972  * @return whether the window is destroyed and freed
1973  */
win_check_fade_finished(session_t * ps,struct managed_win * w)1974 bool win_check_fade_finished(session_t *ps, struct managed_win *w) {
1975 	if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
1976 		// No fading in progress
1977 		assert(w->opacity_target == w->opacity);
1978 		return false;
1979 	}
1980 	if (w->opacity == w->opacity_target) {
1981 		switch (w->state) {
1982 		case WSTATE_UNMAPPING: unmap_win_finish(ps, w); return false;
1983 		case WSTATE_DESTROYING: destroy_win_finish(ps, &w->base); return true;
1984 		case WSTATE_MAPPING: map_win_finish(w); return false;
1985 		case WSTATE_FADING: w->state = WSTATE_MAPPED; break;
1986 		default: unreachable;
1987 		}
1988 	}
1989 
1990 	return false;
1991 }
1992 
1993 /// Skip the current in progress fading of window,
1994 /// transition the window straight to its end state
1995 ///
1996 /// @return whether the window is destroyed and freed
win_skip_fading(session_t * ps,struct managed_win * w)1997 bool win_skip_fading(session_t *ps, struct managed_win *w) {
1998 	if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
1999 		assert(w->opacity_target == w->opacity);
2000 		return false;
2001 	}
2002 	log_debug("Skipping fading process of window %#010x (%s)", w->base.id, w->name);
2003 	w->opacity = w->opacity_target;
2004 	return win_check_fade_finished(ps, w);
2005 }
2006 
2007 /**
2008  * Get the Xinerama screen a window is on.
2009  *
2010  * Return an index >= 0, or -1 if not found.
2011  *
2012  * TODO move to x.c
2013  * TODO use xrandr
2014  */
win_update_screen(session_t * ps,struct managed_win * w)2015 void win_update_screen(session_t *ps, struct managed_win *w) {
2016 	w->xinerama_scr = -1;
2017 
2018 	for (int i = 0; i < ps->xinerama_nscrs; i++) {
2019 		auto e = pixman_region32_extents(&ps->xinerama_scr_regs[i]);
2020 		if (e->x1 <= w->g.x && e->y1 <= w->g.y && e->x2 >= w->g.x + w->widthb &&
2021 		    e->y2 >= w->g.y + w->heightb) {
2022 			w->xinerama_scr = i;
2023 			return;
2024 		}
2025 	}
2026 }
2027 
2028 /// Map an already registered window
map_win_start(session_t * ps,struct managed_win * w)2029 void map_win_start(session_t *ps, struct managed_win *w) {
2030 	assert(ps->server_grabbed);
2031 	assert(w);
2032 
2033 	// Don't care about window mapping if it's an InputOnly window
2034 	// Also, try avoiding mapping a window twice
2035 	if (w->a._class == XCB_WINDOW_CLASS_INPUT_ONLY) {
2036 		return;
2037 	}
2038 
2039 	log_debug("Mapping (%#010x \"%s\")", w->base.id, w->name);
2040 
2041 	assert(w->state != WSTATE_DESTROYING);
2042 	if (w->state != WSTATE_UNMAPPED && w->state != WSTATE_UNMAPPING) {
2043 		log_warn("Mapping an already mapped window");
2044 		return;
2045 	}
2046 
2047 	if (w->state == WSTATE_UNMAPPING) {
2048 		CHECK(!win_skip_fading(ps, w));
2049 		// We skipped the unmapping process, the window was rendered, now it is
2050 		// not anymore. So we need to mark the then unmapping window as damaged.
2051 		//
2052 		// Solves problem when, for example, a window is unmapped then mapped in a
2053 		// different location
2054 		add_damage_from_win(ps, w);
2055 		assert(w);
2056 	}
2057 
2058 	assert(w->state == WSTATE_UNMAPPED);
2059 	assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_NONE) || !ps->o.experimental_backends);
2060 
2061 	// We stopped processing window size change when we were unmapped, refresh the
2062 	// size of the window
2063 	xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(ps->c, w->base.id);
2064 	xcb_get_geometry_reply_t *g = xcb_get_geometry_reply(ps->c, gcookie, NULL);
2065 
2066 	if (!g) {
2067 		log_error("Failed to get the geometry of window %#010x", w->base.id);
2068 		return;
2069 	}
2070 
2071 	w->g = *g;
2072 	free(g);
2073 
2074 	win_on_win_size_change(ps, w);
2075 	log_trace("Window size: %dx%d", w->g.width, w->g.height);
2076 
2077 	// Rant: window size could change after we queried its geometry here and before
2078 	// we get its pixmap. Later, when we get back to the event processing loop, we
2079 	// will get the notification about size change from Xserver and try to refresh the
2080 	// pixmap, while the pixmap is actually already up-to-date (i.e. the notification
2081 	// is stale). There is basically no real way to prevent this, aside from grabbing
2082 	// the server.
2083 
2084 	// XXX Can we assume map_state is always viewable?
2085 	w->a.map_state = XCB_MAP_STATE_VIEWABLE;
2086 
2087 	win_update_screen(ps, w);
2088 
2089 	// Set window event mask before reading properties so that no property
2090 	// changes are lost
2091 	xcb_change_window_attributes(
2092 	    ps->c, w->base.id, XCB_CW_EVENT_MASK,
2093 	    (const uint32_t[]){determine_evmask(ps, w->base.id, WIN_EVMODE_FRAME)});
2094 
2095 	// Get notification when the shape of a window changes
2096 	if (ps->shape_exists) {
2097 		xcb_shape_select_input(ps->c, w->base.id, 1);
2098 	}
2099 
2100 	// Update window mode here to check for ARGB windows
2101 	w->mode = win_calc_mode(w);
2102 
2103 	// Detect client window here instead of in add_win() as the client
2104 	// window should have been prepared at this point
2105 	if (!w->client_win) {
2106 		win_recheck_client(ps, w);
2107 	} else {
2108 		// Re-mark client window here
2109 		win_mark_client(ps, w, w->client_win);
2110 	}
2111 	assert(w->client_win);
2112 
2113 	log_debug("Window (%#010x) has type %s", w->base.id, WINTYPES[w->window_type]);
2114 
2115 	// TODO can we just replace calls below with win_on_factor_change?
2116 
2117 	// Update window focus state
2118 	win_update_focused(ps, w);
2119 
2120 	// Update opacity and dim state
2121 	win_update_opacity_prop(ps, w);
2122 
2123 	// Check for _COMPTON_SHADOW
2124 	win_update_prop_shadow_raw(ps, w);
2125 
2126 	// Many things above could affect shadow
2127 	win_determine_shadow(ps, w);
2128 
2129 	// XXX We need to make sure that win_data is available
2130 	// iff `state` is MAPPED
2131 	w->state = WSTATE_MAPPING;
2132 	w->opacity_target_old = 0;
2133 	w->opacity_target = win_calc_opacity_target(ps, w);
2134 
2135 	log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id,
2136 	          w->opacity, w->opacity_target);
2137 
2138 	win_determine_blur_background(ps, w);
2139 
2140 	// Cannot set w->ever_damaged = false here, since window mapping could be
2141 	// delayed, so a damage event might have already arrived before this function
2142 	// is called. But this should be unnecessary in the first place, since
2143 	// ever_damaged is set to false in unmap_win_finish anyway.
2144 
2145 	// We stopped listening on ShapeNotify events
2146 	// when the window is unmapped (XXX we shouldn't),
2147 	// so the shape of the window might have changed,
2148 	// update. (Issue #35)
2149 	//
2150 	// Also this sets the WIN_FLAGS_IMAGES_STALE flag so later in the critical section
2151 	// the window's image will be bound
2152 	win_update_bounding_shape(ps, w);
2153 
2154 	assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_STALE));
2155 
2156 #ifdef CONFIG_DBUS
2157 	// Send D-Bus signal
2158 	if (ps->o.dbus) {
2159 		cdbus_ev_win_mapped(ps, &w->base);
2160 	}
2161 #endif
2162 
2163 	if (!ps->redirected) {
2164 		CHECK(!win_skip_fading(ps, w));
2165 	}
2166 }
2167 
2168 /**
2169  * Update target window opacity depending on the current state.
2170  */
win_update_opacity_target(session_t * ps,struct managed_win * w)2171 void win_update_opacity_target(session_t *ps, struct managed_win *w) {
2172 	auto opacity_target_old = w->opacity_target;
2173 	w->opacity_target = win_calc_opacity_target(ps, w);
2174 
2175 	if (opacity_target_old == w->opacity_target) {
2176 		return;
2177 	}
2178 
2179 	if (w->state == WSTATE_MAPPED) {
2180 		// Opacity target changed while MAPPED. Transition to FADING.
2181 		assert(w->opacity == opacity_target_old);
2182 		w->opacity_target_old = opacity_target_old;
2183 		w->state = WSTATE_FADING;
2184 		log_debug("Window %#010x (%s) opacity %f, opacity target %f, set "
2185 		          "old target %f",
2186 		          w->base.id, w->name, w->opacity, w->opacity_target,
2187 		          w->opacity_target_old);
2188 	} else if (w->state == WSTATE_MAPPING) {
2189 		// Opacity target changed while fading in.
2190 		if (w->opacity >= w->opacity_target) {
2191 			// Already reached new target opacity. Transition to
2192 			// FADING.
2193 			map_win_finish(w);
2194 			w->opacity_target_old = fmax(opacity_target_old, w->opacity);
2195 			w->state = WSTATE_FADING;
2196 			log_debug("Window %#010x (%s) opacity %f already reached "
2197 			          "new opacity target %f while mapping, set old "
2198 			          "target %f",
2199 			          w->base.id, w->name, w->opacity, w->opacity_target,
2200 			          w->opacity_target_old);
2201 		}
2202 	} else if (w->state == WSTATE_FADING) {
2203 		// Opacity target changed while FADING.
2204 		if ((w->opacity < opacity_target_old && w->opacity > w->opacity_target) ||
2205 		    (w->opacity > opacity_target_old && w->opacity < w->opacity_target)) {
2206 			// Changed while fading in and will fade out or while
2207 			// fading out and will fade in.
2208 			w->opacity_target_old = opacity_target_old;
2209 			log_debug("Window %#010x (%s) opacity %f already reached "
2210 			          "new opacity target %f while fading, set "
2211 			          "old target %f",
2212 			          w->base.id, w->name, w->opacity, w->opacity_target,
2213 			          w->opacity_target_old);
2214 		}
2215 	}
2216 
2217 	if (!ps->redirected) {
2218 		CHECK(!win_skip_fading(ps, w));
2219 	}
2220 }
2221 
2222 /**
2223  * Find a managed window from window id in window linked list of the session.
2224  */
find_win(session_t * ps,xcb_window_t id)2225 struct win *find_win(session_t *ps, xcb_window_t id) {
2226 	if (!id) {
2227 		return NULL;
2228 	}
2229 
2230 	struct win *w = NULL;
2231 	HASH_FIND_INT(ps->windows, &id, w);
2232 	assert(w == NULL || !w->destroyed);
2233 	return w;
2234 }
2235 
2236 /**
2237  * Find a managed window from window id in window linked list of the session.
2238  */
find_managed_win(session_t * ps,xcb_window_t id)2239 struct managed_win *find_managed_win(session_t *ps, xcb_window_t id) {
2240 	struct win *w = find_win(ps, id);
2241 	if (!w || !w->managed) {
2242 		return NULL;
2243 	}
2244 
2245 	auto mw = (struct managed_win *)w;
2246 	assert(mw->state != WSTATE_DESTROYING);
2247 	return mw;
2248 }
2249 
2250 /**
2251  * Find out the WM frame of a client window using existing data.
2252  *
2253  * @param id window ID
2254  * @return struct win object of the found window, NULL if not found
2255  */
find_toplevel(session_t * ps,xcb_window_t id)2256 struct managed_win *find_toplevel(session_t *ps, xcb_window_t id) {
2257 	if (!id) {
2258 		return NULL;
2259 	}
2260 
2261 	HASH_ITER2(ps->windows, w) {
2262 		assert(!w->destroyed);
2263 		if (!w->managed) {
2264 			continue;
2265 		}
2266 
2267 		auto mw = (struct managed_win *)w;
2268 		if (mw->client_win == id) {
2269 			return mw;
2270 		}
2271 	}
2272 
2273 	return NULL;
2274 }
2275 
2276 /**
2277  * Find a managed window that is, or is a parent of `wid`.
2278  *
2279  * @param ps current session
2280  * @param wid window ID
2281  * @return struct _win object of the found window, NULL if not found
2282  */
find_managed_window_or_parent(session_t * ps,xcb_window_t wid)2283 struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid) {
2284 	// TODO this should probably be an "update tree", then find_toplevel.
2285 	//      current approach is a bit more "racy"
2286 	struct win *w = NULL;
2287 
2288 	// We traverse through its ancestors to find out the frame
2289 	// Using find_win here because if we found a unmanaged window we know about, we
2290 	// can stop early.
2291 	while (wid && wid != ps->root && !(w = find_win(ps, wid))) {
2292 		// xcb_query_tree probably fails if you run picom when X is somehow
2293 		// initializing (like add it in .xinitrc). In this case
2294 		// just leave it alone.
2295 		auto reply = xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, wid), NULL);
2296 		if (reply == NULL) {
2297 			break;
2298 		}
2299 
2300 		wid = reply->parent;
2301 		free(reply);
2302 	}
2303 
2304 	if (w == NULL || !w->managed) {
2305 		return NULL;
2306 	}
2307 
2308 	return (struct managed_win *)w;
2309 }
2310 
2311 /**
2312  * Check if a rectangle includes the whole screen.
2313  */
rect_is_fullscreen(const session_t * ps,int x,int y,int wid,int hei)2314 static inline bool rect_is_fullscreen(const session_t *ps, int x, int y, int wid, int hei) {
2315 	return (x <= 0 && y <= 0 && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height);
2316 }
2317 
2318 /**
2319  * Check if a window is fulscreen using EWMH
2320  *
2321  * TODO cache this property
2322  */
2323 static inline bool
win_is_fullscreen_xcb(xcb_connection_t * c,const struct atom * a,const xcb_window_t w)2324 win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_window_t w) {
2325 	xcb_get_property_cookie_t prop =
2326 	    xcb_get_property(c, 0, w, a->a_NET_WM_STATE, XCB_ATOM_ATOM, 0, 12);
2327 	xcb_get_property_reply_t *reply = xcb_get_property_reply(c, prop, NULL);
2328 	if (!reply)
2329 		return false;
2330 
2331 	if (reply->length) {
2332 		xcb_atom_t *val = xcb_get_property_value(reply);
2333 		for (uint32_t i = 0; i < reply->length; i++) {
2334 			if (val[i] != a->a_NET_WM_STATE_FULLSCREEN)
2335 				continue;
2336 			free(reply);
2337 			return true;
2338 		}
2339 	}
2340 	free(reply);
2341 	return false;
2342 }
2343 
2344 /// Set flags on a window. Some sanity checks are performed
win_set_flags(struct managed_win * w,uint64_t flags)2345 void win_set_flags(struct managed_win *w, uint64_t flags) {
2346 	log_debug("Set flags %lu to window %#010x (%s)", flags, w->base.id, w->name);
2347 	if (unlikely(w->state == WSTATE_DESTROYING)) {
2348 		log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name);
2349 		return;
2350 	}
2351 
2352 	w->flags |= flags;
2353 }
2354 
2355 /// Clear flags on a window. Some sanity checks are performed
win_clear_flags(struct managed_win * w,uint64_t flags)2356 void win_clear_flags(struct managed_win *w, uint64_t flags) {
2357 	log_debug("Clear flags %lu from window %#010x (%s)", flags, w->base.id, w->name);
2358 	if (unlikely(w->state == WSTATE_DESTROYING)) {
2359 		log_warn("Flags cleared on a destroyed window %#010x (%s)", w->base.id,
2360 		         w->name);
2361 		return;
2362 	}
2363 
2364 	w->flags = w->flags & (~flags);
2365 }
2366 
win_check_flags_any(struct managed_win * w,uint64_t flags)2367 bool win_check_flags_any(struct managed_win *w, uint64_t flags) {
2368 	return (w->flags & flags) != 0;
2369 }
2370 
win_check_flags_all(struct managed_win * w,uint64_t flags)2371 bool win_check_flags_all(struct managed_win *w, uint64_t flags) {
2372 	return (w->flags & flags) == flags;
2373 }
2374 
2375 /**
2376  * Check if a window is a fullscreen window.
2377  *
2378  * It's not using w->border_size for performance measures.
2379  */
win_is_fullscreen(const session_t * ps,const struct managed_win * w)2380 bool win_is_fullscreen(const session_t *ps, const struct managed_win *w) {
2381 	if (!ps->o.no_ewmh_fullscreen && win_is_fullscreen_xcb(ps->c, ps->atoms, w->client_win))
2382 		return true;
2383 	return rect_is_fullscreen(ps, w->g.x, w->g.y, w->widthb, w->heightb) &&
2384 	       (!w->bounding_shaped || w->rounded_corners);
2385 }
2386 
2387 /**
2388  * Check if a window has BYPASS_COMPOSITOR property set
2389  *
2390  * TODO cache this property
2391  */
win_is_bypassing_compositor(const session_t * ps,const struct managed_win * w)2392 bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *w) {
2393 	bool ret = false;
2394 
2395 	auto prop = x_get_prop(ps, w->client_win, ps->atoms->a_NET_WM_BYPASS_COMPOSITOR,
2396 	                       1L, XCB_ATOM_CARDINAL, 32);
2397 
2398 	if (prop.nitems && *prop.c32 == 1) {
2399 		ret = true;
2400 	}
2401 
2402 	free_winprop(&prop);
2403 	return ret;
2404 }
2405 
2406 /**
2407  * Check if a window is focused, without using any focus rules or forced focus settings
2408  */
win_is_focused_raw(const session_t * ps,const struct managed_win * w)2409 bool win_is_focused_raw(const session_t *ps, const struct managed_win *w) {
2410 	return w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->active_win == w;
2411 }
2412 
2413 // Find the managed window immediately below `i` in the window stack
2414 struct managed_win *
win_stack_find_next_managed(const session_t * ps,const struct list_node * i)2415 win_stack_find_next_managed(const session_t *ps, const struct list_node *i) {
2416 	while (!list_node_is_last(&ps->window_stack, i)) {
2417 		auto next = list_entry(i->next, struct win, stack_neighbour);
2418 		if (next->managed) {
2419 			return (struct managed_win *)next;
2420 		}
2421 		i = &next->stack_neighbour;
2422 	}
2423 	return NULL;
2424 }
2425 
2426 /// Return whether this window is mapped on the X server side
win_is_mapped_in_x(const struct managed_win * w)2427 bool win_is_mapped_in_x(const struct managed_win *w) {
2428 	return w->state == WSTATE_MAPPING || w->state == WSTATE_FADING ||
2429 	       w->state == WSTATE_MAPPED || (w->flags & WIN_FLAGS_MAPPED);
2430 }
2431