Skip to content

Commit

Permalink
Detect if the X server is XWayland and report an error. (jordansissel…
Browse files Browse the repository at this point in the history
…#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 jordansissel#341.
Related to jordansissel#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
  • Loading branch information
jordansissel authored Aug 3, 2021
1 parent ed10a77 commit 9d9be33
Showing 4 changed files with 57 additions and 10 deletions.
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -36,8 +36,8 @@ DEFAULT_LIBS=-L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXtst -lXinerama -lxkbcomm
DEFAULT_INC=-I/usr/X11R6/include -I/usr/local/include

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

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

xdo_version.h:
xdo_version.h: VERSION
sh version.sh --header > $@

VERSION:
2 changes: 1 addition & 1 deletion version.sh
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ fi
if [ -z "$MAJOR" -o -z "$RELEASE" -o -z "$REVISION" ] ; then
MAJOR="3"
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}"
DATE_FMT="+%Y%m%d"
DATE_FMT="%Y%m%d"
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")"
REVISION=1
#$([ -d .svn ] && svn info . | awk '/Revision:/ {print $2}')
49 changes: 44 additions & 5 deletions xdo.c
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
#include <X11/Xutil.h>
#include <X11/extensions/XTest.h>
#include <X11/extensions/Xinerama.h>
#include <X11/extensions/XInput2.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>

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

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

#define DISPLAY_HINT "Is there an Xorg or other X server running? You can try setting 'export DISPLAY=:0' and trying again."
if (display_name == NULL) {
display_name = getenv("DISPLAY");
fprintf(stderr, "Error: No DISPLAY environment variable is set. " DISPLAY_HINT "\n");
return NULL;
}

if (*display_name == '\0') {
fprintf(stderr, "Error: DISPLAY environment variable is empty. " DISPLAY_HINT "\n");
return NULL;
}

if ((xdpy = XOpenDisplay(display_name)) == NULL) {
return NULL;
}

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,
return NULL;
}

// This library and xdotool do not work correctly on Wayland/XWayland.
// Try to detect XWayland and warn the user about problems.
if (appears_to_be_wayland(xdpy)) {
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);
return NULL;
}


/* XXX: Check for NULL here */
xdo = malloc(sizeof(xdo_t));
memset(xdo, 0, sizeof(xdo_t));
@@ -2017,3 +2035,24 @@ int xdo_get_viewport_dimensions(xdo_t *xdo, unsigned int *width,
return xdo_get_window_size(xdo, root, width, height);
}
}

int appears_to_be_wayland(Display *xdpy) {
// XWayland leaks its presence in two extensions (at time of writing, August 2021)
// First: the name of input devices "xwayland-pointer"
// Second: in the Vendor string of XFree86-VidModeExtension

int count;
XDeviceInfo *devices = XListInputDevices(xdpy, &count);
for (int i = 0; i < count; i++) {
// fprintf(stderr, "Device %d: %s\n", i, devices[i].name);
// If the input device name starts with "xwayland-",
// there's a good chance we're running on XWayland.
if (strstr(devices[i].name, "xwayland-") == devices[i].name) {
XFreeDeviceList(devices);
return 1; // Running on wayland
}
}
XFreeDeviceList(devices);

return 0;
}
10 changes: 9 additions & 1 deletion xdotool.c
Original file line number Diff line number Diff line change
@@ -521,6 +521,14 @@ int args_main(int argc, char **argv) {
exit(1);
}

if (!strcasecmp(argv[1], "help")) {
cmd_help(NULL);
exit(EXIT_SUCCESS);
} else if (!strcasecmp(argv[1], "version")) {
cmd_version(NULL);
exit(EXIT_SUCCESS);
}

while ((opt = getopt_long_only(argc, argv, "++hv", long_options, &option_index)) != -1) {
switch (opt) {
case 'h':
@@ -547,7 +555,7 @@ int args_main(int argc, char **argv) {
context.debug = (getenv("DEBUG") != NULL);

if (context.xdo == NULL) {
fprintf(stderr, "Failed creating new xdo instance\n");
fprintf(stderr, "Failed creating new xdo instance.\n");
return 1;
}
context.xdo->debug = context.debug;

0 comments on commit 9d9be33

Please sign in to comment.