kbgwm

sucklessy floating window manager
git clone https://git.neuralcrash.com/kbgwm.git
Log | Files | Refs | README | LICENSE

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 }