-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