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

-rw-r--r-- xoop.c


      1 /*
      2 Copyright (c) 2020, Matt Colligan All rights reserved.
      3 
      4 Permission is hereby granted, free of charge, to any person obtaining a copy
      5 of this software and associated documentation files (the "Software"), to deal
      6 in the Software without restriction, including without limitation the rights
      7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 copies of the Software, and to permit persons to whom the Software is
      9 furnished to do so, subject to the following conditions:
     10 
     11 The above copyright notice and this permission notice shall be included in
     12 all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     20 SOFTWARE.
     21 */
     22 
     23 #include <signal.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <unistd.h>
     27 #include <xcb/randr.h>
     28 #include <xcb/xcb.h>
     29 #include <xcb/xcb_event.h>
     30 #include <xcb/xfixes.h>
     31 #include <xcb/xinput.h>
     32 
     33 #define PROGNAME "xoop"
     34 
     35 xcb_connection_t	*conn;
     36 xcb_screen_t		*screen;
     37 xcb_input_device_id_t	deviceid;
     38 xcb_xfixes_barrier_t    barriers[4];
     39 
     40 enum axis_t { NO_AXIS = 0, X_AXIS = 1 << 0, Y_AXIS = 1 << 1, BOTH_AXES = X_AXIS | Y_AXIS };
     41 enum axis_t axis = NO_AXIS;
     42 
     43 int debug = 0;
     44 uint8_t op_randr = 0;
     45 uint8_t op_xfixes = 0;
     46 uint8_t op_xinput = 0;
     47 int16_t width, height;
     48 
     49 
     50 void exit_angrily(char msg[])
     51 {
     52     fprintf(stderr, "%s", msg);
     53     xcb_disconnect(conn);
     54     exit(EXIT_FAILURE);
     55 }
     56 
     57 
     58 void create_barrier(xcb_xfixes_barrier_t *barrier, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
     59 {
     60     xcb_generic_error_t *error;
     61     *barrier = xcb_generate_id(conn);
     62     xcb_void_cookie_t cookie = xcb_xfixes_create_pointer_barrier_checked(
     63 	conn, *barrier, screen->root, x1, y1, x2, y2, 0, 0, NULL
     64     );
     65     if ((error = xcb_request_check(conn, cookie))) {
     66 	free(error);
     67 	exit_angrily("Could not create barriers.\n");
     68     }
     69     free(error);
     70 }
     71 
     72 
     73 void create_barriers()
     74 {
     75     uint16_t x1 = 0;
     76     uint16_t y1 = 0;
     77     uint16_t x2 = width;
     78     uint16_t y2 = height;
     79 
     80     if (debug)
     81 	printf("Creating barriers: %i %i %i %i\n", x1, y1, x2, y2);
     82 
     83     if (axis & X_AXIS) {
     84 	create_barrier(&barriers[0], x1, y1, x1, y2);
     85 	create_barrier(&barriers[1], x2, y1, x2, y2);
     86     };
     87 
     88     if (axis & Y_AXIS) {
     89 	create_barrier(&barriers[2], x1, y1, x2, y1);
     90 	create_barrier(&barriers[3], x1, y2, x2, y2);
     91     };
     92 }
     93 
     94 
     95 void delete_barriers()
     96 {
     97     for (int i = 0; i < 4; i++) {
     98 	if (debug)
     99 	    printf("Deleting barrier: %i/4\n", i + 1);
    100 
    101 	xcb_xfixes_delete_pointer_barrier(conn, barriers[i]);
    102     }
    103 }
    104 
    105 
    106 void exit_nicely(int sig)
    107 {
    108     (void)(sig); // Unused
    109     delete_barriers();
    110     xcb_disconnect(conn);
    111     exit(EXIT_SUCCESS);
    112 }
    113 
    114 
    115 void check_xfixes()
    116 {
    117     xcb_xfixes_query_version(conn, 5, 0);
    118     const xcb_query_extension_reply_t *ext = xcb_get_extension_data(conn, &xcb_xfixes_id);
    119     if (!ext || !ext->present) {
    120         printf("XFixes extension not available.\n");
    121 	exit(EXIT_FAILURE);
    122     }
    123     op_xfixes = ext->first_event;
    124 }
    125 
    126 
    127 void check_randr()
    128 {
    129     const xcb_query_extension_reply_t *ext = xcb_get_extension_data(conn, &xcb_randr_id);
    130     if (!ext || !ext->present)
    131         exit_angrily("Randr extension not available.\n");
    132     op_randr = ext->first_event;
    133     xcb_randr_select_input(conn, screen->root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
    134 }
    135 
    136 
    137 void check_xinput()
    138 {
    139     const xcb_query_extension_reply_t *ext = xcb_get_extension_data(conn, &xcb_input_id);
    140     if (!ext || !ext->present)
    141         exit_angrily("XInput extension not available.\n");
    142     struct {
    143         xcb_input_event_mask_t head;
    144         xcb_input_xi_event_mask_t mask;
    145     } mask;
    146     mask.head.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
    147     mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t);
    148     mask.mask = XCB_INPUT_XI_EVENT_MASK_BARRIER_HIT;
    149     xcb_input_xi_select_events(conn, screen->root, 1, &mask.head);
    150 }
    151 
    152 
    153 void loop_cursor(xcb_generic_event_t *generic_event)
    154 {
    155     xcb_input_barrier_hit_event_t *event = (xcb_input_barrier_hit_event_t *)generic_event;
    156     int16_t x = 0;
    157     int16_t y = 0;
    158     int32_t far_x = width - 1;
    159     int32_t far_y = height - 1;
    160     int32_t ev_x = (int32_t)(event->root_x / (double)UINT16_MAX);
    161     int32_t ev_y = (int32_t)(event->root_y / (double)UINT16_MAX);
    162 
    163     if (axis & X_AXIS && ev_x == 0) {
    164       x = far_x;
    165       y = ev_y;
    166     } else if (axis & Y_AXIS && ev_y == 0){
    167       x = ev_x;
    168       y = far_y;
    169     } else if (axis & X_AXIS && ev_x == far_x){
    170       y = ev_y;
    171     } else if (axis & Y_AXIS && ev_y == far_y){
    172       x = ev_x;
    173     };
    174     xcb_warp_pointer(conn, XCB_NONE, screen->root, 0, 0, 0, 0, x, y);
    175 
    176     if (debug)
    177 	printf("Warp: (%i, %i) to (%i, %i)\n", ev_x, ev_y, x, y);
    178 }
    179 
    180 
    181 void reset_screen(xcb_generic_event_t *generic_event)
    182 {
    183     xcb_randr_screen_change_notify_event_t *event = (xcb_randr_screen_change_notify_event_t *)generic_event;
    184     width = event->width;
    185     height = event->height;
    186     delete_barriers();
    187     create_barriers();
    188     if (debug)
    189 	printf("Configured screens.\n");
    190 }
    191 
    192 
    193 void event_loop()
    194 {
    195     xcb_generic_event_t *event;
    196     xcb_flush(conn);
    197 
    198     while((event = xcb_wait_for_event(conn))) {
    199 	if (event->response_type == XCB_GE_GENERIC) {
    200 	    loop_cursor(event); /* we only receive 1 event from xinput */
    201 	} else if (event->response_type == op_randr + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
    202 	    reset_screen(event);
    203 	} else {
    204 	    printf("Unknown event: %d\n", event->response_type);
    205 	};
    206 	xcb_flush(conn);
    207 	free(event);
    208     }
    209 }
    210 
    211 
    212 void print_help()
    213 {
    214     printf(
    215 	PROGNAME " [-h|-f|-d]\n"
    216 	"\n"
    217 	"    -h    print help\n"
    218 	"    -x    xoop only the x axis\n"
    219 	"    -y    xoop only the y axis\n"
    220 	"    -f    fork\n"
    221 	"    -d    print debug information\n"
    222     );
    223 }
    224 
    225 
    226 int main(int argc, char *argv[])
    227 {
    228     int opt;
    229     int to_fork = 0;
    230     int pid;
    231 
    232     while ((opt = getopt(argc, argv, "hfdxy")) != -1) {
    233         switch (opt) {
    234 	    case 'h':
    235 		print_help();
    236 		exit(EXIT_SUCCESS);
    237 	    case 'f':
    238 		to_fork = 1;
    239 		break;
    240 	    case 'd':
    241 		debug = 1;
    242 		break;
    243 	    case 'x':
    244     axis |= X_AXIS;
    245 		break;
    246 	    case 'y':
    247     axis |= Y_AXIS;
    248 		break;
    249         }
    250     }
    251 
    252     if (axis == NO_AXIS)
    253 	axis = BOTH_AXES;
    254 
    255     conn = xcb_connect(NULL, NULL);
    256     if (xcb_connection_has_error(conn))
    257 	exit(EXIT_FAILURE);
    258 
    259     screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
    260     width = screen->width_in_pixels;
    261     height = screen->height_in_pixels;
    262     check_xfixes();
    263     check_randr();
    264     check_xinput();
    265     create_barriers();
    266 
    267     signal(SIGINT, exit_nicely);
    268     signal(SIGTERM, exit_nicely);
    269     signal(SIGHUP, exit_nicely);
    270 
    271     if (to_fork) {
    272 	pid = fork();
    273 	if (pid > 0) {
    274 	    exit(EXIT_SUCCESS);
    275 	} else if (pid < 0) {
    276 	    exit(EXIT_FAILURE);
    277 	}
    278     }
    279 
    280     event_loop();
    281 
    282     exit_nicely(0);
    283     return 0;
    284 }
    285