Loop your X cursor around the screen 👉😎👉
git clone https://mcol.xyz/code/xoop
Log | Files | Refs | README | LICENSE

commit e64ec0bd216a7cdb5deca36526c2eecf38b3b32d
parent 0fc77ea1946a0563d7242d781a34566c18d2ed44
Author: mcol <mcol@posteo.net>
Date:   Tue,  6 Oct 2020 01:52:07 +0100

rewrite using Xlib's XFixesCreatePointerBarrier instead of a window

Diffstat:
Mmakefile | 4+++-
Mxoop.c | 336+++++++++++++++++++++++++++++++------------------------------------------------
2 files changed, 136 insertions(+), 204 deletions(-)

diff --git a/makefile b/makefile @@ -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 diff --git a/xoop.c b/xoop.c @@ -20,7 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -29,244 +28,172 @@ SOFTWARE. #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 @@ int main(int argc, char *argv[]) } } - 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);