Fix premature window init causing borders to not update properly

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-04-15 16:41:11 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-04-15 16:41:11 +0200
Commit 07b63258b0acc1f715111642d1e95d34d811e6f9 (patch)
-rw-r--r-- manager.c 151
1 files changed, 59 insertions, 92 deletions
diff --git a/manager.c b/manager.c
...
37
static Atom WM_TAKE_FOCUS;
37
static Atom WM_TAKE_FOCUS;
38
static Atom _NET_SUPPORTED;
38
static Atom _NET_SUPPORTED;
39
  
39
  
  
40
static void update_window_border(Window window, int active) {
  
41
	if (window == None) return;
  
42
	if (!window_exists(window)) return;
  
43
  
  
44
	Atom actual_type;
  
45
	int actual_format;
  
46
	unsigned long nitems, bytes_after;
  
47
	unsigned char *prop = NULL;
  
48
	int has_decorations = 1;
  
49
  
  
50
	// Check _MOTIF_WM_HINTS to see if the window requested no decorations
  
51
	XErrorHandler old = XSetErrorHandler(ignore_x_error);
  
52
	int status = XGetWindowProperty(wm.dpy, window, _MOTIF_WM_HINTS, 0, 5, False, AnyPropertyType,
  
53
			&actual_type, &actual_format, &nitems, &bytes_after, &prop);
  
54
	XSync(wm.dpy, False);
  
55
	XSetErrorHandler(old);
  
56
  
  
57
	if (status == Success) {
  
58
		if (prop && nitems >= 3) {
  
59
			unsigned long flags = ((unsigned long *)prop)[0];
  
60
			unsigned long decorations = ((unsigned long *)prop)[2];
  
61
			// If flags bit 1 is set (MWM_HINTS_DECORATIONS) and decorations bit 0 is cleared (MWM_DECOR_ALL/BORDER), then no border.
  
62
			// Simplification: if decorations is 0, assume no border.
  
63
			if ((flags & 2) && (decorations & 1) == 0) {
  
64
				has_decorations = 0;
  
65
			}
  
66
		}
  
67
		if (prop) XFree(prop);
  
68
	}
  
69
  
  
70
	unsigned int bw = has_decorations ? border_size : 0;
  
71
	XSetWindowBorderWidth(wm.dpy, window, bw);
  
72
  
  
73
	if (active) {
  
74
		if (is_always_on_top(window)) {
  
75
			XSetWindowBorder(wm.dpy, window, wm.borders.on_top_active);
  
76
		} else if (is_sticky(window)) {
  
77
			XSetWindowBorder(wm.dpy, window, wm.borders.sticky_active);
  
78
		} else {
  
79
			XSetWindowBorder(wm.dpy, window, wm.borders.normal_active);
  
80
		}
  
81
	} else {
  
82
		if (is_always_on_top(window)) {
  
83
			XSetWindowBorder(wm.dpy, window, wm.borders.on_top_inactive);
  
84
		} else if (is_sticky(window)) {
  
85
			XSetWindowBorder(wm.dpy, window, wm.borders.sticky_inactive);
  
86
		} else {
  
87
			XSetWindowBorder(wm.dpy, window, wm.borders.normal_inactive);
  
88
		}
  
89
	}
  
90
}
  
91
  
40
static void update_wm_state(Window w, Atom state_atom, int add);
92
static void update_wm_state(Window w, Atom state_atom, int add);
41
static int has_wm_state(Window w, Atom state_atom);
93
static int has_wm_state(Window w, Atom state_atom);
42
static void check_and_clear_maximized_state(Window w, int horizontal, int vertical);
94
static void check_and_clear_maximized_state(Window w, int horizontal, int vertical);
...
140
				add_client(wins[i]);
192
				add_client(wins[i]);
141
				XSelectInput(wm.dpy, wins[i], EnterWindowMask | LeaveWindowMask);
193
				XSelectInput(wm.dpy, wins[i], EnterWindowMask | LeaveWindowMask);
142
				grab_buttons(wins[i]);
194
				grab_buttons(wins[i]);
143
  
195
				update_window_border(wins[i], 0);
144
				XSetWindowBorderWidth(wm.dpy, wins[i], border_size);
  
145
				if (is_sticky(wins[i])) {
  
146
					XSetWindowBorder(wm.dpy, wins[i], wm.borders.sticky_inactive);
  
147
				} else if (is_always_on_top(wins[i])) {
  
148
					XSetWindowBorder(wm.dpy, wins[i], wm.borders.on_top_inactive);
  
149
				} else {
  
150
					XSetWindowBorder(wm.dpy, wins[i], wm.borders.normal_inactive);
  
151
				}
  
152
			}
196
			}
153
		}
197
		}
154
		if (wins) XFree(wins);
198
		if (wins) XFree(wins);
...
339
  
383
  
340
	wm.running = 1;
384
	wm.running = 1;
341
  
385
  
342
	scan_windows();
  
343
  
  
344
	// Grab keys for keybinds.
386
	// Grab keys for keybinds.
345
	for (unsigned int i = 0; i < LENGTH(keybinds); i++) {
387
	for (unsigned int i = 0; i < LENGTH(keybinds); i++) {
346
		KeyCode keycode = XKeysymToKeycode(wm.dpy, keybinds[i].keysym);
388
		KeyCode keycode = XKeysymToKeycode(wm.dpy, keybinds[i].keysym);
...
399
		wm.borders.on_top_inactive = on_top_inactive.pixel;
441
		wm.borders.on_top_inactive = on_top_inactive.pixel;
400
	}
442
	}
401
  
443
  
402
	// Scan for existing windows.
444
	// Scan for existing windows and apply initial layout.
403
	unsigned int nchildren;
445
	scan_windows();
404
	Window root_return, parent_return, *children;
446
	apply_tiling_layout();
405
	XWindowAttributes wa;
  
406
  
447
  
407
	if (XQueryTree(wm.dpy, wm.root, &root_return, &parent_return, &children, &nchildren)) {
  
408
		for (unsigned int i = 0; i < nchildren; i++) {
  
409
			if (!XGetWindowAttributes(wm.dpy, children[i], &wa) || wa.override_redirect)
  
410
				continue;
  
411
			if (wa.map_state == IsViewable) {
  
412
				grab_buttons(children[i]);
  
413
				XSelectInput(wm.dpy, children[i], EnterWindowMask | LeaveWindowMask);
  
414
				add_client(children[i]);
  
415
				log_message(stdout, LOG_DEBUG, "Grabbed existing window 0x%lx", children[i]);
  
416
			}
  
417
		}
  
418
		if (children) XFree(children);
  
419
	}
  
420
	redraw_widgets();
448
	redraw_widgets();
421
	update_client_list();
449
	update_client_list();
422
	init_audio();
450
	init_audio();
...
1115
	if (window == None) return;
1143
	if (window == None) return;
1116
	if (!window_exists(window)) return;
1144
	if (!window_exists(window)) return;
1117
  
1145
  
1118
	Atom actual_type;
  
1119
	int actual_format;
  
1120
	unsigned long nitems, bytes_after;
  
1121
	unsigned char *prop = NULL;
  
1122
	int has_decorations = 1;
  
1123
  
  
1124
	// Check _MOTIF_WM_HINTS to see if the window requested no decorations
  
1125
	XErrorHandler old = XSetErrorHandler(ignore_x_error);
  
1126
	int status = XGetWindowProperty(wm.dpy, window, _MOTIF_WM_HINTS, 0, 5, False, AnyPropertyType,
  
1127
			&actual_type, &actual_format, &nitems, &bytes_after, &prop);
  
1128
	XSync(wm.dpy, False);
  
1129
	XSetErrorHandler(old);
  
1130
  
  
1131
	if (status == Success) {
  
1132
		if (prop && nitems >= 3) {
  
1133
			unsigned long flags = ((unsigned long *)prop)[0];
  
1134
			unsigned long decorations = ((unsigned long *)prop)[2];
  
1135
			// If flags bit 1 is set (MWM_HINTS_DECORATIONS) and decorations bit 0 is cleared (MWM_DECOR_ALL/BORDER), then no border.
  
1136
			// Simplification: if decorations is 0, assume no border.
  
1137
			if ((flags & 2) && (decorations & 1) == 0) {
  
1138
				has_decorations = 0;
  
1139
			}
  
1140
		}
  
1141
		if (prop) XFree(prop);
  
1142
	}
  
1143
  
  
1144
	unsigned int bw = has_decorations ? border_size : 0;
  
1145
  
  
1146
	// Setting current active window to inactive.
1146
	// Setting current active window to inactive.
1147
	if (wm.active != None) {
1147
	if (wm.active != None && wm.active != window) {
1148
		// For safety/speed, let's re-check hints for the old active window too.
1148
		update_window_border(wm.active, 0);
1149
		int old_has_decorations = 1;
  
1150
  
  
1151
		old = XSetErrorHandler(ignore_x_error);
  
1152
		status = XGetWindowProperty(wm.dpy, wm.active, _MOTIF_WM_HINTS, 0, 5, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop);
  
1153
		XSync(wm.dpy, False);
  
1154
		XSetErrorHandler(old);
  
1155
  
  
1156
		if (status == Success) {
  
1157
			if (prop && nitems >= 3) {
  
1158
				unsigned long flags = ((unsigned long *)prop)[0];
  
1159
				unsigned long decorations = ((unsigned long *)prop)[2];
  
1160
				if ((flags & 2) && (decorations & 1) == 0) {
  
1161
					old_has_decorations = 0;
  
1162
				}
  
1163
			}
  
1164
			if (prop) XFree(prop);
  
1165
		}
  
1166
  
  
1167
		XSetWindowBorderWidth(wm.dpy, wm.active, old_has_decorations ? border_size : 0);
  
1168
		if (is_always_on_top(wm.active)) {
  
1169
			XSetWindowBorder(wm.dpy, wm.active, wm.borders.on_top_inactive);
  
1170
		} else if (is_sticky(wm.active)) {
  
1171
			XSetWindowBorder(wm.dpy, wm.active, wm.borders.sticky_inactive);
  
1172
		} else {
  
1173
			XSetWindowBorder(wm.dpy, wm.active, wm.borders.normal_inactive);
  
1174
		}
  
1175
		log_message(stdout, LOG_DEBUG, "Active window 0x%lx border set to inactive", wm.active);
1149
		log_message(stdout, LOG_DEBUG, "Active window 0x%lx border set to inactive", wm.active);
1176
	}
1150
	}
1177
  
1151
  
1178
	// Setting desired window to active.
1152
	// Setting desired window to active.
1179
	XSetWindowBorderWidth(wm.dpy, window, bw);
1153
	update_window_border(window, 1);
1180
	if (is_always_on_top(window)) {
  
1181
		XSetWindowBorder(wm.dpy, window, wm.borders.on_top_active);
  
1182
	} else if (is_sticky(window)) {
  
1183
		XSetWindowBorder(wm.dpy, window, wm.borders.sticky_active);
  
1184
	} else {
  
1185
		XSetWindowBorder(wm.dpy, window, wm.borders.normal_active);
  
1186
	}
  
1187
	XFlush(wm.dpy);
1154
	XFlush(wm.dpy);
1188
  
1155
  
1189
	log_message(stdout, LOG_DEBUG, "Desired window 0x%lx border set to active", window);
1156
	log_message(stdout, LOG_DEBUG, "Desired window 0x%lx border set to active", window);
...