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;
}