tAdd EWMH support through an external tool - glazier - window management experiments
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 0e865c0b44adf838d66f41fb081a5b9a72406c82
 (DIR) parent d30d9c086c242089196344e1a7fd1e88d8965d27
 (HTM) Author: Willy Goiffon <dev@z3bra.org>
       Date:   Sun,  7 Jun 2020 11:43:39 +0200
       
       Add EWMH support through an external tool
       
       Diffstat:
         A ewmh.c                              |     270 +++++++++++++++++++++++++++++++
         A ewmh.h                              |      15 +++++++++++++++
         M makefile                            |      11 +++++++++--
       
       3 files changed, 294 insertions(+), 2 deletions(-)
       ---
 (DIR) diff --git a/ewmh.c b/ewmh.c
       t@@ -0,0 +1,269 @@
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <xcb/xcb.h>
       +#include <xcb/xcb_cursor.h>
       +#include <xcb/xcb_ewmh.h>
       +
       +#include "arg.h"
       +#include "wm.h"
       +
       +#define LEN(x) (sizeof(x)/sizeof(x[0]))
       +
       +struct xatom {
       +        char *name;
       +        xcb_atom_t atom;
       +};
       +
       +enum EWMH_TYPES {
       +        IGNORE,
       +        NORMAL,
       +        POPUP,
       +};
       +
       +enum {
       +        _NET_SUPPORTED,
       +        _NET_CLIENT_LIST,
       +        _NET_CLIENT_LIST_STACKING,
       +        _NET_SUPPORTING_WM_CHECK,
       +        _NET_ACTIVE_WINDOW,
       +        _NET_WM_WINDOW_TYPE,
       +        _NET_WM_WINDOW_TYPE_DESKTOP,
       +        _NET_WM_WINDOW_TYPE_DOCK,
       +        _NET_WM_WINDOW_TYPE_TOOLBAR,
       +        _NET_WM_WINDOW_TYPE_MENU,
       +        _NET_WM_WINDOW_TYPE_UTILITY,
       +        _NET_WM_WINDOW_TYPE_SPLASH,
       +        _NET_WM_WINDOW_TYPE_DIALOG,
       +        _NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
       +        _NET_WM_WINDOW_TYPE_POPUP_MENU,
       +        _NET_WM_WINDOW_TYPE_TOOLTIP,
       +        _NET_WM_WINDOW_TYPE_NOTIFICATION,
       +        _NET_WM_WINDOW_TYPE_COMBO,
       +        _NET_WM_WINDOW_TYPE_DND,
       +        _NET_WM_WINDOW_TYPE_NORMAL,
       +};
       +
       +static void usage(const char *);
       +static int ewmh_init();
       +static int ewmh_supported();
       +static int ewmh_supportingwmcheck();
       +static int ewmh_activewindow(xcb_window_t);
       +static int ewmh_clientlist();
       +static int ewmh_type(xcb_window_t);
       +
       +xcb_connection_t *conn;
       +xcb_screen_t     *scrn;
       +
       +struct xatom ewmh[] = {
       +        [_NET_SUPPORTED]                    = { .name = "_NET_SUPPORTED"                    },
       +        [_NET_CLIENT_LIST]                  = { .name = "_NET_CLIENT_LIST"                  },
       +        [_NET_CLIENT_LIST_STACKING]         = { .name = "_NET_CLIENT_LIST_STACKING"         },
       +        [_NET_SUPPORTING_WM_CHECK]          = { .name = "_NET_SUPPORTING_WM_CHECK"          },
       +        [_NET_ACTIVE_WINDOW]                = { .name = "_NET_ACTIVE_WINDOW"                },
       +        [_NET_WM_WINDOW_TYPE]               = { .name = "_NET_WM_WINDOW_TYPE"               },
       +        [_NET_WM_WINDOW_TYPE_DESKTOP]       = { .name = "_NET_WM_WINDOW_TYPE_DESKTOP"       },
       +        [_NET_WM_WINDOW_TYPE_DOCK]          = { .name = "_NET_WM_WINDOW_TYPE_DOCK"          },
       +        [_NET_WM_WINDOW_TYPE_TOOLBAR]       = { .name = "_NET_WM_WINDOW_TYPE_TOOLBAR"       },
       +        [_NET_WM_WINDOW_TYPE_MENU]          = { .name = "_NET_WM_WINDOW_TYPE_MENU"          },
       +        [_NET_WM_WINDOW_TYPE_UTILITY]       = { .name = "_NET_WM_WINDOW_TYPE_UTILITY"       },
       +        [_NET_WM_WINDOW_TYPE_SPLASH]        = { .name = "_NET_WM_WINDOW_TYPE_SPLASH"        },
       +        [_NET_WM_WINDOW_TYPE_DIALOG]        = { .name = "_NET_WM_WINDOW_TYPE_DIALOG"        },
       +        [_NET_WM_WINDOW_TYPE_DROPDOWN_MENU] = { .name = "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU" },
       +        [_NET_WM_WINDOW_TYPE_POPUP_MENU]    = { .name = "_NET_WM_WINDOW_TYPE_POPUP_MENU"    },
       +        [_NET_WM_WINDOW_TYPE_TOOLTIP]       = { .name = "_NET_WM_WINDOW_TYPE_TOOLTIP"       },
       +        [_NET_WM_WINDOW_TYPE_NOTIFICATION]  = { .name = "_NET_WM_WINDOW_TYPE_NOTIFICATION"  },
       +        [_NET_WM_WINDOW_TYPE_COMBO]         = { .name = "_NET_WM_WINDOW_TYPE_COMBO"         },
       +        [_NET_WM_WINDOW_TYPE_DND]           = { .name = "_NET_WM_WINDOW_TYPE_DND"           },
       +        [_NET_WM_WINDOW_TYPE_NORMAL]        = { .name = "_NET_WM_WINDOW_TYPE_NORMAL"        },
       +};
       +
       +void
       +usage(const char *name)
       +{
       +        fprintf(stderr, "usage: %s [-h]\n", name);
       +}
       +
       +int
       +ewmh_init()
       +{
       +        uint32_t i, n;
       +        xcb_window_t *w;
       +
       +        for (i = 0; i < LEN(ewmh); i++)
       +                ewmh[i].atom = wm_add_atom(ewmh[i].name, strlen(ewmh[i].name));
       +
       +        /* monitor focus events on existing windows */
       +        n = wm_get_windows(scrn->root, &w);
       +        for (i = 0; i < n; i++)
       +                wm_reg_window_event(w[i], XCB_EVENT_MASK_FOCUS_CHANGE);
       +
       +        ewmh_supported();
       +        ewmh_supportingwmcheck();
       +        ewmh_clientlist();
       +
       +        return 0;
       +}
       +
       +int
       +ewmh_supported()
       +{
       +        uint32_t i;
       +        xcb_atom_t supported[LEN(ewmh)];
       +
       +        for (i = 0; i < LEN(ewmh); i++)
       +                supported[i] = ewmh[i].atom;
       +
       +        return wm_set_atom(scrn->root, ewmh[_NET_SUPPORTED].atom, XCB_ATOM_ATOM, i, &supported);
       +}
       +
       +int
       +ewmh_supportingwmcheck()
       +{
       +        int val = 1;
       +        xcb_window_t wid;
       +
       +        wid = xcb_generate_id(conn);
       +
       +        /* dummyest window ever. */
       +        xcb_create_window(conn,
       +                XCB_COPY_FROM_PARENT, wid, scrn->root,
       +                0, 0, 1, 1, 0,                     /* x, y, w, h, border */
       +                XCB_WINDOW_CLASS_INPUT_ONLY,       /* no need for output */
       +                scrn->root_visual,                 /* visual */
       +                XCB_CW_OVERRIDE_REDIRECT, &val);   /* have the WM ignore us */
       +
       +        wm_set_atom(scrn->root, ewmh[_NET_SUPPORTING_WM_CHECK].atom, XCB_ATOM_ATOM, 1, &wid);
       +        wm_set_atom(wid, ewmh[_NET_SUPPORTING_WM_CHECK].atom, XCB_ATOM_ATOM, 1, &wid);
       +
       +        return 0;
       +}
       +
       +int
       +ewmh_activewindow(xcb_window_t wid)
       +{
       +        wm_set_atom(scrn->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 1, &wid);
       +        return 0;
       +}
       +
       +int
       +ewmh_clientlist()
       +{
       +        uint32_t i, c, n;
       +        xcb_window_t *w, *l;
       +
       +        n = wm_get_windows(scrn->root, &w);
       +
       +        l = calloc(n, sizeof(*w));
       +
       +        for (i=0, c=0; i<n; i++)
       +                if (!wm_is_ignored(w[i]) && ewmh_type(w[i]) == NORMAL)
       +                        l[c++] = w[i];
       +
       +        free(w);
       +
       +        wm_set_atom(scrn->root, ewmh[_NET_CLIENT_LIST].atom, XCB_ATOM_WINDOW, c, l);
       +        wm_set_atom(scrn->root, ewmh[_NET_CLIENT_LIST_STACKING].atom, XCB_ATOM_WINDOW, c, l);
       +
       +        free(l);
       +
       +        return 0;
       +}
       +
       +int
       +ewmh_type(xcb_window_t window)
       +{
       +        unsigned long n;
       +        int type = NORMAL; /* treat non-ewmh as normal windows */
       +        xcb_atom_t *atoms;
       +
       +        atoms = wm_get_atom(window, ewmh[_NET_WM_WINDOW_TYPE].atom, XCB_ATOM_ATOM, &n);
       +
       +        /*
       +         * as per the EWMH spec, when multiple types are
       +         * applicable, they must be listed from the most to least
       +         * important.
       +         * To do so, we cycle through them in reverse order, changing
       +         * the window type everytime a known type is encountered.
       +         * Some toolkits like to use toolkit-specific atoms as their
       +         * first value for more appropriate categorization. This function
       +         * only deals with standard EWMH atoms.
       +         */
       +        while (n --> 0) {
       +                if (atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_DESKTOP].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_POPUP_MENU].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_COMBO].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_DND].atom)
       +                        type = IGNORE;
       +
       +                if (atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_MENU].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_DROPDOWN_MENU].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_TOOLTIP].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_NOTIFICATION].atom)
       +                        type = POPUP;
       +
       +                if (atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom
       +                 || atoms[n] == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom)
       +                        type = NORMAL;
       +        }
       +
       +        return type;
       +}
       +
       +int
       +main (int argc, char *argv[])
       +{
       +        int mask;
       +        char *argv0;
       +        xcb_generic_event_t *ev = NULL;
       +
       +        ARGBEGIN {
       +        case 'h':
       +                usage(argv0);
       +                return 0;
       +                break; /* NOTREACHED */
       +        default:
       +                usage(argv0);
       +                return -1;
       +                break; /* NOTREACHED */
       +        } ARGEND;
       +
       +        wm_init_xcb();
       +        wm_get_screen();
       +        ewmh_init();
       +
       +        /* needed to get notified of windows creation */
       +        mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
       +
       +        if (!wm_reg_window_event(scrn->root, mask))
       +                return -1;
       +
       +        for (;;) {
       +                xcb_flush(conn);
       +                ev = xcb_wait_for_event(conn);
       +                if (!ev)
       +                        break;
       +
       +                switch (ev->response_type & ~0x80) {
       +
       +                case XCB_CREATE_NOTIFY:
       +                        wm_reg_window_event(((xcb_create_notify_event_t *)ev)->window, XCB_EVENT_MASK_FOCUS_CHANGE);
       +                        /* FALLTHROUGH */
       +                case XCB_DESTROY_NOTIFY:
       +                        ewmh_clientlist();
       +                        break;
       +
       +                case XCB_FOCUS_IN:
       +                        ewmh_activewindow(((xcb_focus_in_event_t *)ev)->event);
       +                        break;
       +                }
       +
       +                free(ev);
       +        }
       +
       +        return wm_kill_xcb();
       +}
       +\ No newline at end of file
 (DIR) diff --git a/ewmh.h b/ewmh.h
       t@@ -0,0 +1,15 @@
       +enum EWMH_TYPES {
       +        IGNORE,
       +        NORMAL,
       +        POPUP,
       +        FULLSCREEN,
       +};
       +
       +int ewmh_init();
       +int ewmh_supported();
       +int ewmh_supportingwmcheck();
       +int ewmh_activewindow(xcb_window_t);
       +int ewmh_clientlist();
       +int ewmh_type(xcb_window_t);
       +int ewmh_message(xcb_window_t, xcb_atom_t, xcb_client_message_data_t);
       +int ewmh_fullscreen(xcb_window_t, int);
 (DIR) diff --git a/makefile b/makefile
       t@@ -1,18 +1,25 @@
        include config.mk
        
       +all: glazier ewmh
       +
        glazier: glazier.o libwm/libwm.a
       +ewmh: ewmh.o libwm/libwm.a
       +
        glazier.o: glazier.c config.h
        
        config.h: config.def.h
                cp config.def.h config.h
        
        clean:
       -        rm -f glazier *.o
       +        rm -f glazier ewmh *.o
        
       -install: glazier
       +install: glazier ewmh
                mkdir -p $(DESTDIR)$(PREFIX)/bin
                cp glazier $(DESTDIR)$(PREFIX)/bin/glazier
       +        cp ewmh $(DESTDIR)$(PREFIX)/bin/ewmh
                chmod 755 $(DESTDIR)$(PREFIX)/bin/glazier
       +        chmod 755 $(DESTDIR)$(PREFIX)/bin/ewmh
        
        uninstall:
                rm $(DESTDIR)$(PREFIX)/bin/glazier
       +        rm $(DESTDIR)$(PREFIX)/bin/ewmh