Skip to content

Commit

Permalink
Send MSG_CURSOR
Browse files Browse the repository at this point in the history
See QubesOS/qubes-issues#1551.

I use the Xfixes extension to subscribe to cursor change events.
See SelectCursorInput and CursorNotify in the Xfixes specification:

  https://cgit.freedesktop.org/xorg/proto/fixesproto/plain/fixesproto.txt

The returned "cursor name" can be empty, and in general doesn't
have to be meaningful. However, it practice the names are standard
X11 "cursor font" names:

  https://tronche.com/gui/x/xlib/appendix/b/

One exception to that I found is Chromium, which uses its own set
of cursor names. We can recognize these as well.
  • Loading branch information
pwmarcz committed Mar 27, 2020
1 parent b296e45 commit 564ab4d
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 2 deletions.
2 changes: 2 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Build-Depends:
libx11-dev,
libxcomposite-dev,
libxdamage-dev,
libxfixes-dev,
x11proto-xf86dga-dev,
libxt-dev,
libxen-dev,
Expand All @@ -38,6 +39,7 @@ Depends:
qubesdb-vm (>= 4.1.4),
libxdamage1,
libxcomposite1,
libxfixes3,
libxt6,
libx11-6,
python3,
Expand Down
2 changes: 1 addition & 1 deletion gui-agent/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ CC ?= gcc
CFLAGS += -I../include/ `pkg-config --cflags vchan-$(BACKEND_VMM)` -g -Wall -Wextra -Werror -pie -fPIC
OBJS = vmside.o \
../gui-common/txrx-vchan.o ../gui-common/error.o ../common/list.o
LIBS = -lX11 -lXdamage -lXcomposite `pkg-config --libs vchan-$(BACKEND_VMM)` -lqubesdb
LIBS = -lX11 -lXdamage -lXcomposite -lXfixes `pkg-config --libs vchan-$(BACKEND_VMM)` -lqubesdb


all: qubes-gui
Expand Down
204 changes: 203 additions & 1 deletion gui-agent/vmside.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*
*/

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
Expand All @@ -41,6 +42,7 @@
#include <X11/XKBlib.h>
#include <X11/Xlibint.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <qubes-gui-protocol.h>
#include <qubes-xorg-tray-defs.h>
#include "xdriver-shm-cmd.h"
Expand All @@ -51,7 +53,7 @@

#define SOCKET_ADDRESS "/var/run/xf86-qubes-socket"

#define QUBES_GUI_PROTOCOL_VERSION_LINUX (1 << 16 | 1)
#define QUBES_GUI_PROTOCOL_VERSION_LINUX (1 << 16 | 3)

#ifdef __GNUC__
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
Expand All @@ -60,6 +62,7 @@
#endif

int damage_event, damage_error;
int xfixes_event, xfixes_error;
/* from gui-common/error.c */
extern int print_x11_errors;

Expand Down Expand Up @@ -113,6 +116,130 @@ Ghandles *ghandles_for_vchan_reinitialize;

#define SKIP_NONMANAGED_WINDOW if (!list_lookup(windows_list, window)) return

/* Cursor name translation. See X11/cursorfont.h. */

struct supported_cursor {
const char *name;
uint32_t cursor_id;
};

struct supported_cursor supported_cursors[] = {
/* Names as defined by Xlib. Most programs will use these. */
{ "X_cursor", XC_X_cursor },
{ "arrow", XC_arrow },
{ "based_arrow_down", XC_based_arrow_down },
{ "based_arrow_up", XC_based_arrow_up },
{ "boat", XC_boat },
{ "bogosity", XC_bogosity },
{ "bottom_left_corner", XC_bottom_left_corner },
{ "bottom_right_corner", XC_bottom_right_corner },
{ "bottom_side", XC_bottom_side },
{ "bottom_tee", XC_bottom_tee },
{ "box_spiral", XC_box_spiral },
{ "center_ptr", XC_center_ptr },
{ "circle", XC_circle },
{ "clock", XC_clock },
{ "coffee_mug", XC_coffee_mug },
{ "cross", XC_cross },
{ "cross_reverse", XC_cross_reverse },
{ "crosshair", XC_crosshair },
{ "diamond_cross", XC_diamond_cross },
{ "dot", XC_dot },
{ "dotbox", XC_dotbox },
{ "double_arrow", XC_double_arrow },
{ "draft_large", XC_draft_large },
{ "draft_small", XC_draft_small },
{ "draped_box", XC_draped_box },
{ "e xchange", XC_exchange },
{ "fleur", XC_fleur },
{ "gobbler", XC_gobbler },
{ "gumby", XC_gumby },
{ "hand1", XC_hand1 },
{ "hand2", XC_hand2 },
{ "heart", XC_heart },
{ "icon", XC_icon },
{ "iron_cross", XC_iron_cross },
{ "left_ptr", XC_left_ptr },
{ "left_side", XC_left_side },
{ "left_tee", XC_left_tee },
{ "leftbutton", XC_leftbutton },
{ "ll_angle", XC_ll_angle },
{ "lr_angle", XC_lr_angle },
{ "man", XC_man },
{ "middlebutton", XC_middlebutton },
{ "mouse", XC_mouse },
{ "pencil", XC_pencil },
{ "pirate", XC_pirate },
{ "plus", XC_plus },
{ "question_arrow", XC_question_arrow },
{ "right_ptr", XC_right_ptr },
{ "right_side", XC_right_side },
{ "right_tee", XC_right_tee },
{ "rightbutton", XC_rightbutton },
{ "rtl_logo", XC_rtl_logo },
{ "sailboat", XC_sailboat },
{ "sb_down_arrow", XC_sb_down_arrow },
{ "sb_h_double_arrow", XC_sb_h_double_arrow },
{ "sb_left_arrow", XC_sb_left_arrow },
{ "sb_right_arrow", XC_sb_right_arrow },
{ "sb_up_arrow", XC_sb_up_arrow },
{ "sb_v_double_arrow", XC_sb_v_double_arrow },
{ "shuttle", XC_shuttle },
{ "sizing", XC_sizing },
{ "spider", XC_spider },
{ "spraycan", XC_spraycan },
{ "star", XC_star },
{ "target", XC_target },
{ "tcross", XC_tcross },
{ "top_left_arrow", XC_top_left_arrow },
{ "top_left_corner", XC_top_left_corner },
{ "top_right_corner", XC_top_right_corner },
{ "top_side", XC_top_side },
{ "top_tee", XC_top_tee },
{ "trek", XC_trek },
{ "ul_angle", XC_ul_angle },
{ "umbrella", XC_umbrella },
{ "ur_angle", XC_ur_angle },
{ "watch", XC_watch },
{ "xterm", XC_xterm },

/* Chromium (and derived projects) use different names.
* See: https://github.com/chromium/chromium/blob/ccd149af47315e4c6f2fc45d55be1b271f39062c/ui/base/cursor/cursor_loader_x11.cc#L25
*/
{ "pointer", XC_hand2 },
{ "progress", XC_watch },
{ "wait", XC_watch },
{ "cell", XC_plus },
{ "all-scroll", XC_fleur },
{ "v-scroll", XC_fleur },
{ "h-scroll", XC_fleur },
{ "crosshair", XC_cross },
{ "text", XC_xterm },
// { "not-allowed", x11::None },
{ "grabbing", XC_hand2 },
{ "col-resize", XC_sb_h_double_arrow },
{ "row-resize", XC_sb_v_double_arrow },
{ "n-resize", XC_top_side },
{ "e-resize", XC_right_side },
{ "s-resize", XC_bottom_side },
{ "w-resize", XC_left_side },
{ "ne-resize", XC_top_right_corner },
{ "nw-resize", XC_top_left_corner },
{ "se-resize", XC_bottom_right_corner },
{ "sw-resize", XC_bottom_left_corner },
{ "ew-resize", XC_sb_h_double_arrow },
{ "ns-resize", XC_sb_v_double_arrow },
// { "nesw-resize",x11::None},
// { "nwse-resize",x11::None},
{ "dnd-none", XC_hand2 },
{ "dnd-move", XC_hand2 },
{ "dnd-copy", XC_hand2 },
{ "dnd-link", XC_hand2 },

// end marker
{ NULL, 0 }
};

void send_wmname(Ghandles * g, XID window);
void send_wmnormalhints(Ghandles * g, XID window, int ignore_fail);
void send_wmclass(Ghandles * g, XID window, int ignore_fail);
Expand Down Expand Up @@ -147,6 +274,69 @@ void process_xevent_damage(Ghandles * g, XID window,
write_message(g->vchan, hdr, mx);
}

void send_cursor(Ghandles *g, XID window, uint32_t cursor_id)
{
struct msg_hdr hdr;
struct msg_cursor msg;

hdr.type = MSG_CURSOR;
hdr.window = window;
msg.cursor = cursor_id;
write_message(g->vchan, hdr, msg);
}

uint32_t find_cursor(Ghandles *g, Atom atom)
{
char *name;
int i;
uint32_t cursor_id = CURSOR_NONE;

if (atom == None)
return CURSOR_NONE;

name = XGetAtomName(g->display, atom);
if (!name)
return CURSOR_NONE;

for (i = 0; supported_cursors[i].name; i++) {
if (strcmp(supported_cursors[i].name, name) == 0) {
cursor_id = supported_cursors[i].cursor_id;
assert(cursor_id < CURSOR_X11_MAX);
break;
}
}

free(name);

return cursor_id;
}

void process_xevent_cursor(Ghandles *g, XFixesCursorNotifyEvent *ev)
{
if (ev->subtype == XFixesDisplayCursorNotify) {
/* The event from XFixes specifies only the root window, so we need to
* find out the window under pointer.
*/
Window root, window_under_pointer;
int root_x, root_y, win_x, win_y;
unsigned int mask;
Bool ret;
int cursor_id;

ret = XQueryPointer(g->display, ev->window, &root,
&window_under_pointer,
&root_x, &root_y, &win_x, &win_y, &mask);
if (!ret || window_under_pointer == None)
return;

if (!list_lookup(windows_list, window_under_pointer))
return;

cursor_id = find_cursor(g, ev->cursor_name);
send_cursor(g, window_under_pointer, cursor_id);
}
}

void process_xevent_createnotify(Ghandles * g, XCreateWindowEvent * ev)
{
struct msg_hdr hdr;
Expand Down Expand Up @@ -1108,6 +1298,10 @@ void process_xevent(Ghandles * g)
dev->area.width,
dev->area.height);
// fprintf(stderr, "@");
} else if (event_buffer.type == (xfixes_event + XFixesCursorNotify)) {
process_xevent_cursor(
g,
(XFixesCursorNotifyEvent *) &event_buffer);
} else if (g->log_level > 1)
fprintf(stderr, "#");
}
Expand Down Expand Up @@ -2080,6 +2274,14 @@ int main(int argc, char **argv)
perror("XDamageQueryExtension");
exit(1);
}
/* Use XFixes to handle cursor shape. */
if (XFixesQueryExtension(
g.display, &xfixes_event, &xfixes_error)) {
for (i = 0; i < ScreenCount(g.display); i++)
XFixesSelectCursorInput(g.display, RootWindow(g.display, i),
XFixesDisplayCursorNotifyMask);
} else
fprintf(stderr, "XFixes not available, cursor shape handling off");
XAutoRepeatOff(g.display);
signal(SIGCHLD, SIG_IGN);
signal(SIGTERM, handle_sigterm);
Expand Down
2 changes: 2 additions & 0 deletions rpm_spec/gui-agent.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ BuildRequires: gcc
BuildRequires: libX11-devel
BuildRequires: libXcomposite-devel
BuildRequires: libXdamage-devel
BuildRequires: libXfixes-devel
BuildRequires: libXt-devel
BuildRequires: libtool-ltdl-devel
BuildRequires: pulseaudio-libs-devel >= 0.9.21, pulseaudio-libs-devel <= 13.0
Expand All @@ -53,6 +54,7 @@ BuildRequires: qubes-db-devel
BuildRequires: xen-devel
Requires: Xorg %(xserver-sdk-abi-requires ansic)
Requires: Xorg %(xserver-sdk-abi-requires videodrv)
Requires: libXfixes
Requires: qubes-core-vm >= 3.0.14
Requires: xorg-x11-xinit
Requires: qubes-libvchan-@BACKEND_VMM@
Expand Down

0 comments on commit 564ab4d

Please sign in to comment.