tAdd multi-monitor support using XRandR extension - glazier - window management experiments
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 2a1185aafa91b236b58db09e727327ffb0f6345c
 (DIR) parent 66f4ae619cd318e730f1bee75a549195751923b0
 (HTM) Author: Willy Goiffon <dev@z3bra.org>
       Date:   Tue,  9 Jun 2020 20:12:52 +0200
       
       Add multi-monitor support using XRandR extension
       
       Diffstat:
         M config.mk                           |       2 +-
         M glazier.c                           |     103 +++++++++++++++++++++++++++++--
       
       2 files changed, 100 insertions(+), 5 deletions(-)
       ---
 (DIR) diff --git a/config.mk b/config.mk
       t@@ -7,4 +7,4 @@ MANDIR = ${PREFIX}/man
        CPPFLAGS = -I./libwm
        CFLAGS = -Wall -Wextra -pedantic -g
        LDFLAGS = -L./libwm ${LIBS}
       -LIBS = -lxcb -lxcb-cursor -lwm
       +LIBS = -lxcb -lxcb-cursor -lxcb-randr -lwm
 (DIR) diff --git a/glazier.c b/glazier.c
       t@@ -2,6 +2,7 @@
        #include <stdlib.h>
        #include <xcb/xcb.h>
        #include <xcb/xcb_cursor.h>
       +#include <xcb/randr.h>
        
        #include "arg.h"
        #include "wm.h"
       t@@ -21,6 +22,10 @@ struct cursor_t {
                int mode;
        };
        
       +struct geom_t {
       +        int x, y, w, h;
       +};
       +
        enum {
                XHAIR_DFLT,
                XHAIR_MOVE,
       t@@ -44,6 +49,11 @@ static int inflate(xcb_window_t, int);
        static int outline(xcb_drawable_t, int, int, int, int);
        static int ev_callback(xcb_generic_event_t *);
        
       +/* XRandR specific functions */
       +static struct geom_t * monitor(int, int);
       +static int crossedge(xcb_window_t);
       +static int snaptoedge(xcb_window_t);
       +
        /* XCB events callbacks */
        static int cb_default(xcb_generic_event_t *);
        static int cb_create(xcb_generic_event_t *);
       t@@ -299,7 +309,6 @@ cb_create(xcb_generic_event_t *ev)
        int
        cb_mapreq(xcb_generic_event_t *ev)
        {
       -        int x, y;
                xcb_map_request_event_t *e;
        
                e = (xcb_map_request_event_t *)ev;
       t@@ -312,9 +321,8 @@ cb_mapreq(xcb_generic_event_t *ev)
                wm_set_focus(e->window);
        
                /* prevent window to pop outside the screen */
       -        x = wm_get_attribute(e->window, ATTR_X);
       -        y = wm_get_attribute(e->window, ATTR_Y);
       -        wm_move(e->window, ABSOLUTE, x, y);
       +        if (crossedge(e->window))
       +                snaptoedge(e->window);
        
                return 0;
        }
       t@@ -664,6 +672,93 @@ ev_callback(xcb_generic_event_t *ev)
                return cb_default(ev);
        }
        
       +/*
       + * Return the geometry of the monitor pointed to by the given coordinates
       + */
       +struct geom_t *
       +monitor(int x, int y)
       +{
       +        static struct geom_t m;
       +        xcb_randr_get_monitors_cookie_t c;
       +        xcb_randr_get_monitors_reply_t *r;
       +        xcb_randr_monitor_info_iterator_t i;
       +
       +        /* get_active: ignore inactive monitors */
       +        c = xcb_randr_get_monitors(conn, scrn->root, 1);
       +        r = xcb_randr_get_monitors_reply(conn, c, NULL);
       +        i = xcb_randr_get_monitors_monitors_iterator(r);
       +
       +        while (i.rem > 0) {
       +                if (x >= i.data->x
       +                 && y >= i.data->y
       +                 && x <= i.data->width + i.data->x
       +                 && y <= i.data->height + i.data->y) {
       +                        m.x = i.data->x;
       +                        m.y = i.data->y;
       +                        m.w = i.data->width;
       +                        m.h = i.data->height;
       +                        return &m;
       +                }
       +                xcb_randr_monitor_info_next(&i);
       +        }
       +
       +        return NULL;
       +}
       +
       +/*
       + * Returns 1 is the given window's geometry crosses the monitor's edge,
       + * and 0 otherwise
       + */
       +int
       +crossedge(xcb_window_t wid)
       +{
       +        int b;
       +        struct geom_t *m, w;
       +
       +        b   = wm_get_attribute(wid, ATTR_B);
       +        w.x = wm_get_attribute(wid, ATTR_X);
       +        w.y = wm_get_attribute(wid, ATTR_Y);
       +        w.w = wm_get_attribute(wid, ATTR_W);
       +        w.h = wm_get_attribute(wid, ATTR_H);
       +        m = monitor(w.x, w.y);
       +
       +        if ((w.x + w.w + 2*b > m->x + m->w)
       +         || (w.y + w.h + 2*b > m->y + m->h))
       +                return 1;
       +        
       +        return 0;
       +}
       +
       +/*
       + * Moves a window so that its border doesn't cross the monitor's edge
       + */
       +int
       +snaptoedge(xcb_window_t wid)
       +{
       +        int tp, b;
       +        struct geom_t *m, w;
       +
       +        b   = wm_get_attribute(wid, ATTR_B);
       +        w.x = wm_get_attribute(wid, ATTR_X);
       +        w.y = wm_get_attribute(wid, ATTR_Y);
       +        w.w = wm_get_attribute(wid, ATTR_W);
       +        w.h = wm_get_attribute(wid, ATTR_H);
       +        m = monitor(w.x, w.y);
       +
       +        tp = 0;
       +
       +        if (w.w + 2*b > m->w) { tp = 1; w.w = m->w - 2*b; }
       +        if (w.h + 2*b > m->h) { tp = 1; w.h = m->h - 2*b; }
       +
       +        if (w.x + w.w + 2*b > m->x + m->w) { tp = 1; w.x = MAX(m->x + b, m->x + m->w - w.w - 2*b); }
       +        if (w.y + w.h + 2*b > m->y + m->h) { tp = 1; w.y = MAX(m->y + b, m->y + m->h - w.h - 2*b); }
       +
       +        if (tp)
       +                wm_teleport(wid, w.x, w.y, w.w, w.h);
       +        
       +        return 0;
       +}
       +
        int
        main (int argc, char *argv[])
        {