diff --git a/cage.c b/cage.c index f07c856..e28be76 100644 --- a/cage.c +++ b/cage.c @@ -8,6 +8,8 @@ #define _POSIX_C_SOURCE 200112L +#include "config.h" + #include #include #include @@ -17,15 +19,24 @@ #include #include #include +#if CAGE_HAS_XWAYLAND +#include +#endif #include #include #include #include +#if CAGE_HAS_XWAYLAND +#include +#endif #include "output.h" #include "seat.h" #include "server.h" #include "xdg_shell.h" +#if CAGE_HAS_XWAYLAND +#include "xwayland.h" +#endif static bool spawn_primary_client(char *argv[], pid_t *pid_out) @@ -69,6 +80,9 @@ main(int argc, char *argv[]) struct wlr_compositor *compositor = NULL; struct wlr_data_device_manager *data_device_mgr = NULL; struct wlr_xdg_shell *xdg_shell = NULL; +#if CAGE_HAS_XWAYLAND + struct wlr_xwayland *xwayland = NULL; +#endif int ret = 0; if (argc < 2) { @@ -146,6 +160,31 @@ main(int argc, char *argv[]) server.new_xdg_shell_surface.notify = handle_xdg_shell_surface_new; wl_signal_add(&xdg_shell->events.new_surface, &server.new_xdg_shell_surface); +#if CAGE_HAS_XWAYLAND + xwayland = wlr_xwayland_create(server.wl_display, compositor, true); + server.new_xwayland_surface.notify = handle_xwayland_surface_new; + wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface); + + struct wlr_xcursor_manager *xcursor_manager = + wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE); + if (!xcursor_manager) { + wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager"); + ret = 1; + goto end; + } + if (wlr_xcursor_manager_load(xcursor_manager, 1)) { + wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme"); + } + struct wlr_xcursor *xcursor = + wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1); + if (xcursor) { + struct wlr_xcursor_image *image = xcursor->images[0]; + wlr_xwayland_set_cursor(xwayland, image->buffer, + image->width * 4, image->width, image->height, + image->hotspot_x, image->hotspot_y); + } +#endif + const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wlr_log_errno(WLR_ERROR, "Unable to open Wayland socket"); @@ -165,6 +204,12 @@ main(int argc, char *argv[]) "Clients may not be able to connect"); } +#if CAGE_HAS_XWAYLAND + if (xwayland) { + wlr_xwayland_set_seat(xwayland, server.seat->seat); + } +#endif + pid_t pid; if (!spawn_primary_client(argv + 1, &pid)) { ret = 1; @@ -178,6 +223,10 @@ main(int argc, char *argv[]) end: cg_seat_destroy(server.seat); +#if CAGE_HAS_XWAYLAND + wlr_xwayland_destroy(xwayland); + wlr_xcursor_manager_destroy(xcursor_manager); +#endif wlr_xdg_shell_destroy(xdg_shell); wlr_data_device_manager_destroy(data_device_mgr); wlr_compositor_destroy(compositor); diff --git a/meson.build b/meson.build index f65688a..d77e2cf 100644 --- a/meson.build +++ b/meson.build @@ -51,6 +51,9 @@ server_protos = declare_dependency( sources: server_protos_headers, ) +conf_data = configuration_data() +conf_data.set10('CAGE_HAS_XWAYLAND', get_option('xwayland')) + cage_sources = [ 'cage.c', 'output.c', @@ -60,6 +63,9 @@ cage_sources = [ ] cage_headers = [ + configure_file(input: 'config.h.in', + output: 'config.h', + configuration: conf_data), 'output.h', 'seat.h', 'server.h', @@ -67,6 +73,11 @@ cage_headers = [ 'xdg_shell.h', ] +if conf_data.get('CAGE_HAS_XWAYLAND', 0) == 1 + cage_sources += 'xwayland.c' + cage_headers += 'xwayland.h' +endif + executable( meson.project_name(), cage_sources + cage_headers, @@ -78,3 +89,12 @@ executable( ], install: true, ) + +summary = [ + '', + 'Cage @0@'.format(meson.project_version()), + '', + ' xwayland: @0@'.format(conf_data.get('CAGE_HAS_XWAYLAND', false)), + '' +] +message('\n'.join(summary)) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..87763ff --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications') diff --git a/output.c b/output.c index 99f92e3..1a26a21 100644 --- a/output.c +++ b/output.c @@ -8,6 +8,8 @@ #define _POSIX_C_SOURCE 200112L +#include "config.h" + #include #include #include @@ -19,6 +21,9 @@ #include #include #include +#if CAGE_HAS_XWAYLAND +#include +#endif #include "output.h" #include "server.h" @@ -76,6 +81,11 @@ view_for_each_surface(struct cg_view *view, struct render_data *rdata, case CAGE_XDG_SHELL_VIEW: wlr_xdg_surface_for_each_surface(view->xdg_surface, iterator, rdata); break; +#ifdef CAGE_HAS_XWAYLAND + case CAGE_XWAYLAND_VIEW: + wlr_surface_for_each_surface(view->wlr_surface, iterator, rdata); + break; +#endif default: wlr_log(WLR_ERROR, "Unrecognized view type: %d", view->type); } diff --git a/seat.c b/seat.c index 5379e15..dc2786d 100644 --- a/seat.c +++ b/seat.c @@ -6,6 +6,8 @@ * See the LICENSE file accompanying this file. */ +#include "config.h" + #include #include #include @@ -14,15 +16,15 @@ #include #include #include +#if CAGE_HAS_XWAYLAND +#include +#endif #include "output.h" #include "seat.h" #include "server.h" #include "view.h" -#define DEFAULT_XCURSOR "left_ptr" -#define XCURSOR_SIZE 24 - static inline bool have_dialogs_open(struct cg_server *server) { @@ -52,6 +54,13 @@ view_at(struct cg_view *view, double lx, double ly, view_sx, view_sy, &_sx, &_sy); break; +#ifdef CAGE_HAS_XWAYLAND + case CAGE_XWAYLAND_VIEW: + _surface = wlr_surface_surface_at(view->wlr_surface, + view_sx, view_sy, + &_sx, &_sy); + break; +#endif default: wlr_log(WLR_ERROR, "Unrecognized view type: %d", view->type); } @@ -501,6 +510,13 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) return; } +#if CAGE_HAS_XWAYLAND + if (view->type == CAGE_XWAYLAND_VIEW && + !wlr_xwayland_or_surface_wants_focus(view->xwayland_surface)) { + return; + } +#endif + if (prev_view) { view_activate(prev_view, false); } diff --git a/seat.h b/seat.h index e7304c0..afb7809 100644 --- a/seat.h +++ b/seat.h @@ -10,6 +10,9 @@ #include "server.h" #include "view.h" +#define DEFAULT_XCURSOR "left_ptr" +#define XCURSOR_SIZE 24 + struct cg_seat { struct wlr_seat *seat; struct cg_server *server; diff --git a/server.h b/server.h index 20dbdcf..dd912a9 100644 --- a/server.h +++ b/server.h @@ -1,9 +1,14 @@ #ifndef CG_SERVER_H #define CG_SERVER_H +#include "config.h" + #include #include #include +#ifdef CAGE_HAS_XWAYLAND +#include +#endif #include "output.h" #include "seat.h" @@ -20,6 +25,10 @@ struct cg_server { struct wlr_output_layout *output_layout; struct cg_output *output; struct wl_listener new_output; + +#if CAGE_HAS_XWAYLAND + struct wl_listener new_xwayland_surface; +#endif }; #endif diff --git a/view.h b/view.h index 749693c..373fd26 100644 --- a/view.h +++ b/view.h @@ -1,6 +1,8 @@ #ifndef CG_VIEW_H #define CG_VIEW_H +#include "config.h" + #include #include #include @@ -11,6 +13,9 @@ enum cg_view_type { CAGE_XDG_SHELL_VIEW, +#ifdef CAGE_HAS_XWAYLAND + CAGE_XWAYLAND_VIEW, +#endif }; struct cg_view { @@ -22,6 +27,9 @@ struct cg_view { enum cg_view_type type; union { struct wlr_xdg_surface *xdg_surface; +#ifdef CAGE_HAS_XWAYLAND + struct wlr_xwayland_surface *xwayland_surface; +#endif }; struct wl_listener destroy; diff --git a/xwayland.c b/xwayland.c new file mode 100644 index 0000000..804eb13 --- /dev/null +++ b/xwayland.c @@ -0,0 +1,86 @@ +/* + * Cage: A Wayland kiosk. + * + * Copyright (C) 2018 Jente Hidskes + * + * See the LICENSE file accompanying this file. + */ + +#include +#include +#include +#include + +#include "server.h" +#include "view.h" + +static void +activate(struct cg_view *view, bool activate) +{ + wlr_xwayland_surface_activate(view->xwayland_surface, activate); +} + +static void +maximize(struct cg_view *view, int output_width, int output_height) +{ + wlr_xwayland_surface_configure(view->xwayland_surface, 0, 0, output_width, output_height); + wlr_xwayland_surface_set_maximized(view->xwayland_surface, true); +} + +static void +get_geometry(struct cg_view *view, int *width_out, int *height_out) +{ + *width_out = view->xwayland_surface->surface->current.width; + *height_out = view->xwayland_surface->surface->current.height; +} + +static bool +is_primary(struct cg_view *view) +{ + struct wlr_xwayland_surface *parent = view->xwayland_surface->parent; + return parent == NULL; +} + +static void +handle_xwayland_surface_unmap(struct wl_listener *listener, void *data) +{ + struct cg_view *view = wl_container_of(listener, view, unmap); + view_unmap(view); +} + +static void +handle_xwayland_surface_map(struct wl_listener *listener, void *data) +{ + struct cg_view *view = wl_container_of(listener, view, map); + view_map(view, view->xwayland_surface->surface); +} + +static void +handle_xwayland_surface_destroy(struct wl_listener *listener, void *data) +{ + struct cg_view *view = wl_container_of(listener, view, destroy); + view_destroy(view); +} + +void +handle_xwayland_surface_new(struct wl_listener *listener, void *data) +{ + struct cg_server *server = wl_container_of(listener, server, new_xwayland_surface); + struct wlr_xwayland_surface *xwayland_surface = data; + + struct cg_view *view = cg_view_create(server); + view->type = CAGE_XWAYLAND_VIEW; + view->xwayland_surface = xwayland_surface; + + view->map.notify = handle_xwayland_surface_map; + wl_signal_add(&xwayland_surface->events.map, &view->map); + view->unmap.notify = handle_xwayland_surface_unmap; + wl_signal_add(&xwayland_surface->events.unmap, &view->unmap); + view->destroy.notify = handle_xwayland_surface_destroy; + wl_signal_add(&xwayland_surface->events.destroy, &view->destroy); + + view->activate = activate; + view->maximize = maximize; + view->get_geometry = get_geometry; + view->is_primary = is_primary; +} diff --git a/xwayland.h b/xwayland.h new file mode 100644 index 0000000..524bc66 --- /dev/null +++ b/xwayland.h @@ -0,0 +1,8 @@ +#ifndef XWAYLAND_H +#define XWAYLAND_H + +#include + +void handle_xwayland_surface_new(struct wl_listener *listener, void *data); + +#endif