kbgwm

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

kbgwm.c (9521B)


      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 "kbgwm.h"
     19 #include "events.h"
     20 #include "list.h"
     21 #include "log.h"
     22 #include "monitor.h"
     23 #include "xcbutils.h"
     24 #include <X11/keysym.h>
     25 #include <assert.h>
     26 #include <inttypes.h>
     27 #include <math.h>
     28 #include <stdint.h>
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <unistd.h>
     32 #include <xcb/xcb.h>
     33 #include <xcb/xcb_icccm.h>
     34 
     35 #include "config.h"
     36 
     37 bool running = true;
     38 bool moving = false;
     39 bool resizing = false;
     40 xcb_connection_t *c;
     41 xcb_screen_t *screen;
     42 uint_least16_t previous_x;
     43 uint_least16_t previous_y;
     44 uint16_t numlockmask = 0;
     45 xcb_atom_t wm_protocols;
     46 xcb_atom_t wm_delete_window;
     47 
     48 uint_fast8_t current_workspace = 0;
     49 struct item *workspaces[NB_WORKSPACES];
     50 
     51 void mousemove(__attribute__((unused)) const union Arg *arg)
     52 {
     53     LOG_DEBUG("=======[ user action: mousemove ]=======");
     54     moving = true;
     55 
     56     xcb_grab_pointer(
     57         c, 0, screen->root, XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_BUTTON_RELEASE,
     58         XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root, XCB_NONE, XCB_CURRENT_TIME);
     59     xcb_flush(c);
     60 }
     61 
     62 void mouseresize(__attribute__((unused)) const union Arg *arg)
     63 {
     64     LOG_DEBUG("=======[ user action: mouseresize ]=======");
     65     resizing = true;
     66 
     67     xcb_grab_pointer(
     68         c, 0, screen->root, XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_BUTTON_RELEASE,
     69         XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root, XCB_NONE, XCB_CURRENT_TIME);
     70     xcb_flush(c);
     71 }
     72 
     73 void eventLoop()
     74 {
     75     xcb_generic_event_t *event;
     76 
     77     while (running)
     78     {
     79         if ((event = xcb_wait_for_event(c)) != NULL)
     80         {
     81             log_debug_event(event);
     82 
     83             handle_event(event);
     84 
     85             free(event);
     86             LOG_DEBUG("=======[ event: DONE ]=======\n");
     87         }
     88     }
     89 }
     90 
     91 void start(const union Arg *arg)
     92 {
     93     LOG_DEBUG("=======[ user action: start ]=======");
     94     LOG_DEBUG_VA("cmd %s", arg->cmd[0]);
     95 
     96     if (fork() == 0)
     97     {
     98         // Child process
     99         setsid();
    100 
    101         if (execvp((char *)arg->cmd[0], (char **)arg->cmd) == -1)
    102         {
    103             perror(arg->cmd[0]);
    104             exit(-1);
    105         }
    106     }
    107 }
    108 
    109 /*
    110  * Focus
    111  */
    112 
    113 void focus_apply()
    114 {
    115     struct item *item = list_head(&workspaces[current_workspace]);
    116     assert(item != NULL);
    117 
    118     struct client *client = item->data;
    119 
    120     // We change the color of the focused client
    121     xcb_change_window_attributes(c, client->id, XCB_CW_BORDER_PIXEL,
    122                                  (uint32_t[]){0xFF000000 | FOCUS_COLOR});
    123 
    124     // Raise the window so it is on top
    125     xcb_configure_window(c, client->id, XCB_CONFIG_WINDOW_STACK_MODE,
    126                          (uint32_t[]){XCB_STACK_MODE_ABOVE});
    127 
    128     // Set the keyboard on the focused window
    129     xcb_set_input_focus(c, XCB_INPUT_FOCUS_POINTER_ROOT, client->id, XCB_CURRENT_TIME);
    130     client_grab_buttons(client, true);
    131     xcb_flush(c);
    132 
    133     LOG_DEBUG("focus_apply: done");
    134 }
    135 
    136 // Focus the next client in the current workspace list
    137 // arg->b : reverse mode
    138 void focus_next(const union Arg *arg)
    139 {
    140     LOG_DEBUG("=======[ user action: focus_next ]=======");
    141     struct item **workspace = &workspaces[current_workspace];
    142 
    143     // No clients in the current workspace list
    144     // Only one client in the current workspace list
    145     if (list_is_empty(workspace) || list_head(workspace)->next == NULL)
    146         return; // Nothing to be done
    147 
    148     focus_unfocus();
    149 
    150     if (arg->b)
    151         list_move_to_head(workspace, list_tail(workspace));
    152     else
    153         list_move_to_tail(workspace, list_head(workspace));
    154 
    155     focus_apply();
    156 }
    157 
    158 // Remove the focus from the current client
    159 void focus_unfocus()
    160 {
    161     struct item *item = list_head(&workspaces[current_workspace]);
    162 
    163     // No client are focused
    164     if (item == NULL)
    165         return; // Nothing to be done
    166 
    167     struct client *client = item->data;
    168 
    169     // Change the border color to UNFOCUS_COLOR
    170     xcb_change_window_attributes(c, client->id, XCB_CW_BORDER_PIXEL,
    171                                  (uint32_t[]){0xFF000000 | UNFOCUS_COLOR});
    172     client_grab_buttons(client, false);
    173 }
    174 
    175 /*
    176  * Handling events from the event loop
    177  */
    178 
    179 void quit(__attribute__((unused)) const union Arg *arg)
    180 {
    181     LOG_DEBUG("=======[ user action: quit ]=======");
    182     running = false;
    183 }
    184 
    185 /*
    186  * Setup
    187  */
    188 
    189 // Retrieve the numlock keycode
    190 void setup_keyboard()
    191 {
    192     xcb_get_modifier_mapping_reply_t *reply =
    193         xcb_get_modifier_mapping_reply(c, xcb_get_modifier_mapping_unchecked(c), NULL);
    194     if (!reply)
    195     {
    196         LOG_ERROR("Unable to retrieve midifier mapping");
    197         exit(-1);
    198     }
    199 
    200     xcb_keycode_t *modmap = xcb_get_modifier_mapping_keycodes(reply);
    201     if (!modmap)
    202     {
    203         LOG_ERROR("Unable to retrieve midifier mapping keycodes");
    204         exit(-1);
    205     }
    206 
    207     xcb_keycode_t *numlock = xcb_get_keycodes(XK_Num_Lock);
    208 
    209     // Not sure why 8, I looked at both dwm and 2bwm, and they both do the same
    210     for (uint_fast8_t i = 0; i < 8; i++)
    211     {
    212         for (uint_fast8_t j = 0; j < reply->keycodes_per_modifier; j++)
    213         {
    214             xcb_keycode_t keycode = modmap[i * reply->keycodes_per_modifier + j];
    215 
    216             if (keycode == *numlock)
    217             {
    218                 numlockmask = (1 << i);
    219                 LOG_DEBUG_VA("numlock is %d", keycode);
    220             }
    221         }
    222     }
    223 
    224     free(numlock);
    225     free(reply);
    226 }
    227 
    228 /*
    229  * Workspaces
    230  */
    231 
    232 void workspace_change(const union Arg *arg)
    233 {
    234     LOG_DEBUG("=======[ user action: workspace_change ]=======");
    235     LOG_DEBUG_VA("i=%d", arg->i);
    236 
    237     workspace_set(arg->i);
    238 }
    239 
    240 void workspace_next(__attribute__((unused)) const union Arg *arg)
    241 {
    242     LOG_DEBUG("=======[ user action: workspace_next ]=======");
    243 
    244     workspace_set(current_workspace + 1 == workspaces_length ? 0 : current_workspace + 1);
    245 }
    246 
    247 void workspace_previous(__attribute__((unused)) const union Arg *arg)
    248 {
    249     LOG_DEBUG("=======[ user action: workspace_previous ]=======");
    250     workspace_set(current_workspace == 0 ? workspaces_length - 1 : current_workspace - 1);
    251 }
    252 
    253 void workspace_send(const union Arg *arg)
    254 {
    255     LOG_DEBUG("=======[ user action: workspace_send ]=======");
    256     LOG_DEBUG_VA("i=%d", arg->i);
    257     log_debug_globals();
    258 
    259     uint_fast8_t new_workspace = arg->i;
    260 
    261     if (current_workspace == new_workspace || list_is_empty(&workspaces[current_workspace]))
    262         return; // Nothing to be done
    263 
    264     struct client *client = client_remove();
    265     client_add_workspace(client, new_workspace);
    266 
    267     xcb_unmap_window(c, client->id);
    268     xcb_flush(c);
    269     LOG_DEBUG("workspace_send: done");
    270 }
    271 
    272 void workspace_set(uint_fast8_t new_workspace)
    273 {
    274     LOG_DEBUG_VA("workspace_set: old=%d new=%d", current_workspace, new_workspace);
    275 
    276     if (current_workspace == new_workspace)
    277         return; // Nothing to be done
    278 
    279     struct item *item;
    280 
    281     // Unmap the clients of the current workspace (if any)
    282     if ((item = list_head(&workspaces[current_workspace])) != NULL)
    283     {
    284         do
    285         {
    286             xcb_unmap_window(c, ((struct client *)item->data)->id);
    287         } while ((item = item->next) != NULL);
    288     }
    289 
    290     // Map the clients of the new workspace (if any)
    291     if ((item = list_head(&workspaces[new_workspace])) != NULL)
    292     {
    293         do
    294         {
    295             xcb_map_window(c, ((struct client *)item->data)->id);
    296         } while ((item = item->next) != NULL);
    297     }
    298 
    299     xcb_flush(c);
    300     current_workspace = new_workspace;
    301 
    302     if (!list_is_empty(&workspaces[current_workspace]))
    303         focus_apply();
    304 
    305     LOG_DEBUG("workspace_set: done");
    306 }
    307 
    308 /*
    309  * Main
    310  */
    311 
    312 int main(void)
    313 {
    314     /*
    315      * displayname = NULL -> use DISPLAY environment variable
    316      */
    317     int screenp;
    318     c = xcb_connect(NULL, &screenp);
    319 
    320     if (xcb_connection_has_error(c))
    321     {
    322         LOG_ERROR_VA("xcb_connect failed: %d", xcb_connection_has_error(c));
    323         exit(1);
    324     }
    325 
    326     /*
    327      * Find the screen
    328      */
    329     xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(c));
    330 
    331     for (int i = 0; i < screenp; ++i)
    332     {
    333         xcb_screen_next(&iter);
    334     }
    335 
    336     screen = iter.data;
    337 
    338     if (!screen)
    339     {
    340         xcb_disconnect(c);
    341         perror("screen not found");
    342         exit(1);
    343     }
    344 
    345     xcb_flush(c);
    346 
    347     /*
    348      * Initialize variables
    349      */
    350 
    351     for (uint_fast8_t i = 0; i != workspaces_length; i++)
    352         workspaces[i] = NULL;
    353 
    354     wm_protocols = xcb_get_atom(WM_PROTOCOLS);
    355     wm_delete_window = xcb_get_atom(WM_DELETE_WINDOW);
    356 
    357     setup_keyboard();
    358     setup_monitors();
    359     setup_clients();
    360     setup_events();
    361 
    362     // Event loop
    363     eventLoop();
    364 
    365     for (uint_fast8_t i = 0; i != workspaces_length; i++)
    366     {
    367         while (workspaces[i] != NULL)
    368         {
    369             struct client *client = client_remove_workspace(i);
    370             free(client);
    371         }
    372     }
    373 
    374     xcb_disconnect(c);
    375 
    376     return (0);
    377 }