Add XWayland support

With Cage becoming more popular since its mention on Phoronix and
therefore getting more use-cases than just my own project, add XWayland
support. The refactoring of 2cf40f7 makes this much easier. Note that
this is a no-cost addition for those of us not using XWayland as it is a
compile-time option that needs to be explicitly enabled by adding
`-Dxwayland=true` to your meson command.
This commit is contained in:
Jente Hidskes 2018-12-31 17:24:21 +01:00
parent 48f8f69556
commit a34c726a1c
No known key found for this signature in database
GPG key ID: 04BE5A29F32D91EA
10 changed files with 213 additions and 3 deletions

49
cage.c
View file

@ -8,6 +8,8 @@
#define _POSIX_C_SOURCE 200112L
#include "config.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@ -17,15 +19,24 @@
#include <wlr/backend.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/types/wlr_xcursor_manager.h>
#endif
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/xwayland.h>
#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);

View file

@ -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))

1
meson_options.txt Normal file
View file

@ -0,0 +1 @@
option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications')

View file

@ -8,6 +8,8 @@
#define _POSIX_C_SOURCE 200112L
#include "config.h"
#include <stdlib.h>
#include <unistd.h>
#include <wayland-server.h>
@ -19,6 +21,9 @@
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/xwayland.h>
#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);
}

22
seat.c
View file

@ -6,6 +6,8 @@
* See the LICENSE file accompanying this file.
*/
#include "config.h"
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/backend.h>
@ -14,15 +16,15 @@
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/xwayland.h>
#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);
}

3
seat.h
View file

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

View file

@ -1,9 +1,14 @@
#ifndef CG_SERVER_H
#define CG_SERVER_H
#include "config.h"
#include <wayland-server.h>
#include <wlr/backend.h>
#include <wlr/types/wlr_output_layout.h>
#ifdef CAGE_HAS_XWAYLAND
#include <wlr/xwayland.h>
#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

8
view.h
View file

@ -1,6 +1,8 @@
#ifndef CG_VIEW_H
#define CG_VIEW_H
#include "config.h"
#include <stdbool.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
@ -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;

86
xwayland.c Normal file
View file

@ -0,0 +1,86 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2018 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
#include <stdbool.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/xwayland.h>
#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;
}

8
xwayland.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef XWAYLAND_H
#define XWAYLAND_H
#include <wayland-server.h>
void handle_xwayland_surface_new(struct wl_listener *listener, void *data);
#endif