From 86eb9043ad0120d48ce241ddccdcc87dff13c313 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Thu, 24 Feb 2022 01:11:41 +0800 Subject: [PATCH 01/18] WIP: use XInput extension --- gui-daemon/xside.c | 142 ++++++++++++++++++++++++++++----------------- gui-daemon/xside.h | 1 + 2 files changed, 89 insertions(+), 54 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 7f0b081b..9a8c9a6c 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ #include "trayicon.h" #include "shm-args.h" #include "util.h" +#include "xinput.h" /* Supported protocol version */ @@ -378,6 +380,9 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | StructureNotifyMask | PropertyChangeMask); + + // TODO: select XI events here + XSetWMProtocols(g->display, child_win, &g->wmDeleteMessage, 1); if (g->icon_data) { XChangeProperty(g->display, child_win, g->net_wm_icon, XA_CARDINAL, 32, @@ -638,6 +643,9 @@ static void mkghandles(Ghandles * g) if (!XQueryExtension(g->display, "MIT-SHM", &g->shm_major_opcode, &ev_base, &err_base)) fprintf(stderr, "MIT-SHM X extension missing!\n"); + if (!XQueryExtension(g->display, "XInputExtension", &g->xi_opcode, &ev_base, &err_base)) { + fprintf(stderr, "X Input extension not available.\n"); + } /* get the work area */ XSelectInput(g->display, g->root_win, PropertyChangeMask); update_work_area(g); @@ -2309,66 +2317,92 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev) } +static void process_xievent(Ghandles * g, XIDeviceEvent event) { + switch (event->evtype) + { + case XI_KeyPress: + case XI_KeyRelease: + printf(" flags: %s\n", (event->flags & XIKeyRepeat) ? "repeat" : ""); + // TODO: handle events + break; + case XI_ButtonPress: + case XI_ButtonRelease: + case XI_Motion: + printf(" flags: %s\n", (event->flags & XIPointerEmulated) ? "emulated" : ""); + // TODO: handle events + break; + } +} + /* dispatch local Xserver event */ static void process_xevent(Ghandles * g) { XEvent event_buffer; + XGenericEventCookie *cookie = (XGenericEventCookie*)&event_buffer.xcookie; + // XIDeviceEvent xi_event; XNextEvent(g->display, &event_buffer); - switch (event_buffer.type) { - case KeyPress: - case KeyRelease: - process_xevent_keypress(g, (XKeyEvent *) & event_buffer); - break; - case ReparentNotify: - process_xevent_reparent(g, (XReparentEvent *) &event_buffer); - break; - case ConfigureNotify: - process_xevent_configure(g, (XConfigureEvent *) & - event_buffer); - break; - case ButtonPress: - case ButtonRelease: - process_xevent_button(g, (XButtonEvent *) & event_buffer); - break; - case MotionNotify: - process_xevent_motion(g, (XMotionEvent *) & event_buffer); - break; - case EnterNotify: - case LeaveNotify: - process_xevent_crossing(g, - (XCrossingEvent *) & event_buffer); - break; - case FocusIn: - case FocusOut: - process_xevent_focus(g, - (XFocusChangeEvent *) & event_buffer); - break; - case Expose: - process_xevent_expose(g, (XExposeEvent *) & event_buffer); - break; - case MapNotify: - process_xevent_mapnotify(g, (XMapEvent *) & event_buffer); - break; - case PropertyNotify: - process_xevent_propertynotify(g, (XPropertyEvent *) & event_buffer); - break; - case ClientMessage: -// fprintf(stderr, "xclient, atom=%s\n", -// XGetAtomName(g->display, -// event_buffer.xclient.message_type)); - if (event_buffer.xclient.message_type == g->xembed_message) { - process_xevent_xembed(g, (XClientMessageEvent *) & - event_buffer); - } else if ((Atom)event_buffer.xclient.data.l[0] == - g->wmDeleteMessage) { - if (g->log_level > 0) - fprintf(stderr, "close for 0x%x\n", - (int) event_buffer.xclient.window); - process_xevent_close(g, - event_buffer.xclient.window); + if (XGetEventData(g->display, cookie) && + cookie->type == GenericEvent && + cookie->extension == g->xi_opcode) { + XIDeviceEvent xi_event = *(XIDeviceEvent*)&event_buffer; + process_xievent(g, xi_event); + } else { + switch (event_buffer.type) { + case KeyPress: + case KeyRelease: + process_xevent_keypress(g, (XKeyEvent *) & event_buffer); + break; + case ReparentNotify: + process_xevent_reparent(g, (XReparentEvent *) &event_buffer); + break; + case ConfigureNotify: + process_xevent_configure(g, (XConfigureEvent *) & + event_buffer); + break; + case ButtonPress: + case ButtonRelease: + process_xevent_button(g, (XButtonEvent *) & event_buffer); + break; + case MotionNotify: + process_xevent_motion(g, (XMotionEvent *) & event_buffer); + break; + case EnterNotify: + case LeaveNotify: + process_xevent_crossing(g, + (XCrossingEvent *) & event_buffer); + break; + case FocusIn: + case FocusOut: + process_xevent_focus(g, + (XFocusChangeEvent *) & event_buffer); + break; + case Expose: + process_xevent_expose(g, (XExposeEvent *) & event_buffer); + break; + case MapNotify: + process_xevent_mapnotify(g, (XMapEvent *) & event_buffer); + break; + case PropertyNotify: + process_xevent_propertynotify(g, (XPropertyEvent *) & event_buffer); + break; + case ClientMessage: + // fprintf(stderr, "xclient, atom=%s\n", + // XGetAtomName(g->display, + // event_buffer.xclient.message_type)); + if (event_buffer.xclient.message_type == g->xembed_message) { + process_xevent_xembed(g, (XClientMessageEvent *) & + event_buffer); + } else if ((Atom)event_buffer.xclient.data.l[0] == + g->wmDeleteMessage) { + if (g->log_level > 0) + fprintf(stderr, "close for 0x%x\n", + (int) event_buffer.xclient.window); + process_xevent_close(g, + event_buffer.xclient.window); + } + break; + default:; } - break; - default:; } } diff --git a/gui-daemon/xside.h b/gui-daemon/xside.h index 46f294ce..9073ef56 100644 --- a/gui-daemon/xside.h +++ b/gui-daemon/xside.h @@ -172,6 +172,7 @@ struct _global_handles { Atom wm_user_time_window; /* Atom: _NET_WM_USER_TIME_WINDOW */ Atom wm_user_time; /* Atom: _NET_WM_USER_TIME */ int shm_major_opcode; /* MIT-SHM extension opcode */ + int xi_opcode; /* XInput opcode, for repeat flags and such */ /* shared memory handling */ struct shm_args_hdr *shm_args; /* shared memory with Xorg */ uint32_t cmd_shmid; /* shared memory id - received from shmoverride.so through shm.id.$DISPLAY file */ From 60ee007e74284a422cdbbe7ff71669d1cfd4f173 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Thu, 24 Feb 2022 01:23:44 +0800 Subject: [PATCH 02/18] Process xinput raw events too --- gui-daemon/xside.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 9a8c9a6c..22e57977 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -2318,8 +2318,15 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev) } static void process_xievent(Ghandles * g, XIDeviceEvent event) { - switch (event->evtype) + switch (event.evtype) { + case XI_RawButtonPress: + case XI_RawButtonRelease: + case XI_RawMotion: + case XI_RawKeyPress: + case XI_RawKeyRelease: + // TODO: handle raw events, no flags on raw events + break; case XI_KeyPress: case XI_KeyRelease: printf(" flags: %s\n", (event->flags & XIKeyRepeat) ? "repeat" : ""); From dc60cd439a97bc1d8258eda9fbceffb5cf96db1b Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Thu, 24 Feb 2022 19:34:20 +0800 Subject: [PATCH 03/18] WIP check for XInput events --- gui-daemon/xside.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 7f0b081b..df19f011 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -22,6 +22,7 @@ /* high level documentation is here: https://www.qubes-os.org/doc/gui/ */ +#include #include #include #include @@ -374,7 +375,7 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) (const unsigned char *)&g->time_win, 1); (void) XSelectInput(g->display, child_win, - ExposureMask | KeyPressMask | KeyReleaseMask | + ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | StructureNotifyMask | PropertyChangeMask); @@ -2314,6 +2315,13 @@ static void process_xevent(Ghandles * g) { XEvent event_buffer; XNextEvent(g->display, &event_buffer); + bool is_xinput_event = false; + + if (event_buffer.type == GenericEvent && + event_buffer.xcookie.extension == g->xi_opcode) { + is_xinput_event = (XIDeviceEvent*)&event_buffer; + } + switch (event_buffer.type) { case KeyPress: case KeyRelease: From 0611392d1fb206c65ba51e768fa851b2a6843baf Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Thu, 24 Feb 2022 13:22:42 +0100 Subject: [PATCH 04/18] Use XInput Key events instead of X basic events --- gui-daemon/xside.c | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index f58377f6..d46170e2 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -381,7 +381,16 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | StructureNotifyMask | PropertyChangeMask); - // TODO: select XI events here + // select xinput events + XIEventMask xi_mask; + int use_root = 0; + xi_mask.deviceid = XIAllDevices; + xi_mask.mask_len = XIMaskLen(XI_LASTEVENT); + xi_mask.mask = calloc(xi_mask.mask_len, sizeof(char)); + XISetMask(xi_mask.mask, XI_KeyPress); + XISetMask(xi_mask.mask, XI_KeyRelease); + XISelectEvents(g->display, child_win, &xi_mask, 1); + XSetWMProtocols(g->display, child_win, &g->wmDeleteMessage, 1); if (g->icon_data) { @@ -644,7 +653,7 @@ static void mkghandles(Ghandles * g) &g->shm_major_opcode, &ev_base, &err_base)) fprintf(stderr, "MIT-SHM X extension missing!\n"); if (!XQueryExtension(g->display, "XInputExtension", &g->xi_opcode, &ev_base, &err_base)) { - fprintf(stderr, "X Input extension not available.\n"); + fprintf(stderr, "X Input extension not available. Key press events not available. Upgrade your X11 server now.\n"); } /* get the work area */ XSelectInput(g->display, g->root_win, PropertyChangeMask); @@ -2317,6 +2326,7 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev) } +// reference code for handling xi2 events // static void process_xievent(Ghandles * g, XIDeviceEvent event) { // switch (event.evtype) // { @@ -2325,7 +2335,6 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev) // case XI_RawMotion: // case XI_RawKeyPress: // case XI_RawKeyRelease: -// // TODO: handle raw events, no flags on raw events // break; // case XI_KeyPress: // case XI_KeyRelease: @@ -2341,6 +2350,30 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev) // } // } +static XKeyEvent xkeyevent_from_xinput_event(const XIDeviceEvent* xi_event) { + XKeyEvent fake_event; + switch (xi_event->evtype) { + case XI_KeyPress: fake_event.type = KeyPress; break; + case XI_KeyRelease: fake_event.type = KeyRelease; break; + default: assert(false); // stop immediately + } + fake_event.serial = xi_event->serial; + fake_event.send_event = false; + fake_event.display = xi_event->display; + fake_event.window = xi_event->event; + fake_event.root = xi_event->root; + fake_event.subwindow = xi_event->child; // from Manual page XKeyEvent(3) + fake_event.time = xi_event->time; + fake_event.x = xi_event->event_x; + fake_event.y = xi_event->event_y; + fake_event.x_root = xi_event->root_x; + fake_event.y_root = xi_event->root_y; + fake_event.state = xi_event->mods.effective; + fake_event.keycode = xi_event->detail; + fake_event.same_screen = true; // don't know how to fill this + return fake_event; +} + /* dispatch local Xserver event */ static void process_xevent(Ghandles * g) { @@ -2354,7 +2387,8 @@ static void process_xevent(Ghandles * g) case XI_KeyPress: case XI_KeyRelease: if (xi_event && xi_event->flags & XIKeyRepeat) break; // don't send key repeat events - process_xevent_keypress(g, xi_event); + XKeyEvent fake_event = xkeyevent_from_xinput_event(xi_event); + process_xevent_keypress(g, &fake_event); break; } } else { From 48323cdf84ab574ca6269c49d4fea48a65ac28d1 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Thu, 24 Feb 2022 21:22:56 +0800 Subject: [PATCH 05/18] Add libxi in Makefile --- gui-daemon/Makefile | 2 +- gui-daemon/xside.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/gui-daemon/Makefile b/gui-daemon/Makefile index 2c5db666..4d26af19 100644 --- a/gui-daemon/Makefile +++ b/gui-daemon/Makefile @@ -22,7 +22,7 @@ MAKEFLAGS := -rR VCHAN_PKG = $(if $(BACKEND_VMM),vchan-$(BACKEND_VMM),vchan) CC=gcc -pkgs := x11 xext x11-xcb xcb glib-2.0 $(VCHAN_PKG) libpng libnotify libconfig +pkgs := x11 xext x11-xcb xcb glib-2.0 xi $(VCHAN_PKG) libpng libnotify libconfig objs := xside.o png.o trayicon.o ../gui-common/double-buffer.o ../gui-common/txrx-vchan.o \ ../gui-common/error.o list.o extra_cflags := -I../include/ -g -O2 -Wall -Wextra -Werror -pie -fPIC \ diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index d46170e2..7787657a 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -383,7 +383,6 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) // select xinput events XIEventMask xi_mask; - int use_root = 0; xi_mask.deviceid = XIAllDevices; xi_mask.mask_len = XIMaskLen(XI_LASTEVENT); xi_mask.mask = calloc(xi_mask.mask_len, sizeof(char)); @@ -2378,11 +2377,13 @@ static XKeyEvent xkeyevent_from_xinput_event(const XIDeviceEvent* xi_event) { static void process_xevent(Ghandles * g) { XEvent event_buffer; + XGenericEventCookie *cookie = (XGenericEventCookie*)&event_buffer.xcookie; XNextEvent(g->display, &event_buffer); - if (event_buffer.type == GenericEvent && - event_buffer.xcookie.extension == g->xi_opcode) { - XIDeviceEvent* xi_event = (XIDeviceEvent*)&event_buffer; + if (XGetEventData(g->display, cookie) && + cookie->type == GenericEvent && + cookie->extension == g->xi_opcode) { + XIDeviceEvent* xi_event = cookie->data; // from test_xi2.c in xinput cli utility switch (xi_event->evtype) { case XI_KeyPress: case XI_KeyRelease: From cb824aa2616ba50e0da4857c5167d725ae14ca00 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Thu, 24 Feb 2022 21:37:34 +0800 Subject: [PATCH 06/18] Free X resource as well --- gui-daemon/xside.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 7787657a..48f8ee5c 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -2377,7 +2377,7 @@ static XKeyEvent xkeyevent_from_xinput_event(const XIDeviceEvent* xi_event) { static void process_xevent(Ghandles * g) { XEvent event_buffer; - XGenericEventCookie *cookie = (XGenericEventCookie*)&event_buffer.xcookie; + XGenericEventCookie *cookie = &event_buffer.xcookie; XNextEvent(g->display, &event_buffer); if (XGetEventData(g->display, cookie) && @@ -2392,6 +2392,7 @@ static void process_xevent(Ghandles * g) process_xevent_keypress(g, &fake_event); break; } + XFreeEventData(g->display, cookie); } else { switch (event_buffer.type) { case ReparentNotify: From 1bf52ea02c855233f1bf060308c4f4d37965c722 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Fri, 25 Feb 2022 23:05:02 +0800 Subject: [PATCH 07/18] Remove duplicate #include --- gui-daemon/xside.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 48f8ee5c..5da8f7f4 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -22,7 +22,6 @@ /* high level documentation is here: https://www.qubes-os.org/doc/gui/ */ -#include #include #include #include From 70da7d15704272cec8f2fee8e2d4069bb9019d90 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sat, 26 Feb 2022 01:24:28 +0800 Subject: [PATCH 08/18] Update .gitignore --- gui-daemon/.gitignore | 2 ++ shmoverride/.gitignore | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/gui-daemon/.gitignore b/gui-daemon/.gitignore index e84d2134..6e46ce29 100644 --- a/gui-daemon/.gitignore +++ b/gui-daemon/.gitignore @@ -1 +1,3 @@ qubes_guid +qubes-guid +qubes-guid.1 diff --git a/shmoverride/.gitignore b/shmoverride/.gitignore index 979bacea..65c0988c 100644 --- a/shmoverride/.gitignore +++ b/shmoverride/.gitignore @@ -1,3 +1,3 @@ -X_wrapper_qubes +X-wrapper-qubes shmoverride.so From 5219d2ad9b504a2d9230f81f0dff116f598170fe Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sat, 26 Feb 2022 01:27:14 +0800 Subject: [PATCH 09/18] Add XInput/libXi as build dependencies for debian & rpm --- debian/control | 1 + rpm_spec/gui-daemon.spec.in | 1 + 2 files changed, 2 insertions(+) diff --git a/debian/control b/debian/control index 674cc154..385d9dbb 100644 --- a/debian/control +++ b/debian/control @@ -14,6 +14,7 @@ Build-Depends: libxrandr-dev, libxcb1-dev, libx11-xcb-dev, + libxi-dev, libconfig-dev, libpng-dev, libnotify-dev, diff --git a/rpm_spec/gui-daemon.spec.in b/rpm_spec/gui-daemon.spec.in index b644364f..627b79bc 100644 --- a/rpm_spec/gui-daemon.spec.in +++ b/rpm_spec/gui-daemon.spec.in @@ -49,6 +49,7 @@ BuildRequires: pkgconfig(x11-xcb) BuildRequires: pkgconfig(xcb) BuildRequires: libXext-devel BuildRequires: libXrandr-devel +BuildRequires: libXi-devel BuildRequires: libconfig-devel BuildRequires: libpng-devel BuildRequires: libnotify-devel From 576fa94c862d3e537366635fc1306b17950b2b13 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sat, 5 Mar 2022 12:38:13 +0800 Subject: [PATCH 10/18] Don't send evnts to unfocused window --- gui-daemon/xside.c | 60 ++++++++++++++++++++++++++++++---------------- gui-daemon/xside.h | 2 ++ 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 5da8f7f4..5d02e532 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -378,7 +378,7 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | - FocusChangeMask | StructureNotifyMask | PropertyChangeMask); + StructureNotifyMask | PropertyChangeMask); // select xinput events XIEventMask xi_mask; @@ -387,6 +387,8 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) xi_mask.mask = calloc(xi_mask.mask_len, sizeof(char)); XISetMask(xi_mask.mask, XI_KeyPress); XISetMask(xi_mask.mask, XI_KeyRelease); + XISetMask(xi_mask.mask, XI_FocusIn); + XISetMask(xi_mask.mask, XI_FocusOut); XISelectEvents(g->display, child_win, &xi_mask, 1); @@ -448,7 +450,7 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) } static const unsigned long desktop_coordinates_size = 4; -static const long max_display_width = 1UL << 20; +static const unsigned long max_display_width = 1UL << 20; /* * Padding enforced between override-redirect windows and the edge of @@ -484,8 +486,9 @@ static void update_work_area(Ghandles *g) { exit(1); } if (*scratch > max_display_width) { - fputs("Absurd current desktop, crashing\n", stderr); - abort(); + fprintf(stderr, "Absurd current desktop (display width %lu exceeds " + "limit %lu), exiting\n", *scratch, max_display_width); + exit(1); } uint32_t current_desktop = (uint32_t)*scratch; XFree(scratch); @@ -519,7 +522,15 @@ static void update_work_area(Ghandles *g) { } for (unsigned long s = 0; s < desktop_coordinates_size; ++s) { if (scratch[s] > max_display_width) { - fputs("Absurd work area, crashing\n", stderr); + fprintf(stderr, + "PANIC: invalid work area:\n" + " x: %1$lu (limit %5$lu)\n" + " y: %2$lu (limit %5$lu)\n" + " width: %3$lu (limit %5$lu)\n" + "height: %4$lu (limit %5$lu)\n" + "Exiting!\n", + scratch[0], scratch[1], scratch[2], scratch[3], + max_display_width); exit(1); } } @@ -1929,13 +1940,13 @@ static void process_xevent_motion(Ghandles * g, const XMotionEvent * ev) // fprintf(stderr, "motion in 0x%x", ev->window); } -/* handle local Xserver event: FocusIn, FocusOut +/* handle local XInput event: FocusIn, FocusOut * send to relevant window in VM */ -static void process_xevent_focus(Ghandles * g, const XFocusChangeEvent * ev) +static void process_xievent_focus(Ghandles * g, const XILeaveEvent * ev) { struct msg_hdr hdr; struct msg_focus k; - CHECK_NONMANAGED_WINDOW(g, ev->window); + CHECK_NONMANAGED_WINDOW(g, ev->event); /* Ignore everything other than normal, non-temporary focus change. In * practice it ignores NotifyGrab and NotifyUngrab. VM does not have any @@ -1945,8 +1956,8 @@ static void process_xevent_focus(Ghandles * g, const XFocusChangeEvent * ev) */ if (ev->mode != NotifyNormal && ev->mode != NotifyWhileGrabbed) return; - - if (ev->type == FocusIn) { + // it's unclear why XI has it's own set of constant names, despite having the same value + if (ev->type == XI_FocusIn) { char keys[32]; XQueryKeymap(g->display, keys); hdr.type = MSG_KEYMAP_NOTIFY; @@ -2378,18 +2389,32 @@ static void process_xevent(Ghandles * g) XEvent event_buffer; XGenericEventCookie *cookie = &event_buffer.xcookie; XNextEvent(g->display, &event_buffer); - if (XGetEventData(g->display, cookie) && cookie->type == GenericEvent && cookie->extension == g->xi_opcode) { - XIDeviceEvent* xi_event = cookie->data; // from test_xi2.c in xinput cli utility + XIEvent* xi_event = cookie->data; // from test_xi2.c in xinput cli utility + + XIDeviceEvent * xi_device = (XIDeviceEvent *)xi_event; + XILeaveEvent * xi_leave = (XILeaveEvent *)xi_event; switch (xi_event->evtype) { + // ideally raw input events are better, but I'm relying on X server's built-in event filtering and routing feature here case XI_KeyPress: case XI_KeyRelease: - if (xi_event && xi_event->flags & XIKeyRepeat) break; // don't send key repeat events - XKeyEvent fake_event = xkeyevent_from_xinput_event(xi_event); + if (g->focused_remote_winid != xi_device->event) break; // don't send to unfocused window + if (xi_device && xi_device->flags & XIKeyRepeat) break; // don't send key repeat events + XKeyEvent fake_event = xkeyevent_from_xinput_event(xi_device); process_xevent_keypress(g, &fake_event); break; + case XI_FocusIn: + case XI_FocusOut: + if (xi_leave->evtype == XI_FocusIn) { + g->focused_remote_winid = xi_leave->event; + } else { + if (g->focused_remote_winid == xi_leave->event) + g->focused_remote_winid = 0; // load-compare-store in single thread is fine? + } + process_xievent_focus(g, xi_leave); + break; } XFreeEventData(g->display, cookie); } else { @@ -2413,11 +2438,6 @@ static void process_xevent(Ghandles * g) process_xevent_crossing(g, (XCrossingEvent *) & event_buffer); break; - case FocusIn: - case FocusOut: - process_xevent_focus(g, - (XFocusChangeEvent *) & event_buffer); - break; case Expose: process_xevent_expose(g, (XExposeEvent *) & event_buffer); break; @@ -2443,7 +2463,6 @@ static void process_xevent(Ghandles * g) event_buffer.xclient.window); } break; - default:; } } } @@ -2590,7 +2609,6 @@ static void handle_destroy(Ghandles * g, struct genlist *l) { struct genlist *l2; struct windowdata *vm_window = l->data; - g->windows_count--; /* check if this window is referenced anywhere */ check_window_references(g, vm_window); /* then destroy */ diff --git a/gui-daemon/xside.h b/gui-daemon/xside.h index 9073ef56..5548aa3f 100644 --- a/gui-daemon/xside.h +++ b/gui-daemon/xside.h @@ -140,6 +140,7 @@ struct extra_prop { }; /* global variables + * One for one VM * keep them in this struct for readability */ struct _global_handles { @@ -201,6 +202,7 @@ struct _global_handles { Time clipboard_xevent_time; /* timestamp of keypress which triggered last copy/paste */ Window time_win; /* Window to set _NET_WM_USER_TIME on */ int windows_count; /* created window count */ + XID focused_remote_winid; /* focused window; assumed that only one window can be focused, thus receive raw input*/ /* signal was caught */ int volatile reload_requested; pid_t pulseaudio_pid; From a4a1488dd1a8bd1665b50b69f0d547fba192d157 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sat, 5 Mar 2022 13:19:36 +0800 Subject: [PATCH 11/18] Don't ignore grab events --- gui-daemon/xside.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 5d02e532..f4bfee76 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -1948,14 +1948,6 @@ static void process_xievent_focus(Ghandles * g, const XILeaveEvent * ev) struct msg_focus k; CHECK_NONMANAGED_WINDOW(g, ev->event); - /* Ignore everything other than normal, non-temporary focus change. In - * practice it ignores NotifyGrab and NotifyUngrab. VM does not have any - * way to grab focus in dom0, so it shouldn't care about those events. Grab - * is used by window managers during task switching (either classic task - * switcher, or KDE "present windows" feature). - */ - if (ev->mode != NotifyNormal && ev->mode != NotifyWhileGrabbed) - return; // it's unclear why XI has it's own set of constant names, despite having the same value if (ev->type == XI_FocusIn) { char keys[32]; From 035be7f7cc281292aa480ab836d517feb7057381 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sat, 5 Mar 2022 13:34:13 +0800 Subject: [PATCH 12/18] Use base Focus events --- gui-daemon/xside.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 65ec67e6..c627c323 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -378,7 +378,7 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | - StructureNotifyMask | PropertyChangeMask); + FocusChangeMask | StructureNotifyMask | PropertyChangeMask); // select xinput events XIEventMask xi_mask; @@ -387,8 +387,6 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) xi_mask.mask = calloc(xi_mask.mask_len, sizeof(char)); XISetMask(xi_mask.mask, XI_KeyPress); XISetMask(xi_mask.mask, XI_KeyRelease); - XISetMask(xi_mask.mask, XI_FocusIn); - XISetMask(xi_mask.mask, XI_FocusOut); XISelectEvents(g->display, child_win, &xi_mask, 1); @@ -1940,16 +1938,15 @@ static void process_xevent_motion(Ghandles * g, const XMotionEvent * ev) // fprintf(stderr, "motion in 0x%x", ev->window); } -/* handle local XInput event: FocusIn, FocusOut +/* handle local Xserver event: FocusIn, FocusOut * send to relevant window in VM */ -static void process_xievent_focus(Ghandles * g, const XILeaveEvent * ev) +static void process_xevent_focus(Ghandles * g, const XFocusChangeEvent * ev) { struct msg_hdr hdr; struct msg_focus k; - CHECK_NONMANAGED_WINDOW(g, ev->event); + CHECK_NONMANAGED_WINDOW(g, ev->window); - // it's unclear why XI has it's own set of constant names, despite having the same value - if (ev->type == XI_FocusIn) { + if (ev->type == FocusIn) { char keys[32]; XQueryKeymap(g->display, keys); hdr.type = MSG_KEYMAP_NOTIFY; @@ -2397,19 +2394,10 @@ static void process_xevent(Ghandles * g) XKeyEvent fake_event = xkeyevent_from_xinput_event(xi_device); process_xevent_keypress(g, &fake_event); break; - case XI_FocusIn: - case XI_FocusOut: - if (xi_leave->evtype == XI_FocusIn) { - g->focused_remote_winid = xi_leave->event; - } else { - if (g->focused_remote_winid == xi_leave->event) - g->focused_remote_winid = 0; // load-compare-store in single thread is fine? - } - process_xievent_focus(g, xi_leave); - break; } XFreeEventData(g->display, cookie); } else { + XID wid; switch (event_buffer.type) { case ReparentNotify: process_xevent_reparent(g, (XReparentEvent *) &event_buffer); @@ -2430,6 +2418,18 @@ static void process_xevent(Ghandles * g) process_xevent_crossing(g, (XCrossingEvent *) & event_buffer); break; + case FocusIn: + case FocusOut: + wid = ((XFocusChangeEvent *) & event_buffer)->window; + if (event_buffer.type == FocusIn) { + g->focused_remote_winid = wid; + } else { + if (g->focused_remote_winid == wid) + g->focused_remote_winid = 0; // load-compare-store in single thread is fine? + } + process_xevent_focus(g, + (XFocusChangeEvent *) & event_buffer); + break; case Expose: process_xevent_expose(g, (XExposeEvent *) & event_buffer); break; From ee70785aaf5c4d6c0ed5d5dbf639b468955eff8a Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sat, 5 Mar 2022 13:23:32 +0800 Subject: [PATCH 13/18] Pass Focus type --- gui-daemon/xside.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index f4bfee76..65ec67e6 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -1962,7 +1962,7 @@ static void process_xievent_focus(Ghandles * g, const XILeaveEvent * ev) /* override NotifyWhileGrabbed with NotifyNormal b/c VM shouldn't care * about window manager details during focus switching */ - k.mode = NotifyNormal; + k.mode = ev->mode; k.detail = ev->detail; write_message(g->vchan, hdr, k); } From 8ec6f0195e5b34583339887c9dd0820b89ef3e0f Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sat, 5 Mar 2022 13:34:13 +0800 Subject: [PATCH 14/18] Use base Focus events --- gui-daemon/xside.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 65ec67e6..c627c323 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -378,7 +378,7 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | - StructureNotifyMask | PropertyChangeMask); + FocusChangeMask | StructureNotifyMask | PropertyChangeMask); // select xinput events XIEventMask xi_mask; @@ -387,8 +387,6 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) xi_mask.mask = calloc(xi_mask.mask_len, sizeof(char)); XISetMask(xi_mask.mask, XI_KeyPress); XISetMask(xi_mask.mask, XI_KeyRelease); - XISetMask(xi_mask.mask, XI_FocusIn); - XISetMask(xi_mask.mask, XI_FocusOut); XISelectEvents(g->display, child_win, &xi_mask, 1); @@ -1940,16 +1938,15 @@ static void process_xevent_motion(Ghandles * g, const XMotionEvent * ev) // fprintf(stderr, "motion in 0x%x", ev->window); } -/* handle local XInput event: FocusIn, FocusOut +/* handle local Xserver event: FocusIn, FocusOut * send to relevant window in VM */ -static void process_xievent_focus(Ghandles * g, const XILeaveEvent * ev) +static void process_xevent_focus(Ghandles * g, const XFocusChangeEvent * ev) { struct msg_hdr hdr; struct msg_focus k; - CHECK_NONMANAGED_WINDOW(g, ev->event); + CHECK_NONMANAGED_WINDOW(g, ev->window); - // it's unclear why XI has it's own set of constant names, despite having the same value - if (ev->type == XI_FocusIn) { + if (ev->type == FocusIn) { char keys[32]; XQueryKeymap(g->display, keys); hdr.type = MSG_KEYMAP_NOTIFY; @@ -2397,19 +2394,10 @@ static void process_xevent(Ghandles * g) XKeyEvent fake_event = xkeyevent_from_xinput_event(xi_device); process_xevent_keypress(g, &fake_event); break; - case XI_FocusIn: - case XI_FocusOut: - if (xi_leave->evtype == XI_FocusIn) { - g->focused_remote_winid = xi_leave->event; - } else { - if (g->focused_remote_winid == xi_leave->event) - g->focused_remote_winid = 0; // load-compare-store in single thread is fine? - } - process_xievent_focus(g, xi_leave); - break; } XFreeEventData(g->display, cookie); } else { + XID wid; switch (event_buffer.type) { case ReparentNotify: process_xevent_reparent(g, (XReparentEvent *) &event_buffer); @@ -2430,6 +2418,18 @@ static void process_xevent(Ghandles * g) process_xevent_crossing(g, (XCrossingEvent *) & event_buffer); break; + case FocusIn: + case FocusOut: + wid = ((XFocusChangeEvent *) & event_buffer)->window; + if (event_buffer.type == FocusIn) { + g->focused_remote_winid = wid; + } else { + if (g->focused_remote_winid == wid) + g->focused_remote_winid = 0; // load-compare-store in single thread is fine? + } + process_xevent_focus(g, + (XFocusChangeEvent *) & event_buffer); + break; case Expose: process_xevent_expose(g, (XExposeEvent *) & event_buffer); break; From cabf231d87108e5dab9d0921b40d46b240b1bf79 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sun, 6 Mar 2022 03:31:58 +0800 Subject: [PATCH 15/18] Use Focus events from XInput, dropping less keytrokes --- gui-daemon/xside.c | 111 ++++++++++++++++++++------------------------- gui-daemon/xside.h | 1 - 2 files changed, 50 insertions(+), 62 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index c627c323..3a943748 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -378,7 +379,7 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | - FocusChangeMask | StructureNotifyMask | PropertyChangeMask); + StructureNotifyMask | PropertyChangeMask); // select xinput events XIEventMask xi_mask; @@ -387,6 +388,8 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) xi_mask.mask = calloc(xi_mask.mask_len, sizeof(char)); XISetMask(xi_mask.mask, XI_KeyPress); XISetMask(xi_mask.mask, XI_KeyRelease); + XISetMask(xi_mask.mask, XI_FocusIn); + XISetMask(xi_mask.mask, XI_FocusOut); XISelectEvents(g->display, child_win, &xi_mask, 1); @@ -1255,7 +1258,7 @@ static void handle_cursor(Ghandles *g, struct windowdata *vm_window) /* check and handle guid-special keys * currently only for inter-vm clipboard copy */ -static int is_special_keypress(Ghandles * g, const XKeyEvent * ev, XID remote_winid) +static int is_special_keypress(Ghandles * g, const XIDeviceEvent * ev, XID remote_winid) { struct msg_hdr hdr; char *data; @@ -1263,8 +1266,8 @@ static int is_special_keypress(Ghandles * g, const XKeyEvent * ev, XID remote_wi Time clipboard_file_xevent_time; /* copy */ - if (((int)ev->state & SPECIAL_KEYS_MASK) == g->copy_seq_mask - && ev->keycode == XKeysymToKeycode(g->display, g->copy_seq_key)) { + if (((int)ev->mods.effective & SPECIAL_KEYS_MASK) == g->copy_seq_mask + && ev->detail == XKeysymToKeycode(g->display, g->copy_seq_key)) { if (ev->type != KeyPress) return 1; g->clipboard_xevent_time = ev->time; @@ -1285,8 +1288,8 @@ static int is_special_keypress(Ghandles * g, const XKeyEvent * ev, XID remote_wi } /* paste */ - if (((int)ev->state & SPECIAL_KEYS_MASK) == g->paste_seq_mask - && ev->keycode == XKeysymToKeycode(g->display, g->paste_seq_key)) { + if (((int)ev->mods.effective & SPECIAL_KEYS_MASK) == g->paste_seq_mask + && ev->detail == XKeysymToKeycode(g->display, g->paste_seq_key)) { if (ev->type != KeyPress) return 1; inter_appviewer_lock(g, 1); @@ -1344,22 +1347,22 @@ static void update_wm_user_time(Ghandles *const g, const Window window, 1); } -/* handle local Xserver event: XKeyEvent +/* handle local XInput event * send it to relevant window in VM */ -static void process_xevent_keypress(Ghandles * g, const XKeyEvent * ev) +static void process_xievent_keypress(Ghandles * g, const XIDeviceEvent * ev) { struct msg_hdr hdr; struct msg_keypress k; - CHECK_NONMANAGED_WINDOW(g, ev->window); - update_wm_user_time(g, ev->window, ev->time); + CHECK_NONMANAGED_WINDOW(g, ev->event); + update_wm_user_time(g, ev->event, ev->time); if (is_special_keypress(g, ev, vm_window->remote_winid)) return; - k.type = ev->type; - k.x = ev->x; - k.y = ev->y; - k.state = ev->state; - k.keycode = ev->keycode; + k.type = ev->evtype; // ev->type is always Generic Event + k.x = ev->event_x; + k.y = ev->event_y; + k.state = ev->mods.effective; + k.keycode = ev->detail; hdr.type = MSG_KEYPRESS; hdr.window = vm_window->remote_winid; write_message(g->vchan, hdr, k); @@ -1940,13 +1943,13 @@ static void process_xevent_motion(Ghandles * g, const XMotionEvent * ev) /* handle local Xserver event: FocusIn, FocusOut * send to relevant window in VM */ -static void process_xevent_focus(Ghandles * g, const XFocusChangeEvent * ev) +static void process_xievent_focus(Ghandles * g, const XILeaveEvent * ev) { struct msg_hdr hdr; struct msg_focus k; - CHECK_NONMANAGED_WINDOW(g, ev->window); + CHECK_NONMANAGED_WINDOW(g, ev->event); - if (ev->type == FocusIn) { + if (ev->type == XI_FocusIn) { char keys[32]; XQueryKeymap(g->display, keys); hdr.type = MSG_KEYMAP_NOTIFY; @@ -1955,10 +1958,7 @@ static void process_xevent_focus(Ghandles * g, const XFocusChangeEvent * ev) } hdr.type = MSG_FOCUS; hdr.window = vm_window->remote_winid; - k.type = ev->type; - /* override NotifyWhileGrabbed with NotifyNormal b/c VM shouldn't care - * about window manager details during focus switching - */ + k.type = ev->evtype; k.mode = ev->mode; k.detail = ev->detail; write_message(g->vchan, hdr, k); @@ -2348,29 +2348,29 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev) // } // } -static XKeyEvent xkeyevent_from_xinput_event(const XIDeviceEvent* xi_event) { - XKeyEvent fake_event; - switch (xi_event->evtype) { - case XI_KeyPress: fake_event.type = KeyPress; break; - case XI_KeyRelease: fake_event.type = KeyRelease; break; - default: assert(false); // stop immediately - } - fake_event.serial = xi_event->serial; - fake_event.send_event = false; - fake_event.display = xi_event->display; - fake_event.window = xi_event->event; - fake_event.root = xi_event->root; - fake_event.subwindow = xi_event->child; // from Manual page XKeyEvent(3) - fake_event.time = xi_event->time; - fake_event.x = xi_event->event_x; - fake_event.y = xi_event->event_y; - fake_event.x_root = xi_event->root_x; - fake_event.y_root = xi_event->root_y; - fake_event.state = xi_event->mods.effective; - fake_event.keycode = xi_event->detail; - fake_event.same_screen = true; // don't know how to fill this - return fake_event; -} +// static XKeyEvent xkeyevent_from_xinput_event(const XIDeviceEvent* xi_event) { +// XKeyEvent fake_event; +// switch (xi_event->evtype) { +// case XI_KeyPress: fake_event.type = KeyPress; break; +// case XI_KeyRelease: fake_event.type = KeyRelease; break; +// default: assert(false); // stop immediately +// } +// fake_event.serial = xi_event->serial; +// fake_event.send_event = false; +// fake_event.display = xi_event->display; +// fake_event.window = xi_event->event; +// fake_event.root = xi_event->root; +// fake_event.subwindow = xi_event->child; // from Manual page XKeyEvent(3) +// fake_event.time = xi_event->time; +// fake_event.x = xi_event->event_x; +// fake_event.y = xi_event->event_y; +// fake_event.x_root = xi_event->root_x; +// fake_event.y_root = xi_event->root_y; +// fake_event.state = xi_event->mods.effective; +// fake_event.keycode = xi_event->detail; +// fake_event.same_screen = true; // don't know how to fill this +// return fake_event; +// } /* dispatch local Xserver event */ static void process_xevent(Ghandles * g) @@ -2389,15 +2389,16 @@ static void process_xevent(Ghandles * g) // ideally raw input events are better, but I'm relying on X server's built-in event filtering and routing feature here case XI_KeyPress: case XI_KeyRelease: - if (g->focused_remote_winid != xi_device->event) break; // don't send to unfocused window if (xi_device && xi_device->flags & XIKeyRepeat) break; // don't send key repeat events - XKeyEvent fake_event = xkeyevent_from_xinput_event(xi_device); - process_xevent_keypress(g, &fake_event); + process_xievent_keypress(g, xi_device); + break; + case XI_FocusIn: + case XI_FocusOut: + process_xievent_focus(g, xi_leave); break; } XFreeEventData(g->display, cookie); } else { - XID wid; switch (event_buffer.type) { case ReparentNotify: process_xevent_reparent(g, (XReparentEvent *) &event_buffer); @@ -2418,18 +2419,6 @@ static void process_xevent(Ghandles * g) process_xevent_crossing(g, (XCrossingEvent *) & event_buffer); break; - case FocusIn: - case FocusOut: - wid = ((XFocusChangeEvent *) & event_buffer)->window; - if (event_buffer.type == FocusIn) { - g->focused_remote_winid = wid; - } else { - if (g->focused_remote_winid == wid) - g->focused_remote_winid = 0; // load-compare-store in single thread is fine? - } - process_xevent_focus(g, - (XFocusChangeEvent *) & event_buffer); - break; case Expose: process_xevent_expose(g, (XExposeEvent *) & event_buffer); break; diff --git a/gui-daemon/xside.h b/gui-daemon/xside.h index 80fbcc13..007e1f24 100644 --- a/gui-daemon/xside.h +++ b/gui-daemon/xside.h @@ -201,7 +201,6 @@ struct _global_handles { int clipboard_requested; /* if clippoard content was requested by dom0 */ Time clipboard_xevent_time; /* timestamp of keypress which triggered last copy/paste */ Window time_win; /* Window to set _NET_WM_USER_TIME on */ - XID focused_remote_winid; /* focused window; assumed that only one window can be focused, thus receive raw input*/ /* signal was caught */ int volatile reload_requested; From 110a6a9d3ee429d17b30b53bb2bc18060a704791 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Tue, 8 Mar 2022 08:31:49 +0800 Subject: [PATCH 16/18] Handle clipboard hotkeys properly --- gui-daemon/xside.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 3a943748..5d35039c 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -1268,7 +1268,7 @@ static int is_special_keypress(Ghandles * g, const XIDeviceEvent * ev, XID remot /* copy */ if (((int)ev->mods.effective & SPECIAL_KEYS_MASK) == g->copy_seq_mask && ev->detail == XKeysymToKeycode(g->display, g->copy_seq_key)) { - if (ev->type != KeyPress) + if (ev->evtype != KeyPress) return 1; g->clipboard_xevent_time = ev->time; if (g->qrexec_clipboard) { @@ -1290,7 +1290,7 @@ static int is_special_keypress(Ghandles * g, const XIDeviceEvent * ev, XID remot /* paste */ if (((int)ev->mods.effective & SPECIAL_KEYS_MASK) == g->paste_seq_mask && ev->detail == XKeysymToKeycode(g->display, g->paste_seq_key)) { - if (ev->type != KeyPress) + if (ev->evtype != KeyPress) return 1; inter_appviewer_lock(g, 1); clipboard_file_xevent_time = get_clipboard_file_xevent_timestamp(); @@ -2384,17 +2384,16 @@ static void process_xevent(Ghandles * g) XIEvent* xi_event = cookie->data; // from test_xi2.c in xinput cli utility XIDeviceEvent * xi_device = (XIDeviceEvent *)xi_event; - XILeaveEvent * xi_leave = (XILeaveEvent *)xi_event; switch (xi_event->evtype) { // ideally raw input events are better, but I'm relying on X server's built-in event filtering and routing feature here case XI_KeyPress: case XI_KeyRelease: - if (xi_device && xi_device->flags & XIKeyRepeat) break; // don't send key repeat events + if (xi_device && (xi_device->flags & XIKeyRepeat)) break; // don't send key repeat events process_xievent_keypress(g, xi_device); break; case XI_FocusIn: case XI_FocusOut: - process_xievent_focus(g, xi_leave); + process_xievent_focus(g, (XILeaveEvent *)xi_event); break; } XFreeEventData(g->display, cookie); From 403a952545ed20d1ad4879a318278adfef5f76eb Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Sun, 13 Mar 2022 13:34:04 +0100 Subject: [PATCH 17/18] Fix double keypresses --- gui-daemon/xside.c | 94 +++++++++++++--------------------------------- 1 file changed, 26 insertions(+), 68 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 5d35039c..18eca19f 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -383,14 +383,19 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) // select xinput events XIEventMask xi_mask; - xi_mask.deviceid = XIAllDevices; + xi_mask.deviceid = XIAllMasterDevices; // https://stackoverflow.com/questions/44095001/getting-double-rawkeypress-events-using-xinput2 xi_mask.mask_len = XIMaskLen(XI_LASTEVENT); - xi_mask.mask = calloc(xi_mask.mask_len, sizeof(char)); + if (!(xi_mask.mask = calloc(xi_mask.mask_len, sizeof(char)))) { + fputs("Out of memory!\n", stderr); + exit(1); + } XISetMask(xi_mask.mask, XI_KeyPress); XISetMask(xi_mask.mask, XI_KeyRelease); XISetMask(xi_mask.mask, XI_FocusIn); XISetMask(xi_mask.mask, XI_FocusOut); XISelectEvents(g->display, child_win, &xi_mask, 1); + free(xi_mask.mask); + XSync(g->display, False); XSetWMProtocols(g->display, child_win, &g->wmDeleteMessage, 1); @@ -664,6 +669,7 @@ static void mkghandles(Ghandles * g) fprintf(stderr, "MIT-SHM X extension missing!\n"); if (!XQueryExtension(g->display, "XInputExtension", &g->xi_opcode, &ev_base, &err_base)) { fprintf(stderr, "X Input extension not available. Key press events not available. Upgrade your X11 server now.\n"); + exit(1); } /* get the work area */ XSelectInput(g->display, g->root_win, PropertyChangeMask); @@ -1355,7 +1361,10 @@ static void process_xievent_keypress(Ghandles * g, const XIDeviceEvent * ev) struct msg_hdr hdr; struct msg_keypress k; CHECK_NONMANAGED_WINDOW(g, ev->event); + // yes, ev->event is the window number update_wm_user_time(g, ev->event, ev->time); + if (ev->flags & XIKeyRepeat) + return; // don't send key repeat events if (is_special_keypress(g, ev, vm_window->remote_winid)) return; k.type = ev->evtype; // ev->type is always Generic Event @@ -1888,6 +1897,16 @@ static void handle_configure_from_vm(Ghandles * g, struct windowdata *vm_window) } } +static void send_keymap_notify(Ghandles * g) +{ + struct msg_hdr hdr; + char keys[32]; + XQueryKeymap(g->display, keys); + hdr.type = MSG_KEYMAP_NOTIFY; + hdr.window = 0; + write_message(g->vchan, hdr, keys); +} + /* handle local Xserver event: EnterNotify, LeaveNotify * send it to VM, but alwo we use it to fix docked * window position */ @@ -1898,11 +1917,7 @@ static void process_xevent_crossing(Ghandles * g, const XCrossingEvent * ev) CHECK_NONMANAGED_WINDOW(g, ev->window); if (ev->type == EnterNotify) { - char keys[32]; - XQueryKeymap(g->display, keys); - hdr.type = MSG_KEYMAP_NOTIFY; - hdr.window = 0; - write_message(g->vchan, hdr, keys); + send_keymap_notify(g); } /* move tray to correct position in VM */ if (vm_window->is_docked && @@ -1948,13 +1963,10 @@ static void process_xievent_focus(Ghandles * g, const XILeaveEvent * ev) struct msg_hdr hdr; struct msg_focus k; CHECK_NONMANAGED_WINDOW(g, ev->event); + update_wm_user_time(g, ev->event, ev->time); if (ev->type == XI_FocusIn) { - char keys[32]; - XQueryKeymap(g->display, keys); - hdr.type = MSG_KEYMAP_NOTIFY; - hdr.window = 0; - write_message(g->vchan, hdr, keys); + send_keymap_notify(g); } hdr.type = MSG_FOCUS; hdr.window = vm_window->remote_winid; @@ -2309,11 +2321,7 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev) } else if (ev->data.l[1] == XEMBED_FOCUS_IN) { struct msg_hdr hdr; struct msg_focus k; - char keys[32]; - XQueryKeymap(g->display, keys); - hdr.type = MSG_KEYMAP_NOTIFY; - hdr.window = 0; - write_message(g->vchan, hdr, keys); + send_keymap_notify(g); hdr.type = MSG_FOCUS; hdr.window = vm_window->remote_winid; k.type = FocusIn; @@ -2324,54 +2332,6 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev) } -// reference code for handling xi2 events -// static void process_xievent(Ghandles * g, XIDeviceEvent event) { -// switch (event.evtype) -// { -// case XI_RawButtonPress: -// case XI_RawButtonRelease: -// case XI_RawMotion: -// case XI_RawKeyPress: -// case XI_RawKeyRelease: -// break; -// case XI_KeyPress: -// case XI_KeyRelease: -// printf(" flags: %s\n", (event->flags & XIKeyRepeat) ? "repeat" : ""); -// // TODO: handle events -// break; -// case XI_ButtonPress: -// case XI_ButtonRelease: -// case XI_Motion: -// printf(" flags: %s\n", (event->flags & XIPointerEmulated) ? "emulated" : ""); -// // TODO: handle events -// break; -// } -// } - -// static XKeyEvent xkeyevent_from_xinput_event(const XIDeviceEvent* xi_event) { -// XKeyEvent fake_event; -// switch (xi_event->evtype) { -// case XI_KeyPress: fake_event.type = KeyPress; break; -// case XI_KeyRelease: fake_event.type = KeyRelease; break; -// default: assert(false); // stop immediately -// } -// fake_event.serial = xi_event->serial; -// fake_event.send_event = false; -// fake_event.display = xi_event->display; -// fake_event.window = xi_event->event; -// fake_event.root = xi_event->root; -// fake_event.subwindow = xi_event->child; // from Manual page XKeyEvent(3) -// fake_event.time = xi_event->time; -// fake_event.x = xi_event->event_x; -// fake_event.y = xi_event->event_y; -// fake_event.x_root = xi_event->root_x; -// fake_event.y_root = xi_event->root_y; -// fake_event.state = xi_event->mods.effective; -// fake_event.keycode = xi_event->detail; -// fake_event.same_screen = true; // don't know how to fill this -// return fake_event; -// } - /* dispatch local Xserver event */ static void process_xevent(Ghandles * g) { @@ -2383,13 +2343,11 @@ static void process_xevent(Ghandles * g) cookie->extension == g->xi_opcode) { XIEvent* xi_event = cookie->data; // from test_xi2.c in xinput cli utility - XIDeviceEvent * xi_device = (XIDeviceEvent *)xi_event; switch (xi_event->evtype) { // ideally raw input events are better, but I'm relying on X server's built-in event filtering and routing feature here case XI_KeyPress: case XI_KeyRelease: - if (xi_device && (xi_device->flags & XIKeyRepeat)) break; // don't send key repeat events - process_xievent_keypress(g, xi_device); + process_xievent_keypress(g, (XIDeviceEvent *)xi_event); break; case XI_FocusIn: case XI_FocusOut: From 8fd4e7f2b1bbee48d5152fb73c9e3377dd50d2eb Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Fri, 17 Jun 2022 03:08:10 +0800 Subject: [PATCH 18/18] Add more error handling --- gui-daemon/xside.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/gui-daemon/xside.c b/gui-daemon/xside.c index 18eca19f..52f18c4f 100644 --- a/gui-daemon/xside.c +++ b/gui-daemon/xside.c @@ -274,6 +274,14 @@ int x11_error_handler(Display * dpy, XErrorEvent * ev) * handled in handle_mfndump/handle_window_dump */ return 0; } + + char error_msg[1024]; + XGetErrorText(ev->display, ev->error_code, error_msg, sizeof(error_msg)); + int now = (int) time(NULL); // truncate + fprintf(stderr, "[%d] Encountered X Error:\n", now); + fprintf(stderr, error_msg); + + #ifdef MAKE_X11_ERRORS_FATAL /* The exit(1) below will call release_all_mapped_mfns (registerd with * atexit(3)), which would try to release window images with XShmDetach. We @@ -393,7 +401,12 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window) XISetMask(xi_mask.mask, XI_KeyRelease); XISetMask(xi_mask.mask, XI_FocusIn); XISetMask(xi_mask.mask, XI_FocusOut); - XISelectEvents(g->display, child_win, &xi_mask, 1); + + int err = XISelectEvents(g->display, child_win, &xi_mask, 1); + if (err) { + fprintf(stderr, "Failed to subscribe to XI events. ErrCode: %d\n", err); + exit(1); + } free(xi_mask.mask); XSync(g->display, False); @@ -1409,7 +1422,6 @@ static void process_xevent_button(Ghandles * g, const XButtonEvent * ev) update_wm_user_time(g, ev->window, ev->time); k.type = ev->type; - k.x = ev->x; k.y = ev->y; k.state = ev->state; @@ -1901,7 +1913,11 @@ static void send_keymap_notify(Ghandles * g) { struct msg_hdr hdr; char keys[32]; - XQueryKeymap(g->display, keys); + int err = XQueryKeymap(g->display, keys); + if (err) { + fprintf(stderr, "XQueryKeymap failed: %d.\n", err); + return; // non fatal + } hdr.type = MSG_KEYMAP_NOTIFY; hdr.window = 0; write_message(g->vchan, hdr, keys);