git: drawterm

Download patch

ref: 8e6fe7002686d88b0c15f2c47e6cd345b1dd4cda
parent: a6c1ce4e0244ca70403dc4e795a9cee548159560
author: mia soweli <mia@soweli.net>
date: Thu Jun 12 07:31:36 EDT 2025

gui-wl: Use libdecor for window decorations

This replaces the previous rudimentary window decorations with those
provided by libdecor. This means a bit more of a sensible look on GNOME
and no change anywhere else that is sane enough to support server side
decorations.

--- a/Make.linux
+++ b/Make.linux
@@ -8,7 +8,7 @@
 O=o
 OS=posix
 GUI=wl
-LDADD=-lwayland-client -lxkbcommon -ggdb -lm -lrt -lpipewire-0.3
+LDADD=-lwayland-client -lxkbcommon -ggdb -lm -lrt -lpipewire-0.3 -ldecor-0
 LDFLAGS=$(PTHREAD)
 TARG=drawterm
 # AUDIO=none
--- a/gui-wl/wl-cb.c
+++ b/gui-wl/wl-cb.c
@@ -15,6 +15,7 @@
 #include <wayland-client-protocol.h>
 #include <linux/input-event-codes.h>
 #include <xkbcommon/xkbcommon.h>
+#include <libdecor-0/libdecor.h>
 #include "xdg-shell-protocol.h"
 #include "xdg-decoration-protocol.h"
 #include "xdg-primary-selection-protocol.h"
@@ -23,52 +24,6 @@
 #include "screen.h"
 #include "wl-inc.h"
 
-
-static void
-xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
-{
-	Wlwin *wl;
-
-	wl = data;
-	xdg_surface_ack_configure(xdg_surface, serial);
-	wl_surface_commit(wl->surface);
-}
-
-const struct xdg_surface_listener xdg_surface_listener = {
-	.configure = xdg_surface_handle_configure,
-};
-
-static void
-xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
-{
-	wlclose(data);
-}
-
-static void
-xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states)
-{
-	Wlwin *wl;
-	enum xdg_toplevel_state state;
-	int i;
-
-	wl = data;
-	if(width == 0 || height == 0 || (width == wl->dx && height == wl->dy))
-		return;
-	wlresize(wl, width, height);
-
-	wl->maximized = 0;
-	for(i = 0; i < states->size; i++){
-		state = ((enum xdg_toplevel_state *)states->data)[i];
-		if(state == XDG_TOPLEVEL_STATE_MAXIMIZED)
-			wl->maximized = 1;
-	}
-}
-
-const struct xdg_toplevel_listener xdg_toplevel_listener = {
-	.configure = xdg_toplevel_handle_configure,
-	.close = xdg_toplevel_handle_close,
-};
-
 static const struct wl_callback_listener wl_surface_frame_listener;
 
 static void
@@ -367,31 +322,6 @@
 	P9Mouse3 = 4,
 };
 
-static int
-csd_handle_mouse(Wlwin *wl, uint32_t button, uint32_t serial)
-{
-	if(ptinrect(wl->mouse.xy, wl->csd_rects.button_close)){
-		wlclose(wl);
-		return 1;
-	}
-	if(ptinrect(wl->mouse.xy, wl->csd_rects.button_maximize)){
-		wltogglemaximize(wl);
-		return 1;
-	}
-	if(ptinrect(wl->mouse.xy, wl->csd_rects.button_minimize)){
-		wlminimize(wl);
-		return 1;
-	}
-	if(ptinrect(wl->mouse.xy, wl->csd_rects.bar)){
-		switch (button) {
-		case BTN_LEFT: wlmove(wl, serial); break;
-		case BTN_RIGHT: wlmenu(wl, serial); break;
-		}
-		return 1;
-	}
-	return 0;
-}
-
 static void
 pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
 {
@@ -412,8 +342,6 @@
 		wl->mouse.buttons &= ~m;
 
 	wl->mouse.msec = time;
-	if(state && wl->client_side_deco && csd_handle_mouse(wl, button, serial))
-		return;
 
 	absmousetrack(wl->mouse.xy.x, wl->mouse.xy.y, wl->mouse.buttons, wl->mouse.msec);
 }
@@ -438,7 +366,10 @@
 	wl = data;
 	wl->pointerserial = serial;
 	pointer_handle_motion(data, wl_pointer, wl->mouse.msec, surface_x, surface_y);
-	setcursor();
+
+	/* let libdecor handle the cursor for it's surfaces */
+	if (wl->surface == surface)
+		setcursor();
 }
 
 static void
@@ -660,22 +591,6 @@
 };
 
 static void
-zxdg_toplevel_decoration_v1_handle_configure(void *data, struct zxdg_toplevel_decoration_v1 *deco, uint32_t mode)
-{
-	Wlwin *wl = data;
-	int csd = mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
-	if(csd == wl->client_side_deco){
-		return;
-	}
-
-	wl->client_side_deco = csd;
-}
-
-static const struct zxdg_toplevel_decoration_v1_listener zxdg_toplevel_decoration_v1_listener = {
-	.configure = zxdg_toplevel_decoration_v1_handle_configure,
-};
-
-static void
 mode(void *data, struct wl_output*, uint, int x, int y, int)
 {
 	Wlwin *wl;
@@ -722,8 +637,6 @@
 		xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, wl);
 	} else if(strcmp(interface, wl_data_device_manager_interface.name) == 0){
 		wl->data_device_manager = wl_registry_bind(registry, name, &wl_data_device_manager_interface, 3);
-	} else if(strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0){
-		wl->decoman = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1);
 	} else if(strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0){
 		wl->primsel = wl_registry_bind(registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
 	} else if(strcmp(interface, zwlr_virtual_pointer_manager_v1_interface.name) == 0){
@@ -741,13 +654,59 @@
 	.global_remove = handle_global_remove,
 };
 
+static void
+handle_decor_error(struct libdecor *context, enum libdecor_error err, const char *errmsg)
+{
+	panic("libdecor error");
+}
+
+const struct libdecor_interface decor_interface = {
+	.error = handle_decor_error,
+};
+
+static void
+handle_decor_frame_configure(struct libdecor_frame *frame, struct libdecor_configuration *frameconfig, void *aux)
+{
+	Wlwin *wl;
+	struct libdecor_state *state;
+	int dx, dy;
+
+	wl = aux;
+	if (!libdecor_configuration_get_content_size(frameconfig, frame, &dx, &dy)) {
+		dx = 0;
+		dy = 0;
+	}
+
+	dx = (dx == 0) ? wl->dx : dx;
+	dy = (dy == 0) ? wl->dy : dy;
+
+	state = libdecor_state_new(dx, dy);
+	libdecor_frame_commit(frame, state, frameconfig);
+	libdecor_state_free(state);
+
+	if (dx != wl->dx || dy != wl->dy)
+		wlresize(wl, dx, dy);
+}
+
+static void
+handle_decor_frame_commit(struct libdecor_frame *frame, void *aux)
+{
+	Wlwin *wl;
+
+	wl = aux;
+	wl_surface_commit(wl->surface);
+}
+
+const struct libdecor_frame_interface decor_frame_interface = {
+	.configure = handle_decor_frame_configure,
+	.commit = handle_decor_frame_commit,
+};
+
 void
 wlsetcb(Wlwin *wl)
 {
 	struct wl_registry *registry;
-	struct xdg_surface *xdg_surface;
 	struct wl_callback *cb;
-	struct zxdg_toplevel_decoration_v1 *deco;
 
 	//Wayland doesn't do keyboard repeat, but also may
 	//not tell us what the user would like, so we
@@ -770,24 +729,16 @@
 	wlallocbuffer(wl);
 	wl->surface = wl_compositor_create_surface(wl->compositor);
 
-	xdg_surface = xdg_wm_base_get_xdg_surface(wl->xdg_wm_base, wl->surface);
-	wl->xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
-	xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, wl);
-	xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl);
+	wl->decor = libdecor_new(wl->display, &decor_interface);
+	wl->decor_frame = libdecor_decorate(wl->decor, wl->surface, &decor_frame_interface, wl);
 
+	libdecor_frame_set_app_id(wl->decor_frame, "org._9front.drawterm");
+	libdecor_frame_set_title(wl->decor_frame, "drawterm");
+	libdecor_frame_map(wl->decor_frame);
+
 	wl_surface_commit(wl->surface);
 	wl_display_roundtrip(wl->display);
 
-	wl->client_side_deco = wl->decoman == nil;
-	if(wl->decoman != nil){
-		deco = zxdg_decoration_manager_v1_get_toplevel_decoration(wl->decoman, wl->xdg_toplevel);
-		zxdg_toplevel_decoration_v1_add_listener(deco, &zxdg_toplevel_decoration_v1_listener, wl);
-		zxdg_toplevel_decoration_v1_set_mode(deco, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
-		wl_display_roundtrip(wl->display);
-	}
-
-	xdg_toplevel_set_app_id(wl->xdg_toplevel, "drawterm");
-
 	cb = wl_surface_frame(wl->surface);
 	wl_callback_add_listener(cb, &wl_surface_frame_listener, wl);
 
@@ -804,7 +755,7 @@
 void
 wlsettitle(Wlwin *wl, char *s)
 {
-	xdg_toplevel_set_title(wl->xdg_toplevel, s);
+	libdecor_frame_set_title(wl->decor_frame, s);
 }
 
 void
--- a/gui-wl/wl-inc.h
+++ b/gui-wl/wl-inc.h
@@ -1,6 +1,5 @@
 typedef struct Wlwin Wlwin;
 typedef struct Clipboard Clipboard;
-typedef struct Csd Csd;
 
 /* The contents of the clipboard
  * are not stored in the compositor.
@@ -33,18 +32,6 @@
 	Aenter2,
 };
 
-enum CsdSizes {
-	csd_bar_height = 24,
-	csd_button_width = 16,
-};
-
-struct Csd {
-	Rectangle bar;
-	Rectangle button_close;
-	Rectangle button_maximize;
-	Rectangle button_minimize;
-};
-
 struct Wlwin {
 	int dx;
 	int dy;
@@ -55,7 +42,6 @@
 	Rectangle r;
 	int dirty;
 	int alt; /* Kalt state */
-	int maximized;
 
 	/* Wayland State */
 	int runing;
@@ -77,13 +63,14 @@
 	struct wl_data_device *data_device;
 	struct wl_pointer *pointer;
 	struct wl_keyboard *keyboard;
+
 	/* Keyboard state */
 	struct xkb_state *xkb_state;
 	struct xkb_context *xkb_context;
 
-	struct zxdg_decoration_manager_v1 *decoman;
-	int client_side_deco;
-	Csd csd_rects;
+	/* Decoration state */
+	struct libdecor *decor;
+	struct libdecor_frame *decor_frame;
 
 	struct zwp_primary_selection_device_manager_v1 *primsel;
 	struct zwp_primary_selection_device_v1 *primsel_device;
@@ -102,7 +89,4 @@
 void wlresize(Wlwin*, int, int);
 void wlflush(Wlwin*);
 void wlclose(Wlwin*);
-void wltogglemaximize(Wlwin*);
-void wlminimize(Wlwin*);
-void wlmove(Wlwin*, uint32_t);
-void wlmenu(Wlwin*, uint32_t);
+
--- a/gui-wl/wl-screen.c
+++ b/gui-wl/wl-screen.c
@@ -14,6 +14,7 @@
 #include <wayland-client-protocol.h>
 #include <linux/input-event-codes.h>
 #include <xkbcommon/xkbcommon.h>
+#include <libdecor-0/libdecor.h>
 #include "xdg-shell-protocol.h"
 
 #include "screen.h"
@@ -46,77 +47,6 @@
 }
 
 void
-wltogglemaximize(Wlwin *wl)
-{
-	if(wl->maximized)
-		xdg_toplevel_unset_maximized(wl->xdg_toplevel);
-	else
-		xdg_toplevel_set_maximized(wl->xdg_toplevel);
-}
-
-void
-wlminimize(Wlwin *wl)
-{
-	xdg_toplevel_set_minimized(wl->xdg_toplevel);
-}
-
-void
-wlmove(Wlwin *wl, uint32_t serial)
-{
-	xdg_toplevel_move(wl->xdg_toplevel, wl->seat, serial);
-}
-
-void
-wlmenu(Wlwin *wl, uint32_t serial)
-{
-	xdg_toplevel_show_window_menu(wl->xdg_toplevel, wl->seat, serial, wl->mouse.xy.x, wl->mouse.xy.y);
-}
-
-static void
-wlupdatecsdrects(Wlwin *wl)
-{
-	Point offset;
-	Rectangle button;
-
-	if(!wl->client_side_deco){
-		memset(&wl->csd_rects, 0, sizeof wl->csd_rects);
-		return;
-	}
-
-	wl->csd_rects.bar = Rect(0, 0, wl->dx, csd_bar_height);
-
-	offset = Pt(csd_button_width + 4, 0);
-	button = Rect(0, 4, csd_button_width, csd_button_width + 4);
-	button = rectsubpt(button, offset);
-
-	wl->csd_rects.button_close = button = rectaddpt(button, Pt(wl->dx, 0));
-	wl->csd_rects.button_maximize = button = rectsubpt(button, offset);
-	wl->csd_rects.button_minimize = rectsubpt(button, offset);
-}
-
-static void
-wlfillrect(Wlwin *wl, Rectangle rect, uint32_t color)
-{
-	Point p;
-	uint32_t *data = wl->shm_data;
-
-	for(p.y = rect.min.y; p.y < rect.max.y; p.y++)
-		for(p.x = rect.min.x; p.x < rect.max.x; p.x++)
-			data[p.y * wl->dx + p.x] = color;
-}
-
-static void
-wldrawcsd(Wlwin *wl)
-{
-	if(!wl->client_side_deco)
-		return;
-	wlfillrect(wl, wl->csd_rects.bar, 0xAAAAAA);
-	wlfillrect(wl, wl->csd_rects.button_close, DRed >> 8);
-	wlfillrect(wl, wl->csd_rects.button_maximize, DGreen >> 8);
-	wlfillrect(wl, wl->csd_rects.button_minimize, DYellow >> 8);
-}
-
-void
 wlflush(Wlwin *wl)
 {
 	Point p;
@@ -141,11 +71,10 @@
 
 	wl->dx = x;
 	wl->dy = y;
-	wlupdatecsdrects(wl);
 
 	qlock(&drawlock);
 	wlallocbuffer(wl);
-	r = Rect(0, wl->csd_rects.bar.max.y, wl->dx, wl->dy);
+	r = Rect(0, 0, wl->dx, wl->dy);
 	if(gscreen != nil)
 		freememimage(gscreen);
 	gscreen = allocmemimage(r, XRGB32);
@@ -157,7 +86,6 @@
 	qlock(&drawlock);
 	wl->dirty = 1;
 	wl->r = r;
-	wldrawcsd(wl);
 	wlflush(wl);
 	qunlock(&drawlock);
 }
@@ -168,7 +96,7 @@
 	Wlwin *wl;
 	wl = a;
 	while(wl->runing)
-		wl_display_dispatch(wl->display);
+		libdecor_dispatch(wl->decor, -1);
 }
 
 static Wlwin*
@@ -185,11 +113,10 @@
 
 	memimageinit();
 	wlsetcb(wl);
-	wlupdatecsdrects(wl);
 	wlflush(wl);
 	wlsettitle(wl, label);
 
-	r = Rect(0, wl->csd_rects.bar.max.y, wl->dx, wl->dy);
+	r = Rect(0, 0, wl->dx, wl->dy);
 	gscreen = allocmemimage(r, XRGB32);
 	gscreen->clipr = r;
 
@@ -196,7 +123,7 @@
 	wl->runing = 1;
 	kproc("wldispatch", dispatchproc, wl);
 	qlock(&drawlock);
-	wldrawcsd(wl);
+
 	terminit();
 	wlflush(wl);
 	qunlock(&drawlock);
--