tMerge branch 'backpixel' - glazier - window management experiments
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Submodules
(DIR) README
(DIR) LICENSE
---
(DIR) commit cd038ec917b7d5b013ca27608604e8d7d17b7119
(DIR) parent c44c36f74277da16a244fce332986df3696f66b8
(HTM) Author: Willy Goiffon <dev@z3bra.org>
Date: Thu, 1 Jul 2021 09:27:14 +0200
Merge branch 'backpixel'
Diffstat:
M config.def.h | 7 ++++---
M config.mk | 2 +-
M glazier.c | 161 ++++++++++++++++++++++++++-----
3 files changed, 144 insertions(+), 26 deletions(-)
---
(DIR) diff --git a/config.def.h b/config.def.h
t@@ -10,9 +10,10 @@ char *xhair[] = {
int modifier = XCB_MOD_MASK_1;
/* window borders and titlebar */
-int border = 2;
-int border_color = 0x666666;
-int border_color_active = 0xdeadca7;
+int border = 8;
+int inner_border = 2;
+uint32_t border_color = 0x666666;
+uint32_t border_color_active = 0xdeadca7;
/* move/resize step amound in pixels */
int move_step = 8;
(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 -lxcb-randr -lwm
+LIBS = -lxcb -lxcb-cursor -lxcb-image -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/xcb_image.h>
#include <xcb/randr.h>
#include "arg.h"
t@@ -41,6 +42,8 @@ enum {
void usage(char *);
static int takeover();
static int adopt(xcb_window_t);
+static uint32_t backpixel(xcb_window_t);
+static int paint(xcb_window_t);
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 *);
t@@ -140,6 +143,109 @@ adopt(xcb_window_t wid)
}
/*
+ * Return the color of the pixel in one of the window corners.
+ * Each corner is tested in a clockwise fashion until an uncovered region
+ * is found. When such pixel is found, the color is returned.
+ * If no color is found a default of border_color is returned.
+ */
+uint32_t
+backpixel(xcb_window_t wid)
+{
+ int w, h;
+ uint32_t color;
+ xcb_image_t *px;
+
+ w = wm_get_attribute(wid, ATTR_W);
+ h = wm_get_attribute(wid, ATTR_H);
+
+ px = xcb_image_get(conn, wid, 0, 0, 1, 1, 0xffffffff, XCB_IMAGE_FORMAT_Z_PIXMAP);
+ if (px) color = xcb_image_get_pixel(px, 0, 0);
+
+ if (!color) {
+ px = xcb_image_get(conn, wid, w - 1, 0, 1, 1, 0xffffffff, XCB_IMAGE_FORMAT_Z_PIXMAP);
+ if (px) color = xcb_image_get_pixel(px, 0, 0);
+ }
+
+ if (!color) {
+ px = xcb_image_get(conn, wid, 0, h - 1, 1, 1, 0xffffffff, XCB_IMAGE_FORMAT_Z_PIXMAP);
+ if (px) color = xcb_image_get_pixel(px, 0, 0);
+ }
+
+ if (!color) {
+ px = xcb_image_get(conn, wid, w, h, 1, 1, 0xffffffff, XCB_IMAGE_FORMAT_Z_PIXMAP);
+ if (px) color = xcb_image_get_pixel(px, 0, 0);
+ }
+
+ return color ? color : border_color;
+}
+
+/*
+ * Paint double borders around the window. The background is taken from
+ * the window content via backpixel(), and the border line is drawn on
+ * top of it using the colors defined in config.h.
+ *
+ * Note: drawing on the borders require specifying regions from position
+ * the top-left corner of the window itself. Drawing on the border pixmap
+ * is done by drawing outside the window, and then wrapping over to the
+ * left side. For example, assuming a window of 200x100, with a 10px
+ * border, drawing a 5px square in the top left of the border means drawing
+ * a 5x5 rectangle at position 210,110. The area does not wrap around
+ * indefinitely though, so drawing a rectangle of 10x10 or 200x10 at
+ * position 210,110 would have the same effect: draw a 10x10 square in
+ * the top right. uugh…
+ */
+int
+paint(xcb_window_t wid)
+{
+ int val[2], w, h, b, i;
+ xcb_pixmap_t px;
+ xcb_gcontext_t gc;
+
+ w = wm_get_attribute(wid, ATTR_W);
+ h = wm_get_attribute(wid, ATTR_H);
+ b = wm_get_attribute(wid, ATTR_B);
+ i = inner_border;
+
+ if (i > b)
+ return -1;
+
+ px = xcb_generate_id(conn);
+ gc = xcb_generate_id(conn);
+
+ val[0] = backpixel(wid);
+ xcb_create_gc(conn, gc, wid, XCB_GC_FOREGROUND, val);
+ xcb_create_pixmap(conn, scrn->root_depth, px, wid, w + 2*b, h + 2*b);
+
+ /* background color */
+ xcb_rectangle_t bg = { 0, 0, w + 2*b, h + 2*b };
+
+ xcb_poly_fill_rectangle(conn, px, gc, 1, &bg);
+
+ /* abandon all hopes already */
+ xcb_rectangle_t r[] = {
+ {w+(b-i)/2,0,i,h+(b+i)/2}, /* right */
+ {w+b+(b-i)/2,0,i,h+(b+i)/2}, /* left */
+ {0,h+(b-i)/2,w+(b-i)/2+i,i}, /* bottom; bottom-right */
+ {0,h+b+(b-i)/2,w+(b+i)/2,i}, /* top; top-right */
+ {w+b+(b-i)/2,h+b+(b-i)/2,i+(b-i/2),i}, /* top-left corner; top-part */
+ {w+b+(b-i)/2,h+b+(b-i)/2,i,i+(b-i/2)}, /* top-left corner; left-part */
+ {w+b+(b-i)/2,h+(b-i)/2,i+(b-i)/2,i}, /* top-right corner; right-part */
+ {w+(b-i)/2,h+b+(b-i)/2,i,i+(b-i)/2} /* bottom-left corner; bottom-part */
+ };
+
+ val[0] = (wid == wm_get_focus()) ? border_color_active : border_color;
+ xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, val);
+ xcb_poly_fill_rectangle(conn, px, gc, 8, r);
+
+ xcb_change_window_attributes(conn, wid, XCB_CW_BORDER_PIXMAP, &px);
+
+ xcb_free_pixmap(conn, px);
+ xcb_free_gc(conn, gc);
+
+ return 0;
+}
+
+/*
* Inflating a window will grow it both vertically and horizontally in
* all 4 directions, thus making it look like it is inflating.
* The window can be "deflated" by providing a negative `step` value.
t@@ -181,12 +287,14 @@ takeover()
adopt(wid);
if (wm_is_mapped(wid))
- wm_set_border(border, border_color, wid);
+ paint(wid);
}
wid = wm_get_focus();
- if (wid != scrn->root)
- wm_set_border(border, border_color_active, wid);
+ if (wid != scrn->root) {
+ curwid = wid;
+ paint(wid);
+ }
return n;
}
t@@ -313,8 +421,9 @@ cb_mapreq(xcb_generic_event_t *ev)
fprintf(stderr, "%s 0x%08x\n", XEV(e), e->window);
wm_remap(e->window, MAP);
- wm_set_border(border, border_color, e->window);
+ wm_set_border(border, 0, e->window);
wm_set_focus(e->window);
+ paint(e->window);
/* prevent window to pop outside the screen */
if (crossedge(e->window))
t@@ -460,7 +569,7 @@ cb_mouse_release(xcb_generic_event_t *ev)
w = wm_get_attribute(curwid, ATTR_W);
h = wm_get_attribute(curwid, ATTR_H);
xcb_clear_area(conn, 1, curwid, 0, 0, w, h);
- wm_set_border(-1, border_color_active, curwid);
+ paint(curwid);
return 0;
}
t@@ -574,10 +683,10 @@ cb_focus(xcb_generic_event_t *ev)
switch(e->response_type & ~0x80) {
case XCB_FOCUS_IN:
curwid = e->event;
- return wm_set_border(-1, border_color_active, e->event);
+ return paint(e->event);
break; /* NOTREACHED */
case XCB_FOCUS_OUT:
- return wm_set_border(-1, border_color, e->event);
+ return paint(e->event);
break; /* NOTREACHED */
}
t@@ -604,17 +713,26 @@ cb_configreq(xcb_generic_event_t *ev)
e->width, e->height,
e->x, e->y);
- x = wm_get_attribute(e->window, ATTR_X);
- y = wm_get_attribute(e->window, ATTR_Y);
- w = wm_get_attribute(e->window, ATTR_W);
- h = wm_get_attribute(e->window, ATTR_H);
-
- if (e->value_mask & XCB_CONFIG_WINDOW_X) x = e->x;
- if (e->value_mask & XCB_CONFIG_WINDOW_Y) y = e->y;
- if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) w = e->width;
- if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) h = e->height;
-
- wm_teleport(e->window, x, y, w, h);
+ if (e->value_mask &
+ ( XCB_CONFIG_WINDOW_X
+ | XCB_CONFIG_WINDOW_Y
+ | XCB_CONFIG_WINDOW_WIDTH
+ | XCB_CONFIG_WINDOW_HEIGHT)) {
+ x = wm_get_attribute(e->window, ATTR_X);
+ y = wm_get_attribute(e->window, ATTR_Y);
+ w = wm_get_attribute(e->window, ATTR_W);
+ h = wm_get_attribute(e->window, ATTR_H);
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_X) x = e->x;
+ if (e->value_mask & XCB_CONFIG_WINDOW_Y) y = e->y;
+ if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) w = e->width;
+ if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) h = e->height;
+
+ wm_teleport(e->window, x, y, w, h);
+
+ /* redraw border pixmap after move/resize */
+ paint(e->window);
+ }
if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
wm_set_border(e->border_width, border_color, e->window);
t@@ -694,7 +812,6 @@ crossedge(xcb_window_t wid)
r = 1;
free(m);
-
return r;
}
t@@ -720,11 +837,11 @@ snaptoedge(xcb_window_t wid)
if (w + 2*b > m->width) w = m->width - 2*b;
if (h + 2*b > m->height) h = m->height - 2*b;
- if (x + w + 2*b > m->x + m->width) x = MAX(m->x + b, m->x + m->width - w - 2*b);
- if (y + h + 2*b > m->y + m->height) y = MAX(m->y + b, m->y + m->height - h - 2*b);
+ if (x + w + 2*b > m->x + m->width) x = MAX(m->x, m->x + m->width - w - 2*b);
+ if (y + h + 2*b > m->y + m->height) y = MAX(m->y, m->y + m->height - h - 2*b);
wm_teleport(wid, x, y, w, h);
-
+
return 0;
}