events.c (10283B)
1 /* 2 * kbgwm, a sucklessy floating window manager 3 * Copyright (c) 2020-2021, Kebigon <git@kebigon.xyz> 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "events.h" 19 20 #include <assert.h> 21 #include <stdio.h> 22 #include <xcb/randr.h> 23 24 #include "client.h" 25 #include "kbgwm.h" 26 #include "list.h" 27 #include "log.h" 28 #include "monitor.h" 29 #include "xcbutils.h" 30 31 #define CLEANMASK(mask) (mask & ~(numlockmask | XCB_MOD_MASK_LOCK)) 32 33 #define EVENT_HANDLERS_SIZE XCB_CONFIGURE_REQUEST + 1 34 static void (*event_handlers[EVENT_HANDLERS_SIZE])(xcb_generic_event_t *); 35 36 extern int_least16_t randr_base; 37 38 static void handle_key_press(xcb_generic_event_t *e) 39 { 40 xcb_key_press_event_t *event = (xcb_key_press_event_t *)e; 41 xcb_keysym_t keysym = xcb_get_keysym(event->detail); 42 43 for (uint_fast8_t i = 0; i < keys_length; i++) 44 { 45 if (keysym == keys[i].keysym && CLEANMASK(keys[i].modifiers) == CLEANMASK(event->state)) 46 { 47 keys[i].func(&keys[i].arg); 48 break; 49 } 50 } 51 } 52 53 static void handle_button_press(xcb_generic_event_t *e) 54 { 55 xcb_button_press_event_t *event = (xcb_button_press_event_t *)e; 56 57 // Click on the root window 58 if (event->event == event->root && event->child == 0) 59 return; // Nothing to be done 60 61 xcb_window_t window = event->event == event->root ? event->child : event->event; 62 struct item *focused = list_head(&workspaces[current_workspace]); 63 64 // The window clicked is not the one in focus, we have to focus it 65 if (focused == NULL || window != ((struct client *)focused->data)->id) 66 { 67 struct item *to_focus = list_find_client(&workspaces[current_workspace], window); 68 assert(to_focus != NULL); 69 70 focus_unfocus(); 71 list_move_to_head(&workspaces[current_workspace], to_focus); 72 focus_apply(); 73 } 74 75 for (uint_fast8_t i = 0; i < buttons_length; i++) 76 { 77 if (event->detail == buttons[i].keysym && 78 CLEANMASK(buttons[i].modifiers) == CLEANMASK(event->state)) 79 { 80 previous_x = event->root_x; 81 previous_y = event->root_y; 82 83 buttons[i].func(&buttons[i].arg); 84 break; 85 } 86 } 87 } 88 89 static void handle_button_release(__attribute__((unused)) xcb_generic_event_t *event) 90 { 91 // We were not moving or resizing the focused client 92 if (!moving && !resizing) 93 return; // Nothing to be done 94 95 xcb_ungrab_pointer(c, XCB_CURRENT_TIME); 96 xcb_flush(c); 97 98 moving = false; 99 resizing = false; 100 101 struct item *focused = list_head(&workspaces[current_workspace]); 102 if (focused != NULL) 103 client_update_monitor(focused->data); 104 } 105 106 static void handle_motion_notify(xcb_generic_event_t *e) 107 { 108 xcb_motion_notify_event_t *event = (xcb_motion_notify_event_t *)e; 109 110 struct item *focused = list_head(&workspaces[current_workspace]); 111 assert(moving || resizing); 112 assert(focused != NULL); 113 114 struct client *client = focused->data; 115 assert(client->id != screen->root); 116 117 if (client->maximized) 118 client_unmaximize(client); 119 120 int16_t diff_x = event->root_x - previous_x; 121 int16_t diff_y = event->root_y - previous_y; 122 previous_x = event->root_x; 123 previous_y = event->root_y; 124 125 if (moving) 126 { 127 client->x += diff_x; 128 client->y += diff_y; 129 client_sanitize_position(client); 130 131 uint32_t values[2] = {client->x, client->y}; 132 xcb_configure_window(c, client->id, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values); 133 } 134 else if (resizing) 135 { 136 client->width += diff_x; 137 client->height += diff_y; 138 client_sanitize_dimensions(client); 139 140 uint32_t values[2] = {client->width, client->height}; 141 xcb_configure_window(c, client->id, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, 142 values); 143 } 144 145 xcb_flush(c); 146 } 147 148 static void handle_destroy_notify(xcb_generic_event_t *e) 149 { 150 xcb_destroy_notify_event_t *event = (xcb_destroy_notify_event_t *)e; 151 152 client_remove_all_workspaces(event->window); 153 if (focused_client != NULL) 154 focus_apply(); 155 } 156 157 static void handle_unmap_notify(xcb_generic_event_t *e) 158 { 159 xcb_unmap_notify_event_t *event = (xcb_unmap_notify_event_t *)e; 160 struct client *client = client_find_all_workspaces(event->window); 161 162 // We don't know this client 163 if (client == NULL) 164 return; // Nothing to be done 165 166 // true if this came from a SendEvent request 167 bool send_event = event->response_type & 0x80; 168 if (!send_event) 169 return; // Nothing to be done 170 171 client_remove_all_workspaces(event->window); 172 if (focused_client != NULL) 173 focus_apply(); 174 } 175 176 static void handle_map_request(xcb_generic_event_t *e) 177 { 178 xcb_map_request_event_t *event = (xcb_map_request_event_t *)e; 179 180 client_create(event->window); 181 } 182 183 static void handle_configure_notify(xcb_generic_event_t *e) 184 { 185 xcb_configure_notify_event_t *event = (xcb_configure_notify_event_t *)e; 186 187 // When updating the monitors configuration with randr, we get a notification that the root 188 // window changed 189 if (event->window == screen->root) 190 { 191 if (event->width != screen->width_in_pixels) 192 screen->width_in_pixels = event->width; 193 if (event->height != screen->height_in_pixels) 194 screen->height_in_pixels = event->height; 195 } 196 } 197 198 static void handle_configure_request(xcb_generic_event_t *e) 199 { 200 xcb_configure_request_event_t *event = (xcb_configure_request_event_t *)e; 201 struct client *client = client_find_all_workspaces(event->window); 202 203 if (client != NULL) 204 { 205 // The client is maximized 206 if (client->maximized) 207 return; // Nothing to be done 208 209 if (event->value_mask & XCB_CONFIG_WINDOW_X) 210 client->x = event->x; 211 if (event->value_mask & XCB_CONFIG_WINDOW_Y) 212 client->y = event->y; 213 if (event->value_mask & XCB_CONFIG_WINDOW_WIDTH) 214 client->width = event->width; 215 if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT) 216 client->height = event->height; 217 218 // Ignored: XCB_CONFIG_WINDOW_BORDER_WIDTH, XCB_CONFIG_WINDOW_SIBLING, 219 // XCB_CONFIG_WINDOW_STACK_MODE 220 221 client_sanitize_position(client); 222 client_sanitize_dimensions(client); 223 224 uint32_t values[4] = {client->x, client->y, client->width, client->height}; 225 xcb_configure_window(c, client->id, 226 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | 227 XCB_CONFIG_WINDOW_HEIGHT, 228 values); 229 xcb_flush(c); 230 } 231 232 // We don't know the client -> apply the requested change 233 else 234 { 235 uint16_t value_mask = 0; 236 uint32_t value_list[7]; 237 int8_t i = 0; 238 239 if (event->value_mask & XCB_CONFIG_WINDOW_X) 240 { 241 value_mask |= XCB_CONFIG_WINDOW_X; 242 value_list[i++] = event->x; 243 } 244 if (event->value_mask & XCB_CONFIG_WINDOW_Y) 245 { 246 value_mask |= XCB_CONFIG_WINDOW_Y; 247 value_list[i++] = event->y; 248 } 249 if (event->value_mask & XCB_CONFIG_WINDOW_WIDTH) 250 { 251 value_mask |= XCB_CONFIG_WINDOW_WIDTH; 252 value_list[i++] = event->width; 253 } 254 if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT) 255 { 256 value_mask |= XCB_CONFIG_WINDOW_HEIGHT; 257 value_list[i++] = event->height; 258 } 259 if (event->value_mask & XCB_CONFIG_WINDOW_SIBLING) 260 { 261 value_mask |= XCB_CONFIG_WINDOW_SIBLING; 262 value_list[i++] = event->sibling; 263 } 264 if (event->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) 265 { 266 value_mask |= XCB_CONFIG_WINDOW_STACK_MODE; 267 value_list[i++] = event->stack_mode; 268 } 269 if (i != 0) 270 { 271 xcb_configure_window(c, event->window, value_mask, value_list); 272 xcb_flush(c); 273 } 274 } 275 } 276 277 void handle_event(xcb_generic_event_t *event) 278 { 279 if (randr_base > -1 && event->response_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) 280 { 281 monitors_refresh_config(); 282 return; 283 } 284 285 void (*event_handler)(xcb_generic_event_t *) = event_handlers[event->response_type & ~0x80]; 286 if (event_handler == NULL) 287 LOG_INFO_VA("Received unhandled event, response type %d", event->response_type & ~0x80); 288 else 289 event_handler(event); 290 } 291 292 void setup_events() 293 { 294 /* 295 * Initialize event_handlers 296 */ 297 298 for (uint_fast8_t i = 0; i != EVENT_HANDLERS_SIZE; i++) 299 event_handlers[i] = NULL; 300 301 event_handlers[XCB_KEY_PRESS] = handle_key_press; 302 event_handlers[XCB_BUTTON_PRESS] = handle_button_press; 303 event_handlers[XCB_BUTTON_RELEASE] = handle_button_release; 304 event_handlers[XCB_MOTION_NOTIFY] = handle_motion_notify; 305 event_handlers[XCB_DESTROY_NOTIFY] = handle_destroy_notify; 306 event_handlers[XCB_UNMAP_NOTIFY] = handle_unmap_notify; 307 event_handlers[XCB_MAP_REQUEST] = handle_map_request; 308 event_handlers[XCB_CONFIGURE_NOTIFY] = handle_configure_notify; 309 event_handlers[XCB_CONFIGURE_REQUEST] = handle_configure_request; 310 311 /* 312 * Register X11 events 313 */ 314 315 uint32_t values[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_STRUCTURE_NOTIFY | 316 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY}; 317 318 xcb_change_window_attributes_checked(c, screen->root, XCB_CW_EVENT_MASK, values); 319 320 for (uint_fast8_t i = 0; i != keys_length; i++) 321 xcb_register_key_events(keys[i]); 322 323 xcb_flush(c); 324 }