Compare commits

..

No commits in common. "master" and "v0.1.2.1" have entirely different histories.

31 changed files with 1520 additions and 1302 deletions

34
.builds/alpine.yml Normal file
View file

@ -0,0 +1,34 @@
image: alpine/edge
packages:
- eudev-dev
- mesa-dev
- meson
- libinput-dev
- libxkbcommon-dev
- pixman-dev
- scdoc
- 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
git checkout 0.11.0
meson --prefix=/usr build -Dexamples=false
ninja -C build
sudo ninja -C build install
- build: |
cd cage
meson build --werror -Dxwayland=true
ninja -C build
rm -rf build
- build-no-xwayland: |
cd cage
meson build --werror -Dxwayland=false
ninja -C build
rm -rf build

43
.builds/archlinux.yml Normal file
View file

@ -0,0 +1,43 @@
image: archlinux
packages:
- clang
- meson
- libinput
- libxkbcommon
- scdoc
- 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
git checkout 0.11.0
meson --prefix=/usr build -Dexamples=false
ninja -C build
sudo ninja -C build install
- build: |
cd cage
meson build --werror -Dxwayland=true
ninja -C build
rm -rf build
- build-no-xwayland: |
cd cage
meson build --werror -Dxwayland=false
ninja -C build
rm -rf build
- scan-build: |
cd cage
CC=clang meson build --werror -Dxwayland=true
CC=clang ninja -C build scan-build
rm -rf build
- clang-format: |
cd cage
meson build --werror -Dxwayland=true
ninja -C build clang-format
rm -rf build
git diff --exit-code

35
.builds/freebsd.yml Normal file
View file

@ -0,0 +1,35 @@
image: freebsd/latest
packages:
- devel/evdev-proto
- devel/meson
- devel/libepoll-shim
- devel/pkgconf
- graphics/mesa-libs
- graphics/wayland
- graphics/wayland-protocols
- textproc/scdoc
- 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
git checkout 0.11.0
meson --prefix=/usr/local build -Dexamples=false
ninja -C build
sudo ninja -C build install
- build: |
cd cage
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig meson build --werror -Dxwayland=true
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ninja -C build
rm -rf build
- build-no-xwayland: |
cd cage
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig meson build --werror -Dxwayland=false
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ninja -C build
rm -rf build

View file

@ -1 +0,0 @@
subprojects/**/*

View file

@ -1,10 +0,0 @@
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

View file

@ -1,81 +0,0 @@
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

View file

@ -1,4 +1,4 @@
# Cage: a Wayland kiosk
# Cage: a Wayland kiosk [![builds.sr.ht status](https://builds.sr.ht/~hjdskes.svg)](https://builds.sr.ht/~hjdskes?)
<img src="https://www.hjdskes.nl/img/projects/cage/cage.svg" alt="Cage's logo" width="150px" align="right">
@ -8,54 +8,57 @@ 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://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.
Wiki](https://github.com/Hjdskes/cage/wiki/).
## Release signatures
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).
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).
## 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. Optionally, install
scdoc for manual pages. Cage is currently based on branch 0.18 of wlroots.
scdoc for manual pages. Note that Cage is developed against the latest tag of
wlroots, in order to not constantly chase breaking changes as soon as they
occur.
Simply execute the following steps to build Cage:
```
$ meson setup build
$ meson compile -C build
$ meson build
$ ninja -C build
```
By default, this builds a debug build. To build a release build, use `meson
setup build --buildtype=release`.
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.
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.
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
<kbd>Alt</kbd>+<kbd>Esc</kbd> to quit. For more configuration options, see
[Configuration](https://github.com/cage-kiosk/cage/wiki/Configuration).
[Configuration](https://github.com/Hjdskes/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/cage-kiosk/cage/issues/new) on
[GitHub](https://github.com/cage-kiosk/cage).
issue](https://github.com/Hjdskes/cage/issues/new) on
[GitHub](https://github.com/Hjdskes/cage).
## License
Please see
[LICENSE](https://github.com/cage-kiosk/cage/blob/master/LICENSE) on
[GitHub](https://github.com/cage-kiosk/cage).
[LICENSE](https://github.com/Hjdskes/cage/blob/master/LICENSE) on
[GitHub](https://github.com/Hjdskes/cage).
Copyright © 2018-2020 Jente Hidskes <dev@hjdskes.nl>

View file

@ -6,7 +6,7 @@ cage - a Wayland kiosk compositor
# SYNOPSIS
*cage* [options...] [--] [_application_...]
*cage* [-dhmrsv] [--] _application_ [application argument ...]
# DESCRIPTION
@ -19,9 +19,6 @@ activities outside the scope of the running application are prevented.
*-d*
Don't draw client side decorations when possible.
*-D*
Enable debug logging.
*-h*
Show the help message.
@ -30,6 +27,10 @@ activities outside the scope of the running application are prevented.
*last* Cage uses only the last connected monitor.
*extend* Cage extends the display across all connected monitors.
*-r*
Rotate the output 90 degrees clockwise. This can be specified up to three
times, each resulting in an additional 90 degrees clockwise rotation.
*-s*
Allow VT switching
@ -64,7 +65,7 @@ _XKB_DEFAULT_VARIANT_, _XKB_DEFAULT_OPTIONS_
# BUGS
Report bugs at https://github.com/cage-kiosk/cage
Report bugs at https://github.com/Hjdskes/cage
# AUTHORS

347
cage.c
View file

@ -19,27 +19,16 @@
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/types/wlr_xcursor_manager.h>
#endif
@ -61,28 +50,10 @@
#include "xwayland.h"
#endif
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;
struct wl_display *display = data;
/* Close Cage's read pipe. */
close(fd);
@ -93,8 +64,7 @@ sigchld_handler(int fd, uint32_t mask, void *data)
wlr_log(WLR_DEBUG, "Connection closed by server");
}
server->return_app_code = true;
server_terminate(server);
wl_display_terminate(display);
return 0;
}
@ -118,7 +88,7 @@ set_cloexec(int fd)
}
static bool
spawn_primary_client(struct cg_server *server, char *argv[], pid_t *pid_out, struct wl_event_source **sigchld_source)
spawn_primary_client(struct wl_display *display, char *argv[], pid_t *pid_out, struct wl_event_source **sigchld_source)
{
int fd[2];
if (pipe(fd) != 0) {
@ -134,8 +104,6 @@ spawn_primary_client(struct cg_server *server, char *argv[], pid_t *pid_out, str
/* 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");
@ -152,15 +120,15 @@ spawn_primary_client(struct cg_server *server, char *argv[], pid_t *pid_out, str
/* 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);
struct wl_event_loop *event_loop = wl_display_get_event_loop(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);
*sigchld_source = wl_event_loop_add_fd(event_loop, fd[0], mask, sigchld_handler, display);
wlr_log(WLR_DEBUG, "Child process created with pid %d", pid);
return true;
}
static int
static void
cleanup_primary_client(pid_t pid)
{
int status;
@ -169,25 +137,15 @@ cleanup_primary_client(pid_t pid)
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()) {
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");
@ -207,13 +165,13 @@ drop_permissions(void)
static int
handle_signal(int signal, void *data)
{
struct cg_server *server = data;
struct wl_display *display = data;
switch (signal) {
case SIGINT:
/* Fallthrough */
case SIGTERM:
server_terminate(server);
wl_display_terminate(display);
return 0;
default:
return 0;
@ -224,13 +182,16 @@ static void
usage(FILE *file, const char *cage)
{
fprintf(file,
"Usage: %s [OPTIONS] [--] [APPLICATION...]\n"
"Usage: %s [OPTIONS] [--] APPLICATION\n"
"\n"
" -d\t Don't draw client side decorations, when possible\n"
" -D\t Enable debug logging\n"
#ifdef DEBUG
" -D\t Turn on damage tracking debugging\n"
#endif
" -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"
" -r\t Rotate the output 90 degrees clockwise, specify up to three times\n"
" -s\t Allow VT switching\n"
" -v\t Show the version number and exit\n"
"\n"
@ -242,14 +203,20 @@ static bool
parse_args(struct cg_server *server, int argc, char *argv[])
{
int c;
while ((c = getopt(argc, argv, "dDhm:sv")) != -1) {
#ifdef DEBUG
while ((c = getopt(argc, argv, "dDhm:rsv")) != -1) {
#else
while ((c = getopt(argc, argv, "dhm:rsv")) != -1) {
#endif
switch (c) {
case 'd':
server->xdg_decoration = true;
break;
#ifdef DEBUG
case 'D':
server->log_level = WLR_DEBUG;
server->debug_damage_tracking = true;
break;
#endif
case 'h':
usage(stdout, argv[0]);
return false;
@ -260,6 +227,12 @@ parse_args(struct cg_server *server, int argc, char *argv[])
server->output_mode = CAGE_MULTI_OUTPUT_MODE_EXTEND;
}
break;
case 'r':
server->output_transform++;
if (server->output_transform > WL_OUTPUT_TRANSFORM_270) {
server->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
}
break;
case 's':
server->allow_vt_switch = true;
break;
@ -272,26 +245,48 @@ parse_args(struct cg_server *server, int argc, char *argv[])
}
}
if (optind >= argc) {
usage(stderr, argv[0]);
return false;
}
return true;
}
int
main(int argc, char *argv[])
{
struct cg_server server = {.log_level = WLR_INFO};
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 wl_event_source *sigchld_source = NULL;
pid_t pid = 0;
int ret = 0, app_ret = 0;
#ifdef DEBUG
server.log_level = WLR_DEBUG;
struct wlr_renderer *renderer = NULL;
struct wlr_compositor *compositor = NULL;
struct wlr_data_device_manager *data_device_manager = NULL;
struct wlr_server_decoration_manager *server_decoration_manager = NULL;
struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL;
struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager = NULL;
struct wlr_screencopy_manager_v1 *screencopy_manager = NULL;
struct wlr_xdg_output_manager_v1 *output_manager = NULL;
struct wlr_gamma_control_manager_v1 *gamma_control_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;
#endif
pid_t pid = 0;
int ret = 0;
if (!parse_args(&server, argc, argv)) {
return 1;
}
wlr_log_init(server.log_level, NULL);
#ifdef DEBUG
wlr_log_init(WLR_DEBUG, NULL);
#else
wlr_log_init(WLR_ERROR, NULL);
#endif
/* Wayland requires XDG_RUNTIME_DIR to be set. */
if (!getenv("XDG_RUNTIME_DIR")) {
@ -305,14 +300,11 @@ main(int argc, char *argv[])
return 1;
}
server.display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server.wl_display, &server.display_destroy);
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);
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);
server.backend = wlr_backend_autocreate(server.wl_display, NULL);
if (!server.backend) {
wlr_log(WLR_ERROR, "Unable to create the wlroots backend");
ret = 1;
@ -324,68 +316,33 @@ main(int argc, char *argv[])
goto end;
}
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);
renderer = wlr_backend_get_renderer(server.backend);
wlr_renderer_init_wl_display(renderer, server.wl_display);
wl_list_init(&server.views);
wl_list_init(&server.outputs);
server.output_layout = wlr_output_layout_create(server.wl_display);
server.output_layout = wlr_output_layout_create();
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);
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);
compositor = wlr_compositor_create(server.wl_display, renderer);
if (!compositor) {
wlr_log(WLR_ERROR, "Unable to create the wlroots compositor");
ret = 1;
goto end;
}
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)) {
data_device_manager = wlr_data_device_manager_create(server.wl_display);
if (!data_device_manager) {
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. */
@ -399,7 +356,7 @@ main(int argc, char *argv[])
goto end;
}
server.idle = wlr_idle_notifier_v1_create(server.wl_display);
server.idle = wlr_idle_create(server.wl_display);
if (!server.idle) {
wlr_log(WLR_ERROR, "Unable to create the idle tracker");
ret = 1;
@ -416,19 +373,16 @@ 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);
struct wlr_xdg_shell *xdg_shell = wlr_xdg_shell_create(server.wl_display, 5);
xdg_shell = wlr_xdg_shell_create(server.wl_display);
if (!xdg_shell) {
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
ret = 1;
goto end;
}
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);
server.new_xdg_shell_surface.notify = handle_xdg_shell_surface_new;
wl_signal_add(&xdg_shell->events.new_surface, &server.new_xdg_shell_surface);
struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager =
wlr_xdg_decoration_manager_v1_create(server.wl_display);
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;
@ -437,8 +391,7 @@ 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;
struct wlr_server_decoration_manager *server_decoration_manager =
wlr_server_decoration_manager_create(server.wl_display);
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;
@ -448,117 +401,65 @@ main(int argc, char *argv[])
server_decoration_manager, server.xdg_decoration ? WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
: WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
if (!wlr_viewporter_create(server.wl_display)) {
wlr_log(WLR_ERROR, "Unable to create the viewporter interface");
ret = 1;
goto end;
}
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 (!wlr_export_dmabuf_manager_v1_create(server.wl_display)) {
export_dmabuf_manager = wlr_export_dmabuf_manager_v1_create(server.wl_display);
if (!export_dmabuf_manager) {
wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager");
ret = 1;
goto end;
}
if (!wlr_screencopy_manager_v1_create(server.wl_display)) {
screencopy_manager = wlr_screencopy_manager_v1_create(server.wl_display);
if (!screencopy_manager) {
wlr_log(WLR_ERROR, "Unable to create the screencopy manager");
ret = 1;
goto end;
}
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)) {
output_manager = wlr_xdg_output_manager_v1_create(server.wl_display, server.output_layout);
if (!output_manager) {
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)) {
gamma_control_manager = wlr_gamma_control_manager_v1_create(server.wl_display);
if (!gamma_control_manager) {
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);
xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
if (!xwayland) {
wlr_log(WLR_ERROR, "Cannot create XWayland server");
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;
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 {
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
wlr_log(WLR_DEBUG, "XWayland is running on display %s", xwayland->display_name);
}
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);
}
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
@ -582,44 +483,28 @@ main(int argc, char *argv[])
}
#if CAGE_HAS_XWAYLAND
if (xwayland) {
wlr_xwayland_set_seat(xwayland, server.seat->seat);
}
wlr_xwayland_set_seat(xwayland, server.seat->seat);
#endif
if (optind < argc && !spawn_primary_client(&server, argv + optind, &pid, &sigchld_source)) {
if (!spawn_primary_client(server.wl_display, argv + optind, &pid, &sigchld_source)) {
ret = 1;
goto end;
}
seat_center_cursor(server.seat);
/* Place the cursor in the center of the output layout. */
struct wlr_box *layout_box = wlr_output_layout_get_box(server.output_layout, NULL);
wlr_cursor_warp(server.seat->cursor, NULL, layout_box->width / 2, layout_box->height / 2);
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);
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;
cleanup_primary_client(pid);
wl_event_source_remove(sigint_source);
wl_event_source_remove(sigterm_source);
@ -630,8 +515,6 @@ end:
/* 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);
wlr_output_layout_destroy(server.output_layout);
return ret;
}

View file

@ -1,25 +0,0 @@
#!/usr/bin/env bash
if [ "$#" -ne 1 ]; then
echo "usage: $0 <new-version>" >&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"

View file

@ -1,21 +0,0 @@
#!/usr/bin/env bash
if [ "$#" -ne 1 ]; then
echo "usage: $0 <new-version>" >&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

View file

@ -1,12 +0,0 @@
#!/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"

View file

@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -x
if [ "$#" -ne 1 ]; then
echo "usage: $0 <new-version>" >&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 -

View file

@ -8,8 +8,8 @@
#include <stdlib.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#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_notifier_v1_set_inhibited(server->idle, inhibited);
wlr_idle_set_enabled(server->idle, NULL, !inhibited);
}
static void

View file

@ -1,17 +1,16 @@
project('cage', 'c',
version: '0.2.1',
version: '0.1.2',
license: 'MIT',
meson_version: '>=0.58.1',
default_options: [
'c_std=c11',
'warning_level=2',
'werror=true',
'warning_level=3',
],
)
add_project_arguments(
[
'-DWLR_USE_UNSTABLE',
'-Wall',
'-Wundef',
'-Wno-unused-parameter',
],
@ -35,13 +34,14 @@ if is_freebsd
)
endif
wlroots = dependency('wlroots-0.19', fallback: ['wlroots', 'wlroots'])
wlroots = dependency('wlroots', version: '>= 0.11.0')
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_variable('pkgdatadir')
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
wayland_scanner = find_program('wayland-scanner')
wayland_scanner_server = generator(
wayland_scanner,
@ -64,13 +64,22 @@ server_protos = declare_dependency(
sources: server_protos_headers,
)
have_xwayland = wlroots.get_variable(pkgconfig: 'have_xwayland', internal: 'have_xwayland') == 'true'
if get_option('xwayland')
wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include <wlr/config.h>', 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
endif
else
have_xwayland = false
endif
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)
git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'])
git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'])
if git_commit.returncode() == 0 and git_branch.returncode() == 0
version = '@0@-@1@ (branch \'@2@\')'.format(
meson.project_version(),
@ -86,7 +95,7 @@ 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)
scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
sh = find_program('sh', native: true)
mandir = get_option('mandir')
man_files = [
@ -102,7 +111,7 @@ if scdoc.found()
input: filename,
output: output,
command: [
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output)
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
],
install: true,
install_dir: '@0@/man@1@'.format(mandir, section)
@ -114,19 +123,23 @@ cage_sources = [
'cage.c',
'idle_inhibit_v1.c',
'output.c',
'render.c',
'seat.c',
'util.c',
'view.c',
'xdg_shell.c',
]
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',
'render.h',
'seat.h',
'server.h',
'util.h',
'view.h',
'xdg_shell.h',
]
@ -144,16 +157,17 @@ executable(
wayland_server,
wlroots,
xkbcommon,
pixman,
math,
],
install: true,
)
summary = [
'',
'Cage @0@'.format(version),
'',
' xwayland: @0@'.format(have_xwayland),
''
'',
'Cage @0@'.format(version),
'',
' xwayland: @0@'.format(have_xwayland),
''
]
message('\n'.join(summary))

View file

@ -1 +1,2 @@
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications')

605
output.c
View file

@ -1,7 +1,7 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2018-2021 Jente Hidskes
* Copyright (C) 2018-2020 Jente Hidskes
* Copyright (C) 2019 The Sway authors
*
* See the LICENSE file accompanying this file.
@ -11,7 +11,6 @@
#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <wayland-server-core.h>
@ -21,79 +20,220 @@
#if WLR_HAS_X11_BACKEND
#include <wlr/backend/x11.h>
#endif
#include <wlr/render/swapchain.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_output_swapchain_manager.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include "output.h"
#include "render.h"
#include "seat.h"
#include "server.h"
#include "util.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 output_for_each_surface(struct cg_output *output, cg_surface_iterator_func_t iterator, void *user_data);
static void
update_output_manager_config(struct cg_server *server)
{
struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create();
struct surface_iterator_data {
cg_surface_iterator_func_t user_iterator;
void *user_data;
struct cg_output *output;
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;
/* Output-local coordinates. */
double ox, oy;
};
static bool
intersects_with_output(struct cg_output *output, struct wlr_output_layout *output_layout, struct wlr_box *surface_box)
{
/* Since the surface_box's x- and y-coordinates are already output local,
* the x- and y-coordinates of this box need to be 0 for this function to
* work correctly. */
struct wlr_box output_box = {0};
wlr_output_effective_resolution(output->wlr_output, &output_box.width, &output_box.height);
struct wlr_box intersection;
return wlr_box_intersection(&intersection, &output_box, surface_box);
}
static void
output_for_each_surface_iterator(struct wlr_surface *surface, int sx, int sy, void *user_data)
{
struct surface_iterator_data *data = user_data;
struct cg_output *output = data->output;
if (!wlr_surface_has_buffer(surface)) {
return;
}
struct wlr_box surface_box = {
.x = data->ox + sx + surface->sx,
.y = data->oy + sy + surface->sy,
.width = surface->current.width,
.height = surface->current.height,
};
if (!intersects_with_output(output, output->server->output_layout, &surface_box)) {
return;
}
data->user_iterator(data->output, surface, &surface_box, data->user_data);
}
void
output_surface_for_each_surface(struct cg_output *output, struct wlr_surface *surface, double ox, double oy,
cg_surface_iterator_func_t iterator, void *user_data)
{
struct surface_iterator_data data = {
.user_iterator = iterator,
.user_data = user_data,
.output = output,
.ox = ox,
.oy = oy,
};
wlr_surface_for_each_surface(surface, output_for_each_surface_iterator, &data);
}
static void
output_view_for_each_surface(struct cg_output *output, struct cg_view *view, cg_surface_iterator_func_t iterator,
void *user_data)
{
struct surface_iterator_data data = {
.user_iterator = iterator,
.user_data = user_data,
.output = output,
.ox = view->lx,
.oy = view->ly,
};
wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &data.ox, &data.oy);
view_for_each_surface(view, output_for_each_surface_iterator, &data);
}
void
output_view_for_each_popup(struct cg_output *output, struct cg_view *view, cg_surface_iterator_func_t iterator,
void *user_data)
{
struct surface_iterator_data data = {
.user_iterator = iterator,
.user_data = user_data,
.output = output,
.ox = view->lx,
.oy = view->ly,
};
wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &data.ox, &data.oy);
view_for_each_popup(view, output_for_each_surface_iterator, &data);
}
void
output_drag_icons_for_each_surface(struct cg_output *output, struct wl_list *drag_icons,
cg_surface_iterator_func_t iterator, void *user_data)
{
struct cg_drag_icon *drag_icon;
wl_list_for_each (drag_icon, drag_icons, link) {
if (drag_icon->wlr_drag_icon->mapped) {
double ox = drag_icon->lx;
double oy = drag_icon->ly;
wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &ox, &oy);
output_surface_for_each_surface(output, drag_icon->wlr_drag_icon->surface, ox, oy, iterator,
user_data);
}
}
}
static void
output_for_each_surface(struct cg_output *output, cg_surface_iterator_func_t iterator, void *user_data)
{
struct cg_view *view;
wl_list_for_each_reverse (view, &output->server->views, link) {
output_view_for_each_surface(output, view, iterator, user_data);
}
output_drag_icons_for_each_surface(output, &output->server->seat->drag_icons, iterator, user_data);
}
struct send_frame_done_data {
struct timespec when;
};
static void
send_frame_done_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box, void *user_data)
{
struct send_frame_done_data *data = user_data;
wlr_surface_send_frame_done(surface, &data->when);
}
static void
send_frame_done(struct cg_output *output, struct send_frame_done_data *data)
{
output_for_each_surface(output, send_frame_done_iterator, data);
}
static void
count_surface_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *_box, void *data)
{
size_t *n = data;
n++;
}
static bool
scan_out_primary_view(struct cg_output *output)
{
struct cg_server *server = output->server;
struct wlr_output *wlr_output = output->wlr_output;
struct cg_drag_icon *drag_icon;
wl_list_for_each (drag_icon, &server->seat->drag_icons, link) {
if (drag_icon->wlr_drag_icon->mapped) {
return false;
}
}
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;
struct cg_view *view = seat_get_focus(server->seat);
if (!view || !view->wlr_surface) {
return false;
}
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);
size_t n_surfaces = 0;
output_view_for_each_surface(output, view, count_surface_iterator, &n_surfaces);
if (n_surfaces > 1) {
return false;
}
#if CAGE_HAS_XWAYLAND
if (view->type == CAGE_XWAYLAND_VIEW) {
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
if (!wl_list_empty(&xwayland_view->xwayland_surface->children)) {
return false;
}
}
#endif
struct wlr_surface *surface = view->wlr_surface;
if (!surface->buffer) {
return false;
}
if ((float) surface->current.scale != wlr_output->scale ||
surface->current.transform != wlr_output->transform) {
return false;
}
wlr_output_attach_buffer(wlr_output, &surface->buffer->base);
return wlr_output_commit(wlr_output);
}
static void
@ -106,128 +246,190 @@ output_enable(struct cg_output *output)
* 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);
}
update_output_manager_config(output->server);
wlr_output_layout_add_auto(output->server->output_layout, wlr_output);
wlr_output_enable(wlr_output, true);
wlr_output_commit(wlr_output);
}
static void
output_disable(struct cg_output *output)
{
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);
wlr_output_enable(wlr_output, false);
wlr_output_layout_remove(output->server->output_layout, wlr_output);
wlr_output_commit(wlr_output);
}
static void
handle_output_frame(struct wl_listener *listener, void *data)
damage_surface_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box, void *user_data)
{
struct cg_output *output = wl_container_of(listener, output, frame);
struct wlr_output *wlr_output = output->wlr_output;
bool whole = *(bool *) user_data;
if (!output->wlr_output->enabled || !output->scene_output) {
return;
}
scale_box(box, output->wlr_output->scale);
wlr_scene_output_commit(output->scene_output, NULL);
if (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);
struct timespec now = {0};
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_scene_output_send_frame_done(output->scene_output, &now);
}
static void
handle_output_commit(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, commit);
struct wlr_output_event_commit *event = data;
/* 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_request_state(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, request_state);
struct wlr_output_event_request_state *event = data;
if (wlr_output_commit_state(output->wlr_output, event->state)) {
update_output_manager_config(output->server);
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);
}
}
void
handle_output_layout_change(struct wl_listener *listener, void *data)
output_damage_surface(struct cg_output *output, struct wlr_surface *surface, double lx, double ly, bool whole)
{
struct cg_server *server = wl_container_of(listener, server, output_layout_change);
if (!output->wlr_output->enabled) {
wlr_log(WLR_DEBUG, "Not adding damage for disabled output %s", output->wlr_output->name);
return;
}
view_position_all(server);
update_output_manager_config(server);
double ox = lx, oy = ly;
wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &ox, &oy);
output_surface_for_each_surface(output, surface, ox, oy, damage_surface_iterator, &whole);
}
static bool
is_nested_output(struct cg_output *output)
static void
handle_output_damage_frame(struct wl_listener *listener, void *data)
{
if (wlr_output_is_wl(output->wlr_output)) {
return true;
struct cg_output *output = wl_container_of(listener, output, damage_frame);
struct send_frame_done_data frame_data = {0};
if (!output->wlr_output->enabled) {
return;
}
#if WLR_HAS_X11_BACKEND
if (wlr_output_is_x11(output->wlr_output)) {
return true;
/* Check if we can scan-out the primary view. */
static bool last_scanned_out = false;
bool scanned_out = scan_out_primary_view(output);
if (scanned_out && !last_scanned_out) {
wlr_log(WLR_DEBUG, "Scanning out primary view");
}
if (last_scanned_out && !scanned_out) {
wlr_log(WLR_DEBUG, "Stopping primary view scan out");
}
last_scanned_out = scanned_out;
if (scanned_out) {
goto frame_done;
}
bool needs_frame;
pixman_region32_t damage;
pixman_region32_init(&damage);
if (!wlr_output_damage_attach_render(output->damage, &needs_frame, &damage)) {
wlr_log(WLR_ERROR, "Cannot make damage output current");
goto damage_finish;
}
if (!needs_frame) {
wlr_output_rollback(output->wlr_output);
goto damage_finish;
}
output_render(output, &damage);
damage_finish:
pixman_region32_fini(&damage);
frame_done:
clock_gettime(CLOCK_MONOTONIC, &frame_data.when);
send_frame_done(output, &frame_data);
}
static void
handle_output_transform(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, transform);
if (!output->wlr_output->enabled) {
return;
}
struct cg_view *view;
wl_list_for_each (view, &output->server->views, link) {
view_position(view);
}
}
static void
handle_output_mode(struct wl_listener *listener, void *data)
{
struct cg_output *output = wl_container_of(listener, output, mode);
if (!output->wlr_output->enabled) {
return;
}
struct cg_view *view;
wl_list_for_each (view, &output->server->views, link) {
view_position(view);
}
#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->commit.link);
wl_list_remove(&output->request_state.link);
wl_list_remove(&output->frame.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->link);
output_layout_remove(output);
wlr_output_layout_remove(server->output_layout, output->wlr_output);
free(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)) {
if (wl_list_empty(&server->outputs)) {
wl_display_terminate(server->wl_display);
} else if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST) {
struct cg_output *prev = wl_container_of(server->outputs.next, prev, link);
output_enable(prev);
view_position_all(server);
if (prev) {
output_enable(prev);
struct cg_view *view;
wl_list_for_each (view, &server->views, link) {
view_position(view);
}
}
}
}
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);
}
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);
}
@ -237,11 +439,6 @@ 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;
if (!wlr_output_init_render(wlr_output, server->allocator, server->renderer)) {
wlr_log(WLR_ERROR, "Failed to initialize output rendering");
return;
}
struct cg_output *output = calloc(1, sizeof(struct cg_output));
if (!output) {
wlr_log(WLR_ERROR, "Failed to allocate output");
@ -249,51 +446,32 @@ handle_new_output(struct wl_listener *listener, void *data)
}
output->wlr_output = wlr_output;
wlr_output->data = output;
output->server = server;
output->damage = wlr_output_damage_create(wlr_output);
wl_list_insert(&server->outputs, &output->link);
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->mode.notify = handle_output_mode;
wl_signal_add(&wlr_output->events.mode, &output->mode);
output->transform.notify = handle_output_transform;
wl_signal_add(&wlr_output->events.transform, &output->transform);
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);
output->damage_frame.notify = handle_output_damage_frame;
wl_signal_add(&output->damage->events.frame, &output->damage_frame);
output->damage_destroy.notify = handle_output_damage_destroy;
wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
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;
struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
if (preferred_mode) {
wlr_output_set_mode(wlr_output, preferred_mode);
}
wlr_output_set_transform(wlr_output, output->server->output_transform);
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) {
if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST) {
struct cg_output *next = wl_container_of(output->link.next, next, link);
output_disable(next);
if (next) {
output_disable(next);
}
}
if (!wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
@ -301,13 +479,12 @@ handle_new_output(struct wl_listener *listener, void *data)
wlr_output->scale);
}
wlr_log(WLR_DEBUG, "Enabling new output %s", wlr_output->name);
if (wlr_output_commit_state(wlr_output, &state)) {
output_layout_add_auto(output);
}
output_enable(output);
view_position_all(output->server);
update_output_manager_config(output->server);
struct cg_view *view;
wl_list_for_each (view, &output->server->views, link) {
view_position(view);
}
}
void
@ -328,95 +505,3 @@ 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);
}

View file

@ -3,6 +3,7 @@
#include <wayland-server-core.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_damage.h>
#include "server.h"
#include "view.h"
@ -10,20 +11,28 @@
struct cg_output {
struct cg_server *server;
struct wlr_output *wlr_output;
struct wlr_scene_output *scene_output;
struct wlr_output_damage *damage;
struct wl_listener commit;
struct wl_listener request_state;
struct wl_listener mode;
struct wl_listener transform;
struct wl_listener destroy;
struct wl_listener frame;
struct wl_listener damage_frame;
struct wl_listener damage_destroy;
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);
typedef void (*cg_surface_iterator_func_t)(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box,
void *user_data);
void handle_new_output(struct wl_listener *listener, void *data);
void output_surface_for_each_surface(struct cg_output *output, struct wlr_surface *surface, double ox, double oy,
cg_surface_iterator_func_t iterator, void *user_data);
void output_view_for_each_popup(struct cg_output *output, struct cg_view *view, cg_surface_iterator_func_t iterator,
void *user_data);
void output_drag_icons_for_each_surface(struct cg_output *output, struct wl_list *drag_icons,
cg_surface_iterator_func_t iterator, void *user_data);
void output_damage_surface(struct cg_output *output, struct wlr_surface *surface, double lx, double ly, bool whole);
void output_set_window_title(struct cg_output *output, const char *title);
#endif

215
render.c Normal file
View file

@ -0,0 +1,215 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2018-2020 Jente Hidskes
* Copyright (C) 2019 The Sway authors
*
* See the LICENSE file accompanying this file.
*/
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include "output.h"
#include "seat.h"
#include "server.h"
#include "util.h"
#include "view.h"
static void
scissor_output(struct wlr_output *output, pixman_box32_t *rect)
{
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
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);
}
struct render_data {
pixman_region32_t *damage;
};
static void
render_texture(struct wlr_output *wlr_output, pixman_region32_t *output_damage, struct wlr_texture *texture,
const struct wlr_box *box, const float matrix[static 9])
{
struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
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, output_damage);
if (!pixman_region32_not_empty(&damage)) {
goto damage_finish;
}
int nrects;
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
for (int i = 0; i < nrects; i++) {
scissor_output(wlr_output, &rects[i]);
wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0f);
}
damage_finish:
pixman_region32_fini(&damage);
}
static void
render_surface_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box, void *user_data)
{
struct render_data *data = user_data;
struct wlr_output *wlr_output = output->wlr_output;
pixman_region32_t *output_damage = data->damage;
struct wlr_texture *texture = wlr_surface_get_texture(surface);
if (!texture) {
wlr_log(WLR_DEBUG, "Cannot obtain surface texture");
return;
}
scale_box(box, wlr_output->scale);
float matrix[9];
enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
wlr_matrix_project_box(matrix, box, transform, 0.0f, wlr_output->transform_matrix);
render_texture(wlr_output, output_damage, texture, box, matrix);
}
static void
render_drag_icons(struct cg_output *output, pixman_region32_t *damage, struct wl_list *drag_icons)
{
struct render_data data = {
.damage = damage,
};
output_drag_icons_for_each_surface(output, drag_icons, render_surface_iterator, &data);
}
/**
* Render all toplevels without descending into popups.
*/
static void
render_view_toplevels(struct cg_view *view, struct cg_output *output, pixman_region32_t *damage)
{
struct render_data data = {
.damage = damage,
};
double ox = view->lx;
double oy = view->ly;
wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &ox, &oy);
output_surface_for_each_surface(output, view->wlr_surface, ox, oy, render_surface_iterator, &data);
}
static void
render_popup_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box, void *data)
{
/* Render this popup's surface. */
render_surface_iterator(output, surface, box, data);
/* Render this popup's child toplevels. */
output_surface_for_each_surface(output, surface, box->x, box->y, render_surface_iterator, data);
}
static void
render_view_popups(struct cg_view *view, struct cg_output *output, pixman_region32_t *damage)
{
struct render_data data = {
.damage = damage,
};
output_view_for_each_popup(output, view, render_popup_iterator, &data);
}
void
output_render(struct cg_output *output, pixman_region32_t *damage)
{
struct cg_server *server = output->server;
struct wlr_output *wlr_output = output->wlr_output;
struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
if (!renderer) {
wlr_log(WLR_DEBUG, "Expected the output backend to have a renderer");
return;
}
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
if (!pixman_region32_not_empty(damage)) {
wlr_log(WLR_DEBUG, "Output isn't damaged but needs a buffer swap");
goto renderer_end;
}
#ifdef DEBUG
if (server->debug_damage_tracking) {
wlr_renderer_clear(renderer, (float[]){1.0f, 0.0f, 0.0f, 1.0f});
}
#endif
float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
int nrects;
pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
for (int i = 0; i < nrects; i++) {
scissor_output(wlr_output, &rects[i]);
wlr_renderer_clear(renderer, color);
}
// TODO: render only top view, possibly use focused view for this, see #35.
struct cg_view *view;
wl_list_for_each_reverse (view, &server->views, link) {
render_view_toplevels(view, output, damage);
}
struct cg_view *focused_view = seat_get_focus(server->seat);
if (focused_view) {
render_view_popups(focused_view, output, damage);
}
render_drag_icons(output, damage, &server->seat->drag_icons);
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(wlr_output, damage);
wlr_renderer_scissor(renderer, NULL);
wlr_renderer_end(renderer);
int output_width, output_height;
wlr_output_transformed_resolution(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(wlr_output->transform);
wlr_region_transform(&frame_damage, &output->damage->current, transform, output_width, output_height);
#ifdef DEBUG
if (server->debug_damage_tracking) {
pixman_region32_union_rect(&frame_damage, &frame_damage, 0, 0, output_width, output_height);
}
#endif
wlr_output_set_damage(wlr_output, &frame_damage);
pixman_region32_fini(&frame_damage);
if (!wlr_output_commit(wlr_output)) {
wlr_log(WLR_ERROR, "Could not commit output");
}
}

8
render.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef CG_RENDER_H
#define CG_RENDER_H
#include "output.h"
void output_render(struct cg_output *output, pixman_region32_t *damage);
#endif

393
seat.c
View file

@ -6,29 +6,20 @@
* See the LICENSE file accompanying this file.
*/
#define _POSIX_C_SOURCE 200809L
#include "config.h"
#include <assert.h>
#include <linux/input-event-codes.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_keyboard_group.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h>
#if CAGE_HAS_XWAYLAND
@ -49,42 +40,42 @@ 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.
*
* 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.
*/
* 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->lx;
double view_sy = ly - view->ly;
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. */
static struct cg_view *
desktop_view_at(struct cg_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy)
{
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;
}
struct cg_view *view;
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;
wl_list_for_each (view, &server->views, link) {
if (view_at(view, lx, ly, surface, sx, sy)) {
return view;
}
node = &node->parent->node;
}
assert(node != NULL);
return node->data;
return NULL;
}
static void
@ -128,23 +119,23 @@ 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_unset_image(seat->cursor);
wlr_cursor_set_image(seat->cursor, NULL, 0, 0, 0, 0, 0, 0);
} else {
wlr_cursor_set_xcursor(seat->cursor, seat->xcursor_manager, DEFAULT_XCURSOR);
wlr_xcursor_manager_set_cursor_image(seat->xcursor_manager, DEFAULT_XCURSOR, seat->cursor);
}
}
static void
map_input_device_to_output(struct cg_seat *seat, struct wlr_input_device *device, const char *output_name)
map_input_device_to_output(struct cg_seat *seat, struct wlr_input_device *device)
{
if (!output_name) {
if (!device->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) {
if (strcmp(device->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);
@ -162,7 +153,7 @@ handle_touch_destroy(struct wl_listener *listener, void *data)
struct cg_seat *seat = touch->seat;
wl_list_remove(&touch->link);
wlr_cursor_detach_input_device(seat->cursor, &touch->touch->base);
wlr_cursor_detach_input_device(seat->cursor, touch->device);
wl_list_remove(&touch->destroy.link);
free(touch);
@ -170,7 +161,7 @@ handle_touch_destroy(struct wl_listener *listener, void *data)
}
static void
handle_new_touch(struct cg_seat *seat, struct wlr_touch *wlr_touch)
handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device)
{
struct cg_touch *touch = calloc(1, sizeof(struct cg_touch));
if (!touch) {
@ -179,14 +170,14 @@ handle_new_touch(struct cg_seat *seat, struct wlr_touch *wlr_touch)
}
touch->seat = seat;
touch->touch = wlr_touch;
wlr_cursor_attach_input_device(seat->cursor, &wlr_touch->base);
touch->device = device;
wlr_cursor_attach_input_device(seat->cursor, device);
wl_list_insert(&seat->touch, &touch->link);
touch->destroy.notify = handle_touch_destroy;
wl_signal_add(&wlr_touch->base.events.destroy, &touch->destroy);
wl_signal_add(&touch->device->events.destroy, &touch->destroy);
map_input_device_to_output(seat, &wlr_touch->base, wlr_touch->output_name);
map_input_device_to_output(seat, device);
}
static void
@ -196,7 +187,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->pointer->base);
wlr_cursor_detach_input_device(seat->cursor, pointer->device);
wl_list_remove(&pointer->destroy.link);
free(pointer);
@ -204,7 +195,7 @@ handle_pointer_destroy(struct wl_listener *listener, void *data)
}
static void
handle_new_pointer(struct cg_seat *seat, struct wlr_pointer *wlr_pointer)
handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device)
{
struct cg_pointer *pointer = calloc(1, sizeof(struct cg_pointer));
if (!pointer) {
@ -213,44 +204,23 @@ handle_new_pointer(struct cg_seat *seat, struct wlr_pointer *wlr_pointer)
}
pointer->seat = seat;
pointer->pointer = wlr_pointer;
wlr_cursor_attach_input_device(seat->cursor, &wlr_pointer->base);
pointer->device = device;
wlr_cursor_attach_input_device(seat->cursor, device);
wl_list_insert(&seat->pointers, &pointer->link);
pointer->destroy.notify = handle_pointer_destroy;
wl_signal_add(&wlr_pointer->base.events.destroy, &pointer->destroy);
wl_signal_add(&device->events.destroy, &pointer->destroy);
map_input_device_to_output(seat, &wlr_pointer->base, wlr_pointer->output_name);
map_input_device_to_output(seat, device);
}
static void
handle_virtual_pointer(struct wl_listener *listener, void *data)
handle_modifier_event(struct wlr_input_device *device, struct cg_seat *seat)
{
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(seat->seat, device);
wlr_seat_keyboard_notify_modifiers(seat->seat, &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);
}
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);
wlr_idle_notify_activity(seat->server->idle, seat->seat);
}
static bool
@ -258,38 +228,40 @@ handle_keybinding(struct cg_server *server, xkb_keysym_t sym)
{
#ifdef DEBUG
if (sym == XKB_KEY_Escape) {
server_terminate(server);
return true;
}
wl_display_terminate(server->wl_display);
} else
#endif
if (server->allow_vt_switch && sym >= XKB_KEY_XF86Switch_VT_1 && sym <= XKB_KEY_XF86Switch_VT_12) {
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) {
struct wlr_session *session =
wlr_backend_get_session(server->backend);
if (session) {
unsigned vt = sym - XKB_KEY_XF86Switch_VT_1 + 1;
wlr_session_change_vt(server->session, vt);
wlr_session_change_vt(session, vt);
}
}
} else {
return false;
}
wlr_idle_notifier_v1_notify_activity(server->idle, server->seat->seat);
wlr_idle_notify_activity(server->idle, server->seat->seat);
return true;
}
static void
handle_key_event(struct wlr_keyboard *keyboard, struct cg_seat *seat, void *data)
handle_key_event(struct wlr_input_device *device, struct cg_seat *seat, void *data)
{
struct wlr_keyboard_key_event *event = data;
struct wlr_event_keyboard_key *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->xkb_state, keycode, &syms);
int nsyms = xkb_state_key_get_syms(device->keyboard->xkb_state, keycode, &syms);
bool handled = false;
uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
uint32_t modifiers = wlr_keyboard_get_modifiers(device->keyboard);
if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_KEY_PRESSED) {
/* If Alt is held down and this button was pressed, we
* attempt to process it as a compositor
* keybinding. */
@ -300,43 +272,38 @@ handle_key_event(struct wlr_keyboard *keyboard, struct cg_seat *seat, void *data
if (!handled) {
/* Otherwise, we pass it along to the client. */
wlr_seat_set_keyboard(seat->seat, keyboard);
wlr_seat_set_keyboard(seat->seat, device);
wlr_seat_keyboard_notify_key(seat->seat, event->time_msec, event->keycode, event->state);
}
wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
wlr_idle_notify_activity(seat->server->idle, seat->seat);
}
static void
handle_keyboard_group_key(struct wl_listener *listener, void *data)
{
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);
handle_key_event(cg_group->wlr_group->input_device, cg_group->seat, data);
}
static void
handle_keyboard_group_modifiers(struct wl_listener *listener, void *data)
{
struct cg_keyboard_group *group = wl_container_of(listener, group, modifiers);
handle_modifier_event(&group->wlr_group->keyboard, group->seat);
handle_modifier_event(group->wlr_group->input_device, group->seat);
}
static void
cg_keyboard_group_add(struct wlr_keyboard *keyboard, struct cg_seat *seat, bool virtual)
cg_keyboard_group_add(struct wlr_input_device *device, struct cg_seat *seat)
{
/* 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;
}
struct wlr_keyboard *wlr_keyboard = device->keyboard;
struct cg_keyboard_group *group;
wl_list_for_each (group, &seat->keyboard_groups, link) {
struct wlr_keyboard_group *wlr_group = group->wlr_group;
if (wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard)) {
wlr_log(WLR_DEBUG, "Added new keyboard to existing group");
return;
}
}
@ -348,7 +315,6 @@ cg_keyboard_group_add(struct wlr_keyboard *keyboard, struct cg_seat *seat, bool
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.");
@ -356,14 +322,14 @@ cg_keyboard_group_add(struct wlr_keyboard *keyboard, struct cg_seat *seat, bool
}
cg_group->wlr_group->data = cg_group;
wlr_keyboard_set_keymap(&cg_group->wlr_group->keyboard, keyboard->keymap);
wlr_keyboard_set_keymap(&cg_group->wlr_group->keyboard, device->keyboard->keymap);
wlr_keyboard_set_repeat_info(&cg_group->wlr_group->keyboard, keyboard->repeat_info.rate,
keyboard->repeat_info.delay);
wlr_keyboard_set_repeat_info(&cg_group->wlr_group->keyboard, wlr_keyboard->repeat_info.rate,
wlr_keyboard->repeat_info.delay);
wlr_log(WLR_DEBUG, "Created keyboard group");
wlr_keyboard_group_add_keyboard(cg_group->wlr_group, keyboard);
wlr_keyboard_group_add_keyboard(cg_group->wlr_group, wlr_keyboard);
wl_list_insert(&seat->keyboard_groups, &cg_group->link);
wl_signal_add(&cg_group->wlr_group->keyboard.events.key, &cg_group->key);
@ -381,55 +347,36 @@ cleanup:
}
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)
handle_new_keyboard(struct cg_seat *seat, struct wlr_input_device *device)
{
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
wlr_log(WLR_ERROR, "Unable to create XKB context");
wlr_log(WLR_ERROR, "Unable to create XBK context");
return;
}
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
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);
if (!keymap) {
wlr_log(WLR_ERROR, "Unable to configure keyboard: keymap does not exist");
xkb_context_unref(context);
return;
}
wlr_keyboard_set_keymap(keyboard, keymap);
wlr_keyboard_set_keymap(device->keyboard, keymap);
xkb_keymap_unref(keymap);
xkb_context_unref(context);
wlr_keyboard_set_repeat_info(keyboard, 25, 600);
wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
cg_keyboard_group_add(keyboard, seat, virtual);
cg_keyboard_group_add(device, seat);
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);
wlr_seat_set_keyboard(seat->seat, device);
}
static void
@ -440,18 +387,18 @@ handle_new_input(struct wl_listener *listener, void *data)
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
handle_new_keyboard(seat, wlr_keyboard_from_input_device(device), false);
handle_new_keyboard(seat, device);
break;
case WLR_INPUT_DEVICE_POINTER:
handle_new_pointer(seat, wlr_pointer_from_input_device(device));
handle_new_pointer(seat, device);
break;
case WLR_INPUT_DEVICE_TOUCH:
handle_new_touch(seat, wlr_touch_from_input_device(device));
handle_new_touch(seat, device);
break;
case WLR_INPUT_DEVICE_SWITCH:
wlr_log(WLR_DEBUG, "Switch input is not implemented");
return;
case WLR_INPUT_DEVICE_TABLET:
case WLR_INPUT_DEVICE_TABLET_TOOL:
case WLR_INPUT_DEVICE_TABLET_PAD:
wlr_log(WLR_DEBUG, "Tablet input is not implemented");
return;
@ -501,10 +448,10 @@ static void
handle_touch_down(struct wl_listener *listener, void *data)
{
struct cg_seat *seat = wl_container_of(listener, seat, touch_down);
struct wlr_touch_down_event *event = data;
struct wlr_event_touch_down *event = data;
double lx, ly;
wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->touch->base, event->x, event->y, &lx, &ly);
wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device, event->x, event->y, &lx, &ly);
double sx, sy;
struct wlr_surface *surface;
@ -519,43 +466,43 @@ handle_touch_down(struct wl_listener *listener, void *data)
seat->touch_id = event->touch_id;
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);
press_cursor_button(seat, event->device, event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED, lx, ly);
}
wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
wlr_idle_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_touch_up_event *event = data;
struct wlr_event_touch_up *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->touch->base, event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED,
press_cursor_button(seat, event->device, 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_notifier_v1_notify_activity(seat->server->idle, seat->seat);
wlr_idle_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_touch_motion_event *event = data;
struct wlr_event_touch_motion *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->touch->base, event->x, event->y, &lx, &ly);
wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device, event->x, event->y, &lx, &ly);
double sx, sy;
struct wlr_surface *surface;
@ -573,16 +520,7 @@ handle_touch_motion(struct wl_listener *listener, void *data)
seat->touch_ly = ly;
}
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);
wlr_idle_notify_activity(seat->server->idle, seat->seat);
}
static void
@ -591,52 +529,50 @@ 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_notifier_v1_notify_activity(seat->server->idle, seat->seat);
wlr_idle_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_pointer_axis_event *event = data;
struct wlr_event_pointer_axis *event = data;
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);
event->delta_discrete, event->source);
wlr_idle_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_pointer_button_event *event = data;
struct wlr_event_pointer_button *event = data;
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,
press_cursor_button(seat, event->device, event->time_msec, event->button, event->state, seat->cursor->x,
seat->cursor->y);
wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
wlr_idle_notify_activity(seat->server->idle, seat->seat);
}
static void
process_cursor_motion(struct cg_seat *seat, uint32_t time_msec, double dx, double dy, double dx_unaccel,
double dy_unaccel)
process_cursor_motion(struct cg_seat *seat, uint32_t time)
{
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);
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);
}
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);
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);
}
}
struct cg_drag_icon *drag_icon;
@ -644,36 +580,38 @@ process_cursor_motion(struct cg_seat *seat, uint32_t time_msec, double dx, doubl
drag_icon_update_position(drag_icon);
}
wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
wlr_idle_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_pointer_motion_absolute_event *event = data;
struct wlr_event_pointer_motion_absolute *event = data;
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);
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);
}
static void
handle_cursor_motion_relative(struct wl_listener *listener, void *data)
handle_cursor_motion(struct wl_listener *listener, void *data)
{
struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion_relative);
struct wlr_pointer_motion_event *event = data;
struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion);
struct wlr_event_pointer_motion *event = data;
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);
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)
{
struct cg_output *output;
wl_list_for_each (output, &drag_icon->seat->server->outputs, link) {
output_damage_surface(output, drag_icon->wlr_drag_icon->surface, drag_icon->lx, drag_icon->ly, true);
}
}
static void
@ -683,6 +621,8 @@ 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;
@ -700,7 +640,7 @@ drag_icon_update_position(struct cg_drag_icon *drag_icon)
break;
}
wlr_scene_node_set_position(&drag_icon->scene_tree->node, drag_icon->lx, drag_icon->ly);
drag_icon_damage(drag_icon);
}
static void
@ -710,7 +650,6 @@ 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);
}
@ -753,11 +692,6 @@ 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);
@ -772,7 +706,7 @@ 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.link);
wl_list_remove(&seat->cursor_motion_absolute.link);
wl_list_remove(&seat->cursor_button.link);
wl_list_remove(&seat->cursor_axis.link);
@ -780,7 +714,6 @@ handle_destroy(struct wl_listener *listener, void *data)
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);
@ -846,8 +779,8 @@ seat_create(struct cg_server *server, struct wlr_backend *backend)
}
}
seat->cursor_motion_relative.notify = handle_cursor_motion_relative;
wl_signal_add(&seat->cursor->events.motion, &seat->cursor_motion_relative);
seat->cursor_motion.notify = handle_cursor_motion;
wl_signal_add(&seat->cursor->events.motion, &seat->cursor_motion);
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;
@ -863,8 +796,6 @@ seat_create(struct cg_server *server, struct wlr_backend *backend)
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);
@ -881,9 +812,6 @@ seat_create(struct cg_server *server, struct wlr_backend *backend)
seat->new_input.notify = handle_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);
@ -903,11 +831,6 @@ 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);
@ -917,10 +840,7 @@ struct cg_view *
seat_get_focus(struct cg_seat *seat)
{
struct wlr_surface *prev_surface = seat->seat->keyboard_state.focused_surface;
if (!prev_surface) {
return NULL;
}
return view_from_wlr_surface(prev_surface);
return view_from_wlr_surface(seat->server, prev_surface);
}
void
@ -937,7 +857,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_surface_override_redirect_wants_focus(xwayland_view->xwayland_surface)) {
if (!wlr_xwayland_or_surface_wants_focus(xwayland_view->xwayland_surface)) {
return;
}
}
@ -970,14 +890,5 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view)
wlr_seat_keyboard_notify_enter(wlr_seat, view->wlr_surface, NULL, 0, NULL);
}
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);
process_cursor_motion(seat, -1);
}

10
seat.h
View file

@ -27,7 +27,7 @@ struct cg_seat {
struct wlr_cursor *cursor;
struct wlr_xcursor_manager *xcursor_manager;
struct wl_listener cursor_motion_relative;
struct wl_listener cursor_motion;
struct wl_listener cursor_motion_absolute;
struct wl_listener cursor_button;
struct wl_listener cursor_axis;
@ -39,7 +39,6 @@ struct cg_seat {
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;
@ -56,13 +55,12 @@ struct cg_keyboard_group {
struct wl_listener key;
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_pointer *pointer;
struct wlr_input_device *device;
struct wl_listener destroy;
};
@ -70,7 +68,7 @@ struct cg_pointer {
struct cg_touch {
struct wl_list link; // seat::touch
struct cg_seat *seat;
struct wlr_touch *touch;
struct wlr_input_device *device;
struct wl_listener destroy;
};
@ -79,7 +77,6 @@ struct cg_drag_icon {
struct wl_list link; // seat::drag_icons
struct cg_seat *seat;
struct wlr_drag_icon *wlr_drag_icon;
struct wlr_scene_tree *scene_tree;
/* The drag icon has a position in layout coordinates. */
double lx, ly;
@ -91,6 +88,5 @@ struct cg_seat *seat_create(struct cg_server *server, struct wlr_backend *backen
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

View file

@ -4,17 +4,18 @@
#include "config.h"
#include <wayland-server-core.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/util/log.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/xwayland.h>
#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,
@ -24,50 +25,32 @@ struct cg_server {
struct wl_display *wl_display;
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_notifier_v1 *idle;
struct wlr_idle *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 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_toplevel;
struct wl_listener new_xdg_popup;
struct wl_listener new_virtual_keyboard;
struct wl_listener new_virtual_pointer;
struct wl_listener new_xdg_shell_surface;
#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;
bool allow_vt_switch;
bool return_app_code;
bool terminated;
enum wlr_log_importance log_level;
enum wl_output_transform output_transform;
#ifdef DEBUG
bool debug_damage_tracking;
#endif
};
void server_terminate(struct cg_server *server);
#endif

36
util.c Normal file
View file

@ -0,0 +1,36 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2019 The Sway authors
*
* See the LICENSE file accompanying this file.
*/
#include <wlr/types/wlr_box.h>
#include "util.h"
int
scale_length(int length, int offset, float scale)
{
/**
* One does not simply multiply the width by the scale. We allow fractional
* scaling, which means the resulting scaled width might be a decimal.
* So we round it.
*
* But even this can produce undesirable results depending on the X or Y
* offset of the box. For example, with a scale of 1.5, a box with
* width=1 should not scale to 2px if its X coordinate is 1, because the
* X coordinate would have scaled to 2px.
*/
return round((offset + length) * scale) - round(offset * scale);
}
void
scale_box(struct wlr_box *box, float scale)
{
box->width = scale_length(box->width, box->x, scale);
box->height = scale_length(box->height, box->y, scale);
box->x = round(box->x * scale);
box->y = round(box->y * scale);
}

11
util.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef CG_UTIL_H
#define CG_UTIL_H
#include <wlr/types/wlr_box.h>
/** Apply scale to a width or height. */
int scale_length(int length, int offset, float scale);
void scale_box(struct wlr_box *box, float scale);
#endif

189
view.c
View file

@ -1,20 +1,20 @@
/*
* Cage: A Wayland kiosk.
*
* Copyright (C) 2018-2021 Jente Hidskes
* Copyright (C) 2018-2020 Jente Hidskes
*
* See the LICENSE file accompanying this file.
*/
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_surface.h>
#include "output.h"
#include "seat.h"
@ -24,6 +24,96 @@
#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_part(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)
{
@ -46,6 +136,24 @@ view_is_transient_for(struct cg_view *child, struct cg_view *parent)
return child->impl->is_transient_for(child, parent);
}
void
view_damage_part(struct cg_view *view)
{
struct cg_output *output;
wl_list_for_each (output, &view->server->outputs, link) {
output_damage_surface(output, view->wlr_surface, view->lx, view->ly, false);
}
}
void
view_damage_whole(struct cg_view *view)
{
struct cg_output *output;
wl_list_for_each (output, &view->server->outputs, link) {
output_damage_surface(output, view->wlr_surface, view->lx, view->ly, true);
}
}
void
view_activate(struct cg_view *view, bool activate)
{
@ -66,11 +174,6 @@ 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);
}
@ -82,32 +185,33 @@ view_center(struct cg_view *view, struct wlr_box *layout_box)
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)
{
struct wlr_box layout_box;
wlr_output_layout_get_box(view->server->output_layout, NULL, &layout_box);
struct wlr_box *layout_box = wlr_output_layout_get_box(view->server->output_layout, NULL);
if (view_is_primary(view) || view_extends_output_layout(view, &layout_box)) {
view_maximize(view, &layout_box);
if (view_is_primary(view) || view_extends_output_layout(view, layout_box)) {
view_maximize(view, layout_box);
} else {
view_center(view, &layout_box);
view_center(view, layout_box);
}
}
void
view_position_all(struct cg_server *server)
view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
{
struct cg_view *view;
wl_list_for_each (view, &server->views, link) {
view_position(view);
view->impl->for_each_surface(view, iterator, data);
}
void
view_for_each_popup(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
{
if (!view->impl->for_each_popup) {
return;
}
view->impl->for_each_popup(view, iterator, data);
}
void
@ -115,24 +219,28 @@ view_unmap(struct cg_view *view)
{
wl_list_remove(&view->link);
wlr_scene_node_destroy(&view->scene_tree->node);
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);
}
view->wlr_surface->data = NULL;
view->wlr_surface = NULL;
}
void
view_map(struct cg_view *view, struct wlr_surface *surface)
{
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->wlr_surface = surface;
surface->data = view;
struct wlr_subsurface *subsurface;
wl_list_for_each (subsurface, &view->wlr_surface->subsurfaces, parent_link) {
subsurface_create(view, subsurface);
}
view->new_subsurface.notify = handle_new_subsurface;
wl_signal_add(&view->wlr_surface->events.new_subsurface, &view->new_subsurface);
#if CAGE_HAS_XWAYLAND
/* We shouldn't position override-redirect windows. They set
@ -172,11 +280,24 @@ view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type
view->server = server;
view->type = type;
view->impl = impl;
wl_list_init(&view->children);
}
struct cg_view *
view_from_wlr_surface(struct wlr_surface *surface)
view_from_wlr_surface(struct cg_server *server, struct wlr_surface *surface)
{
assert(surface);
return surface->data;
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);
}

40
view.h
View file

@ -5,9 +5,9 @@
#include <stdbool.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/box.h>
#if CAGE_HAS_XWAYLAND
#include <wlr/xwayland.h>
#endif
@ -24,14 +24,16 @@ 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;
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,19 +44,47 @@ 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);
void (*for_each_popup)(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_part(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_position_all(struct cg_server *server);
void view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data);
void view_for_each_popup(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data);
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);
struct cg_view *view_from_wlr_surface(struct wlr_surface *surface);
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);
#endif

View file

@ -6,11 +6,10 @@
* See the LICENSE file accompanying this file.
*/
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
@ -19,9 +18,21 @@
#include "xdg_shell.h"
static void
xdg_decoration_set_mode(struct cg_xdg_decoration *xdg_decoration)
xdg_decoration_handle_destroy(struct wl_listener *listener, void *data)
{
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 {
@ -31,84 +42,104 @@ xdg_decoration_set_mode(struct cg_xdg_decoration *xdg_decoration)
}
static void
xdg_decoration_handle_destroy(struct wl_listener *listener, void *data)
xdg_popup_destroy(struct cg_view_child *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) {
if (!child) {
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);
}
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 cg_server *server = view->server;
struct wlr_box *popup_box = &popup->current.geometry;
struct wlr_box *popup_box = &popup->wlr_popup->geometry;
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_box = wlr_output_layout_get_box(output_layout, wlr_output);
struct wlr_box output_toplevel_box = {
.x = output_box.x - view->lx,
.y = output_box.y - view->ly,
.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, &output_toplevel_box);
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);
}
static struct cg_xdg_shell_view *
@ -121,26 +152,27 @@ 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_toplevel->title;
return xdg_shell_view->xdg_surface->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_xdg_surface *xdg_surface = xdg_shell_view->xdg_toplevel->base;
struct wlr_box geom;
*width_out = xdg_surface->geometry.width;
*height_out = xdg_surface->geometry.height;
wlr_xdg_surface_get_geometry(xdg_shell_view->xdg_surface, &geom);
*width_out = geom.width;
*height_out = geom.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_toplevel *parent = xdg_shell_view->xdg_toplevel->parent;
return parent == NULL;
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 */
}
static bool
@ -150,13 +182,14 @@ 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_toplevel *xdg_toplevel = _child->xdg_toplevel;
struct wlr_xdg_surface *xdg_surface = _child->xdg_surface;
struct cg_xdg_shell_view *_parent = xdg_shell_view_from_view(parent);
while (xdg_toplevel) {
if (xdg_toplevel->parent == _parent->xdg_toplevel) {
struct wlr_xdg_surface *parent_xdg_surface = _parent->xdg_surface;
while (xdg_surface) {
if (xdg_surface->toplevel->parent == parent_xdg_surface) {
return true;
}
xdg_toplevel = xdg_toplevel->parent;
xdg_surface = xdg_surface->toplevel->parent;
}
return false;
}
@ -165,15 +198,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_toplevel, activate);
wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_surface, 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_toplevel, output_width, output_height);
wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_toplevel, true);
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);
}
static void
@ -184,68 +217,81 @@ destroy(struct cg_view *view)
}
static void
handle_xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data)
for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
{
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_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);
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 void
handle_xdg_toplevel_unmap(struct wl_listener *listener, void *data)
for_each_popup(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_popup(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)
{
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);
}
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_part(view);
}
static void
handle_xdg_shell_surface_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_toplevel_map(struct wl_listener *listener, void *data)
handle_xdg_shell_surface_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;
view_map(view, xdg_shell_view->xdg_toplevel->base->surface);
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);
}
static void
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)
handle_xdg_shell_surface_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);
xdg_shell_view->xdg_toplevel = NULL;
wl_list_remove(&xdg_shell_view->new_popup.link);
xdg_shell_view->xdg_surface = NULL;
view_destroy(view);
}
@ -258,13 +304,20 @@ static const struct cg_view_impl xdg_shell_view_impl = {
.activate = activate,
.maximize = maximize,
.destroy = destroy,
.for_each_surface = for_each_surface,
.for_each_popup = for_each_popup,
.wlr_surface_at = wlr_surface_at,
};
void
handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
handle_xdg_shell_surface_new(struct wl_listener *listener, void *data)
{
struct cg_server *server = wl_container_of(listener, server, new_xdg_toplevel);
struct wlr_xdg_toplevel *toplevel = 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_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct cg_xdg_shell_view));
if (!xdg_shell_view) {
@ -273,105 +326,18 @@ handle_new_xdg_toplevel(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_toplevel = toplevel;
xdg_shell_view->xdg_surface = xdg_surface;
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;
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);
}
void
@ -390,8 +356,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);
}

View file

@ -9,33 +9,34 @@
struct cg_xdg_shell_view {
struct cg_view view;
struct wlr_xdg_toplevel *xdg_toplevel;
struct wlr_xdg_surface *xdg_surface;
struct wl_listener destroy;
struct wl_listener commit;
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 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;
};
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_shell_surface_new(struct wl_listener *listener, void *data);
void handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data);

View file

@ -9,6 +9,7 @@
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_box.h>
#include <wlr/util/log.h>
#include <wlr/xwayland.h>
@ -41,15 +42,8 @@ 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);
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;
*width_out = xwayland_view->xwayland_surface->surface->current.width;
*height_out = xwayland_view->xwayland_surface->surface->current.height;
}
static bool
@ -92,7 +86,7 @@ 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, view->lx, view->ly, output_width,
output_height);
wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true, true);
wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true);
}
static void
@ -102,6 +96,18 @@ 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)
{
@ -110,12 +116,24 @@ 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_part(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);
}
@ -130,7 +148,12 @@ handle_xwayland_surface_map(struct wl_listener *listener, void *data)
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);
view_map(view, xwayland_view->xwayland_surface->surface);
view_damage_whole(view);
}
static void
@ -139,6 +162,8 @@ 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;
@ -154,28 +179,12 @@ static const struct cg_view_impl xwayland_view_impl = {
.activate = activate,
.maximize = maximize,
.destroy = destroy,
.for_each_surface = for_each_surface,
/* XWayland doesn't have a separate popup iterator. */
.for_each_popup = NULL,
.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)
{
@ -191,10 +200,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->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->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->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;

View file

@ -10,10 +10,9 @@ struct cg_xwayland_view {
struct cg_view view;
struct wlr_xwayland_surface *xwayland_surface;
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;
};