code: drawterm

Download patch

ref: c8b1153aeecbda4e0d33354df259a9520c107728
parent: 8bf59cf80d50154415df2c4fc049b08e1cb4046c
author: Jacob Moody <moody@posixcafe.org>
date: Fri Apr 28 12:46:59 EDT 2023

gui-wl: client side decorations for gnome (thanks DeedleFake)

This is the hill that gnome is dying on.
We can disagree but they have users and I
cant be asked to care that much.

--- a/gui-wl/wl-cb.c
+++ b/gui-wl/wl-cb.c
@@ -50,10 +50,7 @@
 static void
 xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
 {
-	Wlwin *wl;
-	wl = data;
-	wl->runing = 0;
-	exits(nil);
+	wlclose(data);
 }
 
 static void
@@ -60,11 +57,23 @@
 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];
+		switch (state){
+		case XDG_TOPLEVEL_STATE_MAXIMIZED:
+			wl->maximized = 1;
+			return;
+		}
+	}
 }
 
 const struct xdg_toplevel_listener xdg_toplevel_listener = {
@@ -365,6 +374,34 @@
 	P9Mouse3 = 4,
 };
 
+static int
+csd_handle_mouse(Wlwin *wl, uint32_t button, uint32_t serial)
+{
+	if(!wl->client_side_deco){
+		return 0;
+	}
+	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)
 {
@@ -385,7 +422,8 @@
 		wl->mouse.buttons &= ~m;
 
 	wl->mouse.msec = time;
-	absmousetrack(wl->mouse.xy.x, wl->mouse.xy.y, wl->mouse.buttons, wl->mouse.msec);
+	if(state && !csd_handle_mouse(wl, button, serial))
+		absmousetrack(wl->mouse.xy.x, wl->mouse.xy.y, wl->mouse.buttons, wl->mouse.msec);
 }
 
 static void
@@ -637,6 +675,23 @@
 };
 
 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;
+	wlresize(wl, wl->dx, wl->dy);
+}
+
+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;
@@ -665,29 +720,29 @@
 	struct wl_output *out;
 
 	wl = data;
-	if(strcmp(interface, wl_shm_interface.name) == 0) {
+	if(strcmp(interface, wl_shm_interface.name) == 0){
 		wl->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
-	} else if(strcmp(interface, wl_output_interface.name) == 0) {
+	} else if(strcmp(interface, wl_output_interface.name) == 0){
 		out = wl_registry_bind(registry, name, &wl_output_interface, 2);
 		wl_output_add_listener(out, &output_listener, wl);
-	} else if(strcmp(interface, wl_seat_interface.name) == 0) {
+	} else if(strcmp(interface, wl_seat_interface.name) == 0){
 		//We don't support multiseat
 		if(wl->seat != nil)
 			return;
 		wl->seat = wl_registry_bind(registry, name, &wl_seat_interface, 4);
 		wl_seat_add_listener(wl->seat, &seat_listener, wl);
-	} else if(strcmp(interface, wl_compositor_interface.name) == 0) {
+	} else if(strcmp(interface, wl_compositor_interface.name) == 0){
 		wl->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
-	} else if(strcmp(interface, xdg_wm_base_interface.name) == 0) {
+	} else if(strcmp(interface, xdg_wm_base_interface.name) == 0){
 		wl->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
 		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) {
+	} 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) {
+	} 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) {
+	} 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) {
+	} else if(strcmp(interface, zwlr_virtual_pointer_manager_v1_interface.name) == 0){
 		wl->vpmgr = wl_registry_bind(registry, name, &zwlr_virtual_pointer_manager_v1_interface, 1);
 	}
 }
@@ -733,12 +788,15 @@
 
 	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->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(wl->decoman, &zxdg_toplevel_decoration_v1_listener, wl);
 		zxdg_toplevel_decoration_v1_set_mode(deco, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
 	}
-	xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, wl);
-	xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl);
 
 	wl_surface_commit(wl->surface);
 	wl_display_roundtrip(wl->display);
@@ -806,7 +864,7 @@
 wlsetmouse(Wlwin *wl, Point p)
 {
 	Point delta;
-	if (wl->vpointer == nil)
+	if(wl->vpointer == nil)
 		return;
 
 	delta.x = p.x - wl->mouse.xy.x;
--- a/gui-wl/wl-inc.h
+++ b/gui-wl/wl-inc.h
@@ -1,5 +1,6 @@
 typedef struct Wlwin Wlwin;
 typedef struct Clipboard Clipboard;
+typedef struct Csd Csd;
 
 /* The contents of the clipboard
  * are not stored in the compositor.
@@ -41,6 +42,18 @@
 	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;
@@ -51,6 +64,7 @@
 	Rectangle r;
 	int dirty;
 	int alt; /* Kalt state */
+	int maximized;
 
 	/* Wayland State */
 	int runing;
@@ -77,6 +91,8 @@
 	struct xkb_context *xkb_context;
 
 	struct zxdg_decoration_manager_v1 *decoman;
+	int client_side_deco;
+	Csd csd_rects;
 
 	struct zwp_primary_selection_device_manager_v1 *primsel;
 	struct zwp_primary_selection_device_v1 *primsel_device;
@@ -94,3 +110,8 @@
 void wldrawcursor(Wlwin*, Cursorinfo*);
 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
@@ -45,6 +45,84 @@
 }
 
 void
+wlclose(Wlwin *wl)
+{
+	wl->runing = 0;
+	exits(nil);
+}
+
+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;
@@ -69,10 +147,11 @@
 
 	wl->dx = x;
 	wl->dy = y;
+	wlupdatecsdrects(wl);
 
 	qlock(&drawlock);
 	wlallocbuffer(wl);
-	r = Rect(0, 0, wl->dx, wl->dy);
+	r = Rect(0, wl->csd_rects.bar.max.y, wl->dx, wl->dy);
 	gscreen = allocmemimage(r, XRGB32);
 	gscreen->clipr = ZR;
 	qunlock(&drawlock);
@@ -83,6 +162,7 @@
 	wl->dirty = 1;
 	wl->r = r;
 	wlflush(wl);
+	wldrawcsd(wl);
 	qunlock(&drawlock);
 }
 
@@ -109,10 +189,11 @@
 
 	memimageinit();
 	wlsetcb(wl);
+	wlupdatecsdrects(wl);
 	wlflush(wl);
 	wlsettitle(wl, label);
 
-	r = Rect(0, 0, wl->dx, wl->dy);
+	r = Rect(0, wl->csd_rects.bar.max.y, wl->dx, wl->dy);
 	gscreen = allocmemimage(r, XRGB32);
 	gscreen->clipr = r;
 	gscreen->r = r;
@@ -123,6 +204,7 @@
 	terminit();
 	qlock(&drawlock);
 	wlflush(wl);
+	wldrawcsd(wl);
 	qunlock(&drawlock);
 	return wl;
 }