Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Build-Depends:
libxrandr-dev,
libxcb1-dev,
libx11-xcb-dev,
libxi-dev,
libconfig-dev,
libpng-dev,
libnotify-dev,
Expand Down
2 changes: 2 additions & 0 deletions gui-daemon/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
qubes_guid
qubes-guid
qubes-guid.1
2 changes: 1 addition & 1 deletion gui-daemon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
228 changes: 174 additions & 54 deletions gui-daemon/xside.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

/* high level documentation is here: https://www.qubes-os.org/doc/gui/ */

#include <X11/extensions/XI2.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
Expand All @@ -47,6 +48,7 @@
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include <X11/extensions/shmproto.h>
#include <X11/extensions/XInput2.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/Xlib-xcb.h>
Expand Down Expand Up @@ -378,6 +380,27 @@ static Window mkwindow(Ghandles * g, struct windowdata *vm_window)
ButtonPressMask | ButtonReleaseMask |
PointerMotionMask | EnterWindowMask | LeaveWindowMask |
FocusChangeMask | StructureNotifyMask | PropertyChangeMask);

// select xinput events
XIEventMask xi_mask;
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);
XISetMask(xi_mask.mask, XI_ButtonPress);
XISetMask(xi_mask.mask, XI_ButtonRelease);
XISetMask(xi_mask.mask, XI_Motion);
XISetMask(xi_mask.mask, XI_RawKeyPress);
XISetMask(xi_mask.mask, XI_RawKeyRelease);
XISetMask(xi_mask.mask, XI_RawButtonPress);
XISetMask(xi_mask.mask, XI_RawButtonRelease);
XISetMask(xi_mask.mask, XI_RawMotion);
XISetMask(xi_mask.mask, XI_FocusIn);
XISetMask(xi_mask.mask, XI_FocusOut);
XISelectEvents(g->display, child_win, &xi_mask, 1);


XSetWMProtocols(g->display, child_win, &g->wmDeleteMessage, 1);
if (g->icon_data) {
XChangeProperty(g->display, child_win, g->net_wm_icon, XA_CARDINAL, 32,
Expand Down Expand Up @@ -647,6 +670,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. Key press events not available. Upgrade your X11 server now.\n");
}
/* get the work area */
XSelectInput(g->display, g->root_win, PropertyChangeMask);
update_work_area(g);
Expand Down Expand Up @@ -1329,6 +1355,53 @@ static void update_wm_user_time(Ghandles *const g, const Window window,
1);
}

static XID get_remote_winid(Ghandles * g, XID local_winid) {
if (!local_winid) return 0;
struct windowdata *vm_window;
vm_window=check_nonmanaged_window(g, local_winid);
if (!vm_window) return 0;
return vm_window->remote_winid;
}

/* handle local XInput events:
- RawKeyPress
- RawKeyRelease
- RawButtonPress
- RawButtonRelease
- RawMotion
- KeyPress
- KeyRelease
- ButtonPress
- ButtonRelease
- Motion
- FocusIn
- FocusOut
*/
static void process_xievent(Ghandles * g, XIDeviceEvent ev) {
struct msg_hdr hdr;
ev.event = get_remote_winid(g, ev.event);
if (!ev.event) return; // don't handle non-managed window
ev.root = get_remote_winid(g, ev.root);
ev.child = 0; // child/embeded window (those who don't take focus) aren't mapped in dom0
// TODO: get child window id
ev.serial = 0; // remote VM should set this; maybe SendEvent will set this?
ev.display = 0; // no pointer address to memory
update_wm_user_time(g, ev.event, ev.time);
ev.time = 0; // remote VM can set this
// video games need device ID to tell different input devices apart
// TODO: anonymize this somehow?
// ev->deviceid = 0;
// ev->sourceid = 0;

// TODO: don't send special keypress
// ideally XFCE should handle this, not this program
// if (is_special_keypress(g, ev, vm_window->remote_winid))
// return;
hdr.type = MSG_XINPUT;
hdr.window = ev.event;
write_message(g->vchan, hdr, ev);
}

/* handle local Xserver event: XKeyEvent
* send it to relevant window in VM
*/
Expand Down Expand Up @@ -2318,66 +2391,113 @@ static void process_xevent_xembed(Ghandles * g, const XClientMessageEvent * ev)

}

static bool is_xinput_event_type_raw(int ev_type) {
switch (ev_type) {
case XI_RawKeyPress:
case XI_RawKeyRelease:
case XI_RawButtonPress:
case XI_RawButtonRelease:
case XI_RawMotion:
return true;
case XI_KeyPress:
case XI_KeyRelease:
case XI_ButtonPress:
case XI_ButtonRelease:
case XI_Motion:
return false;
default: assert(false); // Unexpected event type
}
}

/* dispatch local Xserver event */
static void process_xevent(Ghandles * g)
{
XEvent event_buffer;
XGenericEventCookie *cookie = &event_buffer.xcookie;
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 = cookie->data; // from test_xi2.c in xinput cli utility

switch (xi_event->evtype) {
case XI_FocusIn:
g->focused_remote_winid = xi_event->event;
process_xievent(g, *xi_event);
break;
case XI_FocusOut:
if (g->focused_remote_winid == xi_event->event)
g->focused_remote_winid = 0; // load-compare-store in single thread is fine?
process_xievent(g, *xi_event);
break;
default:
// only send raw events to focused window
if (is_xinput_event_type_raw(xi_event->evtype)) {
if (g->focused_remote_winid == xi_event->event) {
process_xievent(g, *xi_event);
}
} else {
process_xievent(g, *xi_event);
}
break;
}
XFreeEventData(g->display, cookie);
} 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;
}
break;
default:;
}
}

Expand Down
4 changes: 4 additions & 0 deletions gui-daemon/xside.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ struct extra_prop {
};

/* global variables
* One for one VM
* keep them in this struct for readability
*/
struct _global_handles {
Expand Down Expand Up @@ -172,6 +173,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 */
Expand Down Expand Up @@ -199,6 +201,8 @@ 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 */
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;
Expand Down
1 change: 1 addition & 0 deletions rpm_spec/gui-daemon.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion shmoverride/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
X_wrapper_qubes
X-wrapper-qubes
shmoverride.so