From a5f2c9993971291778f430576e43ca1866f9127e Mon Sep 17 00:00:00 2001 From: oldosfan Date: Fri, 30 Sep 2022 07:38:12 +0000 Subject: [PATCH] Implement support for XDG window decoration * 12to11.c (XLMain): Initialize window decoration. * Imakefile (SRCS, OBJS): Add decoration.c and decoration.o. * README: Document support for zdg_decoration_manager_v1. * compositor.h: Update prototypes. * xdg_toplevel.c (XdgDecoration, DecorationMode): New types. (enum _DecorationMode): New structures. (struct _XdgToplevel): New fields `decor' and `decoration'. (struct _XdgDecoration): New struct. (SendDecorationConfigure): New function. (Commit): Apply decoration should it be dirty. (HandleResourceDestroy): Detach decoration object. (Destroy): Post error if decoration object would be orphaned. (DestroyDecoration, SetMode, UnsetMode) (HandleDecorationResourceDestroy, XLXdgToplevelGetDecoration): New functions. --- 12to11.c | 1 + Imakefile | 5 +- README | 1 + compositor.h | 6 ++ xdg_toplevel.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 239 insertions(+), 8 deletions(-) diff --git a/12to11.c b/12to11.c index f944a9a..26765e5 100644 --- a/12to11.c +++ b/12to11.c @@ -123,6 +123,7 @@ XLMain (int argc, char **argv) XLInitPrimarySelection (); XLInitExplicitSynchronization (); XLInitWpViewporter (); + XLInitDecoration (); /* This has to come after the rest of the initialization. */ DetermineServerTime (); XLRunCompositor (); diff --git a/Imakefile b/Imakefile index e5c1c01..9a4f08a 100644 --- a/Imakefile +++ b/Imakefile @@ -21,7 +21,7 @@ SRCS = 12to11.c run.c alloc.c fns.c output.c compositor.c \ dmabuf.c buffer.c select.c xdata.c xsettings.c dnd.c \ icon_surface.c primary_selection.c renderer.c \ picture_renderer.c explicit_synchronization.c transform.c \ - wp_viewporter.c + wp_viewporter.c decoration.c OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \ surface.o region.o shm.o atoms.o subcompositor.o positioner.o \ @@ -30,7 +30,7 @@ OBJS = 12to11.o run.o alloc.o fns.o output.o compositor.o \ dmabuf.o buffer.o select.o xdata.o xsettings.o dnd.o \ icon_surface.o primary_selection.o renderer.o \ picture_renderer.o explicit_synchronization.o transform.o \ - wp_viewporter.o + wp_viewporter.o decoration.o GENHEADERS = transfer_atoms.h @@ -110,6 +110,7 @@ ScannerTarget(xdg-shell) ScannerTarget(primary-selection-unstable-v1) ScannerTarget(linux-explicit-synchronization-unstable-v1) ScannerTarget(viewporter) +ScannerTarget(xdg-decoration-unstable-v1) /* Make OBJS depend on scanner headers, and depend on both them and SRCS. */ $(OBJS): $(GENHEADERS) diff --git a/README b/README index 99a3893..f376cd9 100644 --- a/README +++ b/README @@ -65,6 +65,7 @@ complete degree: 'zwp_linux_dmabuf_v1', version: 4 'zwp_primary_selection_device_manager_v1', version: 1 'wp_viewporter', version: 1 + 'zxdg_decoration_manager_v1', version: 1 When built with EGL, the following Wayland protocol is also supported: diff --git a/compositor.h b/compositor.h index c3974aa..6b083bf 100644 --- a/compositor.h +++ b/compositor.h @@ -1140,6 +1140,8 @@ extern void XLGetXdgToplevel (struct wl_client *, struct wl_resource *, extern Bool XLHandleXEventForXdgToplevels (XEvent *); extern Bool XLIsXdgToplevel (Window); extern void XLInitXdgToplevels (void); +extern void XLXdgToplevelGetDecoration (XdgRoleImplementation *, + struct wl_resource *, uint32_t); /* Defined in xdg_popup.c. */ @@ -1426,6 +1428,10 @@ extern void XLInitWpViewporter (void); extern void XLWpViewportReportBadSize (ViewportExt *); extern void XLWpViewportReportOutOfBuffer (ViewportExt *); +/* Defined in decoration.c. */ + +extern void XLInitDecoration (void); + /* Utility functions that don't belong in a specific file. */ #define ArrayElements(arr) (sizeof (arr) / sizeof (arr)[0]) diff --git a/xdg_toplevel.c b/xdg_toplevel.c index 421aeaf..2582871 100644 --- a/xdg_toplevel.c +++ b/xdg_toplevel.c @@ -25,15 +25,18 @@ along with 12to11. If not, see . */ #include "compositor.h" #include "xdg-shell.h" +#include "xdg-decoration-unstable-v1.h" #define ToplevelFromRoleImpl(impl) ((XdgToplevel *) (impl)) typedef struct _XdgToplevel XdgToplevel; +typedef struct _XdgDecoration XdgDecoration; typedef struct _ToplevelState ToplevelState; typedef struct _PropMotifWmHints PropMotifWmHints; typedef struct _XdgUnmapCallback XdgUnmapCallback; typedef enum _How How; +typedef enum _DecorationMode DecorationMode; enum { @@ -45,14 +48,15 @@ enum StatePendingResize = (1 << 5), StatePendingConfigureSize = (1 << 6), StatePendingConfigureStates = (1 << 7), + StateDecorationModeDirty = (1 << 8), }; enum { - SupportsWindowMenu = 1, - SupportsMaximize = (1 << 2), + SupportsWindowMenu = 1, + SupportsMaximize = (1 << 2), SupportsFullscreen = (1 << 3), - SupportsMinimize = (1 << 4), + SupportsMinimize = (1 << 4), }; enum @@ -68,6 +72,12 @@ enum _How Toggle = 2, }; +enum _DecorationMode + { + DecorationModeClient = 0, + DecorationModeWindowManager = 1, + }; + struct _XdgUnmapCallback { /* Function run when the toplevel is unmapped or detached. */ @@ -218,6 +228,21 @@ struct _XdgToplevel /* The width and height used by that timer if StatePendingConfigureSize is set. */ int configure_width, configure_height; + + /* Any decoration resource associated with this toplevel. */ + XdgDecoration *decoration; + + /* The decoration mode. */ + DecorationMode decor; +}; + +struct _XdgDecoration +{ + /* The associated resource. */ + struct wl_resource *resource; + + /* The associated toplevel. */ + XdgToplevel *toplevel; }; /* iconv context used to convert between UTF-8 and Latin-1. */ @@ -361,6 +386,39 @@ SendConfigure (XdgToplevel *toplevel, unsigned int width, toplevel->conf_serial = serial; } +static void +SendDecorationConfigure (XdgToplevel *toplevel) +{ + uint32_t serial; + + /* This should never be NULL when called! */ + XLAssert (toplevel->decoration != NULL); + + serial = wl_display_next_serial (compositor.wl_display); + +#define ServerSide ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE +#define ClientSide ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE + + if (toplevel->decor == DecorationModeClient) + zxdg_toplevel_decoration_v1_send_configure (toplevel->decoration->resource, + ClientSide); + else + zxdg_toplevel_decoration_v1_send_configure (toplevel->decoration->resource, + ServerSide); + +#undef ServerSide +#undef ClientSide + + XLXdgRoleSendConfigure (toplevel->role, serial); + + toplevel->conf_reply = True; + toplevel->conf_serial = serial; + + /* This means that the decoration should be reapplied upon the next + commit. */ + toplevel->state |= StateDecorationModeDirty; +} + /* Forward declaration. */ static void SendStates (XdgToplevel *); @@ -1094,6 +1152,20 @@ Commit (Role *role, Surface *surface, XdgRoleImplementation *impl) if (!(toplevel->state & StateIsMapped)) Map (toplevel); } + + if (!toplevel->conf_reply + && toplevel->state & StateDecorationModeDirty) + { + /* The decoration is dirty and all configure events were + aknowledged; apply the new decoration. */ + + if (toplevel->decor == DecorationModeWindowManager) + SetDecorated (toplevel, True); + else + SetDecorated (toplevel, False); + + toplevel->state &= ~StateDecorationModeDirty; + } } static void @@ -1459,12 +1531,15 @@ HandleResourceDestroy (struct wl_resource *resource) toplevel = wl_resource_get_user_data (resource); toplevel->resource = NULL; + /* If there is an attached decoration resource, detach it. */ + if (toplevel->decoration) + toplevel->decoration->toplevel = NULL; + DestroyBacking (toplevel); } static void -Destroy (struct wl_client *client, - struct wl_resource *resource) +Destroy (struct wl_client *client, struct wl_resource *resource) { XdgToplevel *toplevel; @@ -1474,7 +1549,15 @@ Destroy (struct wl_client *client, XLXdgRoleDetachImplementation (toplevel->role, &toplevel->impl); - wl_resource_destroy (resource); + /* If the resource still has a decoration applied, then this is an + error. */ + if (toplevel->decoration) + wl_resource_post_error (resource, + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED, + "the attached decoration would be orphaned by" + " the destruction of this resource"); + else + wl_resource_destroy (resource); } static void @@ -2129,3 +2212,142 @@ XLIsXdgToplevel (Window window) { return XLLookUpXdgToplevel (window) != NULL; } + + + +static void +DestroyDecoration (struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +SetMode (struct wl_client *client, struct wl_resource *resource, + uint32_t mode) +{ + XdgDecoration *decoration; + + decoration = wl_resource_get_user_data (resource); + + if (!decoration->toplevel) + return; + + switch (mode) + { + case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: + decoration->toplevel->decor = DecorationModeClient; + break; + + case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: + decoration->toplevel->decor = DecorationModeWindowManager; + break; + + default: + wl_resource_post_error (resource, WL_DISPLAY_ERROR_IMPLEMENTATION, + "trying to set bogus decoration mode %u", + mode); + return; + } + + SendDecorationConfigure (decoration->toplevel); +} + +static void +UnsetMode (struct wl_client *client, struct wl_resource *resource) +{ + XdgDecoration *decoration; + + decoration = wl_resource_get_user_data (resource); + + if (!decoration->toplevel) + return; + + /* Default to using window manager decorations. */ + decoration->toplevel->decor = DecorationModeWindowManager; + SendDecorationConfigure (decoration->toplevel); +} + +static struct zxdg_toplevel_decoration_v1_interface decoration_impl = + { + .destroy = DestroyDecoration, + .set_mode = SetMode, + .unset_mode = UnsetMode, + }; + +static void +HandleDecorationResourceDestroy (struct wl_resource *resource) +{ + XdgDecoration *decoration; + + decoration = wl_resource_get_user_data (resource); + + /* Detach the decoration from the toplevel if the latter still + exists. */ + if (decoration->toplevel) + decoration->toplevel->decoration = NULL; + + /* Free the decoration. */ + XLFree (decoration); +} + +void +XLXdgToplevelGetDecoration (XdgRoleImplementation *impl, + struct wl_resource *resource, + uint32_t id) +{ + XdgToplevel *toplevel; + XdgDecoration *decoration; + + toplevel = ToplevelFromRoleImpl (impl); + + /* See if a decoration object is already attached. */ + if (toplevel->decoration) + { +#define AlreadyConstructed ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ALREADY_CONSTRUCTED + wl_resource_post_error (resource, AlreadyConstructed, + "the given toplevel already has a decoration" + "object."); +#undef AlreadyConstructed + return; + } + + /* See if a buffer is already attached. */ + if (toplevel->role->surface + && toplevel->role->surface->current_state.buffer) + { +#define UnconfiguredBuffer ZXDG_TOPLEVEL_DECORATION_V1_ERROR_UNCONFIGURED_BUFFER + wl_resource_post_error (resource, UnconfiguredBuffer, + "given toplevel already has attached buffer"); +#undef UnconfiguredBuffer + return; + } + + decoration = XLSafeMalloc (sizeof *decoration); + + if (!decoration) + { + wl_resource_post_no_memory (resource); + return; + } + + memset (decoration, 0, sizeof *decoration); + decoration->resource + = wl_resource_create (wl_resource_get_client (resource), + &zxdg_toplevel_decoration_v1_interface, + wl_resource_get_version (resource), id); + + if (!decoration->resource) + { + XLFree (decoration); + wl_resource_post_no_memory (resource); + return; + } + + /* Now attach the decoration to the toplevel and vice versa. */ + toplevel->decoration = decoration; + decoration->toplevel = toplevel; + + /* And set the implementation. */ + wl_resource_set_implementation (decoration->resource, &decoration_impl, + decoration, HandleDecorationResourceDestroy); +}