Skip to content

Commit 9d9be33

Browse files
authoredAug 3, 2021
Detect if the X server is XWayland and report an error. (#342)
* Detect if the X server is XWayland and report an error. Also: Detect if $DISPLAY env is not set and report a more informative error message. For the purposes of libxdo/xdotool, XWayland does not work, so it's best to notify the user accordingly if detected. This adds a dependency on XInput `libxi` because the only[1] way I have found to detect XWayland is through this extension [1] There is another detection method which uses XFree86-VidModeExtension, but I don't know how common that extension is compared to XInput2. Fixes #341. Related to #337. * xdo_version.h update when VERSION updates * Fix typo in version.sh which leaves an extra plus '+' character in the date * Run help or version commands without trying to connect to the X11 display * Free when detecting
1 parent ed10a77 commit 9d9be33

File tree

4 files changed

+57
-10
lines changed

4 files changed

+57
-10
lines changed
 

‎Makefile

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ DEFAULT_LIBS=-L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXtst -lXinerama -lxkbcomm
3636
DEFAULT_INC=-I/usr/X11R6/include -I/usr/local/include
3737

3838
XDOTOOL_LIBS=$(shell pkg-config --libs x11 2> /dev/null || echo "$(DEFAULT_LIBS)") $(shell sh platform.sh extralibs)
39-
LIBXDO_LIBS=$(shell pkg-config --libs x11 xtst xinerama xkbcommon 2> /dev/null || echo "$(DEFAULT_LIBS)")
40-
INC=$(shell pkg-config --cflags x11 xtst xinerama xkbcommon 2> /dev/null || echo "$(DEFAULT_INC)")
39+
LIBXDO_LIBS=$(shell pkg-config --libs xi x11 xtst xinerama xkbcommon 2> /dev/null || echo "$(DEFAULT_LIBS)")
40+
INC=$(shell pkg-config --cflags xi x11 xtst xinerama xkbcommon 2> /dev/null || echo "$(DEFAULT_INC)")
4141
CFLAGS+=-std=c99 $(INC)
4242

4343
CMDOBJS= cmd_click.o cmd_mousemove.o cmd_mousemove_relative.o cmd_mousedown.o \
@@ -195,7 +195,7 @@ test: xdotool libxdo.$(VERLIBSUFFIX)
195195
fi
196196
SHELL=$(WITH_SHELL) $(MAKE) -C t
197197

198-
xdo_version.h:
198+
xdo_version.h: VERSION
199199
sh version.sh --header > $@
200200

201201
VERSION:

‎version.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ fi
77
if [ -z "$MAJOR" -o -z "$RELEASE" -o -z "$REVISION" ] ; then
88
MAJOR="3"
99
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}"
10-
DATE_FMT="+%Y%m%d"
10+
DATE_FMT="%Y%m%d"
1111
RELEASE="$(date -u -d "@$SOURCE_DATE_EPOCH" "+$DATE_FMT" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+$DATE_FMT" 2>/dev/null || date -u "+$DATE_FMT")"
1212
REVISION=1
1313
#$([ -d .svn ] && svn info . | awk '/Revision:/ {print $2}')

‎xdo.c

+44-5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <X11/Xutil.h>
2929
#include <X11/extensions/XTest.h>
3030
#include <X11/extensions/Xinerama.h>
31+
#include <X11/extensions/XInput2.h>
3132
#include <X11/keysym.h>
3233
#include <X11/cursorfont.h>
3334

@@ -83,14 +84,23 @@ static Atom atom_UTF8_STRING = -1;
8384
xdo_t* xdo_new(const char *display_name) {
8485
Display *xdpy;
8586

86-
if ((xdpy = XOpenDisplay(display_name)) == NULL) {
87-
/* Can't use _xdo_eprintf yet ... */
88-
fprintf(stderr, "Error: Can't open display: %s\n", display_name);
89-
return NULL;
87+
if (display_name == NULL) {
88+
display_name = XDisplayName(display_name);
9089
}
9190

91+
#define DISPLAY_HINT "Is there an Xorg or other X server running? You can try setting 'export DISPLAY=:0' and trying again."
9292
if (display_name == NULL) {
93-
display_name = getenv("DISPLAY");
93+
fprintf(stderr, "Error: No DISPLAY environment variable is set. " DISPLAY_HINT "\n");
94+
return NULL;
95+
}
96+
97+
if (*display_name == '\0') {
98+
fprintf(stderr, "Error: DISPLAY environment variable is empty. " DISPLAY_HINT "\n");
99+
return NULL;
100+
}
101+
102+
if ((xdpy = XOpenDisplay(display_name)) == NULL) {
103+
return NULL;
94104
}
95105

96106
return xdo_new_with_opened_display(xdpy, display_name, 1);
@@ -106,6 +116,14 @@ xdo_t* xdo_new_with_opened_display(Display *xdpy, const char *display,
106116
return NULL;
107117
}
108118

119+
// This library and xdotool do not work correctly on Wayland/XWayland.
120+
// Try to detect XWayland and warn the user about problems.
121+
if (appears_to_be_wayland(xdpy)) {
122+
fprintf(stderr, "The X server at %s appears to be XWayland. Unfortunately, XWayland does not correctly support the features used by libxdo and xdotool.\n", display);
123+
return NULL;
124+
}
125+
126+
109127
/* XXX: Check for NULL here */
110128
xdo = malloc(sizeof(xdo_t));
111129
memset(xdo, 0, sizeof(xdo_t));
@@ -2017,3 +2035,24 @@ int xdo_get_viewport_dimensions(xdo_t *xdo, unsigned int *width,
20172035
return xdo_get_window_size(xdo, root, width, height);
20182036
}
20192037
}
2038+
2039+
int appears_to_be_wayland(Display *xdpy) {
2040+
// XWayland leaks its presence in two extensions (at time of writing, August 2021)
2041+
// First: the name of input devices "xwayland-pointer"
2042+
// Second: in the Vendor string of XFree86-VidModeExtension
2043+
2044+
int count;
2045+
XDeviceInfo *devices = XListInputDevices(xdpy, &count);
2046+
for (int i = 0; i < count; i++) {
2047+
// fprintf(stderr, "Device %d: %s\n", i, devices[i].name);
2048+
// If the input device name starts with "xwayland-",
2049+
// there's a good chance we're running on XWayland.
2050+
if (strstr(devices[i].name, "xwayland-") == devices[i].name) {
2051+
XFreeDeviceList(devices);
2052+
return 1; // Running on wayland
2053+
}
2054+
}
2055+
XFreeDeviceList(devices);
2056+
2057+
return 0;
2058+
}

‎xdotool.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,14 @@ int args_main(int argc, char **argv) {
521521
exit(1);
522522
}
523523

524+
if (!strcasecmp(argv[1], "help")) {
525+
cmd_help(NULL);
526+
exit(EXIT_SUCCESS);
527+
} else if (!strcasecmp(argv[1], "version")) {
528+
cmd_version(NULL);
529+
exit(EXIT_SUCCESS);
530+
}
531+
524532
while ((opt = getopt_long_only(argc, argv, "++hv", long_options, &option_index)) != -1) {
525533
switch (opt) {
526534
case 'h':
@@ -547,7 +555,7 @@ int args_main(int argc, char **argv) {
547555
context.debug = (getenv("DEBUG") != NULL);
548556

549557
if (context.xdo == NULL) {
550-
fprintf(stderr, "Failed creating new xdo instance\n");
558+
fprintf(stderr, "Failed creating new xdo instance.\n");
551559
return 1;
552560
}
553561
context.xdo->debug = context.debug;

0 commit comments

Comments
 (0)