commit dbaa0c4

uint  ·  2026-08-04 13:32:54 +0000 UTC
parent f6d9d01
X11: XInput2 backend (with fallback)
4 files changed,  +96, -3
+1, -1
1@@ -24,7 +24,7 @@ EOF
2 case "$backend" in
3 	x11)
4 		echo "CONF_CPPFLAGS = -DBACKEND_X11" >> config.mk
5-		echo "CONF_LDFLAGS = -lX11 -lXext" >> config.mk
6+		echo "CONF_LDFLAGS = -lX11 -lXext -lXi" >> config.mk
7 		echo "CONF_SRC = source/maus_x11.c" >> config.mk
8 		echo "CONF_OBJS = build/maus_x11.o" >> config.mk
9 		echo "CONF_LIB_NAME = libmaus_x11.a" >> config.mk
+2, -0
1@@ -20,6 +20,8 @@ typedef struct {
2 
3 	int8_t cur_rel; /* relative cursor */
4 	int8_t ignore_warp;
5+	int8_t xi2;
6+	int    xi2_opcode;
7 	int32_t mouse_x;
8 	int32_t mouse_y;
9 	int8_t  mouse_pos_set;
+1, -0
1@@ -31,6 +31,7 @@ if backend == 'x11'
2 	deps += [
3 		dependency('x11'),
4 		dependency('xext'),
5+		dependency('xi'),
6 	]
7 	backend_sources += ['source/maus_x11.c']
8 
+92, -2
  1@@ -9,6 +9,7 @@
  2 #include <X11/keysym.h>
  3 #include <X11/cursorfont.h>
  4 #include <X11/extensions/XShm.h>
  5+#include <X11/extensions/XInput2.h>
  6 
  7 #include "maus.h"
  8 #include "maus_input.h"
  9@@ -28,6 +29,8 @@ static int8_t handle_event(XEvent* xev, MausEvent* ev, Maus* mw);
 10 static MausKey keysym_to_mauskey(KeySym sym);
 11 static MausMouseButton mouse_button_to_maus(int btn);
 12 static int xerr(Display* dpy, XErrorEvent* ev);
 13+static int8_t xi2_handle_event(Maus* mw, XGenericEventCookie* cookie, MausEvent* ev);
 14+static void xi2_init(Maus* mw);
 15 
 16 static const KeyMapEntry keymap[] = {
 17 	{ XK_BackSpace,    MAUS_KEY_BACKSPACE }, { XK_Tab,              MAUS_KEY_TAB },
 18@@ -210,6 +213,13 @@ static int8_t handle_event(XEvent* xev, MausEvent* ev, Maus* mw)
 19 
 20 	int cx;
 21 	int cy;
 22+	int8_t handled;
 23+
 24+	if (xev->type == GenericEvent && XGetEventData(be->display, &xev->xcookie)) {
 25+		handled = xi2_handle_event(mw, &xev->xcookie, ev);
 26+		XFreeEventData(be->display, &xev->xcookie);
 27+		return handled;
 28+	}
 29 
 30 	switch (xev->type) {
 31 		case Expose:
 32@@ -292,6 +302,9 @@ static int8_t handle_event(XEvent* xev, MausEvent* ev, Maus* mw)
 33 
 34 		case MotionNotify:
 35 			if (be->cur_rel) {
 36+				if (be->xi2)
 37+					return 0;
 38+
 39 				cx = mw->width / 2;
 40 				cy = mw->height / 2;
 41 
 42@@ -412,6 +425,78 @@ static int xerr(Display* dpy, XErrorEvent* ev)
 43 	return 0;
 44 }
 45 
 46+static int8_t xi2_handle_event(Maus* mw, XGenericEventCookie* cookie, MausEvent* ev)
 47+{
 48+	MausBackend* be = &mw->backend;
 49+	XIRawEvent* raw;
 50+	double* values;
 51+	double dx;
 52+	double dy;
 53+	int valuator;
 54+	int value;
 55+
 56+	if (!be->cur_rel || !be->xi2 || cookie->extension != be->xi2_opcode)
 57+		return 0;
 58+	if (cookie->evtype != XI_RawMotion)
 59+		return 0;
 60+
 61+	raw = cookie->data;
 62+	values = raw->raw_values;
 63+	dx = 0.0;
 64+	dy = 0.0;
 65+	value = 0;
 66+
 67+	for (valuator = 0; valuator < raw->valuators.mask_len * 8; valuator++) {
 68+		if (!XIMaskIsSet(raw->valuators.mask, valuator))
 69+			continue;
 70+		if (valuator == 0)
 71+			dx = values[value];
 72+		else if (valuator == 1)
 73+			dy = values[value];
 74+		value++;
 75+	}
 76+
 77+	ev->type = MAUS_EV_MOUSE_MOTION;
 78+	ev->mouse.motion.x = (int32_t)dx;
 79+	ev->mouse.motion.y = (int32_t)dy;
 80+	ev->mouse.motion.dx = ev->mouse.motion.x;
 81+	ev->mouse.motion.dy = ev->mouse.motion.y;
 82+	return ev->mouse.motion.dx != 0 || ev->mouse.motion.dy != 0;
 83+}
 84+
 85+static void xi2_init(Maus* mw)
 86+{
 87+	MausBackend* be = &mw->backend;
 88+	int event;
 89+	int error;
 90+	int major;
 91+	int minor;
 92+	unsigned char mask[(XI_LASTEVENT + 7) / 8];
 93+	XIEventMask evmask;
 94+
 95+	be->xi2 = 0;
 96+	be->xi2_opcode = 0;
 97+
 98+	if (!XQueryExtension(be->display, "XInputExtension", &be->xi2_opcode, &event, &error))
 99+		return;
100+
101+	major = 2;
102+	minor = 0;
103+	if (XIQueryVersion(be->display, &major, &minor) != Success)
104+		return;
105+
106+	memset(mask, 0, sizeof(mask));
107+	XISetMask(mask, XI_RawMotion);
108+
109+	evmask.deviceid = XIAllMasterDevices;
110+	evmask.mask_len = sizeof(mask);
111+	evmask.mask = mask;
112+	XISelectEvents(be->display, be->root, &evmask, 1);
113+	XFlush(be->display);
114+
115+	be->xi2 = 1;
116+}
117+
118 void maus_clipboard_set_text(Maus* mw, const char* text)
119 {
120 	MausBackend* be = &mw->backend;
121@@ -599,6 +684,7 @@ Maus* maus_init(const char* title, int x, int y, int width, int height)
122 	be->mouse_x = 0;
123 	be->mouse_y = 0;
124 	be->mouse_pos_set = 0;
125+	xi2_init(mw);
126 
127 	mw->frame_time_last = maus_get_time_ns();
128 	mw->title = title;
129@@ -788,8 +874,12 @@ void maus_cur_set_mode(Maus* mw, MausCursorState state)
130 		}
131 
132 		be->cur_rel = 1;
133-		be->ignore_warp = 1;
134-		XWarpPointer(dpy, None, win, 0, 0, 0, 0, mw->width/2, mw->height/2);
135+		if (!be->xi2) { /* fallback */
136+			be->ignore_warp = 1;
137+			XWarpPointer(dpy, None, win, 0, 0, 0, 0, mw->width/2, mw->height/2);
138+		}
139+		else
140+			be->ignore_warp = 0;
141 		XFlush(dpy);
142 		return;
143 	}