diff --git a/.builds/alpine.yml b/.builds/alpine.yml
deleted file mode 100644
index 2c6d089..0000000
--- a/.builds/alpine.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-image: alpine/edge
-packages:
- - eudev-dev
- - mesa-dev
- - meson
- - libinput-dev
- - libxkbcommon-dev
- - pixman-dev
- - wayland-dev
- - wayland-protocols
- - xorg-server-xwayland
-sources:
- - https://github.com/swaywm/wlroots
- - https://github.com/Hjdskes/cage
-tasks:
- # Install wlroots, which is required by Cage. Note that we compile a tagged
- # version, instead of master, to avoid any breaking changes in wlroots.
- - wlroots: |
- cd wlroots
- # This corresponds to the tag of 0.6.0
- git checkout c0305f4f864543f8c3fea6f302e91c9b1d3396f3
- meson --prefix=/usr build -Drootston=false -Dexamples=false
- ninja -C build
- sudo ninja -C build install
- - build: |
- cd cage
- meson build -Dxwayland=true
- ninja -C build
diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml
deleted file mode 100644
index e052118..0000000
--- a/.builds/archlinux.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-image: archlinux
-packages:
- - clang
- - meson
- - libinput
- - libxkbcommon
- - wayland
- - wayland-protocols
- - xorg-server-xwayland
-sources:
- - https://github.com/swaywm/wlroots
- - https://github.com/Hjdskes/cage
-tasks:
- # Install wlroots, which is required by Cage. Note that we compile a tagged
- # version, instead of master, to avoid any breaking changes in wlroots.
- - wlroots: |
- cd wlroots
- # This corresponds to the tag of 0.6.0
- git checkout c0305f4f864543f8c3fea6f302e91c9b1d3396f3
- meson --prefix=/usr build -Drootston=false -Dexamples=false
- ninja -C build
- sudo ninja -C build install
- - build: |
- cd cage
- meson build -Dxwayland=true
- ninja -C build
- - scan-build: |
- cd cage
- CC=clang meson build -Dxwayland=true
- CC=clang ninja -C build scan-build
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
deleted file mode 100644
index 274eaaa..0000000
--- a/.builds/freebsd.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-image: freebsd/latest
-packages:
- - devel/evdev-proto
- - devel/meson
- - devel/libepoll-shim
- - devel/pkgconf
- - graphics/mesa-libs
- - graphics/wayland
- - graphics/wayland-protocols
- - x11/libinput
- - x11/libxkbcommon
- - x11/pixman
-sources:
- - https://github.com/swaywm/wlroots
- - https://github.com/Hjdskes/cage
-tasks:
- # Install wlroots, which is required by Cage. Note that we compile a tagged
- # version, instead of master, to avoid any breaking changes in wlroots.
- - wlroots: |
- cd wlroots
- # This corresponds to the tag of 0.6.0
- git checkout c0305f4f864543f8c3fea6f302e91c9b1d3396f3
- meson --prefix=/usr/local build -Drootston=false -Dexamples=false
- ninja -C build
- sudo ninja -C build install
- - build: |
- cd cage
- PKG_CONFIG_PATH=/usr/local/lib/pkgconfig meson build -Dxwayland=true
- PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ninja -C build
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..49b64c2
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,13 @@
+AlignAfterOpenBracket: Align
+AlignTrailingComments: false
+AlwaysBreakAfterReturnType: TopLevelDefinitions
+BreakBeforeBraces: Linux
+ColumnLimit: 120
+ContinuationIndentWidth: 8
+ForEachMacros: [wl_list_for_each, wl_list_for_each_safe, wl_list_for_each_reverse]
+IndentWidth: 8
+ReflowComments: true
+SortIncludes: true
+SpaceAfterCStyleCast: true
+TabWidth: 8
+UseTab: Always
diff --git a/.clang-format-ignore b/.clang-format-ignore
new file mode 100644
index 0000000..60dd059
--- /dev/null
+++ b/.clang-format-ignore
@@ -0,0 +1 @@
+subprojects/**/*
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e9e7800
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,10 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = tab
+indent_size = 8
+max_line_length = 120
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..31693bf
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,81 @@
+name: Continuous integration build
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+env:
+ WLROOTS_VERSION: 0.19
+
+jobs:
+ compile:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ CC: [ gcc, clang ]
+ OS: [ "alpine:edge", "archlinux:base-devel" ]
+ xwayland: [ enabled, disabled ]
+ container: ${{ matrix.OS }}
+ env:
+ CC: ${{ matrix.CC }}
+ steps:
+ - name: Checkout Cage
+ uses: actions/checkout@v2
+
+ - name: Install dependencies (Alpine)
+ if: "matrix.OS == 'alpine:edge'"
+ run: apk add build-base xcb-util-wm-dev libseat-dev clang git eudev-dev mesa-dev libdrm-dev libinput-dev libxkbcommon-dev pixman-dev wayland-dev meson wayland-protocols xwayland-dev scdoc-doc hwdata libdisplay-info-dev
+
+ - name: Install dependencies (Arch)
+ if: "matrix.OS == 'archlinux:base-devel'"
+ run: |
+ pacman-key --init
+ pacman -Syu --noconfirm xcb-util-wm seatd git clang meson libinput libdrm mesa libxkbcommon wayland wayland-protocols xorg-server-xwayland scdoc libdisplay-info
+
+ - name: Fetch wlroots as a subproject
+ run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b $WLROOTS_VERSION
+
+ - name: Compile Cage (XWayland=${{ matrix.xwayland }})
+ run: |
+ meson --fatal-meson-warnings --wrap-mode=nodownload \
+ build-${{ matrix.CC }}-${{matrix.xwayland }} \
+ -Dwlroots:xwayland=${{ matrix.xwayland }}
+ ninja -C build-${{ matrix.CC }}-${{matrix.xwayland }}
+
+ format:
+ runs-on: ubuntu-latest
+ container: "archlinux:base-devel"
+ steps:
+ - name: Checkout Cage
+ uses: actions/checkout@v2
+ - name: Install dependencies
+ run: |
+ pacman-key --init
+ pacman -Syu --noconfirm xcb-util-wm seatd git clang meson libinput libdrm mesa libxkbcommon wayland wayland-protocols xorg-server-xwayland scdoc hwdata libdisplay-info
+ - name: Fetch wlroots as a subproject
+ run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b $WLROOTS_VERSION
+ - name: Check for formatting changes
+ run: |
+ meson --wrap-mode=nodownload build-clang-format -Dwlroots:xwayland=enabled
+ ninja -C build-clang-format clang-format-check
+
+ scan-build:
+ runs-on: ubuntu-latest
+ container: "archlinux:base-devel"
+ env:
+ CC: clang
+ steps:
+ - name: Checkout Cage
+ uses: actions/checkout@v2
+ - name: Install dependencies
+ run: |
+ pacman-key --init
+ pacman -Syu --noconfirm xcb-util-wm seatd git clang meson libinput libdrm mesa libxkbcommon wayland wayland-protocols xorg-server-xwayland scdoc hwdata libdisplay-info
+ - name: Fetch wlroots as a subproject
+ run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b $WLROOTS_VERSION
+ - name: Run scan-build
+ run: |
+ meson --wrap-mode=nodownload build-scan-build -Dwlroots:xwayland=enabled
+ ninja -C build-scan-build scan-build
diff --git a/LICENSE b/LICENSE
index 41a8d9f..b047cf6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,5 @@
-Copyright (c) 2018-2019 Jente Hidskes
+Copyright (c) 2018-2020 Jente Hidskes
+Copyright (c) 2019 The Sway authors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
diff --git a/README.md b/README.md
index 280034e..d285219 100644
--- a/README.md
+++ b/README.md
@@ -1,60 +1,61 @@
-# Cage: a Wayland kiosk [](https://builds.sr.ht/~hjdskes?)
+# Cage: a Wayland kiosk
This is Cage, a Wayland kiosk. A kiosk runs a single, maximized
application.
-This README is only relevant for development resources and
-instructions. For a description of Cage and installation instructions
-for end-users, please see [its project
-page](https://hjdskes.nl/projects/cage).
+This README is only relevant for development resources and instructions. For a
+description of Cage and installation instructions for end-users, please see
+[its project page](https://www.hjdskes.nl/projects/cage) and [the
+Wiki](https://github.com/cage-kiosk/cage/wiki/).
+See [the man page](./cage.1.scd) for a list of possible environment variables and run options.
## Release signatures
-Releases are signed with
-[6EBC43B1](http://keys.gnupg.net/pks/lookup?op=vindex&fingerprint=on&search=0x37C445296EBC43B1)
-and published on [GitHub](https://github.com/Hjdskes/cage/releases).
+Releases up to version 0.1.4 are signed with [6EBC43B1](http://keys.gnupg.net/pks/lookup?op=vindex&fingerprint=on&search=0x37C445296EBC43B1). Releases from 0.1.5 onwards are signed with
+[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
+All releases are published on [GitHub](https://github.com/cage-kiosk/cage/releases).
## Building and running Cage
You can build Cage with the [meson](https://mesonbuild.com/) build system. It
-requires wayland, wlroots and xkbcommon to be installed. Note that Cage is
-developed against the latest tag of wlroots, in order to not constantly chase
-breaking changes as soon as they occur.
+requires wayland, wlroots, and xkbcommon to be installed. Optionally, install
+scdoc for manual pages. Cage is currently based on branch 0.18 of wlroots.
Simply execute the following steps to build Cage:
```
-$ meson build
-$ ninja -C build
+$ meson setup build
+$ meson compile -C build
```
-Cage comes with compile-time support for XWayland. To enable this,
-first make sure that your version of wlroots is compiled with this
-option. Then, add `-Dxwayland=true` to the `meson` command above. Note
-that you'll need to have the XWayland binary installed on your system
-for this to work.
+By default, this builds a debug build. To build a release build, use `meson
+setup build --buildtype=release`.
-You can run Cage by running `./build/cage APPLICATION`. If you run it
-from within an existing X11 or Wayland session, it will open in a
-virtual output as a window in your existing session. If you run it at
-a TTY, it'll run with the KMS+DRM backend. In debug mode (default
-build type with Meson), press Alt+Esc to quit. To build a release
-build, use `meson build --buildtype=release`.
+Cage comes with compile-time support for XWayland. To enable this, make sure
+that your version of wlroots is compiled with this option. Note that you'll
+need to have the XWayland binary installed on your system for this to work.
+
+You can run Cage by running `./build/cage APPLICATION`. If you run it from
+within an existing X11 or Wayland session, it will open in a virtual output as
+a window in your existing session. If you run it at a TTY, it'll run with the
+KMS+DRM backend. In debug mode (default build type with Meson), press
+Alt+Esc to quit. For more configuration options, see
+[Configuration](https://github.com/cage-kiosk/cage/wiki/Configuration).
Cage is based on the annotated source of tinywl and rootston.
## Bugs
For any bug, please [create an
-issue](https://github.com/Hjdskes/cage/issues/new) on
-[GitHub](https://github.com/Hjdskes/cage).
+issue](https://github.com/cage-kiosk/cage/issues/new) on
+[GitHub](https://github.com/cage-kiosk/cage).
## License
Please see
-[LICENSE](https://github.com/Hjdskes/cage/blob/master/LICENSE) on
-[GitHub](https://github.com/Hjdskes/cage).
+[LICENSE](https://github.com/cage-kiosk/cage/blob/master/LICENSE) on
+[GitHub](https://github.com/cage-kiosk/cage).
-Copyright © 2018-2019 Jente Hidskes
+Copyright © 2018-2020 Jente Hidskes
diff --git a/cage.1.scd b/cage.1.scd
new file mode 100644
index 0000000..ed1f518
--- /dev/null
+++ b/cage.1.scd
@@ -0,0 +1,71 @@
+cage(1)
+
+# NAME
+
+cage - a Wayland kiosk compositor
+
+# SYNOPSIS
+
+*cage* [options...] [--] [_application_...]
+
+# DESCRIPTION
+
+Cage runs a single, maximized application. Cage can run multiple applications,
+but only a single one is visible at any point in time. User interaction and
+activities outside the scope of the running application are prevented.
+
+# OPTIONS
+
+*-d*
+ Don't draw client side decorations when possible.
+
+*-D*
+ Enable debug logging.
+
+*-h*
+ Show the help message.
+
+*-m*
+ Set the multi-monitor behavior. Supported modes are:
+ *last* Cage uses only the last connected monitor.
+ *extend* Cage extends the display across all connected monitors.
+
+*-s*
+ Allow VT switching
+
+*-v*
+ Show the version number and exit.
+
+# ENVIRONMENT
+
+_DISPLAY_
+ If compiled with Xwayland support, this will be set to the name of the
+ X display used for Xwayland. Otherwise, probe the X11 backend.
+
+_WAYLAND_DISPLAY_
+ Specifies the name of the Wayland display that Cage is running on.
+
+_XCURSOR_PATH_
+ Directory where cursors are located.
+
+_XCURSOR_SIZE_
+ Specifies the configured cursor size.
+
+_XCURSOR_THEME_
+ Specifies the configured cursor theme.
+
+_XKB_DEFAULT_RULES_, _XKB_DEFAULT_MODEL_, _XKB_DEFAULT_LAYOUT_,
+_XKB_DEFAULT_VARIANT_, _XKB_DEFAULT_OPTIONS_
+ Configures the xkb keyboard settings. See *xkeyboard-config*(7).
+
+# SEE ALSO
+
+*xkeyboard-config(7)*
+
+# BUGS
+
+Report bugs at https://github.com/cage-kiosk/cage
+
+# AUTHORS
+
+Jente Hidskes
diff --git a/cage.c b/cage.c
index 9c7098a..9b7c510 100644
--- a/cage.c
+++ b/cage.c
@@ -1,7 +1,7 @@
/*
* Cage: A Wayland kiosk.
*
- * Copyright (C) 2018-2019 Jente Hidskes
+ * Copyright (C) 2018-2020 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
@@ -10,25 +10,41 @@
#include "config.h"
+#include
#include
#include
#include
#include
#include
#include
-#include
+#include
#include
+#include
#include
#include
#include
-#include
+#include
+#include
#include
+#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
#include
+#include
+#include
+#include
+#include
+#include
#if CAGE_HAS_XWAYLAND
#include
#endif
#include
+#include
#include
#include
#if CAGE_HAS_XWAYLAND
@@ -45,39 +61,143 @@
#include "xwayland.h"
#endif
-static bool
-spawn_primary_client(char *argv[], pid_t *pid_out)
+void
+server_terminate(struct cg_server *server)
{
+ // Workaround for https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/421
+ if (server->terminated) {
+ return;
+ }
+
+ wl_display_terminate(server->wl_display);
+}
+
+static void
+handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ struct cg_server *server = wl_container_of(listener, server, display_destroy);
+ server->terminated = true;
+}
+
+static int
+sigchld_handler(int fd, uint32_t mask, void *data)
+{
+ struct cg_server *server = data;
+
+ /* Close Cage's read pipe. */
+ close(fd);
+
+ if (mask & WL_EVENT_HANGUP) {
+ wlr_log(WLR_DEBUG, "Child process closed normally");
+ } else if (mask & WL_EVENT_ERROR) {
+ wlr_log(WLR_DEBUG, "Connection closed by server");
+ }
+
+ server->return_app_code = true;
+ server_terminate(server);
+ return 0;
+}
+
+static bool
+set_cloexec(int fd)
+{
+ int flags = fcntl(fd, F_GETFD);
+
+ if (flags == -1) {
+ wlr_log(WLR_ERROR, "Unable to set the CLOEXEC flag: fnctl failed");
+ return false;
+ }
+
+ flags = flags | FD_CLOEXEC;
+ if (fcntl(fd, F_SETFD, flags) == -1) {
+ wlr_log(WLR_ERROR, "Unable to set the CLOEXEC flag: fnctl failed");
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+spawn_primary_client(struct cg_server *server, char *argv[], pid_t *pid_out, struct wl_event_source **sigchld_source)
+{
+ int fd[2];
+ if (pipe(fd) != 0) {
+ wlr_log(WLR_ERROR, "Unable to create pipe");
+ return false;
+ }
+
pid_t pid = fork();
if (pid == 0) {
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
+ /* Close read, we only need write in the primary client process. */
+ close(fd[0]);
execvp(argv[0], argv);
+ /* execvp() returns only on failure */
+ wlr_log_errno(WLR_ERROR, "Failed to spawn client");
_exit(1);
} else if (pid == -1) {
wlr_log_errno(WLR_ERROR, "Unable to fork");
return false;
}
+ /* Set this early so that if we fail, the client process will be cleaned up properly. */
*pid_out = pid;
+
+ if (!set_cloexec(fd[0]) || !set_cloexec(fd[1])) {
+ return false;
+ }
+
+ /* Close write, we only need read in Cage. */
+ close(fd[1]);
+
+ struct wl_event_loop *event_loop = wl_display_get_event_loop(server->wl_display);
+ uint32_t mask = WL_EVENT_HANGUP | WL_EVENT_ERROR;
+ *sigchld_source = wl_event_loop_add_fd(event_loop, fd[0], mask, sigchld_handler, server);
+
wlr_log(WLR_DEBUG, "Child process created with pid %d", pid);
return true;
}
+static int
+cleanup_primary_client(pid_t pid)
+{
+ int status;
+
+ waitpid(pid, &status, 0);
+
+ if (WIFEXITED(status)) {
+ wlr_log(WLR_DEBUG, "Child exited normally with exit status %d", WEXITSTATUS(status));
+ return WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ /* Mimic Bash and other shells for the exit status */
+ wlr_log(WLR_DEBUG, "Child was terminated by a signal (%d)", WTERMSIG(status));
+ return 128 + WTERMSIG(status);
+ }
+
+ return 0;
+}
+
static bool
drop_permissions(void)
{
+ if (getuid() == 0 || getgid() == 0) {
+ wlr_log(WLR_INFO, "Running as root user, this is dangerous");
+ return true;
+ }
if (getuid() != geteuid() || getgid() != getegid()) {
- if (setuid(getuid()) != 0 || setgid(getgid()) != 0) {
+ wlr_log(WLR_INFO, "setuid/setgid bit detected, dropping permissions");
+ // Set the gid and uid in the correct order.
+ if (setgid(getgid()) != 0 || setuid(getuid()) != 0) {
wlr_log(WLR_ERROR, "Unable to drop root, refusing to start");
return false;
}
}
- if (setuid(0) != -1) {
- wlr_log(WLR_ERROR, "Unable to drop root (we shouldn't be able to "
- "restore it after setuid), refusing to start");
+ if (setgid(0) != -1 || setuid(0) != -1) {
+ wlr_log(WLR_ERROR,
+ "Unable to drop root (we shouldn't be able to restore it after setuid), refusing to start");
return false;
}
@@ -87,30 +207,32 @@ drop_permissions(void)
static int
handle_signal(int signal, void *data)
{
- struct wl_display *display = data;
+ struct cg_server *server = data;
switch (signal) {
case SIGINT:
/* Fallthrough */
case SIGTERM:
- wl_display_terminate(display);
+ server_terminate(server);
return 0;
default:
- return 1;
+ return 0;
}
}
static void
usage(FILE *file, const char *cage)
{
- fprintf(file, "Usage: %s [OPTIONS] [--] APPLICATION\n"
+ fprintf(file,
+ "Usage: %s [OPTIONS] [--] [APPLICATION...]\n"
"\n"
" -d\t Don't draw client side decorations, when possible\n"
- " -r\t Rotate the output 90 degrees clockwise, specify up to three times\n"
-#ifdef DEBUG
- " -D\t Turn on damage tracking debugging\n"
-#endif
+ " -D\t Enable debug logging\n"
" -h\t Display this help message\n"
+ " -m extend Extend the display across all connected outputs (default)\n"
+ " -m last Use only the last connected output\n"
+ " -s\t Allow VT switching\n"
+ " -v\t Show the version number and exit\n"
"\n"
" Use -- when you want to pass arguments to APPLICATION\n",
cage);
@@ -120,71 +242,62 @@ static bool
parse_args(struct cg_server *server, int argc, char *argv[])
{
int c;
-#ifdef DEBUG
- while ((c = getopt(argc, argv, "drDh")) != -1) {
-#else
- while ((c = getopt(argc, argv, "drh")) != -1) {
-#endif
+ while ((c = getopt(argc, argv, "dDhm:sv")) != -1) {
switch (c) {
case 'd':
server->xdg_decoration = true;
break;
- case 'r':
- server->output_transform++;
- if (server->output_transform > WL_OUTPUT_TRANSFORM_270) {
- server->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
- }
- break;
-#ifdef DEBUG
case 'D':
- server->debug_damage_tracking = true;
+ server->log_level = WLR_DEBUG;
break;
-#endif
case 'h':
usage(stdout, argv[0]);
return false;
+ case 'm':
+ if (strcmp(optarg, "last") == 0) {
+ server->output_mode = CAGE_MULTI_OUTPUT_MODE_LAST;
+ } else if (strcmp(optarg, "extend") == 0) {
+ server->output_mode = CAGE_MULTI_OUTPUT_MODE_EXTEND;
+ }
+ break;
+ case 's':
+ server->allow_vt_switch = true;
+ break;
+ case 'v':
+ fprintf(stdout, "Cage version " CAGE_VERSION "\n");
+ exit(0);
default:
usage(stderr, argv[0]);
return false;
}
}
- if (optind >= argc) {
- usage(stderr, argv[0]);
- return false;
- }
-
return true;
}
int
main(int argc, char *argv[])
{
- struct cg_server server = {0};
- struct wl_event_loop *event_loop = NULL;
- struct wl_event_source *sigint_source = NULL;
- struct wl_event_source *sigterm_source = NULL;
- struct wlr_renderer *renderer = NULL;
- struct wlr_compositor *compositor = NULL;
- struct wlr_data_device_manager *data_device_mgr = NULL;
- struct wlr_server_decoration_manager *server_decoration_manager = NULL;
- struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL;
- struct wlr_xdg_shell *xdg_shell = NULL;
-#if CAGE_HAS_XWAYLAND
- struct wlr_xwayland *xwayland = NULL;
- struct wlr_xcursor_manager *xcursor_manager = NULL;
+ struct cg_server server = {.log_level = WLR_INFO};
+ struct wl_event_source *sigchld_source = NULL;
+ pid_t pid = 0;
+ int ret = 0, app_ret = 0;
+
+#ifdef DEBUG
+ server.log_level = WLR_DEBUG;
#endif
- int ret = 0;
if (!parse_args(&server, argc, argv)) {
return 1;
}
-#ifdef DEBUG
- wlr_log_init(WLR_DEBUG, NULL);
-#else
- wlr_log_init(WLR_ERROR, NULL);
-#endif
+ wlr_log_init(server.log_level, NULL);
+
+ /* Wayland requires XDG_RUNTIME_DIR to be set. */
+ if (!getenv("XDG_RUNTIME_DIR")) {
+ wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set in the environment");
+ return 1;
+ }
server.wl_display = wl_display_create();
if (!server.wl_display) {
@@ -192,11 +305,14 @@ main(int argc, char *argv[])
return 1;
}
- event_loop = wl_display_get_event_loop(server.wl_display);
- sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server.wl_display);
- sigterm_source = wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server.wl_display);
+ server.display_destroy.notify = handle_display_destroy;
+ wl_display_add_destroy_listener(server.wl_display, &server.display_destroy);
- server.backend = wlr_backend_autocreate(server.wl_display, NULL);
+ struct wl_event_loop *event_loop = wl_display_get_event_loop(server.wl_display);
+ struct wl_event_source *sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server);
+ struct wl_event_source *sigterm_source = wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server);
+
+ server.backend = wlr_backend_autocreate(event_loop, &server.session);
if (!server.backend) {
wlr_log(WLR_ERROR, "Unable to create the wlroots backend");
ret = 1;
@@ -208,46 +324,82 @@ main(int argc, char *argv[])
goto end;
}
- renderer = wlr_backend_get_renderer(server.backend);
- wlr_renderer_init_wl_display(renderer, server.wl_display);
+ server.renderer = wlr_renderer_autocreate(server.backend);
+ if (!server.renderer) {
+ wlr_log(WLR_ERROR, "Unable to create the wlroots renderer");
+ ret = 1;
+ goto end;
+ }
+
+ server.allocator = wlr_allocator_autocreate(server.backend, server.renderer);
+ if (!server.allocator) {
+ wlr_log(WLR_ERROR, "Unable to create the wlroots allocator");
+ ret = 1;
+ goto end;
+ }
+
+ wlr_renderer_init_wl_display(server.renderer, server.wl_display);
wl_list_init(&server.views);
+ wl_list_init(&server.outputs);
- server.output_layout = wlr_output_layout_create();
+ server.output_layout = wlr_output_layout_create(server.wl_display);
if (!server.output_layout) {
wlr_log(WLR_ERROR, "Unable to create output layout");
ret = 1;
goto end;
}
+ server.output_layout_change.notify = handle_output_layout_change;
+ wl_signal_add(&server.output_layout->events.change, &server.output_layout_change);
- compositor = wlr_compositor_create(server.wl_display, renderer);
+ server.scene = wlr_scene_create();
+ if (!server.scene) {
+ wlr_log(WLR_ERROR, "Unable to create scene");
+ ret = 1;
+ goto end;
+ }
+
+ server.scene_output_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout);
+
+ struct wlr_compositor *compositor = wlr_compositor_create(server.wl_display, 6, server.renderer);
if (!compositor) {
wlr_log(WLR_ERROR, "Unable to create the wlroots compositor");
ret = 1;
goto end;
}
- data_device_mgr = wlr_data_device_manager_create(server.wl_display);
- if (!data_device_mgr) {
+ if (!wlr_subcompositor_create(server.wl_display)) {
+ wlr_log(WLR_ERROR, "Unable to create the wlroots subcompositor");
+ ret = 1;
+ goto end;
+ }
+
+ if (!wlr_data_device_manager_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the data device manager");
ret = 1;
goto end;
}
+ if (!wlr_primary_selection_v1_device_manager_create(server.wl_display)) {
+ wlr_log(WLR_ERROR, "Unable to create primary selection device manager");
+ ret = 1;
+ goto end;
+ }
+
/* Configure a listener to be notified when new outputs are
* available on the backend. We use this only to detect the
* first output and ignore subsequent outputs. */
server.new_output.notify = handle_new_output;
wl_signal_add(&server.backend->events.new_output, &server.new_output);
- server.seat = seat_create(&server);
+ server.seat = seat_create(&server, server.backend);
if (!server.seat) {
wlr_log(WLR_ERROR, "Unable to create the seat");
ret = 1;
goto end;
}
- server.idle = wlr_idle_create(server.wl_display);
+ server.idle = wlr_idle_notifier_v1_create(server.wl_display);
if (!server.idle) {
wlr_log(WLR_ERROR, "Unable to create the idle tracker");
ret = 1;
@@ -264,16 +416,19 @@ main(int argc, char *argv[])
wl_signal_add(&server.idle_inhibit_v1->events.new_inhibitor, &server.new_idle_inhibitor_v1);
wl_list_init(&server.inhibitors);
- xdg_shell = wlr_xdg_shell_create(server.wl_display);
+ struct wlr_xdg_shell *xdg_shell = wlr_xdg_shell_create(server.wl_display, 5);
if (!xdg_shell) {
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
ret = 1;
goto end;
}
- server.new_xdg_shell_surface.notify = handle_xdg_shell_surface_new;
- wl_signal_add(&xdg_shell->events.new_surface, &server.new_xdg_shell_surface);
+ server.new_xdg_toplevel.notify = handle_new_xdg_toplevel;
+ wl_signal_add(&xdg_shell->events.new_toplevel, &server.new_xdg_toplevel);
+ server.new_xdg_popup.notify = handle_new_xdg_popup;
+ wl_signal_add(&xdg_shell->events.new_popup, &server.new_xdg_popup);
- xdg_decoration_manager = wlr_xdg_decoration_manager_v1_create(server.wl_display);
+ struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager =
+ wlr_xdg_decoration_manager_v1_create(server.wl_display);
if (!xdg_decoration_manager) {
wlr_log(WLR_ERROR, "Unable to create the XDG decoration manager");
ret = 1;
@@ -282,51 +437,128 @@ main(int argc, char *argv[])
wl_signal_add(&xdg_decoration_manager->events.new_toplevel_decoration, &server.xdg_toplevel_decoration);
server.xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration;
- server_decoration_manager = wlr_server_decoration_manager_create(server.wl_display);
+ struct wlr_server_decoration_manager *server_decoration_manager =
+ wlr_server_decoration_manager_create(server.wl_display);
if (!server_decoration_manager) {
wlr_log(WLR_ERROR, "Unable to create the server decoration manager");
ret = 1;
goto end;
}
- wlr_server_decoration_manager_set_default_mode(server_decoration_manager,
- server.xdg_decoration ?
- WLR_SERVER_DECORATION_MANAGER_MODE_SERVER :
- WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
+ wlr_server_decoration_manager_set_default_mode(
+ server_decoration_manager, server.xdg_decoration ? WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
+ : WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
-#if CAGE_HAS_XWAYLAND
- xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
- if (!xwayland) {
- wlr_log(WLR_ERROR, "Cannot create XWayland server");
+ if (!wlr_viewporter_create(server.wl_display)) {
+ wlr_log(WLR_ERROR, "Unable to create the viewporter interface");
ret = 1;
goto end;
}
- server.new_xwayland_surface.notify = handle_xwayland_surface_new;
- wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
- xcursor_manager = wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE);
- if (!xcursor_manager) {
- wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager");
- ret = 1;
+ struct wlr_presentation *presentation = wlr_presentation_create(server.wl_display, server.backend, 2);
+ if (!presentation) {
+ wlr_log(WLR_ERROR, "Unable to create the presentation interface");
+ ret = 1;
goto end;
}
- if (setenv("DISPLAY", xwayland->display_name, true) < 0) {
- wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.",
- "Clients may not be able to connect");
- } else {
- wlr_log(WLR_DEBUG, "XWayland is running on display %s", xwayland->display_name);
+ if (!wlr_export_dmabuf_manager_v1_create(server.wl_display)) {
+ wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager");
+ ret = 1;
+ goto end;
}
- if (wlr_xcursor_manager_load(xcursor_manager, 1)) {
- wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
+ if (!wlr_screencopy_manager_v1_create(server.wl_display)) {
+ wlr_log(WLR_ERROR, "Unable to create the screencopy manager");
+ ret = 1;
+ goto end;
}
- 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);
+
+ if (!wlr_single_pixel_buffer_manager_v1_create(server.wl_display)) {
+ wlr_log(WLR_ERROR, "Unable to create the single pixel buffer manager");
+ ret = 1;
+ goto end;
+ }
+
+ if (!wlr_xdg_output_manager_v1_create(server.wl_display, server.output_layout)) {
+ wlr_log(WLR_ERROR, "Unable to create the output manager");
+ ret = 1;
+ goto end;
+ }
+
+ server.output_manager_v1 = wlr_output_manager_v1_create(server.wl_display);
+ if (!server.output_manager_v1) {
+ wlr_log(WLR_ERROR, "Unable to create the output manager");
+ ret = 1;
+ goto end;
+ }
+ server.output_manager_apply.notify = handle_output_manager_apply;
+ wl_signal_add(&server.output_manager_v1->events.apply, &server.output_manager_apply);
+ server.output_manager_test.notify = handle_output_manager_test;
+ wl_signal_add(&server.output_manager_v1->events.test, &server.output_manager_test);
+
+ if (!wlr_gamma_control_manager_v1_create(server.wl_display)) {
+ wlr_log(WLR_ERROR, "Unable to create the gamma control manager");
+ ret = 1;
+ goto end;
+ }
+
+ struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard =
+ wlr_virtual_keyboard_manager_v1_create(server.wl_display);
+ if (!virtual_keyboard) {
+ wlr_log(WLR_ERROR, "Unable to create the virtual keyboard manager");
+ ret = 1;
+ goto end;
+ }
+ wl_signal_add(&virtual_keyboard->events.new_virtual_keyboard, &server.new_virtual_keyboard);
+
+ struct wlr_virtual_pointer_manager_v1 *virtual_pointer =
+ wlr_virtual_pointer_manager_v1_create(server.wl_display);
+ if (!virtual_pointer) {
+ wlr_log(WLR_ERROR, "Unable to create the virtual pointer manager");
+ ret = 1;
+ goto end;
+ }
+ wl_signal_add(&virtual_pointer->events.new_virtual_pointer, &server.new_virtual_pointer);
+
+ server.relative_pointer_manager = wlr_relative_pointer_manager_v1_create(server.wl_display);
+ if (!server.relative_pointer_manager) {
+ wlr_log(WLR_ERROR, "Unable to create the relative pointer manager");
+ ret = 1;
+ goto end;
+ }
+
+#if CAGE_HAS_XWAYLAND
+ struct wlr_xcursor_manager *xcursor_manager = NULL;
+ struct wlr_xwayland *xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
+ if (!xwayland) {
+ wlr_log(WLR_ERROR, "Cannot create XWayland server");
+ } else {
+ server.new_xwayland_surface.notify = handle_xwayland_surface_new;
+ wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
+
+ 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 (setenv("DISPLAY", xwayland->display_name, true) < 0) {
+ wlr_log_errno(WLR_ERROR,
+ "Unable to set DISPLAY for XWayland. Clients may not be able to connect");
+ } else {
+ wlr_log(WLR_DEBUG, "XWayland is running on display %s", xwayland->display_name);
+ }
+
+ 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
@@ -334,7 +566,7 @@ main(int argc, char *argv[])
if (!socket) {
wlr_log_errno(WLR_ERROR, "Unable to open Wayland socket");
ret = 1;
- goto end;
+ goto end;
}
if (!wlr_backend_start(server.backend)) {
@@ -344,48 +576,62 @@ main(int argc, char *argv[])
}
if (setenv("WAYLAND_DISPLAY", socket, true) < 0) {
- wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.",
- "Clients may not be able to connect");
+ wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY. Clients may not be able to connect");
} else {
- wlr_log(WLR_DEBUG, "Cage is running on Wayland display %s", socket);
+ wlr_log(WLR_DEBUG, "Cage " CAGE_VERSION " is running on Wayland display %s", socket);
}
#if CAGE_HAS_XWAYLAND
- wlr_xwayland_set_seat(xwayland, server.seat->seat);
+ if (xwayland) {
+ wlr_xwayland_set_seat(xwayland, server.seat->seat);
+ }
#endif
- pid_t pid;
- if (!spawn_primary_client(argv + optind, &pid)) {
+ if (optind < argc && !spawn_primary_client(&server, argv + optind, &pid, &sigchld_source)) {
ret = 1;
goto end;
}
+ seat_center_cursor(server.seat);
wl_display_run(server.wl_display);
#if CAGE_HAS_XWAYLAND
+ if (xwayland) {
+ wl_list_remove(&server.new_xwayland_surface.link);
+ }
wlr_xwayland_destroy(xwayland);
wlr_xcursor_manager_destroy(xcursor_manager);
#endif
wl_display_destroy_clients(server.wl_display);
- waitpid(pid, NULL, 0);
+ wl_list_remove(&server.new_virtual_pointer.link);
+ wl_list_remove(&server.new_virtual_keyboard.link);
+ wl_list_remove(&server.output_manager_apply.link);
+ wl_list_remove(&server.output_manager_test.link);
+ wl_list_remove(&server.xdg_toplevel_decoration.link);
+ wl_list_remove(&server.new_xdg_toplevel.link);
+ wl_list_remove(&server.new_xdg_popup.link);
+ wl_list_remove(&server.new_idle_inhibitor_v1.link);
+ wl_list_remove(&server.new_output.link);
+ wl_list_remove(&server.output_layout_change.link);
end:
+ if (pid != 0)
+ app_ret = cleanup_primary_client(pid);
+ if (!ret && server.return_app_code)
+ ret = app_ret;
+
wl_event_source_remove(sigint_source);
wl_event_source_remove(sigterm_source);
- seat_destroy(server.seat);
- wlr_server_decoration_manager_destroy(server_decoration_manager);
- wlr_xdg_decoration_manager_v1_destroy(xdg_decoration_manager);
- wlr_xdg_shell_destroy(xdg_shell);
- wlr_idle_inhibit_v1_destroy(server.idle_inhibit_v1);
- if (server.idle) {
- wlr_idle_destroy(server.idle);
+ if (sigchld_source) {
+ wl_event_source_remove(sigchld_source);
}
- wlr_data_device_manager_destroy(data_device_mgr);
- wlr_compositor_destroy(compositor);
- wlr_output_layout_destroy(server.output_layout);
+ seat_destroy(server.seat);
/* This function is not null-safe, but we only ever get here
with a proper wl_display. */
wl_display_destroy(server.wl_display);
+ wlr_scene_node_destroy(&server.scene->tree.node);
+ wlr_allocator_destroy(server.allocator);
+ wlr_renderer_destroy(server.renderer);
return ret;
}
diff --git a/config.h.in b/config.h.in
index 3305da9..51137ec 100644
--- a/config.h.in
+++ b/config.h.in
@@ -3,4 +3,6 @@
#mesondefine CAGE_HAS_XWAYLAND
+#mesondefine CAGE_VERSION
+
#endif
diff --git a/contrib/increment-version b/contrib/increment-version
new file mode 100755
index 0000000..11a3354
--- /dev/null
+++ b/contrib/increment-version
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+if [ "$#" -ne 1 ]; then
+ echo "usage: $0 " >&2
+ exit 1
+fi
+
+new_version="$1"
+
+if [ "$new_version" != "${new_version#v}" ]; then
+ echo "Error: The new version shouldn't be prefixed with a \"v\"." >&2
+ exit 1
+fi
+
+set -x
+
+sed -i meson.build -e "s/^ version: '.*'/ version: '$new_version'/"
+
+echo -n "Minimum wlroots version? "
+read -r wlr_version_min
+
+sed -i meson.build -e "s/'wlroots', version: '.*'/'wlroots', version: '>= $wlr_version_min'/"
+
+git add meson.build
+git commit -m "Update version to $new_version"
\ No newline at end of file
diff --git a/contrib/release b/contrib/release
new file mode 100755
index 0000000..514d370
--- /dev/null
+++ b/contrib/release
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+if [ "$#" -ne 1 ]; then
+ echo "usage: $0 " >&2
+ exit 1
+fi
+
+new_version="$1"
+
+if [ "$new_version" != "${new_version#v}" ]; then
+ echo "Error: The new version shouldn't be prefixed with a \"v\"." >&2
+ exit 1
+fi
+
+set -x
+
+./increment_version "$new_version"
+./tag-release "$new_version"
+./sign-release
+
+git push --tags
\ No newline at end of file
diff --git a/contrib/sign-release b/contrib/sign-release
new file mode 100755
index 0000000..b11fd02
--- /dev/null
+++ b/contrib/sign-release
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -x
+
+project="$(basename "$(pwd)")"
+last=$(git describe --tags --abbrev=0)
+
+prefix="$project-${last#v}"
+archive="$prefix.tar.gz"
+
+git archive --prefix="$prefix/" -o "$archive" "$last"
+gpg --output "$archive".sig --detach-sig "$archive"
diff --git a/contrib/tag-release b/contrib/tag-release
new file mode 100755
index 0000000..4452964
--- /dev/null
+++ b/contrib/tag-release
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -x
+
+if [ "$#" -ne 1 ]; then
+ echo "usage: $0 " >&2
+ exit 1
+fi
+
+last=$(git describe --tags --abbrev=0)
+echo "Last release was $last"
+
+next="v$1"
+
+shortlog="$(git shortlog --no-merges "$last"..)"
+
+printf "Shortlog: \n\n%s\n\nRelease $next? [y/N] " "$shortlog"
+read -r answer
+
+if [ "$answer" != "y" ]; then
+ exit 0
+fi
+
+project="$(basename "$(pwd)")"
+
+(echo "$project $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F -
diff --git a/idle_inhibit_v1.c b/idle_inhibit_v1.c
index 3b950ad..683cfe2 100644
--- a/idle_inhibit_v1.c
+++ b/idle_inhibit_v1.c
@@ -1,15 +1,15 @@
/*
* Cage: A Wayland kiosk.
- *
+ *
* Copyright (C) 2018-2019 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
#include
-#include
-#include
+#include
#include
+#include
#include "idle_inhibit_v1.h"
#include "server.h"
@@ -32,7 +32,7 @@ idle_inhibit_v1_check_active(struct cg_server *server)
Hence, we simply check for any inhibitors and inhibit
accordingly. */
bool inhibited = !wl_list_empty(&server->inhibitors);
- wlr_idle_set_enabled(server->idle, NULL, !inhibited);
+ wlr_idle_notifier_v1_set_inhibited(server->idle, inhibited);
}
static void
diff --git a/idle_inhibit_v1.h b/idle_inhibit_v1.h
index 5cde94f..e9bb25b 100644
--- a/idle_inhibit_v1.h
+++ b/idle_inhibit_v1.h
@@ -1,7 +1,7 @@
#ifndef CG_IDLE_INHIBIT_H
#define CG_IDLE_INHIBIT_H
-#include
+#include
void handle_idle_inhibitor_v1_new(struct wl_listener *listener, void *data);
diff --git a/meson.build b/meson.build
index 0f2c9ff..f8ccd76 100644
--- a/meson.build
+++ b/meson.build
@@ -1,9 +1,10 @@
project('cage', 'c',
- version: '0.1.1',
+ version: '0.2.1',
license: 'MIT',
+ meson_version: '>=0.58.1',
default_options: [
'c_std=c11',
- 'warning_level=3',
+ 'warning_level=2',
'werror=true',
],
)
@@ -11,7 +12,6 @@ project('cage', 'c',
add_project_arguments(
[
'-DWLR_USE_UNSTABLE',
- '-Wall',
'-Wundef',
'-Wno-unused-parameter',
],
@@ -35,14 +35,13 @@ if is_freebsd
)
endif
-wlroots = dependency('wlroots', version: '>= 0.6.0')
+wlroots = dependency('wlroots-0.19', fallback: ['wlroots', 'wlroots'])
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
wayland_server = dependency('wayland-server')
-pixman = dependency('pixman-1')
xkbcommon = dependency('xkbcommon')
math = cc.find_library('m')
-wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
+wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
wayland_scanner = find_program('wayland-scanner')
wayland_scanner_server = generator(
wayland_scanner,
@@ -65,19 +64,51 @@ server_protos = declare_dependency(
sources: server_protos_headers,
)
-if get_option('xwayland')
- wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include ', dependencies: wlroots) == '1'
- if not wlroots_has_xwayland
- error('Cannot build Cage with XWayland support: wlroots has been built without it')
- else
- have_xwayland = true
+have_xwayland = wlroots.get_variable(pkgconfig: 'have_xwayland', internal: 'have_xwayland') == 'true'
+
+version = '@0@'.format(meson.project_version())
+git = find_program('git', native: true, required: false)
+if git.found()
+ git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
+ git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
+ if git_commit.returncode() == 0 and git_branch.returncode() == 0
+ version = '@0@-@1@ (branch \'@2@\')'.format(
+ meson.project_version(),
+ git_commit.stdout().strip(),
+ git_branch.stdout().strip(),
+ )
endif
-else
- have_xwayland = false
endif
conf_data = configuration_data()
conf_data.set10('CAGE_HAS_XWAYLAND', have_xwayland)
+conf_data.set_quoted('CAGE_VERSION', version)
+
+scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
+if scdoc.found()
+ scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
+ sh = find_program('sh', native: true)
+ mandir = get_option('mandir')
+ man_files = [
+ 'cage.1.scd'
+ ]
+ foreach filename : man_files
+ topic = filename.split('.')[-3].split('/')[-1]
+ section = filename.split('.')[-2]
+ output = '@0@.@1@'.format(topic, section)
+
+ custom_target(
+ output,
+ input: filename,
+ output: output,
+ command: [
+ sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output)
+ ],
+ install: true,
+ install_dir: '@0@/man@1@'.format(mandir, section)
+ )
+ endforeach
+endif
cage_sources = [
'cage.c',
@@ -90,8 +121,8 @@ cage_sources = [
cage_headers = [
configure_file(input: 'config.h.in',
- output: 'config.h',
- configuration: conf_data),
+ output: 'config.h',
+ configuration: conf_data),
'idle_inhibit_v1.h',
'output.h',
'seat.h',
@@ -113,17 +144,16 @@ executable(
wayland_server,
wlroots,
xkbcommon,
- pixman,
math,
],
install: true,
)
summary = [
- '',
- 'Cage @0@'.format(meson.project_version()),
- '',
- ' xwayland: @0@'.format(conf_data.get('CAGE_HAS_XWAYLAND', false)),
- ''
+ '',
+ 'Cage @0@'.format(version),
+ '',
+ ' xwayland: @0@'.format(have_xwayland),
+ ''
]
message('\n'.join(summary))
diff --git a/meson_options.txt b/meson_options.txt
index 87763ff..e40a23d 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1 +1 @@
-option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications')
+option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
diff --git a/output.c b/output.c
index 658f422..093836b 100644
--- a/output.c
+++ b/output.c
@@ -1,7 +1,8 @@
/*
* Cage: A Wayland kiosk.
*
- * Copyright (C) 2018-2019 Jente Hidskes
+ * Copyright (C) 2018-2021 Jente Hidskes
+ * Copyright (C) 2019 The Sway authors
*
* See the LICENSE file accompanying this file.
*/
@@ -9,336 +10,224 @@
#define _POSIX_C_SOURCE 200112L
#include "config.h"
-#include
+#include
#include
#include
-#include
+#include
#include
#include
+#include
#if WLR_HAS_X11_BACKEND
#include
#endif
+#include
#include
+#include
#include
-#include
#include
-#include
#include
-#include
+#include
+#include
+#include
#include
#include
#include
#include "output.h"
+#include "seat.h"
#include "server.h"
#include "view.h"
+#if CAGE_HAS_XWAYLAND
+#include "xwayland.h"
+#endif
+
+#define OUTPUT_CONFIG_UPDATED \
+ (WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM | \
+ WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED)
static void
-scissor_output(struct wlr_output *output, pixman_box32_t *rect)
+update_output_manager_config(struct cg_server *server)
{
- struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
+ struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create();
- struct wlr_box box = {
- .x = rect->x1,
- .y = rect->y1,
- .width = rect->x2 - rect->x1,
- .height = rect->y2 - rect->y1,
- };
-
- int output_width, output_height;
- wlr_output_transformed_resolution(output, &output_width, &output_height);
- enum wl_output_transform transform = wlr_output_transform_invert(output->transform);
- wlr_box_transform(&box, &box, transform, output_width, output_height);
-
- wlr_renderer_scissor(renderer, &box);
-}
-
-static void
-send_frame_done(struct wlr_surface *surface, int _unused, int _not_used, void *data)
-{
- struct timespec *now = data;
- wlr_surface_send_frame_done(surface, now);
-}
-
-/* Used to move all of the data necessary to damage a surface. */
-struct damage_data {
struct cg_output *output;
- double x;
- double y;
- bool whole;
-};
+ wl_list_for_each (output, &server->outputs, link) {
+ struct wlr_output *wlr_output = output->wlr_output;
+ struct wlr_output_configuration_head_v1 *config_head =
+ wlr_output_configuration_head_v1_create(config, wlr_output);
+ struct wlr_box output_box;
+
+ wlr_output_layout_get_box(server->output_layout, wlr_output, &output_box);
+ if (!wlr_box_empty(&output_box)) {
+ config_head->state.x = output_box.x;
+ config_head->state.y = output_box.y;
+ }
+ }
+
+ wlr_output_manager_v1_set_configuration(server->output_manager_v1, config);
+}
+
+static inline void
+output_layout_add_auto(struct cg_output *output)
+{
+ assert(output->scene_output != NULL);
+ struct wlr_output_layout_output *layout_output =
+ wlr_output_layout_add_auto(output->server->output_layout, output->wlr_output);
+ wlr_scene_output_layout_add_output(output->server->scene_output_layout, layout_output, output->scene_output);
+}
+
+static inline void
+output_layout_add(struct cg_output *output, int32_t x, int32_t y)
+{
+ assert(output->scene_output != NULL);
+ bool exists = wlr_output_layout_get(output->server->output_layout, output->wlr_output);
+ struct wlr_output_layout_output *layout_output =
+ wlr_output_layout_add(output->server->output_layout, output->wlr_output, x, y);
+ if (exists) {
+ return;
+ }
+ wlr_scene_output_layout_add_output(output->server->scene_output_layout, layout_output, output->scene_output);
+}
+
+static inline void
+output_layout_remove(struct cg_output *output)
+{
+ wlr_output_layout_remove(output->server->output_layout, output->wlr_output);
+}
static void
-damage_surface(struct wlr_surface *surface, int sx, int sy, void *data)
+output_enable(struct cg_output *output)
{
- struct damage_data *ddata = data;
- struct cg_output *output = ddata->output;
struct wlr_output *wlr_output = output->wlr_output;
- if (!wlr_surface_has_buffer(surface)) {
- return;
+ /* Outputs get enabled by the backend before firing the new_output event,
+ * so we can't do a check for already enabled outputs here unless we
+ * duplicate the enabled property in cg_output. */
+ wlr_log(WLR_DEBUG, "Enabling output %s", wlr_output->name);
+
+ struct wlr_output_state state = {0};
+ wlr_output_state_set_enabled(&state, true);
+
+ if (wlr_output_commit_state(wlr_output, &state)) {
+ output_layout_add_auto(output);
}
- double x = ddata->x + sx, y = ddata->y + sy;
- wlr_output_layout_output_coords(output->server->output_layout, wlr_output, &x, &y);
-
- struct wlr_box box = {
- .x = x * wlr_output->scale,
- .y = y * wlr_output->scale,
- .width = surface->current.width * wlr_output->scale,
- .height = surface->current.height * wlr_output->scale,
- };
-
- if (ddata->whole) {
- wlr_output_damage_add_box(output->damage, &box);
- } else if (pixman_region32_not_empty(&surface->buffer_damage)) {
- pixman_region32_t damage;
- pixman_region32_init(&damage);
- wlr_surface_get_effective_damage(surface, &damage);
-
- wlr_region_scale(&damage, &damage, wlr_output->scale);
- if (ceil(wlr_output->scale) > surface->current.scale) {
- /* When scaling up a surface it'll become
- blurry, so we need to expand the damage
- region. */
- wlr_region_expand(&damage, &damage,
- ceil(wlr_output->scale) - surface->current.scale);
- }
- pixman_region32_translate(&damage, box.x, box.y);
- wlr_output_damage_add(output->damage, &damage);
- pixman_region32_fini(&damage);
- }
-}
-
-/* Used to move all of the data necessary to render a surface from the
- * top-level frame handler to the per-surface render function. */
-struct render_data {
- struct wlr_output_layout *output_layout;
- struct wlr_output *output;
- struct timespec *when;
- pixman_region32_t *damage;
- double x, y;
-};
-
-static void
-render_surface(struct wlr_surface *surface, int sx, int sy, void *data)
-{
- struct render_data *rdata = data;
- struct wlr_output *output = rdata->output;
-
- if (!wlr_surface_has_buffer(surface)) {
- return;
- }
-
- struct wlr_texture *texture = wlr_surface_get_texture(surface);
- if (!texture) {
- wlr_log(WLR_DEBUG, "Cannot obtain surface texture");
- return;
- }
-
- double x = rdata->x + sx, y = rdata->y + sy;
- wlr_output_layout_output_coords(rdata->output_layout, output, &x, &y);
-
- struct wlr_box box = {
- .x = x * output->scale,
- .y = y * output->scale,
- .width = surface->current.width * output->scale,
- .height = surface->current.height * output->scale,
- };
-
- pixman_region32_t damage;
- pixman_region32_init(&damage);
- pixman_region32_union_rect(&damage, &damage, box.x, box.y, box.width, box.height);
- pixman_region32_intersect(&damage, &damage, rdata->damage);
- if (!pixman_region32_not_empty(&damage)) {
- goto buffer_damage_finish;
- }
-
- float matrix[9];
- enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
- wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix);
-
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
- for (int i = 0; i < nrects; i++) {
- scissor_output(output, &rects[i]);
- wlr_render_texture_with_matrix(surface->renderer, texture, matrix, 1);
- }
-
- buffer_damage_finish:
- pixman_region32_fini(&damage);
+ update_output_manager_config(output->server);
}
static void
-drag_icons_for_each_surface(struct cg_server *server, wlr_surface_iterator_func_t iterator,
- void *data)
+output_disable(struct cg_output *output)
{
- struct render_data *rdata = data;
-
- struct cg_drag_icon *drag_icon;
- wl_list_for_each(drag_icon, &server->seat->drag_icons, link) {
- if (!drag_icon->wlr_drag_icon->mapped) {
- continue;
- }
- rdata->x = drag_icon->x;
- rdata->y = drag_icon->y;
- wlr_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
- iterator,
- data);
+ struct wlr_output *wlr_output = output->wlr_output;
+ if (!wlr_output->enabled) {
+ wlr_log(WLR_DEBUG, "Not disabling already disabled output %s", wlr_output->name);
+ return;
}
+
+ wlr_log(WLR_DEBUG, "Disabling output %s", wlr_output->name);
+ struct wlr_output_state state = {0};
+ wlr_output_state_set_enabled(&state, false);
+ wlr_output_commit_state(wlr_output, &state);
+ output_layout_remove(output);
}
static void
-handle_output_damage_frame(struct wl_listener *listener, void *data)
+handle_output_frame(struct wl_listener *listener, void *data)
{
- struct cg_output *output = wl_container_of(listener, output, damage_frame);
- struct wlr_renderer *renderer = wlr_backend_get_renderer(output->server->backend);
+ struct cg_output *output = wl_container_of(listener, output, frame);
- struct timespec now;
+ if (!output->wlr_output->enabled || !output->scene_output) {
+ return;
+ }
+
+ wlr_scene_output_commit(output->scene_output, NULL);
+
+ struct timespec now = {0};
clock_gettime(CLOCK_MONOTONIC, &now);
-
- bool needs_frame;
- pixman_region32_t buffer_damage;
- pixman_region32_init(&buffer_damage);
- if (!wlr_output_damage_attach_render(output->damage, &needs_frame, &buffer_damage)) {
- wlr_log(WLR_ERROR, "Cannot make damage output current");
- goto buffer_damage_finish;
- }
-
- if (!needs_frame) {
- wlr_log(WLR_DEBUG, "Output doesn't need frame and isn't damaged");
- goto buffer_damage_finish;
- }
-
- wlr_renderer_begin(renderer, output->wlr_output->width, output->wlr_output->height);
-
- if (!pixman_region32_not_empty(&buffer_damage)) {
- wlr_log(WLR_DEBUG, "Output isn't damaged but needs a buffer frame");
- goto renderer_end;
- }
-
-#ifdef DEBUG
- if (output->server->debug_damage_tracking) {
- wlr_renderer_clear(renderer, (float[]){1, 0, 0, 1});
- }
-#endif
-
- float color[4] = {0.3, 0.3, 0.3, 1.0};
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(&buffer_damage, &nrects);
- for (int i = 0; i < nrects; i++) {
- scissor_output(output->wlr_output, &rects[i]);
- wlr_renderer_clear(renderer, color);
- }
-
- struct render_data rdata = {
- .output_layout = output->server->output_layout,
- .output = output->wlr_output,
- .when = &now,
- .damage = &buffer_damage,
- };
-
- struct cg_view *view;
- wl_list_for_each_reverse(view, &output->server->views, link) {
- rdata.x = view->x;
- rdata.y = view->y;
- view_for_each_surface(view, render_surface, &rdata);
- }
-
- drag_icons_for_each_surface(output->server, render_surface, &rdata);
-
- renderer_end:
- /* Draw software cursor in case hardware cursors aren't
- available. This is a no-op when they are. */
- wlr_output_render_software_cursors(output->wlr_output, &buffer_damage);
- wlr_renderer_scissor(renderer, NULL);
- wlr_renderer_end(renderer);
-
- int output_width, output_height;
- wlr_output_transformed_resolution(output->wlr_output, &output_width, &output_height);
-
- pixman_region32_t frame_damage;
- pixman_region32_init(&frame_damage);
-
- enum wl_output_transform transform = wlr_output_transform_invert(output->wlr_output->transform);
- wlr_region_transform(&frame_damage, &output->damage->current, transform, output_width, output_height);
-
-#ifdef DEBUG
- if (output->server->debug_damage_tracking) {
- pixman_region32_union_rect(&frame_damage, &frame_damage, 0, 0, output_width, output_height);
- }
-#endif
-
- wlr_output_set_damage(output->wlr_output, &frame_damage);
- pixman_region32_fini(&frame_damage);
-
- if (!wlr_output_commit(output->wlr_output)) {
- wlr_log(WLR_ERROR, "Could not commit output");
- goto buffer_damage_finish;
- }
-
- buffer_damage_finish:
- pixman_region32_fini(&buffer_damage);
-
- wl_list_for_each_reverse(view, &output->server->views, link) {
- view_for_each_surface(view, send_frame_done, &now);
- }
- drag_icons_for_each_surface(output->server, send_frame_done, &now);
+ wlr_scene_output_send_frame_done(output->scene_output, &now);
}
static void
-handle_output_transform(struct wl_listener *listener, void *data)
+handle_output_commit(struct wl_listener *listener, void *data)
{
- struct cg_output *output = wl_container_of(listener, output, transform);
+ struct cg_output *output = wl_container_of(listener, output, commit);
+ struct wlr_output_event_commit *event = data;
- struct cg_view *view;
- wl_list_for_each(view, &output->server->views, link) {
- view_position(view);
+ /* Notes:
+ * - output layout change will also be called if needed to position the views
+ * - always update output manager configuration even if the output is now disabled */
+
+ if (event->state->committed & OUTPUT_CONFIG_UPDATED) {
+ update_output_manager_config(output->server);
}
}
static void
-handle_output_mode(struct wl_listener *listener, void *data)
+handle_output_request_state(struct wl_listener *listener, void *data)
{
- struct cg_output *output = wl_container_of(listener, output, mode);
+ struct cg_output *output = wl_container_of(listener, output, request_state);
+ struct wlr_output_event_request_state *event = data;
- struct cg_view *view;
- wl_list_for_each(view, &output->server->views, link) {
- view_position(view);
+ if (wlr_output_commit_state(output->wlr_output, event->state)) {
+ update_output_manager_config(output->server);
}
}
+void
+handle_output_layout_change(struct wl_listener *listener, void *data)
+{
+ struct cg_server *server = wl_container_of(listener, server, output_layout_change);
+
+ view_position_all(server);
+ update_output_manager_config(server);
+}
+
+static bool
+is_nested_output(struct cg_output *output)
+{
+ if (wlr_output_is_wl(output->wlr_output)) {
+ return true;
+ }
+#if WLR_HAS_X11_BACKEND
+ if (wlr_output_is_x11(output->wlr_output)) {
+ return true;
+ }
+#endif
+ return false;
+}
+
static void
output_destroy(struct cg_output *output)
{
struct cg_server *server = output->server;
+ bool was_nested_output = is_nested_output(output);
+
+ output->wlr_output->data = NULL;
wl_list_remove(&output->destroy.link);
- wl_list_remove(&output->mode.link);
- wl_list_remove(&output->transform.link);
- wl_list_remove(&output->damage_frame.link);
- wl_list_remove(&output->damage_destroy.link);
+ wl_list_remove(&output->commit.link);
+ wl_list_remove(&output->request_state.link);
+ wl_list_remove(&output->frame.link);
+ wl_list_remove(&output->link);
+
+ output_layout_remove(output);
+
free(output);
- server->output = NULL;
- /* Since there is no use in continuing without our (single)
- * output, terminate. */
- wl_display_terminate(server->wl_display);
-}
-
-static void
-handle_output_damage_destroy(struct wl_listener *listener, void *data)
-{
- struct cg_output *output = wl_container_of(listener, output, damage_destroy);
- output_destroy(output);
+ if (wl_list_empty(&server->outputs) && was_nested_output) {
+ server_terminate(server);
+ } else if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST && !wl_list_empty(&server->outputs)) {
+ struct cg_output *prev = wl_container_of(server->outputs.next, prev, link);
+ output_enable(prev);
+ view_position_all(server);
+ }
}
static void
handle_output_destroy(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, destroy);
- wlr_output_damage_destroy(output->damage);
output_destroy(output);
}
@@ -347,83 +236,78 @@ handle_new_output(struct wl_listener *listener, void *data)
{
struct cg_server *server = wl_container_of(listener, server, new_output);
struct wlr_output *wlr_output = data;
- struct wlr_output_mode *preferred_mode;
- preferred_mode = wlr_output_preferred_mode(wlr_output);
- if (preferred_mode) {
- wlr_output_set_mode(wlr_output, preferred_mode);
+ if (!wlr_output_init_render(wlr_output, server->allocator, server->renderer)) {
+ wlr_log(WLR_ERROR, "Failed to initialize output rendering");
+ return;
}
- server->output = calloc(1, sizeof(struct cg_output));
- server->output->wlr_output = wlr_output;
- server->output->server = server;
- server->output->damage = wlr_output_damage_create(wlr_output);
+ struct cg_output *output = calloc(1, sizeof(struct cg_output));
+ if (!output) {
+ wlr_log(WLR_ERROR, "Failed to allocate output");
+ return;
+ }
- server->output->mode.notify = handle_output_mode;
- wl_signal_add(&wlr_output->events.mode, &server->output->mode);
- server->output->transform.notify = handle_output_transform;
- wl_signal_add(&wlr_output->events.transform, &server->output->transform);
- server->output->destroy.notify = handle_output_destroy;
- wl_signal_add(&wlr_output->events.destroy, &server->output->destroy);
- server->output->damage_frame.notify = handle_output_damage_frame;
- wl_signal_add(&server->output->damage->events.frame, &server->output->damage_frame);
- server->output->damage_destroy.notify = handle_output_damage_destroy;
- wl_signal_add(&server->output->damage->events.destroy, &server->output->damage_destroy);
+ output->wlr_output = wlr_output;
+ wlr_output->data = output;
+ output->server = server;
- wlr_output_set_transform(wlr_output, server->output_transform);
+ wl_list_insert(&server->outputs, &output->link);
- wlr_output_layout_add_auto(server->output_layout, wlr_output);
+ output->commit.notify = handle_output_commit;
+ wl_signal_add(&wlr_output->events.commit, &output->commit);
+ output->request_state.notify = handle_output_request_state;
+ wl_signal_add(&wlr_output->events.request_state, &output->request_state);
+ output->destroy.notify = handle_output_destroy;
+ wl_signal_add(&wlr_output->events.destroy, &output->destroy);
+ output->frame.notify = handle_output_frame;
+ wl_signal_add(&wlr_output->events.frame, &output->frame);
- /* Disconnect the signal now, because we only use one static output. */
- wl_list_remove(&server->new_output.link);
+ output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
+ if (!output->scene_output) {
+ wlr_log(WLR_ERROR, "Failed to allocate scene output");
+ return;
+ }
- if (wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
- wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f",
- wlr_output->name,
+ struct wlr_output_state state = {0};
+ wlr_output_state_set_enabled(&state, true);
+ if (!wl_list_empty(&wlr_output->modes)) {
+ struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
+ if (preferred_mode) {
+ wlr_output_state_set_mode(&state, preferred_mode);
+ }
+ if (!wlr_output_test_state(wlr_output, &state)) {
+ struct wlr_output_mode *mode;
+ wl_list_for_each (mode, &wlr_output->modes, link) {
+ if (mode == preferred_mode) {
+ continue;
+ }
+
+ wlr_output_state_set_mode(&state, mode);
+ if (wlr_output_test_state(wlr_output, &state)) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST && wl_list_length(&server->outputs) > 1) {
+ struct cg_output *next = wl_container_of(output->link.next, next, link);
+ output_disable(next);
+ }
+
+ if (!wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
+ wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f", wlr_output->name,
wlr_output->scale);
}
- /* Place the cursor in the center of the screen. */
- wlr_cursor_warp(server->seat->cursor, NULL, wlr_output->width / 2, wlr_output->height / 2);
- wlr_output_damage_add_whole(server->output->damage);
-}
+ wlr_log(WLR_DEBUG, "Enabling new output %s", wlr_output->name);
+ if (wlr_output_commit_state(wlr_output, &state)) {
+ output_layout_add_auto(output);
+ }
-void
-output_damage_view_surface(struct cg_output *cg_output, struct cg_view *view)
-{
- struct damage_data data = {
- .output = cg_output,
- .x = view->x,
- .y = view->y,
- .whole = false,
- };
- view_for_each_surface(view, damage_surface, &data);
-}
-
-void
-output_damage_view_whole(struct cg_output *cg_output, struct cg_view *view)
-{
- struct damage_data data = {
- .output = cg_output,
- .x = view->x,
- .y = view->y,
- .whole = true,
- };
- view_for_each_surface(view, damage_surface, &data);
-}
-
-void
-output_damage_drag_icon(struct cg_output *cg_output, struct cg_drag_icon *drag_icon)
-{
- struct damage_data data = {
- .output = cg_output,
- .x = drag_icon->x,
- .y = drag_icon->y,
- .whole = true,
- };
- wlr_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
- damage_surface,
- &data);
+ view_position_all(output->server);
+ update_output_manager_config(output->server);
}
void
@@ -431,6 +315,11 @@ output_set_window_title(struct cg_output *output, const char *title)
{
struct wlr_output *wlr_output = output->wlr_output;
+ if (!wlr_output->enabled) {
+ wlr_log(WLR_DEBUG, "Not setting window title for disabled output %s", wlr_output->name);
+ return;
+ }
+
if (wlr_output_is_wl(wlr_output)) {
wlr_wl_output_set_title(wlr_output, title);
#if WLR_HAS_X11_BACKEND
@@ -439,3 +328,95 @@ output_set_window_title(struct cg_output *output, const char *title)
#endif
}
}
+
+static bool
+output_config_apply(struct cg_server *server, struct wlr_output_configuration_v1 *config, bool test_only)
+{
+ bool ok = false;
+
+ size_t states_len;
+ struct wlr_backend_output_state *states = wlr_output_configuration_v1_build_state(config, &states_len);
+ if (states == NULL) {
+ return false;
+ }
+
+ struct wlr_output_swapchain_manager swapchain_manager;
+ wlr_output_swapchain_manager_init(&swapchain_manager, server->backend);
+
+ ok = wlr_output_swapchain_manager_prepare(&swapchain_manager, states, states_len);
+ if (!ok || test_only) {
+ goto out;
+ }
+
+ for (size_t i = 0; i < states_len; i++) {
+ struct wlr_backend_output_state *backend_state = &states[i];
+ struct cg_output *output = backend_state->output->data;
+
+ struct wlr_swapchain *swapchain =
+ wlr_output_swapchain_manager_get_swapchain(&swapchain_manager, backend_state->output);
+ struct wlr_scene_output_state_options options = {
+ .swapchain = swapchain,
+ };
+ struct wlr_output_state *state = &backend_state->base;
+ if (!wlr_scene_output_build_state(output->scene_output, state, &options)) {
+ ok = false;
+ goto out;
+ }
+ }
+
+ ok = wlr_backend_commit(server->backend, states, states_len);
+ if (!ok) {
+ goto out;
+ }
+
+ wlr_output_swapchain_manager_apply(&swapchain_manager);
+
+ struct wlr_output_configuration_head_v1 *head;
+ wl_list_for_each (head, &config->heads, link) {
+ struct cg_output *output = head->state.output->data;
+
+ if (head->state.enabled) {
+ output_layout_add(output, head->state.x, head->state.y);
+ } else {
+ output_layout_remove(output);
+ }
+ }
+
+out:
+ wlr_output_swapchain_manager_finish(&swapchain_manager);
+ for (size_t i = 0; i < states_len; i++) {
+ wlr_output_state_finish(&states[i].base);
+ }
+ free(states);
+ return ok;
+}
+
+void
+handle_output_manager_apply(struct wl_listener *listener, void *data)
+{
+ struct cg_server *server = wl_container_of(listener, server, output_manager_apply);
+ struct wlr_output_configuration_v1 *config = data;
+
+ if (output_config_apply(server, config, false)) {
+ wlr_output_configuration_v1_send_succeeded(config);
+ } else {
+ wlr_output_configuration_v1_send_failed(config);
+ }
+
+ wlr_output_configuration_v1_destroy(config);
+}
+
+void
+handle_output_manager_test(struct wl_listener *listener, void *data)
+{
+ struct cg_server *server = wl_container_of(listener, server, output_manager_test);
+ struct wlr_output_configuration_v1 *config = data;
+
+ if (output_config_apply(server, config, true)) {
+ wlr_output_configuration_v1_send_succeeded(config);
+ } else {
+ wlr_output_configuration_v1_send_failed(config);
+ }
+
+ wlr_output_configuration_v1_destroy(config);
+}
diff --git a/output.h b/output.h
index 9c052f2..fa72545 100644
--- a/output.h
+++ b/output.h
@@ -1,30 +1,29 @@
#ifndef CG_OUTPUT_H
#define CG_OUTPUT_H
-#include
+#include
#include
-#include
-#include "seat.h"
#include "server.h"
#include "view.h"
struct cg_output {
struct cg_server *server;
struct wlr_output *wlr_output;
- struct wlr_output_damage *damage;
+ struct wlr_scene_output *scene_output;
- struct wl_listener mode;
- struct wl_listener transform;
+ struct wl_listener commit;
+ struct wl_listener request_state;
struct wl_listener destroy;
- struct wl_listener damage_frame;
- struct wl_listener damage_destroy;
+ struct wl_listener frame;
+
+ struct wl_list link; // cg_server::outputs
};
+void handle_output_manager_apply(struct wl_listener *listener, void *data);
+void handle_output_manager_test(struct wl_listener *listener, void *data);
+void handle_output_layout_change(struct wl_listener *listener, void *data);
void handle_new_output(struct wl_listener *listener, void *data);
-void output_damage_view_surface(struct cg_output *output, struct cg_view *view);
-void output_damage_view_whole(struct cg_output *cg_output, struct cg_view *view);
-void output_damage_drag_icon(struct cg_output *output, struct cg_drag_icon *icon);
void output_set_window_title(struct cg_output *output, const char *title);
#endif
diff --git a/seat.c b/seat.c
index 980adf3..5f659a4 100644
--- a/seat.c
+++ b/seat.c
@@ -1,23 +1,34 @@
/*
* Cage: A Wayland kiosk.
*
- * Copyright (C) 2018-2019 Jente Hidskes
+ * Copyright (C) 2018-2020 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
+#define _POSIX_C_SOURCE 200809L
+
#include "config.h"
-#include
+#include
#include
-#include
+#include
+#include
+#include
#include
+#include
+#include
#include
#include
-#include
+#include
+#include
#include
+#include
+#include
#include
-#include
+#include
+#include
+#include
#include
#include
#if CAGE_HAS_XWAYLAND
@@ -38,58 +49,54 @@ static void drag_icon_update_position(struct cg_drag_icon *drag_icon);
* menus or tooltips. This function tests if any of those are underneath the
* coordinates lx and ly (in output Layout Coordinates). If so, it sets the
* surface pointer to that wlr_surface and the sx and sy coordinates to the
- * coordinates relative to that surface's top-left corner. */
-static bool
-view_at(struct cg_view *view, double lx, double ly,
- struct wlr_surface **surface, double *sx, double *sy)
-{
- double view_sx = lx - view->x;
- double view_sy = ly - view->y;
-
- double _sx, _sy;
- struct wlr_surface *_surface = view_wlr_surface_at(view, view_sx, view_sy, &_sx, &_sy);
- if (_surface != NULL) {
- *sx = _sx;
- *sy = _sy;
- *surface = _surface;
- return true;
- }
-
- return false;
-}
-
-/* This iterates over all of our surfaces and attempts to find one
- * under the cursor. This relies on server->views being ordered from
- * top-to-bottom. If desktop_view_at returns a view, there is also a
- * surface. There cannot be a surface without a view, either. It's
- * both or nothing. */
+ * coordinates relative to that surface's top-left corner.
+ *
+ * This function iterates over all of our surfaces and attempts to find one
+ * under the cursor. If desktop_view_at returns a view, there is also a
+ * surface. There cannot be a surface without a view, either. It's both or
+ * nothing.
+ */
static struct cg_view *
-desktop_view_at(struct cg_server *server, double lx, double ly,
- struct wlr_surface **surface, double *sx, double *sy)
+desktop_view_at(struct cg_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy)
{
- struct cg_view *view;
-
- wl_list_for_each(view, &server->views, link) {
- if (view_at(view, lx, ly, surface, sx, sy)) {
- return view;
- }
+ struct wlr_scene_node *node = wlr_scene_node_at(&server->scene->tree.node, lx, ly, sx, sy);
+ if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) {
+ return NULL;
}
- return NULL;
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+ struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer);
+ if (!scene_surface) {
+ return NULL;
+ }
+
+ *surface = scene_surface->surface;
+
+ /* Walk up the tree until we find a node with a data pointer. When done,
+ * we've found the node representing the view. */
+ while (!node->data) {
+ if (!node->parent) {
+ node = NULL;
+ break;
+ }
+
+ node = &node->parent->node;
+ }
+
+ assert(node != NULL);
+ return node->data;
}
static void
-press_cursor_button(struct cg_seat *seat, struct wlr_input_device *device,
- uint32_t time, uint32_t button, uint32_t state,
- double lx, double ly)
+press_cursor_button(struct cg_seat *seat, struct wlr_input_device *device, uint32_t time, uint32_t button,
+ uint32_t state, double lx, double ly)
{
struct cg_server *server = seat->server;
if (state == WLR_BUTTON_PRESSED) {
double sx, sy;
struct wlr_surface *surface;
- struct cg_view *view = desktop_view_at(server, lx, ly,
- &surface, &sx, &sy);
+ struct cg_view *view = desktop_view_at(server, lx, ly, &surface, &sx, &sy);
struct cg_view *current = seat_get_focus(seat);
if (view == current) {
return;
@@ -104,10 +111,11 @@ press_cursor_button(struct cg_seat *seat, struct wlr_input_device *device,
}
static void
-update_capabilities(struct cg_seat *seat) {
+update_capabilities(struct cg_seat *seat)
+{
uint32_t caps = 0;
- if (!wl_list_empty(&seat->keyboards)) {
+ if (!wl_list_empty(&seat->keyboard_groups)) {
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
}
if (!wl_list_empty(&seat->pointers)) {
@@ -120,21 +128,41 @@ update_capabilities(struct cg_seat *seat) {
/* Hide cursor if the seat doesn't have pointer capability. */
if ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
- wlr_cursor_set_image(seat->cursor, NULL, 0, 0, 0, 0, 0, 0);
+ wlr_cursor_unset_image(seat->cursor);
} else {
- wlr_xcursor_manager_set_cursor_image(seat->xcursor_manager,
- DEFAULT_XCURSOR,
- seat->cursor);
+ wlr_cursor_set_xcursor(seat->cursor, seat->xcursor_manager, DEFAULT_XCURSOR);
}
}
static void
-handle_touch_destroy(struct wl_listener *listener, void *data) {
+map_input_device_to_output(struct cg_seat *seat, struct wlr_input_device *device, const char *output_name)
+{
+ if (!output_name) {
+ wlr_log(WLR_INFO, "Input device %s cannot be mapped to an output device\n", device->name);
+ return;
+ }
+
+ struct cg_output *output;
+ wl_list_for_each (output, &seat->server->outputs, link) {
+ if (strcmp(output_name, output->wlr_output->name) == 0) {
+ wlr_log(WLR_INFO, "Mapping input device %s to output device %s\n", device->name,
+ output->wlr_output->name);
+ wlr_cursor_map_input_to_output(seat->cursor, device, output->wlr_output);
+ return;
+ }
+ }
+
+ wlr_log(WLR_INFO, "Couldn't map input device %s to an output\n", device->name);
+}
+
+static void
+handle_touch_destroy(struct wl_listener *listener, void *data)
+{
struct cg_touch *touch = wl_container_of(listener, touch, destroy);
struct cg_seat *seat = touch->seat;
wl_list_remove(&touch->link);
- wlr_cursor_detach_input_device(seat->cursor, touch->device);
+ wlr_cursor_detach_input_device(seat->cursor, &touch->touch->base);
wl_list_remove(&touch->destroy.link);
free(touch);
@@ -142,7 +170,7 @@ handle_touch_destroy(struct wl_listener *listener, void *data) {
}
static void
-handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device)
+handle_new_touch(struct cg_seat *seat, struct wlr_touch *wlr_touch)
{
struct cg_touch *touch = calloc(1, sizeof(struct cg_touch));
if (!touch) {
@@ -151,14 +179,14 @@ handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device)
}
touch->seat = seat;
- touch->device = device;
- wlr_cursor_attach_input_device(seat->cursor, device);
+ touch->touch = wlr_touch;
+ wlr_cursor_attach_input_device(seat->cursor, &wlr_touch->base);
wl_list_insert(&seat->touch, &touch->link);
touch->destroy.notify = handle_touch_destroy;
- wl_signal_add(&touch->device->events.destroy, &touch->destroy);
+ wl_signal_add(&wlr_touch->base.events.destroy, &touch->destroy);
- wlr_cursor_map_input_to_output(seat->cursor, device, seat->server->output->wlr_output);
+ map_input_device_to_output(seat, &wlr_touch->base, wlr_touch->output_name);
}
static void
@@ -168,7 +196,7 @@ handle_pointer_destroy(struct wl_listener *listener, void *data)
struct cg_seat *seat = pointer->seat;
wl_list_remove(&pointer->link);
- wlr_cursor_detach_input_device(seat->cursor, pointer->device);
+ wlr_cursor_detach_input_device(seat->cursor, &pointer->pointer->base);
wl_list_remove(&pointer->destroy.link);
free(pointer);
@@ -176,7 +204,7 @@ handle_pointer_destroy(struct wl_listener *listener, void *data)
}
static void
-handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device)
+handle_new_pointer(struct cg_seat *seat, struct wlr_pointer *wlr_pointer)
{
struct cg_pointer *pointer = calloc(1, sizeof(struct cg_pointer));
if (!pointer) {
@@ -185,60 +213,83 @@ handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device)
}
pointer->seat = seat;
- pointer->device = device;
- wlr_cursor_attach_input_device(seat->cursor, device);
+ pointer->pointer = wlr_pointer;
+ wlr_cursor_attach_input_device(seat->cursor, &wlr_pointer->base);
wl_list_insert(&seat->pointers, &pointer->link);
pointer->destroy.notify = handle_pointer_destroy;
- wl_signal_add(&device->events.destroy, &pointer->destroy);
+ wl_signal_add(&wlr_pointer->base.events.destroy, &pointer->destroy);
- wlr_cursor_map_input_to_output(seat->cursor, device, seat->server->output->wlr_output);
+ map_input_device_to_output(seat, &wlr_pointer->base, wlr_pointer->output_name);
}
static void
-handle_keyboard_modifiers(struct wl_listener *listener, void *data)
+handle_virtual_pointer(struct wl_listener *listener, void *data)
{
- struct cg_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers);
+ struct cg_server *server = wl_container_of(listener, server, new_virtual_pointer);
+ struct cg_seat *seat = server->seat;
+ struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
+ struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
+ struct wlr_pointer *wlr_pointer = &pointer->pointer;
- wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device);
- wlr_seat_keyboard_notify_modifiers(keyboard->seat->seat,
- &keyboard->device->keyboard->modifiers);
+ /* We'll want to map the device back to an output later, this is a bit
+ * sub-optimal (we could just keep the suggested_output), but just copy
+ * its name so we do like other devices
+ */
+ if (event->suggested_output != NULL) {
+ wlr_pointer->output_name = strdup(event->suggested_output->name);
+ }
+ /* TODO: event->suggested_seat should be checked if we handle multiple seats */
+ handle_new_pointer(seat, wlr_pointer);
+ update_capabilities(seat);
+}
- wlr_idle_notify_activity(keyboard->seat->server->idle, keyboard->seat->seat);
+static void
+handle_modifier_event(struct wlr_keyboard *keyboard, struct cg_seat *seat)
+{
+ wlr_seat_set_keyboard(seat->seat, keyboard);
+ wlr_seat_keyboard_notify_modifiers(seat->seat, &keyboard->modifiers);
+
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static bool
handle_keybinding(struct cg_server *server, xkb_keysym_t sym)
{
- switch (sym) {
#ifdef DEBUG
- case XKB_KEY_Escape:
- wl_display_terminate(server->wl_display);
- break;
+ if (sym == XKB_KEY_Escape) {
+ server_terminate(server);
+ return true;
+ }
#endif
- default:
+ if (server->allow_vt_switch && sym >= XKB_KEY_XF86Switch_VT_1 && sym <= XKB_KEY_XF86Switch_VT_12) {
+ if (wlr_backend_is_multi(server->backend)) {
+ if (server->session) {
+ unsigned vt = sym - XKB_KEY_XF86Switch_VT_1 + 1;
+ wlr_session_change_vt(server->session, vt);
+ }
+ }
+ } else {
return false;
}
- wlr_idle_notify_activity(server->idle, server->seat->seat);
+ wlr_idle_notifier_v1_notify_activity(server->idle, server->seat->seat);
return true;
}
static void
-handle_keyboard_key(struct wl_listener *listener, void *data)
+handle_key_event(struct wlr_keyboard *keyboard, struct cg_seat *seat, void *data)
{
- struct cg_keyboard *keyboard = wl_container_of(listener, keyboard, key);
- struct cg_seat *seat = keyboard->seat;
- struct wlr_event_keyboard_key *event = data;
+ struct wlr_keyboard_key_event *event = data;
/* Translate from libinput keycode to an xkbcommon keycode. */
xkb_keycode_t keycode = event->keycode + 8;
const xkb_keysym_t *syms;
- int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, keycode, &syms);
+ int nsyms = xkb_state_key_get_syms(keyboard->xkb_state, keycode, &syms);
bool handled = false;
- uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard);
- if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_KEY_PRESSED) {
+ uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
+ if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
/* If Alt is held down and this button was pressed, we
* attempt to process it as a compositor
* keybinding. */
@@ -249,77 +300,136 @@ handle_keyboard_key(struct wl_listener *listener, void *data)
if (!handled) {
/* Otherwise, we pass it along to the client. */
- wlr_seat_set_keyboard(seat->seat, keyboard->device);
- wlr_seat_keyboard_notify_key(seat->seat, event->time_msec,
- event->keycode, event->state);
+ wlr_seat_set_keyboard(seat->seat, keyboard);
+ wlr_seat_keyboard_notify_key(seat->seat, event->time_msec, event->keycode, event->state);
}
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
-handle_keyboard_destroy(struct wl_listener *listener, void *data)
+handle_keyboard_group_key(struct wl_listener *listener, void *data)
{
- struct cg_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
- struct cg_seat *seat = keyboard->seat;
-
- wl_list_remove(&keyboard->destroy.link);
- wl_list_remove(&keyboard->modifiers.link);
- wl_list_remove(&keyboard->key.link);
- wl_list_remove(&keyboard->link);
- free(keyboard);
-
- update_capabilities(seat);
+ struct cg_keyboard_group *cg_group = wl_container_of(listener, cg_group, key);
+ handle_key_event(&cg_group->wlr_group->keyboard, cg_group->seat, data);
}
static void
-handle_new_keyboard(struct cg_seat *seat, struct wlr_input_device *device)
+handle_keyboard_group_modifiers(struct wl_listener *listener, void *data)
{
- struct cg_keyboard *keyboard = calloc(1, sizeof(struct cg_keyboard));
- if (!keyboard) {
- wlr_log(WLR_ERROR, "Cannot allocate keyboard");
+ struct cg_keyboard_group *group = wl_container_of(listener, group, modifiers);
+ handle_modifier_event(&group->wlr_group->keyboard, group->seat);
+}
+
+static void
+cg_keyboard_group_add(struct wlr_keyboard *keyboard, struct cg_seat *seat, bool virtual)
+{
+ /* We apparently should not group virtual keyboards,
+ * so create a new group with it
+ */
+ if (!virtual) {
+ struct cg_keyboard_group *group;
+ wl_list_for_each (group, &seat->keyboard_groups, link) {
+ if (group->is_virtual)
+ continue;
+ struct wlr_keyboard_group *wlr_group = group->wlr_group;
+ if (wlr_keyboard_group_add_keyboard(wlr_group, keyboard)) {
+ wlr_log(WLR_DEBUG, "Added new keyboard to existing group");
+ return;
+ }
+ }
+ }
+
+ /* This is reached if and only if the keyboard could not be inserted into
+ * any group */
+ struct cg_keyboard_group *cg_group = calloc(1, sizeof(struct cg_keyboard_group));
+ if (cg_group == NULL) {
+ wlr_log(WLR_ERROR, "Failed to allocate keyboard group.");
return;
}
+ cg_group->seat = seat;
+ cg_group->is_virtual = virtual;
+ cg_group->wlr_group = wlr_keyboard_group_create();
+ if (cg_group->wlr_group == NULL) {
+ wlr_log(WLR_ERROR, "Failed to create wlr keyboard group.");
+ goto cleanup;
+ }
+ cg_group->wlr_group->data = cg_group;
+ wlr_keyboard_set_keymap(&cg_group->wlr_group->keyboard, keyboard->keymap);
+
+ wlr_keyboard_set_repeat_info(&cg_group->wlr_group->keyboard, keyboard->repeat_info.rate,
+ keyboard->repeat_info.delay);
+
+ wlr_log(WLR_DEBUG, "Created keyboard group");
+
+ wlr_keyboard_group_add_keyboard(cg_group->wlr_group, keyboard);
+ wl_list_insert(&seat->keyboard_groups, &cg_group->link);
+
+ wl_signal_add(&cg_group->wlr_group->keyboard.events.key, &cg_group->key);
+ cg_group->key.notify = handle_keyboard_group_key;
+ wl_signal_add(&cg_group->wlr_group->keyboard.events.modifiers, &cg_group->modifiers);
+ cg_group->modifiers.notify = handle_keyboard_group_modifiers;
+
+ return;
+
+cleanup:
+ if (cg_group && cg_group->wlr_group) {
+ wlr_keyboard_group_destroy(cg_group->wlr_group);
+ }
+ free(cg_group);
+}
+
+static void
+keyboard_group_destroy(struct cg_keyboard_group *keyboard_group)
+{
+ wl_list_remove(&keyboard_group->key.link);
+ wl_list_remove(&keyboard_group->modifiers.link);
+ wlr_keyboard_group_destroy(keyboard_group->wlr_group);
+ wl_list_remove(&keyboard_group->link);
+ free(keyboard_group);
+}
+
+static void
+handle_new_keyboard(struct cg_seat *seat, struct wlr_keyboard *keyboard, bool virtual)
+{
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
- wlr_log(WLR_ERROR, "Unable to create XBK context");
- free(keyboard);
+ wlr_log(WLR_ERROR, "Unable to create XKB context");
return;
}
- struct xkb_rule_names rules = { 0 };
- rules.rules = getenv("XKB_DEFAULT_RULES");
- rules.model = getenv("XKB_DEFAULT_MODEL");
- rules.layout = getenv("XKB_DEFAULT_LAYOUT");
- rules.variant = getenv("XKB_DEFAULT_VARIANT");
- rules.options = getenv("XKB_DEFAULT_OPTIONS");
- struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
- XKB_KEYMAP_COMPILE_NO_FLAGS);
+ struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!keymap) {
wlr_log(WLR_ERROR, "Unable to configure keyboard: keymap does not exist");
- free(keyboard);
xkb_context_unref(context);
return;
}
- keyboard->seat = seat;
- keyboard->device = device;
- wlr_keyboard_set_keymap(device->keyboard, keymap);
+ wlr_keyboard_set_keymap(keyboard, keymap);
xkb_keymap_unref(keymap);
xkb_context_unref(context);
- wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
+ wlr_keyboard_set_repeat_info(keyboard, 25, 600);
- wl_list_insert(&seat->keyboards, &keyboard->link);
- keyboard->destroy.notify = handle_keyboard_destroy;
- wl_signal_add(&device->events.destroy, &keyboard->destroy);
- keyboard->key.notify = handle_keyboard_key;
- wl_signal_add(&device->keyboard->events.key, &keyboard->key);
- keyboard->modifiers.notify = handle_keyboard_modifiers;
- wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers);
+ cg_keyboard_group_add(keyboard, seat, virtual);
- wlr_seat_set_keyboard(seat->seat, device);
+ wlr_seat_set_keyboard(seat->seat, keyboard);
+}
+
+static void
+handle_virtual_keyboard(struct wl_listener *listener, void *data)
+{
+ struct cg_server *server = wl_container_of(listener, server, new_virtual_keyboard);
+ struct cg_seat *seat = server->seat;
+ struct wlr_virtual_keyboard_v1 *keyboard = data;
+ struct wlr_keyboard *wlr_keyboard = &keyboard->keyboard;
+
+ /* TODO: If multiple seats are supported, check keyboard->seat
+ * to select the appropriate one */
+
+ handle_new_keyboard(seat, wlr_keyboard, true);
+ update_capabilities(seat);
}
static void
@@ -330,18 +440,18 @@ handle_new_input(struct wl_listener *listener, void *data)
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
- handle_new_keyboard(seat, device);
+ handle_new_keyboard(seat, wlr_keyboard_from_input_device(device), false);
break;
case WLR_INPUT_DEVICE_POINTER:
- handle_new_pointer(seat, device);
+ handle_new_pointer(seat, wlr_pointer_from_input_device(device));
break;
case WLR_INPUT_DEVICE_TOUCH:
- handle_new_touch(seat, device);
+ handle_new_touch(seat, wlr_touch_from_input_device(device));
break;
case WLR_INPUT_DEVICE_SWITCH:
wlr_log(WLR_DEBUG, "Switch input is not implemented");
return;
- case WLR_INPUT_DEVICE_TABLET_TOOL:
+ case WLR_INPUT_DEVICE_TABLET:
case WLR_INPUT_DEVICE_TABLET_PAD:
wlr_log(WLR_DEBUG, "Tablet input is not implemented");
return;
@@ -383,8 +493,7 @@ handle_request_set_cursor(struct wl_listener *listener, void *data)
/* This can be sent by any client, so we check to make sure
* this one actually has pointer focus first. */
if (focused_client == event->seat_client->client) {
- wlr_cursor_set_surface(seat->cursor, event->surface,
- event->hotspot_x, event->hotspot_y);
+ wlr_cursor_set_surface(seat->cursor, event->surface, event->hotspot_x, event->hotspot_y);
}
}
@@ -392,90 +501,88 @@ static void
handle_touch_down(struct wl_listener *listener, void *data)
{
struct cg_seat *seat = wl_container_of(listener, seat, touch_down);
- struct wlr_event_touch_down *event = data;
+ struct wlr_touch_down_event *event = data;
double lx, ly;
- wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device,
- event->x, event->y, &lx, &ly);
+ wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->touch->base, event->x, event->y, &lx, &ly);
double sx, sy;
struct wlr_surface *surface;
- struct cg_view *view = desktop_view_at(seat->server, lx, ly,
- &surface, &sx, &sy);
+ struct cg_view *view = desktop_view_at(seat->server, lx, ly, &surface, &sx, &sy);
uint32_t serial = 0;
if (view) {
- serial = wlr_seat_touch_notify_down(seat->seat, surface,
- event->time_msec, event->touch_id,
- sx, sy);
+ serial = wlr_seat_touch_notify_down(seat->seat, surface, event->time_msec, event->touch_id, sx, sy);
}
if (serial && wlr_seat_touch_num_points(seat->seat) == 1) {
seat->touch_id = event->touch_id;
- seat->touch_x = lx;
- seat->touch_y = ly;
- press_cursor_button(seat, event->device, event->time_msec,
- BTN_LEFT, WLR_BUTTON_PRESSED, lx, ly);
+ seat->touch_lx = lx;
+ seat->touch_ly = ly;
+ press_cursor_button(seat, &event->touch->base, event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED, lx, ly);
}
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
handle_touch_up(struct wl_listener *listener, void *data)
{
struct cg_seat *seat = wl_container_of(listener, seat, touch_up);
- struct wlr_event_touch_up *event = data;
+ struct wlr_touch_up_event *event = data;
if (!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
return;
}
if (wlr_seat_touch_num_points(seat->seat) == 1) {
- press_cursor_button(seat, event->device, event->time_msec,
- BTN_LEFT, WLR_BUTTON_RELEASED,
- seat->touch_x, seat->touch_y);
+ press_cursor_button(seat, &event->touch->base, event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED,
+ seat->touch_lx, seat->touch_ly);
}
wlr_seat_touch_notify_up(seat->seat, event->time_msec, event->touch_id);
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
handle_touch_motion(struct wl_listener *listener, void *data)
{
struct cg_seat *seat = wl_container_of(listener, seat, touch_motion);
- struct wlr_event_touch_motion *event = data;
+ struct wlr_touch_motion_event *event = data;
if (!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
return;
}
double lx, ly;
- wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device,
- event->x, event->y, &lx, &ly);
+ wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->touch->base, event->x, event->y, &lx, &ly);
double sx, sy;
struct wlr_surface *surface;
- struct cg_view *view = desktop_view_at(seat->server, lx, ly,
- &surface, &sx, &sy);
+ struct cg_view *view = desktop_view_at(seat->server, lx, ly, &surface, &sx, &sy);
if (view) {
- wlr_seat_touch_point_focus(seat->seat, surface,
- event->time_msec, event->touch_id, sx, sy);
- wlr_seat_touch_notify_motion(seat->seat, event->time_msec,
- event->touch_id, sx, sy);
+ wlr_seat_touch_point_focus(seat->seat, surface, event->time_msec, event->touch_id, sx, sy);
+ wlr_seat_touch_notify_motion(seat->seat, event->time_msec, event->touch_id, sx, sy);
} else {
- wlr_seat_touch_point_clear_focus(seat->seat, event->time_msec,
- event->touch_id);
+ wlr_seat_touch_point_clear_focus(seat->seat, event->time_msec, event->touch_id);
}
if (event->touch_id == seat->touch_id) {
- seat->touch_x = lx;
- seat->touch_y = ly;
+ seat->touch_lx = lx;
+ seat->touch_ly = ly;
}
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
+}
+
+static void
+handle_touch_frame(struct wl_listener *listener, void *data)
+{
+ struct cg_seat *seat = wl_container_of(listener, seat, touch_frame);
+
+ wlr_seat_touch_notify_frame(seat->seat);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
@@ -484,91 +591,89 @@ handle_cursor_frame(struct wl_listener *listener, void *data)
struct cg_seat *seat = wl_container_of(listener, seat, cursor_frame);
wlr_seat_pointer_notify_frame(seat->seat);
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
handle_cursor_axis(struct wl_listener *listener, void *data)
{
struct cg_seat *seat = wl_container_of(listener, seat, cursor_axis);
- struct wlr_event_pointer_axis *event = data;
+ struct wlr_pointer_axis_event *event = data;
- wlr_seat_pointer_notify_axis(seat->seat,
- event->time_msec, event->orientation, event->delta,
- event->delta_discrete, event->source);
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
+ wlr_seat_pointer_notify_axis(seat->seat, event->time_msec, event->orientation, event->delta,
+ event->delta_discrete, event->source, event->relative_direction);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
handle_cursor_button(struct wl_listener *listener, void *data)
{
struct cg_seat *seat = wl_container_of(listener, seat, cursor_button);
- struct wlr_event_pointer_button *event = data;
+ struct wlr_pointer_button_event *event = data;
- wlr_seat_pointer_notify_button(seat->seat, event->time_msec,
- event->button, event->state);
- press_cursor_button(seat, event->device, event->time_msec,
- event->button, event->state,
- seat->cursor->x, seat->cursor->y);
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
+ wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button, event->state);
+ press_cursor_button(seat, &event->pointer->base, event->time_msec, event->button, event->state, seat->cursor->x,
+ seat->cursor->y);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
-process_cursor_motion(struct cg_seat *seat, uint32_t time)
+process_cursor_motion(struct cg_seat *seat, uint32_t time_msec, double dx, double dy, double dx_unaccel,
+ double dy_unaccel)
{
double sx, sy;
struct wlr_seat *wlr_seat = seat->seat;
struct wlr_surface *surface = NULL;
- struct cg_view *view = desktop_view_at(seat->server,
- seat->cursor->x, seat->cursor->y,
- &surface, &sx, &sy);
-
+ struct cg_view *view = desktop_view_at(seat->server, seat->cursor->x, seat->cursor->y, &surface, &sx, &sy);
if (!view) {
wlr_seat_pointer_clear_focus(wlr_seat);
} else {
wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
+ wlr_seat_pointer_notify_motion(wlr_seat, time_msec, sx, sy);
+ }
- bool focus_changed = wlr_seat->pointer_state.focused_surface != surface;
- if (!focus_changed && time > 0) {
- wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
- }
+ if (dx != 0 || dy != 0) {
+ wlr_relative_pointer_manager_v1_send_relative_motion(seat->server->relative_pointer_manager, wlr_seat,
+ (uint64_t) time_msec * 1000, dx, dy, dx_unaccel,
+ dy_unaccel);
}
struct cg_drag_icon *drag_icon;
- wl_list_for_each(drag_icon, &seat->drag_icons, link) {
+ wl_list_for_each (drag_icon, &seat->drag_icons, link) {
drag_icon_update_position(drag_icon);
}
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
handle_cursor_motion_absolute(struct wl_listener *listener, void *data)
{
struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion_absolute);
- struct wlr_event_pointer_motion_absolute *event = data;
+ struct wlr_pointer_motion_absolute_event *event = data;
- wlr_cursor_warp_absolute(seat->cursor, event->device, event->x, event->y);
- process_cursor_motion(seat, event->time_msec);
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
+ double lx, ly;
+ wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->pointer->base, event->x, event->y, &lx, &ly);
+
+ double dx = lx - seat->cursor->x;
+ double dy = ly - seat->cursor->y;
+
+ wlr_cursor_warp_absolute(seat->cursor, &event->pointer->base, event->x, event->y);
+ process_cursor_motion(seat, event->time_msec, dx, dy, dx, dy);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
-handle_cursor_motion(struct wl_listener *listener, void *data)
+handle_cursor_motion_relative(struct wl_listener *listener, void *data)
{
- struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion);
- struct wlr_event_pointer_motion *event = data;
+ struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion_relative);
+ struct wlr_pointer_motion_event *event = data;
- wlr_cursor_move(seat->cursor, event->device, event->delta_x, event->delta_y);
- process_cursor_motion(seat, event->time_msec);
- wlr_idle_notify_activity(seat->server->idle, seat->seat);
-}
-
-static void
-drag_icon_damage(struct cg_drag_icon *drag_icon)
-{
- output_damage_drag_icon(drag_icon->seat->server->output, drag_icon);
+ wlr_cursor_move(seat->cursor, &event->pointer->base, event->delta_x, event->delta_y);
+ process_cursor_motion(seat, event->time_msec, event->delta_x, event->delta_y, event->unaccel_dx,
+ event->unaccel_dy);
+ wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
}
static void
@@ -578,26 +683,24 @@ drag_icon_update_position(struct cg_drag_icon *drag_icon)
struct cg_seat *seat = drag_icon->seat;
struct wlr_touch_point *point;
- drag_icon_damage(drag_icon);
-
switch (wlr_icon->drag->grab_type) {
case WLR_DRAG_GRAB_KEYBOARD:
return;
case WLR_DRAG_GRAB_KEYBOARD_POINTER:
- drag_icon->x = seat->cursor->x;
- drag_icon->y = seat->cursor->y;
+ drag_icon->lx = seat->cursor->x;
+ drag_icon->ly = seat->cursor->y;
break;
case WLR_DRAG_GRAB_KEYBOARD_TOUCH:
point = wlr_seat_touch_get_point(seat->seat, wlr_icon->drag->touch_id);
if (!point) {
return;
}
- drag_icon->x = seat->touch_x;
- drag_icon->y = seat->touch_y;
+ drag_icon->lx = seat->touch_lx;
+ drag_icon->ly = seat->touch_ly;
break;
}
- drag_icon_damage(drag_icon);
+ wlr_scene_node_set_position(&drag_icon->scene_tree->node, drag_icon->lx, drag_icon->ly);
}
static void
@@ -607,6 +710,7 @@ handle_drag_icon_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&drag_icon->link);
wl_list_remove(&drag_icon->destroy.link);
+ wlr_scene_node_destroy(&drag_icon->scene_tree->node);
free(drag_icon);
}
@@ -616,23 +720,20 @@ handle_request_start_drag(struct wl_listener *listener, void *data)
struct cg_seat *seat = wl_container_of(listener, seat, request_start_drag);
struct wlr_seat_request_start_drag_event *event = data;
- if (wlr_seat_validate_pointer_grab_serial(seat->seat,
- event->origin, event->serial)) {
+ if (wlr_seat_validate_pointer_grab_serial(seat->seat, event->origin, event->serial)) {
wlr_seat_start_pointer_drag(seat->seat, event->drag, event->serial);
return;
}
struct wlr_touch_point *point;
- if (wlr_seat_validate_touch_grab_serial(seat->seat,
- event->origin, event->serial, &point)) {
- wlr_seat_start_touch_drag(seat->seat,
- event->drag, event->serial, point);
+ if (wlr_seat_validate_touch_grab_serial(seat->seat, event->origin, event->serial, &point)) {
+ wlr_seat_start_touch_drag(seat->seat, event->drag, event->serial, point);
return;
}
// TODO: tablet grabs
- wlr_log(WLR_DEBUG, "Ignoring start_drag request: "
- "could not validate pointer/touch serial %" PRIu32, event->serial);
+ wlr_log(WLR_DEBUG, "Ignoring start_drag request: could not validate pointer/touch serial %" PRIu32,
+ event->serial);
wlr_data_source_destroy(event->drag->source);
}
@@ -652,6 +753,11 @@ handle_start_drag(struct wl_listener *listener, void *data)
}
drag_icon->seat = seat;
drag_icon->wlr_drag_icon = wlr_drag_icon;
+ drag_icon->scene_tree = wlr_scene_subsurface_tree_create(&seat->server->scene->tree, wlr_drag_icon->surface);
+ if (!drag_icon->scene_tree) {
+ free(drag_icon);
+ return;
+ }
drag_icon->destroy.notify = handle_drag_icon_destroy;
wl_signal_add(&wlr_drag_icon->events.destroy, &drag_icon->destroy);
@@ -666,17 +772,30 @@ handle_destroy(struct wl_listener *listener, void *data)
{
struct cg_seat *seat = wl_container_of(listener, seat, destroy);
wl_list_remove(&seat->destroy.link);
+ wl_list_remove(&seat->cursor_motion_relative.link);
+ wl_list_remove(&seat->cursor_motion_absolute.link);
+ wl_list_remove(&seat->cursor_button.link);
+ wl_list_remove(&seat->cursor_axis.link);
+ wl_list_remove(&seat->cursor_frame.link);
+ wl_list_remove(&seat->touch_down.link);
+ wl_list_remove(&seat->touch_up.link);
+ wl_list_remove(&seat->touch_motion.link);
+ wl_list_remove(&seat->touch_frame.link);
+ wl_list_remove(&seat->request_set_cursor.link);
+ wl_list_remove(&seat->request_set_selection.link);
+ wl_list_remove(&seat->request_set_primary_selection.link);
- struct cg_keyboard *keyboard, *keyboard_tmp;
- wl_list_for_each_safe(keyboard, keyboard_tmp, &seat->keyboards, link) {
- handle_keyboard_destroy(&keyboard->destroy, NULL);
+ struct cg_keyboard_group *group, *group_tmp;
+ wl_list_for_each_safe (group, group_tmp, &seat->keyboard_groups, link) {
+ wlr_keyboard_group_destroy(group->wlr_group);
+ free(group);
}
struct cg_pointer *pointer, *pointer_tmp;
- wl_list_for_each_safe(pointer, pointer_tmp, &seat->pointers, link) {
+ wl_list_for_each_safe (pointer, pointer_tmp, &seat->pointers, link) {
handle_pointer_destroy(&pointer->destroy, NULL);
}
struct cg_touch *touch, *touch_tmp;
- wl_list_for_each_safe(touch, touch_tmp, &seat->touch, link) {
+ wl_list_for_each_safe (touch, touch_tmp, &seat->touch, link) {
handle_touch_destroy(&touch->destroy, NULL);
}
wl_list_remove(&seat->new_input.link);
@@ -685,22 +804,11 @@ handle_destroy(struct wl_listener *listener, void *data)
if (seat->cursor) {
wlr_cursor_destroy(seat->cursor);
}
- wl_list_remove(&seat->cursor_motion.link);
- wl_list_remove(&seat->cursor_motion_absolute.link);
- wl_list_remove(&seat->cursor_button.link);
- wl_list_remove(&seat->cursor_axis.link);
- wl_list_remove(&seat->cursor_frame.link);
- wl_list_remove(&seat->touch_down.link);
- wl_list_remove(&seat->touch_up.link);
- wl_list_remove(&seat->touch_motion.link);
- wl_list_remove(&seat->request_set_cursor.link);
- wl_list_remove(&seat->request_set_selection.link);
- wl_list_remove(&seat->request_set_primary_selection.link);
free(seat);
}
struct cg_seat *
-seat_create(struct cg_server *server)
+seat_create(struct cg_server *server, struct wlr_backend *backend)
{
struct cg_seat *seat = calloc(1, sizeof(struct cg_seat));
if (!seat) {
@@ -738,8 +846,8 @@ seat_create(struct cg_server *server)
}
}
- seat->cursor_motion.notify = handle_cursor_motion;
- wl_signal_add(&seat->cursor->events.motion, &seat->cursor_motion);
+ seat->cursor_motion_relative.notify = handle_cursor_motion_relative;
+ wl_signal_add(&seat->cursor->events.motion, &seat->cursor_motion_relative);
seat->cursor_motion_absolute.notify = handle_cursor_motion_absolute;
wl_signal_add(&seat->cursor->events.motion_absolute, &seat->cursor_motion_absolute);
seat->cursor_button.notify = handle_cursor_button;
@@ -755,6 +863,8 @@ seat_create(struct cg_server *server)
wl_signal_add(&seat->cursor->events.touch_up, &seat->touch_up);
seat->touch_motion.notify = handle_touch_motion;
wl_signal_add(&seat->cursor->events.touch_motion, &seat->touch_motion);
+ seat->touch_frame.notify = handle_touch_frame;
+ wl_signal_add(&seat->cursor->events.touch_frame, &seat->touch_frame);
seat->request_set_cursor.notify = handle_request_set_cursor;
wl_signal_add(&seat->seat->events.request_set_cursor, &seat->request_set_cursor);
@@ -764,16 +874,19 @@ seat_create(struct cg_server *server)
wl_signal_add(&seat->seat->events.request_set_primary_selection, &seat->request_set_primary_selection);
wl_list_init(&seat->keyboards);
+ wl_list_init(&seat->keyboard_groups);
wl_list_init(&seat->pointers);
wl_list_init(&seat->touch);
seat->new_input.notify = handle_new_input;
- wl_signal_add(&server->backend->events.new_input, &seat->new_input);
+ wl_signal_add(&backend->events.new_input, &seat->new_input);
+
+ server->new_virtual_keyboard.notify = handle_virtual_keyboard;
+ server->new_virtual_pointer.notify = handle_virtual_pointer;
wl_list_init(&seat->drag_icons);
seat->request_start_drag.notify = handle_request_start_drag;
- wl_signal_add(&seat->seat->events.request_start_drag,
- &seat->request_start_drag);
+ wl_signal_add(&seat->seat->events.request_start_drag, &seat->request_start_drag);
seat->start_drag.notify = handle_start_drag;
wl_signal_add(&seat->seat->events.start_drag, &seat->start_drag);
@@ -790,6 +903,11 @@ seat_destroy(struct cg_seat *seat)
wl_list_remove(&seat->request_start_drag.link);
wl_list_remove(&seat->start_drag.link);
+ struct cg_keyboard_group *keyboard_group, *keyboard_group_tmp;
+ wl_list_for_each_safe (keyboard_group, keyboard_group_tmp, &seat->keyboard_groups, link) {
+ keyboard_group_destroy(keyboard_group);
+ }
+
// Destroying the wlr seat will trigger the destroy handler on our seat,
// which will in turn free it.
wlr_seat_destroy(seat->seat);
@@ -799,7 +917,10 @@ struct cg_view *
seat_get_focus(struct cg_seat *seat)
{
struct wlr_surface *prev_surface = seat->seat->keyboard_state.focused_surface;
- return view_from_wlr_surface(seat->server, prev_surface);
+ if (!prev_surface) {
+ return NULL;
+ }
+ return view_from_wlr_surface(prev_surface);
}
void
@@ -816,7 +937,7 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view)
#if CAGE_HAS_XWAYLAND
if (view->type == CAGE_XWAYLAND_VIEW) {
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
- if (!wlr_xwayland_or_surface_wants_focus(xwayland_view->xwayland_surface)) {
+ if (!wlr_xwayland_surface_override_redirect_wants_focus(xwayland_view->xwayland_surface)) {
return;
}
}
@@ -835,19 +956,28 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view)
view_activate(view, true);
char *title = view_get_title(view);
- output_set_window_title(server->output, title);
+ struct cg_output *output;
+ wl_list_for_each (output, &server->outputs, link) {
+ output_set_window_title(output, title);
+ }
free(title);
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(wlr_seat);
if (keyboard) {
- wlr_seat_keyboard_notify_enter(wlr_seat, view->wlr_surface,
- keyboard->keycodes,
- keyboard->num_keycodes,
+ wlr_seat_keyboard_notify_enter(wlr_seat, view->wlr_surface, keyboard->keycodes, keyboard->num_keycodes,
&keyboard->modifiers);
} else {
- wlr_seat_keyboard_notify_enter(wlr_seat, view->wlr_surface,
- NULL, 0, NULL);
+ wlr_seat_keyboard_notify_enter(wlr_seat, view->wlr_surface, NULL, 0, NULL);
}
- process_cursor_motion(seat, -1);
+ process_cursor_motion(seat, -1, 0, 0, 0, 0);
+}
+
+void
+seat_center_cursor(struct cg_seat *seat)
+{
+ /* Place the cursor in the center of the output layout. */
+ struct wlr_box layout_box;
+ wlr_output_layout_get_box(seat->server->output_layout, NULL, &layout_box);
+ wlr_cursor_warp(seat->cursor, NULL, layout_box.width / 2, layout_box.height / 2);
}
diff --git a/seat.h b/seat.h
index 7e5d6b6..4b7bfda 100644
--- a/seat.h
+++ b/seat.h
@@ -1,7 +1,7 @@
#ifndef CG_SEAT_H
#define CG_SEAT_H
-#include
+#include
#include
#include
#include
@@ -20,24 +20,26 @@ struct cg_seat {
struct wl_listener destroy;
struct wl_list keyboards;
+ struct wl_list keyboard_groups;
struct wl_list pointers;
struct wl_list touch;
struct wl_listener new_input;
struct wlr_cursor *cursor;
struct wlr_xcursor_manager *xcursor_manager;
- struct wl_listener cursor_motion;
+ struct wl_listener cursor_motion_relative;
struct wl_listener cursor_motion_absolute;
struct wl_listener cursor_button;
struct wl_listener cursor_axis;
struct wl_listener cursor_frame;
int32_t touch_id;
- double touch_x;
- double touch_y;
+ double touch_lx;
+ double touch_ly;
struct wl_listener touch_down;
struct wl_listener touch_up;
struct wl_listener touch_motion;
+ struct wl_listener touch_frame;
struct wl_list drag_icons;
struct wl_listener request_start_drag;
@@ -48,20 +50,19 @@ struct cg_seat {
struct wl_listener request_set_primary_selection;
};
-struct cg_keyboard {
- struct wl_list link; // seat::keyboards
+struct cg_keyboard_group {
+ struct wlr_keyboard_group *wlr_group;
struct cg_seat *seat;
- struct wlr_input_device *device;
-
- struct wl_listener modifiers;
struct wl_listener key;
- struct wl_listener destroy;
+ struct wl_listener modifiers;
+ struct wl_list link; // cg_seat::keyboard_groups
+ bool is_virtual;
};
struct cg_pointer {
struct wl_list link; // seat::pointers
struct cg_seat *seat;
- struct wlr_input_device *device;
+ struct wlr_pointer *pointer;
struct wl_listener destroy;
};
@@ -69,7 +70,7 @@ struct cg_pointer {
struct cg_touch {
struct wl_list link; // seat::touch
struct cg_seat *seat;
- struct wlr_input_device *device;
+ struct wlr_touch *touch;
struct wl_listener destroy;
};
@@ -78,15 +79,18 @@ struct cg_drag_icon {
struct wl_list link; // seat::drag_icons
struct cg_seat *seat;
struct wlr_drag_icon *wlr_drag_icon;
- double x;
- double y;
+ struct wlr_scene_tree *scene_tree;
+
+ /* The drag icon has a position in layout coordinates. */
+ double lx, ly;
struct wl_listener destroy;
};
-struct cg_seat *seat_create(struct cg_server *server);
+struct cg_seat *seat_create(struct cg_server *server, struct wlr_backend *backend);
void seat_destroy(struct cg_seat *seat);
struct cg_view *seat_get_focus(struct cg_seat *seat);
void seat_set_focus(struct cg_seat *seat, struct cg_view *view);
+void seat_center_cursor(struct cg_seat *seat);
#endif
diff --git a/server.h b/server.h
index 80e9951..00c2a61 100644
--- a/server.h
+++ b/server.h
@@ -3,46 +3,71 @@
#include "config.h"
-#include
-#include
-#include
+#include
#include
+#include
#include
+#include
#include
+#include
+
#if CAGE_HAS_XWAYLAND
#include
#endif
-#include "output.h"
-#include "seat.h"
-#include "view.h"
+enum cg_multi_output_mode {
+ CAGE_MULTI_OUTPUT_MODE_EXTEND,
+ CAGE_MULTI_OUTPUT_MODE_LAST,
+};
struct cg_server {
struct wl_display *wl_display;
- struct wlr_backend *backend;
struct wl_list views;
+ struct wlr_backend *backend;
+ struct wlr_renderer *renderer;
+ struct wlr_allocator *allocator;
+ struct wlr_session *session;
+ struct wl_listener display_destroy;
struct cg_seat *seat;
- struct wlr_idle *idle;
+ struct wlr_idle_notifier_v1 *idle;
struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1;
struct wl_listener new_idle_inhibitor_v1;
struct wl_list inhibitors;
+ enum cg_multi_output_mode output_mode;
struct wlr_output_layout *output_layout;
- struct cg_output *output;
+ struct wlr_scene_output_layout *scene_output_layout;
+
+ struct wlr_scene *scene;
+ /* Includes disabled outputs; depending on the output_mode
+ * some outputs may be disabled. */
+ struct wl_list outputs; // cg_output::link
struct wl_listener new_output;
+ struct wl_listener output_layout_change;
struct wl_listener xdg_toplevel_decoration;
- struct wl_listener new_xdg_shell_surface;
+ struct wl_listener new_xdg_toplevel;
+ struct wl_listener new_xdg_popup;
+
+ struct wl_listener new_virtual_keyboard;
+ struct wl_listener new_virtual_pointer;
#if CAGE_HAS_XWAYLAND
struct wl_listener new_xwayland_surface;
#endif
+ struct wlr_output_manager_v1 *output_manager_v1;
+ struct wl_listener output_manager_apply;
+ struct wl_listener output_manager_test;
+
+ struct wlr_relative_pointer_manager_v1 *relative_pointer_manager;
bool xdg_decoration;
- enum wl_output_transform output_transform;
-#ifdef DEBUG
- bool debug_damage_tracking;
-#endif
+ bool allow_vt_switch;
+ bool return_app_code;
+ bool terminated;
+ enum wlr_log_importance log_level;
};
+void server_terminate(struct cg_server *server);
+
#endif
diff --git a/view.c b/view.c
index 6b55606..8cbeb5e 100644
--- a/view.c
+++ b/view.c
@@ -1,20 +1,20 @@
/*
* Cage: A Wayland kiosk.
*
- * Copyright (C) 2018-2019 Jente Hidskes
+ * Copyright (C) 2018-2021 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
#define _POSIX_C_SOURCE 200809L
+#include
#include
#include
#include
-#include
-#include
+#include
#include
-#include
+#include
#include "output.h"
#include "seat.h"
@@ -24,96 +24,6 @@
#include "xwayland.h"
#endif
-static void
-view_child_handle_commit(struct wl_listener *listener, void *data)
-{
- struct cg_view_child *child = wl_container_of(listener, child, commit);
- view_damage_surface(child->view);
-}
-
-static void subsurface_create(struct cg_view *view, struct wlr_subsurface *wlr_subsurface);
-
-static void
-view_child_handle_new_subsurface(struct wl_listener *listener, void *data)
-{
- struct cg_view_child *child = wl_container_of(listener, child, new_subsurface);
- struct wlr_subsurface *wlr_subsurface = data;
- subsurface_create(child->view, wlr_subsurface);
-}
-
-void
-view_child_finish(struct cg_view_child *child)
-{
- if (!child) {
- return;
- }
-
- view_damage_whole(child->view);
-
- wl_list_remove(&child->link);
- wl_list_remove(&child->commit.link);
- wl_list_remove(&child->new_subsurface.link);
-}
-
-void
-view_child_init(struct cg_view_child *child, struct cg_view *view, struct wlr_surface *wlr_surface)
-{
- child->view = view;
- child->wlr_surface = wlr_surface;
-
- child->commit.notify = view_child_handle_commit;
- wl_signal_add(&wlr_surface->events.commit, &child->commit);
- child->new_subsurface.notify = view_child_handle_new_subsurface;
- wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface);
-
- wl_list_insert(&view->children, &child->link);
-}
-
-static void
-subsurface_destroy(struct cg_view_child *child)
-{
- if (!child) {
- return;
- }
-
- struct cg_subsurface *subsurface = (struct cg_subsurface *) child;
- wl_list_remove(&subsurface->destroy.link);
- view_child_finish(&subsurface->view_child);
- free(subsurface);
-}
-
-static void
-subsurface_handle_destroy(struct wl_listener *listener, void *data)
-{
- struct cg_subsurface *subsurface = wl_container_of(listener, subsurface, destroy);
- struct cg_view_child *view_child = (struct cg_view_child *) subsurface;
- subsurface_destroy(view_child);
-}
-
-static void
-subsurface_create(struct cg_view *view, struct wlr_subsurface *wlr_subsurface)
-{
- struct cg_subsurface *subsurface = calloc(1, sizeof(struct cg_subsurface));
- if (!subsurface) {
- return;
- }
-
- view_child_init(&subsurface->view_child, view, wlr_subsurface->surface);
- subsurface->view_child.destroy = subsurface_destroy;
- subsurface->wlr_subsurface = wlr_subsurface;
-
- subsurface->destroy.notify = subsurface_handle_destroy;
- wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
-}
-
-static void
-handle_new_subsurface(struct wl_listener *listener, void *data)
-{
- struct cg_view *view = wl_container_of(listener, view, new_subsurface);
- struct wlr_subsurface *wlr_subsurface = data;
- subsurface_create(view, wlr_subsurface);
-}
-
char *
view_get_title(struct cg_view *view)
{
@@ -131,67 +41,73 @@ view_is_primary(struct cg_view *view)
}
bool
-view_is_transient_for(struct cg_view *child, struct cg_view *parent) {
+view_is_transient_for(struct cg_view *child, struct cg_view *parent)
+{
return child->impl->is_transient_for(child, parent);
}
-void
-view_damage_surface(struct cg_view *view)
-{
- output_damage_view_surface(view->server->output, view);
-}
-
-void
-view_damage_whole(struct cg_view *view)
-{
- output_damage_view_whole(view->server->output, view);
-}
-
void
view_activate(struct cg_view *view, bool activate)
{
view->impl->activate(view, activate);
}
-static void
-view_maximize(struct cg_view *view)
+static bool
+view_extends_output_layout(struct cg_view *view, struct wlr_box *layout_box)
{
- struct cg_output *output = view->server->output;
- int output_width, output_height;
-
- wlr_output_transformed_resolution(output->wlr_output, &output_width, &output_height);
- view->impl->maximize(view, output_width, output_height);
-}
-
-static void
-view_center(struct cg_view *view)
-{
- struct wlr_output *output = view->server->output->wlr_output;
-
- int output_width, output_height;
- wlr_output_transformed_resolution(output, &output_width, &output_height);
-
int width, height;
view->impl->get_geometry(view, &width, &height);
- view->x = (output_width - width) / 2;
- view->y = (output_height - height) / 2;
+ return (layout_box->height < height || layout_box->width < width);
+}
+
+static void
+view_maximize(struct cg_view *view, struct wlr_box *layout_box)
+{
+ view->lx = layout_box->x;
+ view->ly = layout_box->y;
+
+ if (view->scene_tree) {
+ wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly);
+ }
+
+ view->impl->maximize(view, layout_box->width, layout_box->height);
+}
+
+static void
+view_center(struct cg_view *view, struct wlr_box *layout_box)
+{
+ int width, height;
+ view->impl->get_geometry(view, &width, &height);
+
+ view->lx = (layout_box->width - width) / 2;
+ view->ly = (layout_box->height - height) / 2;
+
+ if (view->scene_tree) {
+ wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly);
+ }
}
void
view_position(struct cg_view *view)
{
- if (view_is_primary(view)) {
- view_maximize(view);
+ struct wlr_box layout_box;
+ wlr_output_layout_get_box(view->server->output_layout, NULL, &layout_box);
+
+ if (view_is_primary(view) || view_extends_output_layout(view, &layout_box)) {
+ view_maximize(view, &layout_box);
} else {
- view_center(view);
+ view_center(view, &layout_box);
}
}
void
-view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
+view_position_all(struct cg_server *server)
{
- view->impl->for_each_surface(view, iterator, data);
+ struct cg_view *view;
+ wl_list_for_each (view, &server->views, link) {
+ view_position(view);
+ }
}
void
@@ -199,28 +115,24 @@ view_unmap(struct cg_view *view)
{
wl_list_remove(&view->link);
- wl_list_remove(&view->new_subsurface.link);
-
- struct cg_view_child *child, *tmp;
- wl_list_for_each_safe(child, tmp, &view->children, link) {
- child->destroy(child);
- }
+ wlr_scene_node_destroy(&view->scene_tree->node);
+ view->wlr_surface->data = NULL;
view->wlr_surface = NULL;
}
void
view_map(struct cg_view *view, struct wlr_surface *surface)
{
- view->wlr_surface = surface;
-
- struct wlr_subsurface *subsurface;
- wl_list_for_each(subsurface, &view->wlr_surface->subsurfaces, parent_link) {
- subsurface_create(view, subsurface);
+ view->scene_tree = wlr_scene_subsurface_tree_create(&view->server->scene->tree, surface);
+ if (!view->scene_tree) {
+ wl_resource_post_no_memory(surface->resource);
+ return;
}
+ view->scene_tree->node.data = view;
- view->new_subsurface.notify = handle_new_subsurface;
- wl_signal_add(&view->wlr_surface->events.new_subsurface, &view->new_subsurface);
+ view->wlr_surface = surface;
+ surface->data = view;
#if CAGE_HAS_XWAYLAND
/* We shouldn't position override-redirect windows. They set
@@ -239,14 +151,6 @@ void
view_destroy(struct cg_view *view)
{
struct cg_server *server = view->server;
- bool ever_been_mapped = true;
-
-#if CAGE_HAS_XWAYLAND
- if (view->type == CAGE_XWAYLAND_VIEW) {
- struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
- ever_been_mapped = xwayland_view->ever_been_mapped;
- }
-#endif
if (view->wlr_surface != NULL) {
view_unmap(view);
@@ -259,38 +163,20 @@ view_destroy(struct cg_view *view)
if (!empty) {
struct cg_view *prev = wl_container_of(server->views.next, prev, link);
seat_set_focus(server->seat, prev);
- } else if (ever_been_mapped) {
- /* The list is empty and the last view has been
- mapped, so we can safely exit. */
- wl_display_terminate(server->wl_display);
}
}
void
-view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type,
- const struct cg_view_impl *impl)
+view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type, const struct cg_view_impl *impl)
{
view->server = server;
view->type = type;
view->impl = impl;
-
- wl_list_init(&view->children);
}
struct cg_view *
-view_from_wlr_surface(struct cg_server *server, struct wlr_surface *surface)
+view_from_wlr_surface(struct wlr_surface *surface)
{
- struct cg_view *view;
- wl_list_for_each(view, &server->views, link) {
- if (view->wlr_surface == surface) {
- return view;
- }
- }
- return NULL;
-}
-
-struct wlr_surface *
-view_wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
-{
- return view->impl->wlr_surface_at(view, sx, sy, sub_x, sub_y);
+ assert(surface);
+ return surface->data;
}
diff --git a/view.h b/view.h
index 03e41ca..9b2ab75 100644
--- a/view.h
+++ b/view.h
@@ -4,10 +4,10 @@
#include "config.h"
#include
-#include
-#include
-#include
+#include
+#include
#include
+#include
#if CAGE_HAS_XWAYLAND
#include
#endif
@@ -24,14 +24,14 @@ enum cg_view_type {
struct cg_view {
struct cg_server *server;
struct wl_list link; // server::views
- struct wl_list children; // cg_view_child::link
struct wlr_surface *wlr_surface;
- int x, y;
+ struct wlr_scene_tree *scene_tree;
+
+ /* The view has a position in layout coordinates. */
+ int lx, ly;
enum cg_view_type type;
const struct cg_view_impl *impl;
-
- struct wl_listener new_subsurface;
};
struct cg_view_impl {
@@ -42,49 +42,19 @@ struct cg_view_impl {
void (*activate)(struct cg_view *view, bool activate);
void (*maximize)(struct cg_view *view, int output_width, int output_height);
void (*destroy)(struct cg_view *view);
- void (*for_each_surface)(struct cg_view *view, wlr_surface_iterator_func_t iterator,
- void *data);
- struct wlr_surface *(*wlr_surface_at)(struct cg_view *view, double sx, double sy,
- double *sub_x, double *sub_y);
-};
-
-struct cg_view_child {
- struct cg_view *view;
- struct wlr_surface *wlr_surface;
- struct wl_list link;
-
- struct wl_listener commit;
- struct wl_listener new_subsurface;
-
- void (*destroy)(struct cg_view_child *child);
-};
-
-struct cg_subsurface {
- struct cg_view_child view_child;
- struct wlr_subsurface *wlr_subsurface;
-
- struct wl_listener destroy;
};
char *view_get_title(struct cg_view *view);
bool view_is_primary(struct cg_view *view);
bool view_is_transient_for(struct cg_view *child, struct cg_view *parent);
-void view_damage_surface(struct cg_view *view);
-void view_damage_whole(struct cg_view *view);
void view_activate(struct cg_view *view, bool activate);
void view_position(struct cg_view *view);
-void view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data);
+void view_position_all(struct cg_server *server);
void view_unmap(struct cg_view *view);
void view_map(struct cg_view *view, struct wlr_surface *surface);
void view_destroy(struct cg_view *view);
-void view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type,
- const struct cg_view_impl *impl);
+void view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type, const struct cg_view_impl *impl);
-struct cg_view *view_from_wlr_surface(struct cg_server *server, struct wlr_surface *surface);
-struct wlr_surface *view_wlr_surface_at(struct cg_view *view, double sx, double sy,
- double *sub_x, double *sub_y);
-
-void view_child_finish(struct cg_view_child *child);
-void view_child_init(struct cg_view_child *child, struct cg_view *view, struct wlr_surface *wlr_surface);
+struct cg_view *view_from_wlr_surface(struct wlr_surface *surface);
#endif
diff --git a/xdg_shell.c b/xdg_shell.c
index f964f76..5493918 100644
--- a/xdg_shell.c
+++ b/xdg_shell.c
@@ -6,10 +6,11 @@
* See the LICENSE file accompanying this file.
*/
+#include
#include
#include
-#include
-#include
+#include
+#include
#include
#include
@@ -18,21 +19,9 @@
#include "xdg_shell.h"
static void
-xdg_decoration_handle_destroy(struct wl_listener *listener, void *data)
+xdg_decoration_set_mode(struct cg_xdg_decoration *xdg_decoration)
{
- struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, destroy);
-
- wl_list_remove(&xdg_decoration->destroy.link);
- wl_list_remove(&xdg_decoration->request_mode.link);
- free(xdg_decoration);
-}
-
-static void
-xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data)
-{
- struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, request_mode);
enum wlr_xdg_toplevel_decoration_v1_mode mode;
-
if (xdg_decoration->server->xdg_decoration) {
mode = WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
} else {
@@ -42,101 +31,84 @@ xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data)
}
static void
-xdg_popup_destroy(struct cg_view_child *child)
+xdg_decoration_handle_destroy(struct wl_listener *listener, void *data)
{
- if (!child) {
+ struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, destroy);
+
+ wl_list_remove(&xdg_decoration->destroy.link);
+ wl_list_remove(&xdg_decoration->commit.link);
+ wl_list_remove(&xdg_decoration->request_mode.link);
+ free(xdg_decoration);
+}
+
+static void
+xdg_decoration_handle_commit(struct wl_listener *listener, void *data)
+{
+ struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, commit);
+
+ if (xdg_decoration->wlr_decoration->toplevel->base->initial_commit) {
+ xdg_decoration_set_mode(xdg_decoration);
+ }
+}
+
+static void
+xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data)
+{
+ struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, request_mode);
+
+ if (xdg_decoration->wlr_decoration->toplevel->base->initialized) {
+ xdg_decoration_set_mode(xdg_decoration);
+ }
+}
+
+static struct cg_view *
+popup_get_view(struct wlr_xdg_popup *popup)
+{
+ while (true) {
+ if (popup->parent == NULL) {
+ return NULL;
+ }
+ struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(popup->parent);
+ if (xdg_surface == NULL) {
+ return NULL;
+ }
+ switch (xdg_surface->role) {
+ case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
+ return xdg_surface->data;
+ case WLR_XDG_SURFACE_ROLE_POPUP:
+ popup = xdg_surface->popup;
+ break;
+ case WLR_XDG_SURFACE_ROLE_NONE:
+ return NULL;
+ }
+ }
+}
+
+static void
+popup_unconstrain(struct wlr_xdg_popup *popup)
+{
+ struct cg_view *view = popup_get_view(popup);
+ if (view == NULL) {
return;
}
- struct cg_xdg_popup *popup = (struct cg_xdg_popup *) child;
- wl_list_remove(&popup->destroy.link);
- wl_list_remove(&popup->map.link);
- wl_list_remove(&popup->unmap.link);
- wl_list_remove(&popup->new_popup.link);
- view_child_finish(&popup->view_child);
- free(popup);
-}
+ struct cg_server *server = view->server;
+ struct wlr_box *popup_box = &popup->current.geometry;
-static void
-handle_xdg_popup_map(struct wl_listener *listener, void *data)
-{
- struct cg_xdg_popup *popup = wl_container_of(listener, popup, map);
- view_damage_whole(popup->view_child.view);
-}
-
-static void
-handle_xdg_popup_unmap(struct wl_listener *listener, void *data)
-{
- struct cg_xdg_popup *popup = wl_container_of(listener, popup, unmap);
- view_damage_whole(popup->view_child.view);
-}
-
-static void
-handle_xdg_popup_destroy(struct wl_listener *listener, void *data)
-{
- struct cg_xdg_popup *popup = wl_container_of(listener, popup, destroy);
- struct cg_view_child *view_child = (struct cg_view_child *) popup;
- xdg_popup_destroy(view_child);
-}
-
-static void xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup);
-
-static void
-popup_handle_new_xdg_popup(struct wl_listener *listener, void *data)
-{
- struct cg_xdg_popup *popup = wl_container_of(listener, popup, new_popup);
- struct wlr_xdg_popup *wlr_popup = data;
- xdg_popup_create(popup->view_child.view, wlr_popup);
-}
-
-static void
-popup_unconstrain(struct cg_xdg_popup *popup)
-{
- struct cg_view *view = popup->view_child.view;
- struct wlr_output *output = view->server->output->wlr_output;
- struct wlr_output_layout *output_layout = view->server->output_layout;
-
- struct wlr_box *output_box = wlr_output_layout_get_box(output_layout, output);
+ struct wlr_output_layout *output_layout = server->output_layout;
+ struct wlr_output *wlr_output =
+ wlr_output_layout_output_at(output_layout, view->lx + popup_box->x, view->ly + popup_box->y);
+ struct wlr_box output_box;
+ wlr_output_layout_get_box(output_layout, wlr_output, &output_box);
struct wlr_box output_toplevel_box = {
- .x = output_box->x - view->x,
- .y = output_box->y - view->y,
- .width = output_box->width,
- .height = output_box->height
+ .x = output_box.x - view->lx,
+ .y = output_box.y - view->ly,
+ .width = output_box.width,
+ .height = output_box.height,
};
- wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup, &output_toplevel_box);
-}
-
-static void
-xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup)
-{
- struct cg_xdg_popup *popup = calloc(1, sizeof(struct cg_xdg_popup));
- if (!popup) {
- return;
- }
-
- popup->wlr_popup = wlr_popup;
- view_child_init(&popup->view_child, view, wlr_popup->base->surface);
- popup->view_child.destroy = xdg_popup_destroy;
- popup->destroy.notify = handle_xdg_popup_destroy;
- wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
- popup->map.notify = handle_xdg_popup_map;
- wl_signal_add(&wlr_popup->base->events.map, &popup->map);
- popup->unmap.notify = handle_xdg_popup_unmap;
- wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
- popup->new_popup.notify = popup_handle_new_xdg_popup;
- wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
-
- popup_unconstrain(popup);
-}
-
-static void
-handle_new_xdg_popup(struct wl_listener *listener, void *data)
-{
- struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup);
- struct wlr_xdg_popup *wlr_popup = data;
- xdg_popup_create(&xdg_shell_view->view, wlr_popup);
+ wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box);
}
static struct cg_xdg_shell_view *
@@ -149,27 +121,26 @@ static char *
get_title(struct cg_view *view)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
- return xdg_shell_view->xdg_surface->toplevel->title;
+ return xdg_shell_view->xdg_toplevel->title;
}
static void
get_geometry(struct cg_view *view, int *width_out, int *height_out)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
- struct wlr_box geom;
+ struct wlr_xdg_surface *xdg_surface = xdg_shell_view->xdg_toplevel->base;
- wlr_xdg_surface_get_geometry(xdg_shell_view->xdg_surface, &geom);
- *width_out = geom.width;
- *height_out = geom.height;
+ *width_out = xdg_surface->geometry.width;
+ *height_out = xdg_surface->geometry.height;
}
static bool
is_primary(struct cg_view *view)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
- struct wlr_xdg_surface *parent = xdg_shell_view->xdg_surface->toplevel->parent;
- /* FIXME: role is 0? */
- return parent == NULL; /*&& role == WLR_XDG_SURFACE_ROLE_TOPLEVEL */
+ struct wlr_xdg_toplevel *parent = xdg_shell_view->xdg_toplevel->parent;
+
+ return parent == NULL;
}
static bool
@@ -179,14 +150,13 @@ is_transient_for(struct cg_view *child, struct cg_view *parent)
return false;
}
struct cg_xdg_shell_view *_child = xdg_shell_view_from_view(child);
- struct wlr_xdg_surface *xdg_surface = _child->xdg_surface;
+ struct wlr_xdg_toplevel *xdg_toplevel = _child->xdg_toplevel;
struct cg_xdg_shell_view *_parent = xdg_shell_view_from_view(parent);
- struct wlr_xdg_surface *parent_xdg_surface = _parent->xdg_surface;
- while (xdg_surface) {
- if (xdg_surface->toplevel->parent == parent_xdg_surface) {
+ while (xdg_toplevel) {
+ if (xdg_toplevel->parent == _parent->xdg_toplevel) {
return true;
}
- xdg_surface = xdg_surface->toplevel->parent;
+ xdg_toplevel = xdg_toplevel->parent;
}
return false;
}
@@ -195,15 +165,15 @@ static void
activate(struct cg_view *view, bool activate)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
- wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_surface, activate);
+ wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_toplevel, activate);
}
static void
maximize(struct cg_view *view, int output_width, int output_height)
{
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
- wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_surface, output_width, output_height);
- wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_surface, true);
+ wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_toplevel, output_width, output_height);
+ wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_toplevel, true);
}
static void
@@ -214,74 +184,68 @@ destroy(struct cg_view *view)
}
static void
-for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
-{
- struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
- wlr_xdg_surface_for_each_surface(xdg_shell_view->xdg_surface, iterator, data);
-}
-
-static struct wlr_surface *
-wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
-{
- struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
- return wlr_xdg_surface_surface_at(xdg_shell_view->xdg_surface, sx, sy, sub_x, sub_y);
-}
-
-static void
-handle_xdg_shell_surface_request_fullscreen(struct wl_listener *listener, void *data)
+handle_xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_fullscreen);
- struct wlr_xdg_toplevel_set_fullscreen_event *event = data;
- wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_surface, event->fullscreen);
+
+ /**
+ * Certain clients do not like figuring out their own window geometry if they
+ * display in fullscreen mode, so we set it here.
+ */
+ struct wlr_box layout_box;
+ wlr_output_layout_get_box(xdg_shell_view->view.server->output_layout, NULL, &layout_box);
+ wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_toplevel, layout_box.width, layout_box.height);
+
+ wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_toplevel,
+ xdg_shell_view->xdg_toplevel->requested.fullscreen);
}
static void
-handle_xdg_shell_surface_commit(struct wl_listener *listener, void *data)
-{
- struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit);
- struct cg_view *view = &xdg_shell_view->view;
- view_damage_surface(view);
-}
-
-static void
-handle_xdg_shell_surface_unmap(struct wl_listener *listener, void *data)
+handle_xdg_toplevel_unmap(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap);
struct cg_view *view = &xdg_shell_view->view;
- view_damage_whole(view);
-
- wl_list_remove(&xdg_shell_view->commit.link);
-
view_unmap(view);
}
static void
-handle_xdg_shell_surface_map(struct wl_listener *listener, void *data)
+handle_xdg_toplevel_map(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, map);
struct cg_view *view = &xdg_shell_view->view;
- xdg_shell_view->commit.notify = handle_xdg_shell_surface_commit;
- wl_signal_add(&xdg_shell_view->xdg_surface->surface->events.commit, &xdg_shell_view->commit);
-
- view_map(view, xdg_shell_view->xdg_surface->surface);
-
- view_damage_whole(view);
+ view_map(view, xdg_shell_view->xdg_toplevel->base->surface);
}
static void
-handle_xdg_shell_surface_destroy(struct wl_listener *listener, void *data)
+handle_xdg_toplevel_commit(struct wl_listener *listener, void *data)
+{
+ struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit);
+
+ if (!xdg_shell_view->xdg_toplevel->base->initial_commit) {
+ return;
+ }
+
+ wlr_xdg_toplevel_set_wm_capabilities(xdg_shell_view->xdg_toplevel, XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
+
+ /* When an xdg_surface performs an initial commit, the compositor must
+ * reply with a configure so the client can map the surface. */
+ view_position(&xdg_shell_view->view);
+}
+
+static void
+handle_xdg_toplevel_destroy(struct wl_listener *listener, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy);
struct cg_view *view = &xdg_shell_view->view;
+ wl_list_remove(&xdg_shell_view->commit.link);
wl_list_remove(&xdg_shell_view->map.link);
wl_list_remove(&xdg_shell_view->unmap.link);
wl_list_remove(&xdg_shell_view->destroy.link);
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
- wl_list_remove(&xdg_shell_view->new_popup.link);
- xdg_shell_view->xdg_surface = NULL;
+ xdg_shell_view->xdg_toplevel = NULL;
view_destroy(view);
}
@@ -294,19 +258,13 @@ static const struct cg_view_impl xdg_shell_view_impl = {
.activate = activate,
.maximize = maximize,
.destroy = destroy,
- .for_each_surface = for_each_surface,
- .wlr_surface_at = wlr_surface_at,
};
void
-handle_xdg_shell_surface_new(struct wl_listener *listener, void *data)
+handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
{
- struct cg_server *server = wl_container_of(listener, server, new_xdg_shell_surface);
- struct wlr_xdg_surface *xdg_surface = data;
-
- if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
- return;
- }
+ struct cg_server *server = wl_container_of(listener, server, new_xdg_toplevel);
+ struct wlr_xdg_toplevel *toplevel = data;
struct cg_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct cg_xdg_shell_view));
if (!xdg_shell_view) {
@@ -315,18 +273,105 @@ handle_xdg_shell_surface_new(struct wl_listener *listener, void *data)
}
view_init(&xdg_shell_view->view, server, CAGE_XDG_SHELL_VIEW, &xdg_shell_view_impl);
- xdg_shell_view->xdg_surface = xdg_surface;
+ xdg_shell_view->xdg_toplevel = toplevel;
- xdg_shell_view->map.notify = handle_xdg_shell_surface_map;
- wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
- xdg_shell_view->unmap.notify = handle_xdg_shell_surface_unmap;
- wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap);
- xdg_shell_view->destroy.notify = handle_xdg_shell_surface_destroy;
- wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy);
- xdg_shell_view->request_fullscreen.notify = handle_xdg_shell_surface_request_fullscreen;
- wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen);
- xdg_shell_view->new_popup.notify = handle_new_xdg_popup;
- wl_signal_add(&xdg_surface->events.new_popup, &xdg_shell_view->new_popup);
+ xdg_shell_view->commit.notify = handle_xdg_toplevel_commit;
+ wl_signal_add(&toplevel->base->surface->events.commit, &xdg_shell_view->commit);
+ xdg_shell_view->map.notify = handle_xdg_toplevel_map;
+ wl_signal_add(&toplevel->base->surface->events.map, &xdg_shell_view->map);
+ xdg_shell_view->unmap.notify = handle_xdg_toplevel_unmap;
+ wl_signal_add(&toplevel->base->surface->events.unmap, &xdg_shell_view->unmap);
+ xdg_shell_view->destroy.notify = handle_xdg_toplevel_destroy;
+ wl_signal_add(&toplevel->events.destroy, &xdg_shell_view->destroy);
+ xdg_shell_view->request_fullscreen.notify = handle_xdg_toplevel_request_fullscreen;
+ wl_signal_add(&toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen);
+
+ toplevel->base->data = xdg_shell_view;
+}
+
+static void
+popup_handle_destroy(struct wl_listener *listener, void *data)
+{
+ struct cg_xdg_popup *popup = wl_container_of(listener, popup, destroy);
+ wl_list_remove(&popup->destroy.link);
+ wl_list_remove(&popup->commit.link);
+ wl_list_remove(&popup->reposition.link);
+ free(popup);
+}
+
+static void
+popup_handle_commit(struct wl_listener *listener, void *data)
+{
+ struct cg_xdg_popup *popup = wl_container_of(listener, popup, commit);
+
+ if (popup->xdg_popup->base->initial_commit) {
+ popup_unconstrain(popup->xdg_popup);
+ }
+}
+
+static void
+popup_handle_reposition(struct wl_listener *listener, void *data)
+{
+ struct cg_xdg_popup *popup = wl_container_of(listener, popup, reposition);
+
+ popup_unconstrain(popup->xdg_popup);
+}
+
+void
+handle_new_xdg_popup(struct wl_listener *listener, void *data)
+{
+ struct cg_server *server = wl_container_of(listener, server, new_xdg_popup);
+ struct wlr_xdg_popup *wlr_popup = data;
+
+ struct cg_view *view = popup_get_view(wlr_popup);
+ if (view == NULL) {
+ return;
+ }
+
+ struct wlr_scene_tree *parent_scene_tree = NULL;
+ struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(wlr_popup->parent);
+ if (parent == NULL) {
+ return;
+ }
+ switch (parent->role) {
+ case WLR_XDG_SURFACE_ROLE_TOPLEVEL:;
+ parent_scene_tree = view->scene_tree;
+ break;
+ case WLR_XDG_SURFACE_ROLE_POPUP:
+ parent_scene_tree = parent->data;
+ break;
+ case WLR_XDG_SURFACE_ROLE_NONE:
+ break;
+ }
+ if (parent_scene_tree == NULL) {
+ return;
+ }
+
+ struct cg_xdg_popup *popup = calloc(1, sizeof(*popup));
+ if (popup == NULL) {
+ wlr_log(WLR_ERROR, "Failed to allocate popup");
+ return;
+ }
+
+ popup->xdg_popup = wlr_popup;
+
+ popup->destroy.notify = popup_handle_destroy;
+ wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
+
+ popup->commit.notify = popup_handle_commit;
+ wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
+
+ popup->reposition.notify = popup_handle_reposition;
+ wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
+
+ struct wlr_scene_tree *popup_scene_tree = wlr_scene_xdg_surface_create(parent_scene_tree, wlr_popup->base);
+ if (popup_scene_tree == NULL) {
+ wlr_log(WLR_ERROR, "Failed to allocate scene-graph node for XDG popup");
+ free(popup);
+ return;
+ }
+
+ wlr_popup->base->data = popup_scene_tree;
}
void
@@ -345,8 +390,8 @@ handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data)
xdg_decoration->destroy.notify = xdg_decoration_handle_destroy;
wl_signal_add(&wlr_decoration->events.destroy, &xdg_decoration->destroy);
+ xdg_decoration->commit.notify = xdg_decoration_handle_commit;
+ wl_signal_add(&wlr_decoration->toplevel->base->surface->events.commit, &xdg_decoration->commit);
xdg_decoration->request_mode.notify = xdg_decoration_handle_request_mode;
wl_signal_add(&wlr_decoration->events.request_mode, &xdg_decoration->request_mode);
-
- xdg_decoration_handle_request_mode(&xdg_decoration->request_mode, wlr_decoration);
}
diff --git a/xdg_shell.h b/xdg_shell.h
index 60ba535..f819549 100644
--- a/xdg_shell.h
+++ b/xdg_shell.h
@@ -1,7 +1,7 @@
#ifndef CG_XDG_SHELL_H
#define CG_XDG_SHELL_H
-#include
+#include
#include
#include
@@ -9,34 +9,33 @@
struct cg_xdg_shell_view {
struct cg_view view;
- struct wlr_xdg_surface *xdg_surface;
+ struct wlr_xdg_toplevel *xdg_toplevel;
struct wl_listener destroy;
- struct wl_listener unmap;
- struct wl_listener map;
struct wl_listener commit;
- struct wl_listener request_fullscreen;
- struct wl_listener new_popup;
-};
-
-struct cg_xdg_popup {
- struct cg_view_child view_child;
- struct wlr_xdg_popup *wlr_popup;
-
- struct wl_listener destroy;
- struct wl_listener map;
struct wl_listener unmap;
- struct wl_listener new_popup;
+ struct wl_listener map;
+ struct wl_listener request_fullscreen;
};
struct cg_xdg_decoration {
struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration;
struct cg_server *server;
struct wl_listener destroy;
+ struct wl_listener commit;
struct wl_listener request_mode;
};
-void handle_xdg_shell_surface_new(struct wl_listener *listener, void *data);
+struct cg_xdg_popup {
+ struct wlr_xdg_popup *xdg_popup;
+
+ struct wl_listener destroy;
+ struct wl_listener commit;
+ struct wl_listener reposition;
+};
+
+void handle_new_xdg_toplevel(struct wl_listener *listener, void *data);
+void handle_new_xdg_popup(struct wl_listener *listener, void *data);
void handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data);
diff --git a/xwayland.c b/xwayland.c
index 0cbe412..3df7a08 100644
--- a/xwayland.c
+++ b/xwayland.c
@@ -1,17 +1,16 @@
/*
* Cage: A Wayland kiosk.
*
- * Copyright (C) 2018-2019 Jente Hidskes
+ * Copyright (C) 2018-2020 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
#include
#include
-#include
-#include
-#include
+#include
#include
+#include
#include "server.h"
#include "view.h"
@@ -42,8 +41,15 @@ static void
get_geometry(struct cg_view *view, int *width_out, int *height_out)
{
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
- *width_out = xwayland_view->xwayland_surface->surface->current.width;
- *height_out = xwayland_view->xwayland_surface->surface->current.height;
+ struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface;
+ if (xsurface->surface == NULL) {
+ *width_out = 0;
+ *height_out = 0;
+ return;
+ }
+
+ *width_out = xsurface->surface->current.width;
+ *height_out = xsurface->surface->current.height;
}
static bool
@@ -84,8 +90,9 @@ static void
maximize(struct cg_view *view, int output_width, int output_height)
{
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
- wlr_xwayland_surface_configure(xwayland_view->xwayland_surface, 0, 0, output_width, output_height);
- wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true);
+ wlr_xwayland_surface_configure(xwayland_view->xwayland_surface, view->lx, view->ly, output_width,
+ output_height);
+ wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true, true);
}
static void
@@ -95,18 +102,6 @@ destroy(struct cg_view *view)
free(xwayland_view);
}
-static void
-for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
-{
- wlr_surface_for_each_surface(view->wlr_surface, iterator, data);
-}
-
-static struct wlr_surface *
-wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
-{
- return wlr_surface_surface_at(view->wlr_surface, sx, sy, sub_x, sub_y);
-}
-
static void
handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *data)
{
@@ -115,24 +110,12 @@ handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *d
wlr_xwayland_surface_set_fullscreen(xwayland_view->xwayland_surface, xwayland_surface->fullscreen);
}
-static void
-handle_xwayland_surface_commit(struct wl_listener *listener, void *data)
-{
- struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, commit);
- struct cg_view *view = &xwayland_view->view;
- view_damage_surface(view);
-}
-
static void
handle_xwayland_surface_unmap(struct wl_listener *listener, void *data)
{
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, unmap);
struct cg_view *view = &xwayland_view->view;
- view_damage_whole(view);
-
- wl_list_remove(&xwayland_view->commit.link);
-
view_unmap(view);
}
@@ -143,17 +126,11 @@ handle_xwayland_surface_map(struct wl_listener *listener, void *data)
struct cg_view *view = &xwayland_view->view;
if (!xwayland_view_should_manage(view)) {
- view->x = xwayland_view->xwayland_surface->x;
- view->y = xwayland_view->xwayland_surface->y;
+ view->lx = xwayland_view->xwayland_surface->x;
+ view->ly = xwayland_view->xwayland_surface->y;
}
- xwayland_view->commit.notify = handle_xwayland_surface_commit;
- wl_signal_add(&xwayland_view->xwayland_surface->surface->events.commit, &xwayland_view->commit);
-
- xwayland_view->ever_been_mapped = true;
view_map(view, xwayland_view->xwayland_surface->surface);
-
- view_damage_whole(view);
}
static void
@@ -162,8 +139,6 @@ handle_xwayland_surface_destroy(struct wl_listener *listener, void *data)
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, destroy);
struct cg_view *view = &xwayland_view->view;
- wl_list_remove(&xwayland_view->map.link);
- wl_list_remove(&xwayland_view->unmap.link);
wl_list_remove(&xwayland_view->destroy.link);
wl_list_remove(&xwayland_view->request_fullscreen.link);
xwayland_view->xwayland_surface = NULL;
@@ -179,10 +154,28 @@ static const struct cg_view_impl xwayland_view_impl = {
.activate = activate,
.maximize = maximize,
.destroy = destroy,
- .for_each_surface = for_each_surface,
- .wlr_surface_at = wlr_surface_at,
};
+void
+handle_xwayland_associate(struct wl_listener *listener, void *data)
+{
+ struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, associate);
+ struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface;
+
+ xwayland_view->map.notify = handle_xwayland_surface_map;
+ wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map);
+ xwayland_view->unmap.notify = handle_xwayland_surface_unmap;
+ wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap);
+}
+
+void
+handle_xwayland_dissociate(struct wl_listener *listener, void *data)
+{
+ struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, dissociate);
+ wl_list_remove(&xwayland_view->map.link);
+ wl_list_remove(&xwayland_view->unmap.link);
+}
+
void
handle_xwayland_surface_new(struct wl_listener *listener, void *data)
{
@@ -198,10 +191,10 @@ handle_xwayland_surface_new(struct wl_listener *listener, void *data)
view_init(&xwayland_view->view, server, CAGE_XWAYLAND_VIEW, &xwayland_view_impl);
xwayland_view->xwayland_surface = xwayland_surface;
- xwayland_view->map.notify = handle_xwayland_surface_map;
- wl_signal_add(&xwayland_surface->events.map, &xwayland_view->map);
- xwayland_view->unmap.notify = handle_xwayland_surface_unmap;
- wl_signal_add(&xwayland_surface->events.unmap, &xwayland_view->unmap);
+ xwayland_view->associate.notify = handle_xwayland_associate;
+ wl_signal_add(&xwayland_surface->events.associate, &xwayland_view->associate);
+ xwayland_view->dissociate.notify = handle_xwayland_dissociate;
+ wl_signal_add(&xwayland_surface->events.dissociate, &xwayland_view->dissociate);
xwayland_view->destroy.notify = handle_xwayland_surface_destroy;
wl_signal_add(&xwayland_surface->events.destroy, &xwayland_view->destroy);
xwayland_view->request_fullscreen.notify = handle_xwayland_surface_request_fullscreen;
diff --git a/xwayland.h b/xwayland.h
index fa9c62e..bca4b02 100644
--- a/xwayland.h
+++ b/xwayland.h
@@ -1,7 +1,7 @@
#ifndef CG_XWAYLAND_H
#define CG_XWAYLAND_H
-#include
+#include
#include
#include "view.h"
@@ -9,25 +9,11 @@
struct cg_xwayland_view {
struct cg_view view;
struct wlr_xwayland_surface *xwayland_surface;
-
- /* Some applications that aren't yet Wayland-native or
- otherwise "special" (e.g. Firefox Nightly and Google
- Chrome/Chromium) spawn an XWayland surface upon startup
- that is almost immediately closed again. This makes Cage
- think there are no views left, which results in it
- exiting. However, after this initial (unmapped) surface,
- the "real" application surface is opened. This leads to
- these applications' startup sequences being interrupted by
- Cage exiting. Hence, to work around this issue, Cage checks
- whether an XWayland surface has ever been mapped and exits
- only if 1) the XWayland surface has ever been mapped and 2)
- this was the last surface Cage manages. */
- bool ever_been_mapped;
-
struct wl_listener destroy;
+ struct wl_listener associate;
+ struct wl_listener dissociate;
struct wl_listener unmap;
struct wl_listener map;
- struct wl_listener commit;
struct wl_listener request_fullscreen;
};