diff --git a/hw/xwayland/man/Xwayland.man b/hw/xwayland/man/Xwayland.man index 4e7877730fc81e1d80b4753c2ab84d72aa8011ca..02b333be9f215bff8f2cd8a9947a812ba9dccf0d 100644 --- a/hw/xwayland/man/Xwayland.man +++ b/hw/xwayland/man/Xwayland.man @@ -46,6 +46,14 @@ Like all of the X servers, \fIXwayland\fP accepts the command line options described in the \fIXserver\fP(@miscmansuffix@) manual page. The following additional arguments are supported as well. .TP 8 +.B \-decorate +Add decorations to the Xwayland root window when running rootful. + +This option has no effect when \fIXwayland\fP is built without libdecor +support (optional). + +This option is not compatible with rootless mode (\fI-rootless\fP). +.TP 8 .B \-eglstream Use EGLStream backend for NVidia GPUs. If \fIXwayland\fP was compiled with EGLStream support, this option will instruct \fIXwayland\fP to try that diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build index 40aec15879bf37ee931379343812978a689bb83d..6c04c4cf60875af6f6f4c7e61a67314d569bf90a 100644 --- a/hw/xwayland/meson.build +++ b/hw/xwayland/meson.build @@ -128,6 +128,10 @@ if libdrm_dep.found() xwayland_dep += libdrm_dep endif +if have_libdecor + xwayland_dep += libdecor_dep +endif + xwayland_server = executable( 'Xwayland', srcs, @@ -158,6 +162,7 @@ xwayland_data.set('PACKAGE_VERSION', meson.project_version()) xwayland_data.set('xwayland_path', xwayland_path) xwayland_data.set('have_glamor', build_glamor ? 'true' : 'false') xwayland_data.set('have_eglstream', build_eglstream ? 'true' : 'false') +xwayland_data.set('have_libdecor', have_libdecor ? 'true' : 'false') configure_file( input: 'xwayland.pc.in', output: 'xwayland.pc', diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c index a69fc4dc707d82aaef59e8429085a9ea5b96a59a..9b1ad0ec559970d75fec3a2fbc0627fa8dfd086d 100644 --- a/hw/xwayland/xwayland-screen.c +++ b/hw/xwayland/xwayland-screen.c @@ -513,6 +513,33 @@ xwl_display_pollout (struct xwl_screen *xwl_screen, int timeout) return xserver_poll(&poll_fd, 1, timeout); } +#ifdef XWL_HAS_LIBDECOR +static void +xwl_dispatch_events_with_libdecor(struct xwl_screen *xwl_screen) +{ + int ret = 0; + + assert(!xwl_screen->rootless); + + ret = libdecor_dispatch(xwl_screen->libdecor_context, 0); + if (ret == -1) + xwl_give_up("failed to dispatch Wayland events with libdecor: %s\n", + strerror(errno)); +} + +static void +handle_libdecor_error(struct libdecor *context, + enum libdecor_error error, + const char *message) +{ + xwl_give_up("libdecor error (%d): %s\n", error, message); +} + +static struct libdecor_interface libdecor_iface = { + .error = handle_libdecor_error, +}; +#endif + static void xwl_dispatch_events (struct xwl_screen *xwl_screen) { @@ -551,6 +578,12 @@ socket_handler(int fd, int ready, void *data) { struct xwl_screen *xwl_screen = data; +#ifdef XWL_HAS_LIBDECOR + if (xwl_screen->libdecor_context) { + xwl_dispatch_events_with_libdecor(xwl_screen); + return; + } +#endif xwl_read_events (xwl_screen); } @@ -565,12 +598,24 @@ block_handler(void *data, void *timeout) struct xwl_screen *xwl_screen = data; xwl_screen_post_damage(xwl_screen); +#ifdef XWL_HAS_LIBDECOR + if (xwl_screen->libdecor_context) { + xwl_dispatch_events_with_libdecor(xwl_screen); + return; + } +#endif xwl_dispatch_events (xwl_screen); } void xwl_sync_events (struct xwl_screen *xwl_screen) { +#ifdef XWL_HAS_LIBDECOR + if (xwl_screen->libdecor_context) { + xwl_dispatch_events_with_libdecor(xwl_screen); + return; + } +#endif xwl_dispatch_events (xwl_screen); xwl_read_events (xwl_screen); } @@ -689,6 +734,13 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) xwl_screen->host_grab = 1; xwl_screen->has_grab = 1; } + else if (strcmp(argv[i], "-decorate") == 0) { +#ifdef XWL_HAS_LIBDECOR + xwl_screen->decorate = 1; +#else + ErrorF("This build does not have libdecor support\n"); +#endif + } } if (use_fixed_size) { @@ -745,11 +797,17 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) ®istry_listener, xwl_screen); xwl_screen_roundtrip(xwl_screen); + if (xwl_screen->fullscreen && xwl_screen->rootless) { ErrorF("error, cannot set fullscreen when running rootless\n"); return FALSE; } + if (xwl_screen->fullscreen && xwl_screen->decorate) { + ErrorF("error, cannot use the decorate option when running fullscreen\n"); + return FALSE; + } + if (xwl_screen->fullscreen && !xwl_screen_has_viewport_support(xwl_screen)) { ErrorF("missing viewport support in the compositor, ignoring fullscreen\n"); xwl_screen->fullscreen = FALSE; @@ -796,7 +854,16 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) return FALSE; #endif - xwl_screen->wayland_fd = wl_display_get_fd(xwl_screen->display); +#ifdef XWL_HAS_LIBDECOR + if (xwl_screen->decorate && !xwl_screen->rootless) { + xwl_screen->libdecor_context = libdecor_new(xwl_screen->display, &libdecor_iface); + xwl_screen->wayland_fd = libdecor_get_fd(xwl_screen->libdecor_context); + } + else +#endif + { + xwl_screen->wayland_fd = wl_display_get_fd(xwl_screen->display); + } SetNotifyFd(xwl_screen->wayland_fd, socket_handler, X_NOTIFY_READ, xwl_screen); RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, xwl_screen); diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h index 6ddb6f7519d94d1e8cf9cde05de81c740eab8ed2..fd201cdf5f540563a57d849cf12f69f58d9089af 100644 --- a/hw/xwayland/xwayland-screen.h +++ b/hw/xwayland/xwayland-screen.h @@ -38,6 +38,10 @@ #include "xwayland-glamor.h" #include "xwayland-drm-lease.h" +#ifdef XWL_HAS_LIBDECOR +#include <libdecor.h> +#endif + struct xwl_format { uint32_t format; int num_modifiers; @@ -61,6 +65,7 @@ struct xwl_screen { int fullscreen; int host_grab; int has_grab; + int decorate; CreateScreenResourcesProcPtr CreateScreenResources; CloseScreenProcPtr CloseScreen; @@ -123,6 +128,10 @@ struct xwl_screen { /* The preferred GLVND vendor. If NULL, "mesa" is assumed. */ const char *glvnd_vendor; +#ifdef XWL_HAS_LIBDECOR + int libdecor_fd; + struct libdecor *libdecor_context; +#endif }; /* Apps which use randr/vidmode to change the mode when going fullscreen, diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c index 8b7e447afb9879177cee71ac86cf449ca2077097..0f6cacd3f4332dacb6b795a84682b88f839e35d3 100644 --- a/hw/xwayland/xwayland-window.c +++ b/hw/xwayland/xwayland-window.c @@ -498,6 +498,11 @@ xwl_window_rootful_update_title(struct xwl_window *xwl_window) snprintf(title, sizeof(title), "Xwayland on :%s%s", display, grab_message); +#ifdef XWL_HAS_LIBDECOR + if (xwl_window->libdecor_frame) + libdecor_frame_set_title(xwl_window->libdecor_frame, title); + else +#endif if (xwl_window->xdg_toplevel) xdg_toplevel_set_title(xwl_window->xdg_toplevel, title); } @@ -507,10 +512,78 @@ xwl_window_rootful_set_app_id(struct xwl_window *xwl_window) { const char *app_id = "org.freedesktop.Xwayland"; +#ifdef XWL_HAS_LIBDECOR + if (xwl_window->libdecor_frame) + libdecor_frame_set_app_id(xwl_window->libdecor_frame, app_id); + else +#endif if (xwl_window->xdg_toplevel) xdg_toplevel_set_app_id(xwl_window->xdg_toplevel, app_id); } +#ifdef XWL_HAS_LIBDECOR +static void +xwl_window_update_libdecor_size(struct xwl_window *xwl_window, int width, int height) +{ + struct libdecor_state *state; + + if (xwl_window->libdecor_frame) { + state = libdecor_state_new(width, height); + libdecor_frame_commit(xwl_window->libdecor_frame, state, NULL); + libdecor_state_free(state); + } +} + +static void +handle_libdecor_configure(struct libdecor_frame *frame, + struct libdecor_configuration *configuration, + void *data) +{ + struct xwl_window *xwl_window = data; + struct xwl_screen *xwl_screen = xwl_window->xwl_screen; + struct libdecor_state *state; + + state = libdecor_state_new(xwl_screen->width, xwl_screen->height); + libdecor_frame_commit(frame, state, configuration); + libdecor_state_free(state); + + if (libdecor_frame_has_capability(frame, LIBDECOR_ACTION_RESIZE)) + libdecor_frame_unset_capabilities(frame, LIBDECOR_ACTION_RESIZE); + if (libdecor_frame_has_capability(frame, LIBDECOR_ACTION_FULLSCREEN)) + libdecor_frame_unset_capabilities(frame, LIBDECOR_ACTION_FULLSCREEN); +} + +static void +handle_libdecor_close(struct libdecor_frame *frame, + void *data) +{ + DebugF("Terminating on compositor request"); + GiveUp(0); +} + +static void +handle_libdecor_commit(struct libdecor_frame *frame, + void *data) +{ + struct xwl_window *xwl_window = data; + wl_surface_commit(xwl_window->surface); +} + +static void +handle_libdecor_dismiss_popup(struct libdecor_frame *frame, + const char *seat_name, + void *data) +{ +} + +static struct libdecor_frame_interface libdecor_frame_iface = { + handle_libdecor_configure, + handle_libdecor_close, + handle_libdecor_commit, + handle_libdecor_dismiss_popup, +}; +#endif + static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, @@ -591,29 +664,43 @@ xwl_create_root_surface(struct xwl_window *xwl_window) WindowPtr window = xwl_window->window; struct wl_region *region; - xwl_window->xdg_surface = - xdg_wm_base_get_xdg_surface(xwl_screen->xdg_wm_base, xwl_window->surface); - if (xwl_window->xdg_surface == NULL) { - ErrorF("Failed creating xdg_wm_base xdg_surface\n"); - goto err_surf; - } - xwl_window->xdg_toplevel = - xdg_surface_get_toplevel(xwl_window->xdg_surface); - if (xwl_window->xdg_surface == NULL) { - ErrorF("Failed creating xdg_toplevel\n"); - goto err_surf; +#ifdef XWL_HAS_LIBDECOR + if (xwl_screen->decorate) { + xwl_window->libdecor_frame = + libdecor_decorate(xwl_screen->libdecor_context, + xwl_window->surface, + &libdecor_frame_iface, + xwl_window); + libdecor_frame_map(xwl_window->libdecor_frame); } + else +#endif + { + xwl_window->xdg_surface = + xdg_wm_base_get_xdg_surface(xwl_screen->xdg_wm_base, xwl_window->surface); + if (xwl_window->xdg_surface == NULL) { + ErrorF("Failed creating xdg_wm_base xdg_surface\n"); + goto err_surf; + } + + xwl_window->xdg_toplevel = + xdg_surface_get_toplevel(xwl_window->xdg_surface); + if (xwl_window->xdg_surface == NULL) { + ErrorF("Failed creating xdg_toplevel\n"); + goto err_surf; + } - wl_surface_add_listener(xwl_window->surface, - &surface_listener, xwl_window); + wl_surface_add_listener(xwl_window->surface, + &surface_listener, xwl_window); - xdg_surface_add_listener(xwl_window->xdg_surface, - &xdg_surface_listener, xwl_window); + xdg_surface_add_listener(xwl_window->xdg_surface, + &xdg_surface_listener, xwl_window); - xdg_toplevel_add_listener(xwl_window->xdg_toplevel, - &xdg_toplevel_listener, - NULL); + xdg_toplevel_add_listener(xwl_window->xdg_toplevel, + &xdg_toplevel_listener, + NULL); + } xwl_window_rootful_update_title(xwl_window); xwl_window_rootful_set_app_id(xwl_window); @@ -908,8 +995,14 @@ xwl_resize_window(WindowPtr window, xwl_screen->ResizeWindow = screen->ResizeWindow; screen->ResizeWindow = xwl_resize_window; - if (xwl_window && (xwl_window_get(window) || xwl_window_is_toplevel(window))) - xwl_window_check_resolution_change_emulation(xwl_window); + if (xwl_window) { + if (xwl_window_get(window) || xwl_window_is_toplevel(window)) + xwl_window_check_resolution_change_emulation(xwl_window); +#ifdef XWL_HAS_LIBDECOR + if (window == screen->root) + xwl_window_update_libdecor_size(xwl_window, width, height); +#endif + } } void diff --git a/hw/xwayland/xwayland-window.h b/hw/xwayland/xwayland-window.h index 7fb5425930c8a7f5c5c28e2002cdb1b34449e1be..3dbfd2dc283a80e041cfbb8c8c1fe835fe2fff6e 100644 --- a/hw/xwayland/xwayland-window.h +++ b/hw/xwayland/xwayland-window.h @@ -58,6 +58,9 @@ struct xwl_window { struct xorg_list frame_callback_list; Bool present_flipped; #endif +#ifdef XWL_HAS_LIBDECOR + struct libdecor_frame *libdecor_frame; +#endif }; struct xwl_window *xwl_window_get(WindowPtr window); diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c index e4983ed1e91d8e8ad43c4f2341fab2b85ea4c7a2..51d3147f1be52a90eb84a493b0275e78d3ff8599 100644 --- a/hw/xwayland/xwayland.c +++ b/hw/xwayland/xwayland.c @@ -106,6 +106,9 @@ ddxUseMsg(void) ErrorF("-version show the server version and exit\n"); ErrorF("-noTouchPointerEmulation disable touch pointer emulation\n"); ErrorF("-force-xrandr-emulation force non-native modes to be exposed when viewporter is not exposed by the compositor\n"); +#ifdef XWL_HAS_LIBDECOR + ErrorF("-decorate add decorations to Xwayland when rootful (experimental)\n"); +#endif } static int init_fd = -1; @@ -237,6 +240,9 @@ ddxProcessArgument(int argc, char *argv[], int i) else if (strcmp(argv[i], "-host-grab") == 0) { return 1; } + else if (strcmp(argv[i], "-decorate") == 0) { + return 1; + } return 0; } diff --git a/hw/xwayland/xwayland.pc.in b/hw/xwayland/xwayland.pc.in index 70db37db75919845cc41e060aa52ece1541e80bf..c62a95a0234c2797007b0af5b6829a626ba37bcb 100644 --- a/hw/xwayland/xwayland.pc.in +++ b/hw/xwayland/xwayland.pc.in @@ -16,3 +16,4 @@ have_force_xrandr_emulation=true have_geometry=true have_fullscreen=true have_host_grab=true +have_decorate=@have_libdecor@ diff --git a/include/meson.build b/include/meson.build index 7f1e4fd8fb5b0f5f02f6b948b42abc515798d5af..57466dc79b2208573f0ddf472a094cc765c24bbf 100644 --- a/include/meson.build +++ b/include/meson.build @@ -415,6 +415,7 @@ configure_file(output : 'xwin-config.h', xwayland_data = configuration_data() xwayland_data.set('XWL_HAS_GLAMOR', build_glamor and (gbm_dep.found() or build_eglstream) ? '1' : false) xwayland_data.set('XWL_HAS_EGLSTREAM', build_eglstream ? '1' : false) +xwayland_data.set('XWL_HAS_LIBDECOR', have_libdecor ? '1' : false) configure_file(output : 'xwayland-config.h', input : 'xwayland-config.h.meson.in', diff --git a/include/xwayland-config.h.meson.in b/include/xwayland-config.h.meson.in index 0943ff57de60a50f78acb0453b0d9e1b947d686e..bb121e355e5e0f7c722fbcfa95edd67e848154f0 100644 --- a/include/xwayland-config.h.meson.in +++ b/include/xwayland-config.h.meson.in @@ -9,3 +9,6 @@ /* Build eglstream support for Xwayland */ #mesondefine XWL_HAS_EGLSTREAM + +/* Build Xwayland with libdecor support*/ +#mesondefine XWL_HAS_LIBDECOR diff --git a/meson.build b/meson.build index 0793f0e54061d0037bb3f47aa5324ab23bd686de..7bcec7d4e8da10425ef56072366dad225dbd305e 100644 --- a/meson.build +++ b/meson.build @@ -336,6 +336,21 @@ if build_glamor epoxy_dep = dependency('epoxy', required: false) endif +if build_xwayland + libdecor_dep = dependency('libdecor-0', required: false) + libdecor_option = get_option('libdecor') + if libdecor_option == 'auto' + have_libdecor = libdecor_dep.found() + else + have_libdecor = libdecor_option == 'true' + if have_libdecor and not libdecor_dep.found() + error('libdecor support requested but not found') + endif + endif +else + have_libdecor = false +endif + eglstream_option = get_option('xwayland_eglstream') if build_xwayland and build_glamor eglstream_dep = dependency('wayland-eglstream-protocols', required:false) diff --git a/meson_options.txt b/meson_options.txt index 3a494478e1c3b098f496d8e7621e86df133fda98..16377dcc348fa3948117ceacbb8f35d082858abf 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -135,6 +135,8 @@ option('libunwind', type: 'boolean', value: false, description: 'Use libunwind for backtrace reporting') option('xwayland-path', type: 'string', description: 'Directory containing Xwayland executable') +option('libdecor', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto', + description: 'Whether Xwayland should use libdecor when running rootful.') option('docs', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto', description: 'Build documentation')