Fix Wine bug not sending keypresses and mouse position offset

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-04-16 12:22:40 +0200
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-04-16 12:22:40 +0200
Commit a01fd03d79e61e9ca0afe4cab4208179b6df36de (patch)
-rw-r--r-- glitch.h 2
-rw-r--r-- main.c 6
-rw-r--r-- manager.c 210
3 files changed, 167 insertions, 51 deletions
diff --git a/glitch.h b/glitch.h
...
156
void init_window_manager(void);
156
void init_window_manager(void);
157
void deinit_window_manager(void);
157
void deinit_window_manager(void);
158
void handle_map_request(void);
158
void handle_map_request(void);
  
159
void handle_map_notify(void);
159
void handle_unmap_notify(void);
160
void handle_unmap_notify(void);
160
void handle_destroy_notify(void);
161
void handle_destroy_notify(void);
161
void handle_property_notify(void);
162
void handle_property_notify(void);
...
170
void handle_enter_notify(void);
171
void handle_enter_notify(void);
171
void handle_expose(void);
172
void handle_expose(void);
172
void handle_configure_request(void);
173
void handle_configure_request(void);
  
174
void handle_configure_notify(void);
173
  
175
  
174
Window get_active_window(void);
176
Window get_active_window(void);
175
void set_active_window(Window window, Time time);
177
void set_active_window(Window window, Time time);
...
diff --git a/main.c b/main.c
...
72
			case MapRequest:
72
			case MapRequest:
73
				handle_map_request();
73
				handle_map_request();
74
				break;
74
				break;
  
75
			case MapNotify:
  
76
				handle_map_notify();
  
77
				break;
75
			case UnmapNotify:
78
			case UnmapNotify:
76
				handle_unmap_notify();
79
				handle_unmap_notify();
77
				break;
80
				break;
...
115
				break;
118
				break;
116
			case ConfigureRequest:
119
			case ConfigureRequest:
117
				handle_configure_request();
120
				handle_configure_request();
  
121
				break;
  
122
			case ConfigureNotify:
  
123
				handle_configure_notify();
118
				break;
124
				break;
119
		}
125
		}
120
		XUnlockDisplay(wm.dpy);
126
		XUnlockDisplay(wm.dpy);
...
diff --git a/manager.c b/manager.c
...
36
static Atom WM_DELETE_WINDOW;
36
static Atom WM_DELETE_WINDOW;
37
static Atom WM_TAKE_FOCUS;
37
static Atom WM_TAKE_FOCUS;
38
static Atom _NET_SUPPORTED;
38
static Atom _NET_SUPPORTED;
  
39
static Atom _NET_FRAME_EXTENTS;
  
40
static Atom WM_STATE_ATOM;
39
  
41
  
40
static void update_window_border(Window window, int active) {
42
static void update_window_border(Window window, int active) {
41
	if (window == None) return;
43
	if (window == None) return;
...
96
static void remove_client(Window w);
98
static void remove_client(Window w);
97
static Window get_toplevel_window(Window w);
99
static Window get_toplevel_window(Window w);
98
static void set_fullscreen(Window w, int full);
100
static void set_fullscreen(Window w, int full);
  
101
static void send_configure(Window w);
  
102
static Client *wintoclient(Window w);
99
  
103
  
100
int x_error_handler(Display *dpy, XErrorEvent *ee) {
104
int x_error_handler(Display *dpy, XErrorEvent *ee) {
101
	(void) dpy;
105
	(void) dpy;
...
115
	return 0;
119
	return 0;
116
}
120
}
117
  
121
  
  
122
static void set_client_state(Window w, long state) {
  
123
	long data[] = { state, None };
  
124
	XChangeProperty(wm.dpy, w, WM_STATE_ATOM, WM_STATE_ATOM, 32, PropModeReplace, (unsigned char *)data, 2);
  
125
}
  
126
  
118
static void add_client(Window w) {
127
static void add_client(Window w) {
119
	// Check if already in list or is root.
128
	// Check if already in list or is root.
120
	if (w == wm.root) return;
129
	if (w == wm.root) return;
...
141
	}
150
	}
142
	wm.clients = new_c;
151
	wm.clients = new_c;
143
	log_message(stdout, LOG_DEBUG, "Added client 0x%lx", w);
152
	log_message(stdout, LOG_DEBUG, "Added client 0x%lx", w);
  
153
  
  
154
	unsigned long extents[4] = {0, 0, 0, 0};
  
155
	XChangeProperty(wm.dpy, w, _NET_FRAME_EXTENTS, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)extents, 4);
  
156
	set_client_state(w, NormalState);
144
}
157
}
145
  
158
  
146
static void remove_client(Window w) {
159
static void remove_client(Window w) {
...
157
				c->next->prev = c->prev;
170
				c->next->prev = c->prev;
158
			}
171
			}
159
  
172
  
  
173
			set_client_state(w, WithdrawnState);
160
			free(c);
174
			free(c);
161
			log_message(stdout, LOG_DEBUG, "Removed client 0x%lx", w);
175
			log_message(stdout, LOG_DEBUG, "Removed client 0x%lx", w);
162
			return;
176
			return;
...
258
	_NET_SUPPORTED = XInternAtom(wm.dpy, "_NET_SUPPORTED", False);
272
	_NET_SUPPORTED = XInternAtom(wm.dpy, "_NET_SUPPORTED", False);
259
	_NET_SUPPORTING_WM_CHECK = XInternAtom(wm.dpy, "_NET_SUPPORTING_WM_CHECK", False);
273
	_NET_SUPPORTING_WM_CHECK = XInternAtom(wm.dpy, "_NET_SUPPORTING_WM_CHECK", False);
260
	_NET_WM_NAME = XInternAtom(wm.dpy, "_NET_WM_NAME", False);
274
	_NET_WM_NAME = XInternAtom(wm.dpy, "_NET_WM_NAME", False);
261
	_GLITCH_PRE_HMAX_GEOM = XInternAtom(wm.dpy, "_GLITCH_PRE_HMAX_GEOM", False);
275
	_NET_FRAME_EXTENTS = XInternAtom(wm.dpy, "_NET_FRAME_EXTENTS", False);
262
	_GLITCH_PRE_VMAX_GEOM = XInternAtom(wm.dpy, "_GLITCH_PRE_VMAX_GEOM", False);
276
	WM_STATE_ATOM = XInternAtom(wm.dpy, "WM_STATE", False);
263
	_GLITCH_PRE_FULLSCREEN_GEOM = XInternAtom(wm.dpy, "_GLITCH_PRE_FULLSCREEN_GEOM", False);
  
264
	_MOTIF_WM_HINTS = XInternAtom(wm.dpy, "_MOTIF_WM_HINTS", False);
  
265
	WM_PROTOCOLS = XInternAtom(wm.dpy, "WM_PROTOCOLS", False);
  
266
	WM_DELETE_WINDOW = XInternAtom(wm.dpy, "WM_DELETE_WINDOW", False);
  
267
	WM_TAKE_FOCUS = XInternAtom(wm.dpy, "WM_TAKE_FOCUS", False);
  
268
  
277
  
269
	// Create supporting window for EWMH compliance.
278
	// Create supporting window for EWMH compliance.
270
	Window check_win = XCreateSimpleWindow(wm.dpy, wm.root, 0, 0, 1, 1, 0, 0, 0);
279
	Window check_win = XCreateSimpleWindow(wm.dpy, wm.root, 0, 0, 1, 1, 0, 0, 0);
  
280
	XMapWindow(wm.dpy, check_win);
271
	XChangeProperty(wm.dpy, check_win, _NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&check_win, 1);
281
	XChangeProperty(wm.dpy, check_win, _NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&check_win, 1);
272
	XChangeProperty(wm.dpy, check_win, _NET_WM_NAME, XA_STRING, 8, PropModeReplace, (unsigned char *)"glitch", 6);
282
	XChangeProperty(wm.dpy, check_win, _NET_WM_NAME, XA_STRING, 8, PropModeReplace, (unsigned char *)"LG3D", 4);
273
	XChangeProperty(wm.dpy, wm.root, _NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&check_win, 1);
283
	XChangeProperty(wm.dpy, wm.root, _NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&check_win, 1);
274
	XChangeProperty(wm.dpy, wm.root, _NET_WM_NAME, XA_STRING, 8, PropModeReplace, (unsigned char *)"glitch", 6);
284
	XChangeProperty(wm.dpy, wm.root, _NET_WM_NAME, XA_STRING, 8, PropModeReplace, (unsigned char *)"LG3D", 4);
275
  
285
  
276
	// Set supported atoms.
286
	// Set supported atoms.
277
	Atom net_atoms[] = {
287
	Atom net_atoms[] = {
278
		_NET_SUPPORTED,
288
		_NET_SUPPORTED,
279
		_NET_SUPPORTING_WM_CHECK,
289
		_NET_SUPPORTING_WM_CHECK,
280
		_NET_WM_NAME,
290
		_NET_WM_NAME,
  
291
		_NET_FRAME_EXTENTS,
281
		_NET_WM_DESKTOP,
292
		_NET_WM_DESKTOP,
282
		_NET_CURRENT_DESKTOP,
293
		_NET_CURRENT_DESKTOP,
283
		_NET_NUMBER_OF_DESKTOPS,
294
		_NET_NUMBER_OF_DESKTOPS,
...
289
		_NET_WM_STATE_MAXIMIZED_HORZ,
300
		_NET_WM_STATE_MAXIMIZED_HORZ,
290
		_NET_WM_STATE_MAXIMIZED_VERT,
301
		_NET_WM_STATE_MAXIMIZED_VERT,
291
		_NET_WM_STATE_ABOVE,
302
		_NET_WM_STATE_ABOVE,
  
303
		WM_STATE_ATOM,
292
		WM_DELETE_WINDOW,
304
		WM_DELETE_WINDOW,
293
		WM_TAKE_FOCUS
305
		WM_TAKE_FOCUS
294
	};
306
	};
...
600
}
612
}
601
  
613
  
602
void set_active_window(Window window, Time time) {
614
void set_active_window(Window window, Time time) {
  
615
	(void)time; // Use CurrentTime for more reliable focus stealing/pulling.
  
616
  
603
	if (window != None) {
617
	if (window != None) {
604
		if (!window_exists(window)) return;
618
		if (!window_exists(window)) return;
605
  
619
  
606
		XChangeProperty(wm.dpy, wm.root, _NET_ACTIVE_WINDOW, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&window, 1);
  
607
		wm.active = window;
620
		wm.active = window;
  
621
		XChangeProperty(wm.dpy, wm.root, _NET_ACTIVE_WINDOW, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&window, 1);
608
  
622
  
609
		// Check for WM_TAKE_FOCUS support.
623
		// Check for WM_TAKE_FOCUS support.
610
		int take_focus = 0;
624
		int take_focus = 0;
...
620
			XFree(protocols);
634
			XFree(protocols);
621
		}
635
		}
622
  
636
  
  
637
		// Check WM_HINTS if needed (logging only for now as we force focus anyway)
  
638
		XWMHints *hints = XGetWMHints(wm.dpy, window);
  
639
		if (hints) {
  
640
			log_message(stdout, LOG_DEBUG, "Window hints: input=%d", !!(hints->flags & InputHint && hints->input));
  
641
			XFree(hints);
  
642
		}
  
643
  
  
644
		// Always set X focus for managed windows.
  
645
		XSetInputFocus(wm.dpy, window, RevertToParent, CurrentTime);
  
646
		log_message(stdout, LOG_DEBUG, "Set input focus to 0x%lx", window);
  
647
  
623
		if (take_focus) {
648
		if (take_focus) {
624
			XEvent ev;
649
			XEvent ev = {0};
625
			ev.type = ClientMessage;
650
			ev.type = ClientMessage;
626
			ev.xclient.window = window;
651
			ev.xclient.window = window;
627
			ev.xclient.message_type = WM_PROTOCOLS;
652
			ev.xclient.message_type = WM_PROTOCOLS;
628
			ev.xclient.format = 32;
653
			ev.xclient.format = 32;
629
			ev.xclient.data.l[0] = WM_TAKE_FOCUS;
654
			ev.xclient.data.l[0] = WM_TAKE_FOCUS;
630
			ev.xclient.data.l[1] = time;
655
			ev.xclient.data.l[1] = CurrentTime;
631
			XSendEvent(wm.dpy, window, False, NoEventMask, &ev);
656
			XSendEvent(wm.dpy, window, False, NoEventMask, &ev);
632
			log_message(stdout, LOG_DEBUG, "Sent WM_TAKE_FOCUS to 0x%lx with time %lu", window, time);
657
			log_message(stdout, LOG_DEBUG, "Sent WM_TAKE_FOCUS to 0x%lx", window);
633
		}
  
634
  
  
635
		int expects_input = 1;
  
636
		XWMHints *hints = XGetWMHints(wm.dpy, window);
  
637
		if (hints) {
  
638
			if ((hints->flags & InputHint) && !hints->input) {
  
639
				expects_input = 0;
  
640
			}
  
641
			XFree(hints);
  
642
		}
  
643
  
  
644
		if (expects_input) {
  
645
			XSetInputFocus(wm.dpy, window, RevertToPointerRoot, time);
  
646
			log_message(stdout, LOG_DEBUG, "Set input focus to 0x%lx with time %lu", window, time);
  
647
		} else {
  
648
			log_message(stdout, LOG_DEBUG, "Window 0x%lx does not expect input, skipping XSetInputFocus", window);
  
649
		}
658
		}
650
	} else {
659
	} else {
651
		XDeleteProperty(wm.dpy, wm.root, _NET_ACTIVE_WINDOW);
660
		XDeleteProperty(wm.dpy, wm.root, _NET_ACTIVE_WINDOW);
652
		wm.active = None;
661
		wm.active = None;
653
		XSetInputFocus(wm.dpy, wm.root, RevertToPointerRoot, time);
662
		XSetInputFocus(wm.dpy, wm.root, RevertToParent, CurrentTime);
  
663
		log_message(stdout, LOG_DEBUG, "Reset focus to root");
654
	}
664
	}
655
	XFlush(wm.dpy);
665
	XFlush(wm.dpy);
656
}
666
}
...
701
	log_message(stdout, LOG_DEBUG, "ConfigureRequest for 0x%lx (x=%d, y=%d, w=%d, h=%d)", ev->window, ev->x, ev->y, ev->width, ev->height);
711
	log_message(stdout, LOG_DEBUG, "ConfigureRequest for 0x%lx (x=%d, y=%d, w=%d, h=%d)", ev->window, ev->x, ev->y, ev->width, ev->height);
702
}
712
}
703
  
713
  
  
714
void handle_configure_notify(void) {
  
715
	XConfigureEvent *ev = &wm.ev.xconfigure;
  
716
  
  
717
	if (ev->window == wm.root) return;
  
718
	
  
719
	// Only send synthetic events for windows we manage as top-level clients.
  
720
	Client *c;
  
721
	for (c = wm.clients; c; c = c->next) {
  
722
		if (c->window == ev->window) {
  
723
			log_message(stdout, LOG_DEBUG, "Sending synthetic ConfigureNotify to 0x%lx (x=%d, y=%d, w=%d, h=%d)", ev->window, ev->x, ev->y, ev->width, ev->height);
  
724
			send_configure(c->window);
  
725
			break;
  
726
		}
  
727
	}
  
728
}
  
729
  
  
730
void handle_map_notify(void) {
  
731
	Window window = wm.ev.xmap.window;
  
732
	if (window == wm.root) return;
  
733
  
  
734
	log_message(stdout, LOG_DEBUG, "MapNotify for 0x%lx", window);
  
735
  
  
736
	// Check if this is a managed window.
  
737
	Client *c;
  
738
	for (c = wm.clients; c; c = c->next) {
  
739
		if (c->window == window) {
  
740
			// Shows, raises and focuses the window.
  
741
			set_active_border(window);
  
742
			set_active_window(window, CurrentTime);
  
743
			
  
744
			// Ensure it has synthetic configure after mapping.
  
745
			send_configure(window);
  
746
			
  
747
			log_message(stdout, LOG_DEBUG, "Focused and configured 0x%lx after MapNotify", window);
  
748
			break;
  
749
		}
  
750
	}
  
751
	redraw_widgets();
  
752
}
  
753
  
704
// https://tronche.com/gui/x/xlib/events/structure-control/map.html
754
// https://tronche.com/gui/x/xlib/events/structure-control/map.html
705
void handle_map_request(void) {
755
void handle_map_request(void) {
706
	Window window = wm.ev.xmaprequest.window;
756
	Window window = wm.ev.xmaprequest.window;
...
711
	// Move window under cursor position and clamps inside the screen bounds.
761
	// Move window under cursor position and clamps inside the screen bounds.
712
	XWindowAttributes check_attr;
762
	XWindowAttributes check_attr;
713
	if (XGetWindowAttributes(wm.dpy, window, &check_attr)) {
763
	if (XGetWindowAttributes(wm.dpy, window, &check_attr)) {
714
		XSelectInput(wm.dpy, window, EnterWindowMask | LeaveWindowMask);
764
		XSelectInput(wm.dpy, window, EnterWindowMask | LeaveWindowMask | StructureNotifyMask | FocusChangeMask);
715
  
765
  
716
		Window root_return, child_return;
766
		Window root_return, child_return;
717
		int root_x, root_y, win_x, win_y;
767
		int root_x, root_y, win_x, win_y;
...
736
	// Tag window with current desktop.
786
	// Tag window with current desktop.
737
	unsigned long desktop = wm.current_desktop;
787
	unsigned long desktop = wm.current_desktop;
738
	XChangeProperty(wm.dpy, window, _NET_WM_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&desktop, 1);
788
	XChangeProperty(wm.dpy, window, _NET_WM_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&desktop, 1);
739
  
  
740
	XMapWindow(wm.dpy, window);
  
741
	raise_window(window);
  
742
  
  
743
	// Shows, raises and focuses the window.
  
744
	set_active_border(window);
  
745
	set_active_window(window, CurrentTime);
  
746
  
789
  
747
	// Grab buttons for click-to-focus.
790
	// Grab buttons for click-to-focus.
748
	grab_buttons(window);
791
	grab_buttons(window);
...
750
	add_client(window);
793
	add_client(window);
751
	apply_tiling_layout();
794
	apply_tiling_layout();
752
  
795
  
  
796
	XMapWindow(wm.dpy, window);
  
797
	raise_window(window);
  
798
  
753
	XSync(wm.dpy, False);
799
	XSync(wm.dpy, False);
754
	XSetErrorHandler(old);
800
	XSetErrorHandler(old);
755
  
801
  
756
	log_message(stdout, LOG_DEBUG, "Window 0x%lx mapped and grabbed on desktop %d", window, wm.current_desktop);
802
	log_message(stdout, LOG_DEBUG, "Window 0x%lx added and requested map on desktop %d", window, wm.current_desktop);
757
	redraw_widgets();
  
758
	update_client_list();
803
	update_client_list();
759
}
804
}
760
  
805
  
...
762
void handle_unmap_notify(void) {
807
void handle_unmap_notify(void) {
763
	Window window = wm.ev.xunmap.window;
808
	Window window = wm.ev.xunmap.window;
764
	if (window == wm.active) {
809
	if (window == wm.active) {
765
		wm.active = None;
810
		set_active_window(None, CurrentTime);
766
	}
811
	}
767
  
812
  
768
	// So if we get an unmap for a sticky window, it means the application closed it.
813
	// So if we get an unmap for a sticky window, it means the application closed it.
...
779
void handle_destroy_notify(void) {
824
void handle_destroy_notify(void) {
780
	Window window = wm.ev.xdestroywindow.window;
825
	Window window = wm.ev.xdestroywindow.window;
781
	if (window == wm.active) {
826
	if (window == wm.active) {
782
		wm.active = None;
827
		set_active_window(None, CurrentTime);
783
	}
828
	}
784
	remove_client(window);
829
	remove_client(window);
785
	apply_tiling_layout();
830
	apply_tiling_layout();
...
806
				wm.attr.y + (wm.start.button == 1 ? ydiff : 0),
851
				wm.attr.y + (wm.start.button == 1 ? ydiff : 0),
807
				MAX(100, wm.attr.width  + (wm.start.button == 3 ? xdiff : 0)),
852
				MAX(100, wm.attr.width  + (wm.start.button == 3 ? xdiff : 0)),
808
				MAX(100, wm.attr.height + (wm.start.button == 3 ? ydiff : 0)));
853
				MAX(100, wm.attr.height + (wm.start.button == 3 ? ydiff : 0)));
  
854
		send_configure(wm.start.subwindow);
809
  
855
  
810
		// Reset maximization state on manual move/resize.
856
		// Reset maximization state on manual move/resize.
811
		if (wm.start.button == 1) {
857
		if (wm.start.button == 1) {
...
847
	XFlush(wm.dpy);
893
	XFlush(wm.dpy);
848
}
894
}
849
  
895
  
  
896
static Client *wintoclient(Window w) {
  
897
	if (w == None || w == wm.root) return NULL;
  
898
	Client *c;
  
899
	for (c = wm.clients; c; c = c->next) {
  
900
		if (c->window == w) return c;
  
901
	}
  
902
	// Search parent hierarchy
  
903
	Window root, parent, *children;
  
904
	unsigned int nchildren;
  
905
	XErrorHandler old = XSetErrorHandler(ignore_x_error);
  
906
	while (w != wm.root && w != None) {
  
907
		if (XQueryTree(wm.dpy, w, &root, &parent, &children, &nchildren)) {
  
908
			if (children) XFree(children);
  
909
			for (c = wm.clients; c; c = c->next) {
  
910
				if (c->window == parent) {
  
911
					XSetErrorHandler(old);
  
912
					return c;
  
913
				}
  
914
			}
  
915
			w = parent;
  
916
		} else {
  
917
			break;
  
918
		}
  
919
	}
  
920
	XSetErrorHandler(old);
  
921
	return NULL;
  
922
}
  
923
  
  
924
static void send_configure(Window w) {
  
925
	XWindowAttributes wa;
  
926
	if (!XGetWindowAttributes(wm.dpy, w, &wa)) return;
  
927
  
  
928
	XConfigureEvent ce;
  
929
	ce.type = ConfigureNotify;
  
930
	ce.display = wm.dpy;
  
931
	ce.event = w;
  
932
	ce.window = w;
  
933
	ce.x = wa.x;
  
934
	ce.y = wa.y;
  
935
	ce.width = wa.width;
  
936
	ce.height = wa.height;
  
937
	ce.border_width = wa.border_width;
  
938
	ce.above = None;
  
939
	ce.override_redirect = False;
  
940
	XSendEvent(wm.dpy, w, False, StructureNotifyMask, (XEvent *)&ce);
  
941
}
  
942
  
850
static void set_fullscreen(Window window, int full) {
943
static void set_fullscreen(Window window, int full) {
851
	int currently_fullscreen = has_wm_state(window, _NET_WM_STATE_FULLSCREEN);
944
	int currently_fullscreen = has_wm_state(window, _NET_WM_STATE_FULLSCREEN);
852
  
945
  
...
862
			int screen_height = DisplayHeight(wm.dpy, wm.screen);
955
			int screen_height = DisplayHeight(wm.dpy, wm.screen);
863
  
956
  
864
			XMoveResizeWindow(wm.dpy, window, 0, 0, screen_width, screen_height);
957
			XMoveResizeWindow(wm.dpy, window, 0, 0, screen_width, screen_height);
  
958
			send_configure(window);
865
			XSetWindowBorderWidth(wm.dpy, window, 0);
959
			XSetWindowBorderWidth(wm.dpy, window, 0);
866
			XRaiseWindow(wm.dpy, window);
960
			XRaiseWindow(wm.dpy, window);
867
  
961
  
...
883
		if (status == Success && prop && nitems == 4) {
977
		if (status == Success && prop && nitems == 4) {
884
			long *geom = (long *)prop;
978
			long *geom = (long *)prop;
885
			XMoveResizeWindow(wm.dpy, window, (int)geom[0], (int)geom[1], (unsigned int)geom[2], (unsigned int)geom[3]);
979
			XMoveResizeWindow(wm.dpy, window, (int)geom[0], (int)geom[1], (unsigned int)geom[2], (unsigned int)geom[3]);
  
980
			send_configure(window);
886
		}
981
		}
887
		if (prop) XFree(prop);
982
		if (prop) XFree(prop);
888
  
983
  
...
921
		if (new_y < 0) new_y = 0;
1016
		if (new_y < 0) new_y = 0;
922
  
1017
  
923
		XMoveWindow(wm.dpy, wm.active, new_x, new_y);
1018
		XMoveWindow(wm.dpy, wm.active, new_x, new_y);
  
1019
		send_configure(wm.active);
924
		log_message(stdout, LOG_DEBUG, "Centered window 0x%lx at (%d, %d)", wm.active, new_x, new_y);
1020
		log_message(stdout, LOG_DEBUG, "Centered window 0x%lx at (%d, %d)", wm.active, new_x, new_y);
925
	}
1021
	}
926
}
1022
}
927
  
1023
  
928
// https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html
1024
// https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html
929
void handle_button_press(void) {
1025
void handle_button_press(void) {
930
	Window window;
1026
	Window window = None;
931
	if (wm.ev.xbutton.window == wm.root) {
1027
	Client *c = wintoclient(wm.ev.xbutton.window);
932
		window = wm.ev.xbutton.subwindow;
1028
	if (!c) c = wintoclient(wm.ev.xbutton.subwindow);
933
	} else {
1029
  
  
1030
	if (c) {
  
1031
		window = c->window;
  
1032
	} else if (wm.ev.xbutton.window != wm.root) {
934
		window = wm.ev.xbutton.window;
1033
		window = wm.ev.xbutton.window;
  
1034
	} else {
  
1035
		window = wm.ev.xbutton.subwindow;
935
	}
1036
	}
936
  
1037
  
937
	log_message(stdout, LOG_DEBUG, "ButtonPress: window=0x%lx, subwindow=0x%lx, button=%d, state=0x%x, targeting=0x%lx", 
1038
	log_message(stdout, LOG_DEBUG, "ButtonPress: window=0x%lx, subwindow=0x%lx, button=%d, state=0x%x, targeting=0x%lx", 
...
1161
  
1262
  
1162
void handle_focus_in(void) {
1263
void handle_focus_in(void) {
1163
	Window window = wm.ev.xfocus.window;
1264
	Window window = wm.ev.xfocus.window;
1164
	if (window != wm.root) {
1265
	if (window == wm.root || window == None) return;
1165
		log_message(stdout, LOG_DEBUG, "Window 0x%lx focus in", window);
1266
  
  
1267
	Client *c = wintoclient(window);
  
1268
	if (wm.active != None && (!c || c->window != wm.active)) {
  
1269
		log_message(stdout, LOG_DEBUG, "Window 0x%lx focus in (foreign), forcing back to active 0x%lx", window, wm.active);
  
1270
		set_active_window(wm.active, CurrentTime);
1166
	}
1271
	}
1167
}
1272
}
1168
  
1273
  
...
1220
	check_and_clear_maximized_state(wm.active, 1, 0);
1325
	check_and_clear_maximized_state(wm.active, 1, 0);
1221
	log_message(stdout, LOG_DEBUG, "Move window 0x%lx on X by %d", wm.active, arg->i);
1326
	log_message(stdout, LOG_DEBUG, "Move window 0x%lx on X by %d", wm.active, arg->i);
1222
  
1327
  
1223
	XSync(wm.dpy, True);
1328
	XSync(wm.dpy, False);
1224
	XFlush(wm.dpy);
1329
	XFlush(wm.dpy);
1225
}
1330
}
1226
  
1331
  
...
1233
	check_and_clear_maximized_state(wm.active, 0, 1);
1338
	check_and_clear_maximized_state(wm.active, 0, 1);
1234
	log_message(stdout, LOG_DEBUG, "Move window 0x%lx on Y by %d", wm.active, arg->i);
1339
	log_message(stdout, LOG_DEBUG, "Move window 0x%lx on Y by %d", wm.active, arg->i);
1235
  
1340
  
1236
	XSync(wm.dpy, True);
1341
	XSync(wm.dpy, False);
1237
	XFlush(wm.dpy);
1342
	XFlush(wm.dpy);
1238
}
1343
}
1239
  
1344
  
...
1255
	}
1360
	}
1256
  
1361
  
1257
	if (supported) {
1362
	if (supported) {
1258
		XEvent ev;
1363
		XEvent ev = {0};
1259
		ev.type = ClientMessage;
1364
		ev.type = ClientMessage;
1260
		ev.xclient.window = wm.active;
1365
		ev.xclient.window = wm.active;
1261
		ev.xclient.message_type = WM_PROTOCOLS;
1366
		ev.xclient.message_type = WM_PROTOCOLS;
...
1716
				if (desktop == wm.current_desktop && !is_sticky(c->window) && !is_always_on_top(c->window) && !has_wm_state(c->window, _NET_WM_STATE_FULLSCREEN)) {
1821
				if (desktop == wm.current_desktop && !is_sticky(c->window) && !is_always_on_top(c->window) && !has_wm_state(c->window, _NET_WM_STATE_FULLSCREEN)) {
1717
					if (n == 1) {
1822
					if (n == 1) {
1718
						XMoveResizeWindow(wm.dpy, c->window, 0, 0, screen_width - 2 * border_size, screen_height - 2 * border_size);
1823
						XMoveResizeWindow(wm.dpy, c->window, 0, 0, screen_width - 2 * border_size, screen_height - 2 * border_size);
  
1824
						send_configure(c->window);
1719
					} else if (i == 0) { // Master
1825
					} else if (i == 0) { // Master
1720
						XMoveResizeWindow(wm.dpy, c->window, 0, 0, mw - 2 * border_size, screen_height - 2 * border_size);
1826
						XMoveResizeWindow(wm.dpy, c->window, 0, 0, mw - 2 * border_size, screen_height - 2 * border_size);
  
1827
						send_configure(c->window);
1721
					} else { // Stack
1828
					} else { // Stack
1722
						int h = screen_height / (n - 1);
1829
						int h = screen_height / (n - 1);
1723
						XMoveResizeWindow(wm.dpy, c->window, mw, (i - 1) * h, screen_width - mw - 2 * border_size, h - 2 * border_size);
1830
						XMoveResizeWindow(wm.dpy, c->window, mw, (i - 1) * h, screen_width - mw - 2 * border_size, h - 2 * border_size);
  
1831
						send_configure(c->window);
1724
					}
1832
					}
1725
					i++;
1833
					i++;
1726
				}
1834
				}
...