Loop your X cursor around the screen 👉😎👉
git clone https://github.com/m-col/xoop
Files | Refs | Readme | License

Commit e64ec0bd216a7cdb5deca36526c2eecf38b3b32d
Parent: 0fc77ea1946a0563d7242d781a34566c18d2ed44
Author: mcol <mcol@posteo.net>
Date: 2020-10-06 01:52:07 +0100
Committer: mcol <mcol@posteo.net>
Committed: 2020-10-06 01:52:07 +0100

rewrite using Xlib's XFixesCreatePointerBarrier instead of a window

xoop.c Modified

@@ -20,7 +20,6 @@
 SOFTWARE.
 */
 
-
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -29,244 +28,172 @@
 #include <xcb/shape.h>
 #include <xcb/xcb.h>
 #include <xcb/xcb_event.h>
+#include <xcb/xfixes.h>
+#include <xcb/xinput.h>
+#include <X11/Xlib-xcb.h>
+#include <X11/extensions/Xfixes.h>
 
 #define PROGNAME "xoop"
 
-xcb_connection_t    *conn;
-xcb_screen_t	    *screen;
-xcb_window_t	     wid;
+Display                 *dpy;
+xcb_connection_t	*conn;
+xcb_screen_t		*screen;
+xcb_input_device_id_t	deviceid;
+PointerBarrier          barriers[4];
 
+int num_barriers = 0;
 int debug = 0;
-int randr_base = 0;
-uint32_t axis = 0; /* 0: both, 1: x, 2: y, 3: both specified */
+int axis = 0;
+const int BOTH_AXES = 0;
+const int X_ONLY = 1;
+const int Y_ONLY = 2;
+int fixes_opcode, fixes_event_base, fixes_error_base;
+uint8_t op_randr = 0;
+uint8_t op_xinput = 0;
 
+int16_t width, height;
 
-void set_window_type() {
-    xcb_intern_atom_reply_t *wm_window_type,
-			    *wm_window_type_dock,
-			    *wm_state,
-			    *wm_state_above,
-			    *wm_desktop;
 
-    wm_window_type = xcb_intern_atom_reply(
-	conn,
-	xcb_intern_atom(conn, 0, 19, "_NET_WM_WINDOW_TYPE"),
-	NULL
-    );
-    wm_window_type_dock = xcb_intern_atom_reply(
-	conn,
-	xcb_intern_atom(conn, 0, 24, "_NET_WM_WINDOW_TYPE_DOCK"),
-	NULL
-    );
-    xcb_change_property(
-	conn, XCB_PROP_MODE_REPLACE, wid, wm_window_type->atom, XCB_ATOM_ATOM, 32, 1, &wm_window_type_dock->atom
-    );
+void exit_angrily(char msg[])
+{
+    fprintf(stderr, msg);
+    xcb_disconnect(conn);
+    exit(EXIT_FAILURE);
+}
 
-    wm_state = xcb_intern_atom_reply(
-	conn,
-	xcb_intern_atom(conn, 0, 13, "_NET_WM_STATE"),
-	NULL
-    );
-    wm_state_above = xcb_intern_atom_reply(
-	conn,
-	xcb_intern_atom(conn, 0, 19, "_NET_WM_STATE_ABOVE"),
-	NULL
-    );
-    xcb_change_property(
-	conn, XCB_PROP_MODE_REPLACE, wid, wm_state->atom, XCB_ATOM_ATOM, 32, 1, &wm_state_above->atom
-    );
 
-    wm_desktop = xcb_intern_atom_reply(
-        conn,
-        xcb_intern_atom(conn, 0, 15, "_NET_WM_DESKTOP"),
-        NULL
-    );
-    xcb_change_property(
-        conn, XCB_PROP_MODE_REPLACE, wid, wm_desktop->atom, XCB_ATOM_INTEGER, 1, 1, (uint32_t []){0xFFFFFFFF}
-    );
+void create_barriers()
+{
+    int x1 = 0;
+    int y1 = 0;
+    int x2 = (int)width;
+    int y2 = (int)height;
 
-    xcb_configure_window(conn, wid, XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t []){XCB_STACK_MODE_ABOVE});
+    if (debug)
+	printf("Creating barriers: %i %i %i %i\n", x1, y1, x2, y2);
 
-    xcb_change_property(
-	conn, XCB_PROP_MODE_REPLACE, wid, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 4, PROGNAME
-    );
+    if (axis == X_ONLY || axis == BOTH_AXES) {
+	barriers[0] = XFixesCreatePointerBarrier(dpy, DefaultRootWindow(dpy), x1, y1, x1, y2, 0, 0, NULL);
+	barriers[1] = XFixesCreatePointerBarrier(dpy, DefaultRootWindow(dpy), x2, y1, x2, y2, 0, 0, NULL);
+    };
 
-    free(wm_window_type);
-    free(wm_window_type_dock);
-    free(wm_desktop);
+    if (axis == Y_ONLY || axis == BOTH_AXES) {
+	barriers[2] = XFixesCreatePointerBarrier(dpy, DefaultRootWindow(dpy), x1, y1, x2, y1, 0, 0, NULL);
+	barriers[3] = XFixesCreatePointerBarrier(dpy, DefaultRootWindow(dpy), x1, y2, x2, y2, 0, 0, NULL);
+    };
+    XSync(dpy, False);
 }
 
 
-void set_window_shape(uint16_t width, uint16_t height)
+void delete_barriers()
 {
-    if (axis == 1) {
-	height += 2;
-    } else if (axis == 2) {
-	width += 2;
-    };
-    uint32_t dimensions[2] = {width, height};
-    xcb_configure_window(conn, wid, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, dimensions);
-
-    xcb_pixmap_t pixmap = xcb_generate_id(conn);
-    xcb_gcontext_t gc = xcb_generate_id(conn);
-    xcb_create_pixmap(conn, 1, pixmap, wid, width, height);
-    xcb_create_gc(
-	conn, gc, pixmap, XCB_GC_FOREGROUND, &screen->white_pixel
-    );
-    xcb_rectangle_t rect = {0, 0, width, height};
-    xcb_poly_fill_rectangle(conn, pixmap, gc, 1, &rect);
-    xcb_shape_mask(
-	conn, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_INPUT, wid, 0, 0, pixmap
-    );
+    for (int i = 0; i < 4; i++) {
+	if (debug)
+	    printf("Deleting barrier: %i/4\n", i + 1);
+        XFixesDestroyPointerBarrier(dpy, barriers[i]);
+    }
+}
 
-    xcb_pixmap_t pixmap2 = xcb_generate_id(conn);
-    xcb_gcontext_t gc2 = xcb_generate_id(conn);
-    xcb_create_pixmap(conn, 1, pixmap2, wid, width, height);
-    xcb_create_gc(
-	conn, gc2, pixmap2, XCB_GC_FOREGROUND, &screen->white_pixel
-    );
-    xcb_rectangle_t rect2 = {1, 1, width - 2, height - 2};
-    xcb_poly_fill_rectangle(conn, pixmap2, gc2, 1, &rect2);
-    xcb_shape_mask(
-	conn, XCB_SHAPE_SO_SUBTRACT, XCB_SHAPE_SK_INPUT, wid, 0, 0, pixmap2
-    );
 
-    if (debug) {
-	xcb_shape_mask(
-	    conn, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, wid, 0, 0, pixmap
-	);
-	xcb_shape_mask(
-	    conn, XCB_SHAPE_SO_SUBTRACT, XCB_SHAPE_SK_BOUNDING, wid, 0, 0, pixmap2
-	);
-    };
-
-    xcb_free_pixmap(conn, pixmap);
-    xcb_free_pixmap(conn, pixmap2);
-    xcb_free_gc(conn, gc);
-    xcb_free_gc(conn, gc2);
+void exit_nicely()
+{
+    delete_barriers();
+    xcb_disconnect(conn);
+    exit(EXIT_SUCCESS);
 }
 
 
-void setup_window()
+void check_xfixes()
 {
-    int class = XCB_WINDOW_CLASS_INPUT_ONLY;
-    uint32_t value_mask = XCB_CW_EVENT_MASK;
-    uint32_t value_list[1] = {XCB_EVENT_MASK_ENTER_WINDOW};
-    uint32_t value_list_debug[2] = {screen->white_pixel, XCB_EVENT_MASK_ENTER_WINDOW};
-    uint32_t *values = value_list;
-
-    if (debug) {
-	class = XCB_WINDOW_CLASS_INPUT_OUTPUT;
-	value_mask |= XCB_CW_BACK_PIXEL;
-	values = value_list_debug;
-    };
-
-    int16_t x, y = 0;
-    uint16_t width = screen->width_in_pixels;
-    uint16_t height = screen->height_in_pixels;
-    if (axis == 1) {
-	y = -1;
-	height += 2;
-    } else if (axis == 2) {
-	x = -1;
-	width += 2;
-    };
+    if (
+	!XQueryExtension(dpy, "XFIXES", &fixes_opcode, &fixes_event_base, &fixes_error_base)
+    )
+	exit_angrily("XFixes extension not available.\n");
+}
 
-    wid = xcb_generate_id(conn);
-    xcb_create_window(
-	conn,
-	XCB_COPY_FROM_PARENT,
-	wid,
-	screen->root,
-	x,
-	y,
-	width,
-	height,
-	0,
-	class,
-	XCB_COPY_FROM_PARENT,
-	value_mask,
-	values
-    );
 
-    set_window_type();
-    set_window_shape(screen->width_in_pixels, screen->height_in_pixels);
-    xcb_map_window(conn, wid);
+void check_randr()
+{
+    const xcb_query_extension_reply_t *ext = xcb_get_extension_data(conn, &xcb_randr_id);
+    if (!ext || !ext->present)
+        exit_angrily("Randr extension not available.\n");
+    op_randr = ext->first_event;
+    xcb_randr_select_input(conn, screen->root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
 }
 
 
-void configure_randr()
+void check_xinput()
 {
-    const xcb_query_extension_reply_t *randr = xcb_get_extension_data(conn, &xcb_randr_id);
-    if (randr->present) {
-	randr_base = randr->first_event;
-	xcb_randr_select_input(conn, screen->root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
-    };
+    const xcb_query_extension_reply_t *ext = xcb_get_extension_data(conn, &xcb_input_id);
+    if (!ext || !ext->present)
+        exit_angrily("XInput extension not available.\n");
+    struct {
+        xcb_input_event_mask_t head;
+        xcb_input_xi_event_mask_t mask;
+    } mask;
+    mask.head.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
+    mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t);
+    mask.mask = XCB_INPUT_XI_EVENT_MASK_BARRIER_HIT;
+    xcb_input_xi_select_events(conn, screen->root, 1, &mask.head);
 }
 
 
-void event_loop()
+void loop_cursor(xcb_generic_event_t *generic_event)
 {
-    xcb_generic_event_t *event;
-    xcb_enter_notify_event_t *entry;
-    xcb_randr_screen_change_notify_event_t *change;
+    xcb_input_barrier_hit_event_t *event = (xcb_input_barrier_hit_event_t *)generic_event;
     int16_t x = 0;
     int16_t y = 0;
+    int32_t far_x = width - 1;
+    int32_t far_y = height - 1;
+    int32_t ev_x = (int32_t)(event->root_x / (double)UINT16_MAX);
+    int32_t ev_y = (int32_t)(event->root_y / (double)UINT16_MAX);
+
+    if (ev_x == 0) {
+	x = far_x;
+	y = ev_y;
+    } else if (ev_y == 0){
+	x = ev_x;
+	y = far_y;
+    } else if (ev_x == far_x){
+	y = ev_y;
+    } else if (ev_y == far_y){
+	x = ev_x;
+    };
+    xcb_warp_pointer(conn, XCB_NONE, screen->root, 0, 0, 0, 0, x, y);
 
-    int16_t far_x = screen->width_in_pixels - 1;
-    int16_t far_y = screen->height_in_pixels - 1;
+    if (debug)
+	printf("Warp: (%i, %i) to (%i, %i)\n", ev_x, ev_y, x, y);
+}
 
-    xcb_flush(conn);
 
-    while((event = xcb_wait_for_event(conn))) {
-	switch (event->response_type) {
-
-	    case XCB_ENTER_NOTIFY:
-		entry = (xcb_enter_notify_event_t *)event;
-		if (entry->event_x == 0) {
-		    x = far_x;
-		    y = entry->event_y;
-		} else if (entry->event_y == 0){
-		    x = entry->event_x;
-		    y = far_y;
-		} else if (entry->event_x == far_x){
-		    x = 0;
-		    y = entry->event_y;
-		} else if (entry->event_y == far_y){
-		    x = entry->event_x;
-		    y = 0;
-		};
-		xcb_warp_pointer(conn, XCB_NONE, screen->root, 0, 0, 0, 0, x, y);
-		if (debug) {
-		    printf("Entry: %d, %d\n", entry->event_x, entry->event_y);
-		    printf("Warp: (%d, %d) to (%d, %d)\n", entry->event_x, entry->event_y, x, y);
-		};
-		break;
+void reset_screen(xcb_generic_event_t *generic_event)
+{
+    xcb_randr_screen_change_notify_event_t *event = (xcb_randr_screen_change_notify_event_t *)generic_event;
+    width = event->width;
+    height = event->height;
+    delete_barriers();
+    create_barriers();
+    if (debug)
+	printf("Configured screens.\n");
+}
 
-	    default:
-		if (event->response_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
-		    change = (xcb_randr_screen_change_notify_event_t *)event;
-		    set_window_shape(change->width, change->height);
-		    far_x = change->width - 1;
-		    far_y = change->height - 1;
-		    if (debug) printf("Screen changed.\n");
-		} else {
-		    printf("Unknown event: %d\n", event->response_type);
-		};
 
-	}
+void event_loop()
+{
+    xcb_generic_event_t *event;
+    xcb_flush(conn);
+
+    while((event = xcb_wait_for_event(conn))) {
+	if (event->response_type == XCB_GE_GENERIC) {
+	    loop_cursor(event); /* we only receive 1 event from xinput */
+	} else if (event->response_type == op_randr + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
+	    reset_screen(event);
+	} else {
+	    printf("Unknown event: %d\n", event->response_type);
+	};
 	xcb_flush(conn);
 	free(event);
     }
-    free(entry);
-}
-
-
-void exit_nicely()
-{
-    xcb_unmap_window(conn, wid);
-    xcb_disconnect(conn);
-    exit(EXIT_SUCCESS);
 }
 
 
@@ -310,18 +237,21 @@
         }
     }
 
-    if (axis == 3) {
-	printf("You specified -x and -y but can only specify one.\n");
-	exit(EXIT_FAILURE);
-    };
+    if (axis == 3)
+	axis = BOTH_AXES;
 
-    conn = xcb_connect(NULL, NULL);
+    dpy = XOpenDisplay(NULL);
+    conn = XGetXCBConnection(dpy);
     if (xcb_connection_has_error(conn))
 	exit(EXIT_FAILURE);
 
     screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
-    configure_randr();
-    setup_window();
+    width = screen->width_in_pixels;
+    height = screen->height_in_pixels;
+    check_xfixes();
+    check_randr();
+    check_xinput();
+    create_barriers();
 
     signal(SIGINT, exit_nicely);
     signal(SIGTERM, exit_nicely);

makefile Modified

@@ -1,6 +1,8 @@
 OUT        = xoop
 SRC 	   = xoop.c
-CFLAGS 	  += -Wall -Wextra -pedantic -lxcb -lxcb-shape -lxcb-randr
+CFLAGS 	  += -Wall -Wextra -pedantic \
+	     -lxcb -lxcb-shape -lxcb-randr -lxcb-xinput -lxcb-xfixes \
+	     -lX11 -lX11-xcb -lXfixes
 PREFIX    ?= /usr/local
 BINPREFIX ?= $(PREFIX)/bin