diff --git a/bino/.SRCINFO b/bino/.SRCINFO index 38e61f3..72eb71e 100644 --- a/bino/.SRCINFO +++ b/bino/.SRCINFO @@ -1,6 +1,6 @@ pkgbase = bino pkgdesc = A video player with focus on 3D and Virtual Reality - pkgver = 2.3 + pkgver = 2.2 pkgrel = 1 url = https://bino3d.org/ arch = x86_64 @@ -10,10 +10,10 @@ pkgbase = bino depends = qt6-multimedia depends = qt6-svg depends = qvr - source = https://bino3d.org/releases/bino-2.3.tar.gz - source = https://bino3d.org/releases/bino-2.3.tar.gz.sig + source = https://bino3d.org/releases/bino-2.2.tar.gz + source = https://bino3d.org/releases/bino-2.2.tar.gz.sig validpgpkeys = 2F61B4828BBA779AECB3F32703A2A4AB1E32FD34 - sha512sums = 9750deb7e777237c87d3bd1e7d5411b23b55c4c3bac5361848effb59c3253fb4f9355fddbae62c922026318c349657a7fdfecdf4041cd756a92f0440c1e29f4c + sha512sums = 73e6207db4a71673f88ebbd8999054acfc8f70a069fc74089976786082373d0eb7ae675786b1d3cf8907987267d9b1b50ddb05366b8b70f880c6898da765cd7b sha512sums = SKIP pkgname = bino diff --git a/bino/PKGBUILD b/bino/PKGBUILD index c2b36f1..954e504 100644 --- a/bino/PKGBUILD +++ b/bino/PKGBUILD @@ -4,7 +4,7 @@ # Contributor: archtux pkgname=bino -pkgver=2.3 +pkgver=2.2 pkgrel=1 pkgdesc='A video player with focus on 3D and Virtual Reality' arch=('x86_64') @@ -13,7 +13,7 @@ license=('GPL-3.0-or-later') depends=('qt6-multimedia' 'qt6-svg' 'qvr') makedepends=('cmake' 'qt6-tools') source=(https://bino3d.org/releases/$pkgname-$pkgver.tar.gz{,.sig}) -sha512sums=('9750deb7e777237c87d3bd1e7d5411b23b55c4c3bac5361848effb59c3253fb4f9355fddbae62c922026318c349657a7fdfecdf4041cd756a92f0440c1e29f4c' +sha512sums=('73e6207db4a71673f88ebbd8999054acfc8f70a069fc74089976786082373d0eb7ae675786b1d3cf8907987267d9b1b50ddb05366b8b70f880c6898da765cd7b' 'SKIP') validpgpkeys=('2F61B4828BBA779AECB3F32703A2A4AB1E32FD34') diff --git a/cage-vi/.SRCINFO b/cage-vi/.SRCINFO new file mode 100644 index 0000000..7e5f5a8 --- /dev/null +++ b/cage-vi/.SRCINFO @@ -0,0 +1,25 @@ +pkgbase = cage-vi + pkgdesc = A Wayland kiosk, with virtual input patch applied + pkgver = 0.1.4+54+gbd5b20e + pkgrel = 1 + url = https://www.hjdskes.nl/projects/cage/ + arch = x86_64 + license = MIT + makedepends = meson + makedepends = pixman + makedepends = scdoc + makedepends = wayland-protocols + makedepends = xorg-xwayland + depends = glibc + depends = wayland + depends = wlroots + optdepends = polkit: System privilege control. Required if not using seatd service + optdepends = xorg-xwayland: X11 support + provides = cage + conflicts = cage + source = https://github.com/Hjdskes/cage/releases/download/v0.1.4/cage-0.1.4.tar.gz + source = bd5b20e.patch + sha512sums = 55773fac44bf9e98086e53cbc20d17e3aad7046bedfe638ef8f896543388481be3989fede6f950d4f8cb0583f701cbf79617dfa011c7777a7c8b22c9bcddd64f + sha512sums = e30d9596bf12a793e744a170dc859224f62d807bfc1f980ad26044ffe6170bedd1a1e8a9e9321b3bffb93aa3d42a152e67f60dd6735c6e13a4a2b987131a3394 + +pkgname = cage-vi diff --git a/cage-vi/PKGBUILD b/cage-vi/PKGBUILD new file mode 100644 index 0000000..c68cc30 --- /dev/null +++ b/cage-vi/PKGBUILD @@ -0,0 +1,45 @@ +pkgname=cage-vi +_pkgname=${pkgname%%-*} +pkgver=0.1.4+54+gbd5b20e +_pkgver=${pkgver%%+*} +pkgrel=1 +pkgdesc='A Wayland kiosk, with virtual input patch applied' +arch=('x86_64') +url="https://www.hjdskes.nl/projects/${_pkgname}/" +license=('MIT') +depends=('glibc' 'wayland' 'wlroots') +makedepends=('meson' 'pixman' 'scdoc' 'wayland-protocols' 'xorg-xwayland') +optdepends=( + 'polkit: System privilege control. Required if not using seatd service' + 'xorg-xwayland: X11 support' +) +provides=("${_pkgname}") +conflicts=("${_pkgname}") +source=( + "https://github.com/Hjdskes/${_pkgname}/releases/download/v${_pkgver}/${_pkgname}-${_pkgver}.tar.gz" + 'bd5b20e.patch' +) +sha512sums=('55773fac44bf9e98086e53cbc20d17e3aad7046bedfe638ef8f896543388481be3989fede6f950d4f8cb0583f701cbf79617dfa011c7777a7c8b22c9bcddd64f' + 'e30d9596bf12a793e744a170dc859224f62d807bfc1f980ad26044ffe6170bedd1a1e8a9e9321b3bffb93aa3d42a152e67f60dd6735c6e13a4a2b987131a3394') + +#pkgver() { +# cd "${srcdir}/${_pkgname}-${_pkgver}" +# git describe --long --tags | sed 's/^v//;s/-/+/g' +#} + + +prepare() { + cd "${srcdir}/${_pkgname}-${_pkgver}" + patch -Np1 -i '../bd5b20e.patch' + meson --buildtype=release -Dxwayland=true --prefix /usr "$srcdir/build" +} + +build() { + cd "${srcdir}/${_pkgname}-${_pkgver}" + ninja -C "$srcdir/build" +} + +package() { + cd "${srcdir}/${_pkgname}-${_pkgver}" + DESTDIR="$pkgdir/" ninja -C "$srcdir/build" install +} diff --git a/cage-vi/bd5b20e.patch b/cage-vi/bd5b20e.patch new file mode 100644 index 0000000..d59ba45 --- /dev/null +++ b/cage-vi/bd5b20e.patch @@ -0,0 +1,2914 @@ +diff --git a/.builds/alpine.yml b/.builds/alpine.yml +deleted file mode 100644 +index ef32a95..0000000 +--- a/.builds/alpine.yml ++++ /dev/null +@@ -1,35 +0,0 @@ +-image: alpine/edge +-packages: +- - eudev-dev +- - mesa-dev +- - meson +- - libinput-dev +- - libxkbcommon-dev +- - pixman-dev +- - scdoc +- - wayland-dev +- - wayland-protocols +- - xcb-util-wm-dev +- - 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.14.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 +diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml +deleted file mode 100644 +index 99cdd24..0000000 +--- a/.builds/archlinux.yml ++++ /dev/null +@@ -1,45 +0,0 @@ +-image: archlinux +-packages: +- - clang +- - meson +- - libinput +- - libxkbcommon +- - mesa +- - scdoc +- - wayland +- - wayland-protocols +- - xcb-util-wm +- - xorg-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.14.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 +diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml +deleted file mode 100644 +index 92a2119..0000000 +--- a/.builds/freebsd.yml ++++ /dev/null +@@ -1,37 +0,0 @@ +-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 +- - x11/xcb-util-wm +- - x11-servers/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.14.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 +diff --git a/.clang-format-ignore b/.clang-format-ignore +new file mode 100644 +index 0000000..60dd059 +--- /dev/null ++++ b/.clang-format-ignore +@@ -0,0 +1 @@ ++subprojects/**/* +\ No newline at end of file +diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml +new file mode 100644 +index 0000000..a98291a +--- /dev/null ++++ b/.github/workflows/main.yml +@@ -0,0 +1,76 @@ ++name: Continuous integration build ++on: ++ push: ++ branches: [ master ] ++ pull_request: ++ branches: [ master ] ++ ++jobs: ++ compile: ++ runs-on: ubuntu-latest ++ strategy: ++ matrix: ++ CC: [ gcc, clang ] ++ OS: [ "alpine:edge", "archlinux:base-devel" ] ++ xwayland: [ true, false ] ++ 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 scdoc-doc hwdata ++ ++ - 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 ++ ++ - name: Fetch wlroots as a subproject ++ run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b 0.16.0 ++ ++ # TODO: use --fatal-meson-warnings when on wlroots 0.15.0 ++ - name: Compile Cage (XWayland=${{ matrix.xwayland }}) ++ run: | ++ meson build-${{ matrix.CC }}-${{matrix.xwayland }} -Dxwayland=${{ 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 ++ - name: Fetch wlroots as a subproject ++ run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b 0.16.0 ++ - name: Check for formatting changes ++ run: | ++ meson build-clang-format -Dxwayland=true ++ 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 ++ - name: Fetch wlroots as a subproject ++ run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b 0.16.0 ++ - name: Run scan-build ++ run: | ++ meson build-scan-build -Dxwayland=true ++ ninja -C build-scan-build scan-build +diff --git a/README.md b/README.md +index e1b6f56..752387e 100644 +--- a/README.md ++++ b/README.md +@@ -1,4 +1,4 @@ +-# Cage: a Wayland kiosk [![builds.sr.ht status](https://builds.sr.ht/~hjdskes.svg)](https://builds.sr.ht/~hjdskes?) ++# Cage: a Wayland kiosk + + Cage's logo + +diff --git a/cage.c b/cage.c +index 5392535..c5eda4d 100644 +--- a/cage.c ++++ b/cage.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -27,8 +28,15 @@ + #include + #include + #include ++#include ++#include + #include + #include ++#include ++#include ++#include ++#include ++#include + #if CAGE_HAS_XWAYLAND + #include + #endif +@@ -145,7 +153,12 @@ cleanup_primary_client(pid_t pid) + 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"); +@@ -185,9 +198,6 @@ usage(FILE *file, const char *cage) + "Usage: %s [OPTIONS] [--] APPLICATION\n" + "\n" + " -d\t Don't draw client side decorations, when possible\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" +@@ -203,20 +213,11 @@ static bool + parse_args(struct cg_server *server, int argc, char *argv[]) + { + int c; +-#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->debug_damage_tracking = true; +- break; +-#endif + case 'h': + usage(stdout, argv[0]); + return false; +@@ -261,15 +262,20 @@ main(int argc, char *argv[]) + struct wl_event_source *sigint_source = NULL; + struct wl_event_source *sigterm_source = NULL; + struct wl_event_source *sigchld_source = NULL; +- struct wlr_renderer *renderer = NULL; + struct wlr_compositor *compositor = NULL; ++ struct wlr_subcompositor *subcompositor = 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_single_pixel_buffer_manager_v1 *single_pixel_buffer = NULL; + struct wlr_xdg_output_manager_v1 *output_manager = NULL; + struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL; ++ struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard = NULL; ++ struct wlr_virtual_pointer_manager_v1 *virtual_pointer = NULL; ++ struct wlr_viewporter *viewporter = NULL; ++ struct wlr_presentation *presentation = NULL; + struct wlr_xdg_shell *xdg_shell = NULL; + #if CAGE_HAS_XWAYLAND + struct wlr_xwayland *xwayland = NULL; +@@ -316,8 +322,21 @@ main(int argc, char *argv[]) + goto end; + } + +- renderer = wlr_backend_get_renderer(server.backend); +- wlr_renderer_init_wl_display(renderer, server.wl_display); ++ server.renderer = wlr_renderer_autocreate(server.backend); ++ if (!server.renderer) { ++ wlr_log(WLR_ERROR, "Unable to create the wlroots renderer"); ++ ret = 1; ++ goto end; ++ } ++ ++ server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); ++ if (!server.allocator) { ++ wlr_log(WLR_ERROR, "Unable to create the wlroots allocator"); ++ ret = 1; ++ goto end; ++ } ++ ++ wlr_renderer_init_wl_display(server.renderer, server.wl_display); + + wl_list_init(&server.views); + wl_list_init(&server.outputs); +@@ -329,13 +348,29 @@ main(int argc, char *argv[]) + goto end; + } + +- compositor = wlr_compositor_create(server.wl_display, renderer); ++ server.scene = wlr_scene_create(); ++ if (!server.scene) { ++ wlr_log(WLR_ERROR, "Unable to create scene"); ++ ret = 1; ++ goto end; ++ } ++ ++ wlr_scene_attach_output_layout(server.scene, server.output_layout); ++ ++ compositor = wlr_compositor_create(server.wl_display, server.renderer); + if (!compositor) { + wlr_log(WLR_ERROR, "Unable to create the wlroots compositor"); + ret = 1; + goto end; + } + ++ subcompositor = wlr_subcompositor_create(server.wl_display); ++ if (!subcompositor) { ++ wlr_log(WLR_ERROR, "Unable to create the wlroots subcompositor"); ++ ret = 1; ++ goto end; ++ } ++ + 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"); +@@ -373,7 +408,7 @@ main(int argc, char *argv[]) + wl_signal_add(&server.idle_inhibit_v1->events.new_inhibitor, &server.new_idle_inhibitor_v1); + wl_list_init(&server.inhibitors); + +- xdg_shell = wlr_xdg_shell_create(server.wl_display); ++ xdg_shell = wlr_xdg_shell_create(server.wl_display, 4); + if (!xdg_shell) { + wlr_log(WLR_ERROR, "Unable to create the XDG shell interface"); + ret = 1; +@@ -401,6 +436,21 @@ main(int argc, char *argv[]) + server_decoration_manager, server.xdg_decoration ? WLR_SERVER_DECORATION_MANAGER_MODE_SERVER + : WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT); + ++ viewporter = wlr_viewporter_create(server.wl_display); ++ if (!viewporter) { ++ wlr_log(WLR_ERROR, "Unable to create the viewporter interface"); ++ ret = 1; ++ goto end; ++ } ++ ++ presentation = wlr_presentation_create(server.wl_display, server.backend); ++ if (!presentation) { ++ wlr_log(WLR_ERROR, "Unable to create the presentation interface"); ++ ret = 1; ++ goto end; ++ } ++ wlr_scene_set_presentation(server.scene, presentation); ++ + 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"); +@@ -415,6 +465,13 @@ main(int argc, char *argv[]) + goto end; + } + ++ single_pixel_buffer = wlr_single_pixel_buffer_manager_v1_create(server.wl_display); ++ if (!single_pixel_buffer) { ++ wlr_log(WLR_ERROR, "Unable to create the single pixel buffer manager"); ++ ret = 1; ++ goto end; ++ } ++ + 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"); +@@ -429,6 +486,22 @@ main(int argc, char *argv[]) + goto end; + } + ++ 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); ++ ++ 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); ++ + #if CAGE_HAS_XWAYLAND + xwayland = wlr_xwayland_create(server.wl_display, compositor, true); + if (!xwayland) { +@@ -492,8 +565,9 @@ main(int argc, char *argv[]) + } + + /* 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); ++ struct wlr_box layout_box; ++ wlr_output_layout_get_box(server.output_layout, NULL, &layout_box); ++ wlr_cursor_warp(server.seat->cursor, NULL, layout_box.width / 2, layout_box.height / 2); + + wl_display_run(server.wl_display); + +diff --git a/meson.build b/meson.build +index 3a84794..fbd7e16 100644 +--- a/meson.build ++++ b/meson.build +@@ -1,16 +1,17 @@ + project('cage', 'c', + version: '0.1.4', + license: 'MIT', ++ meson_version: '>=0.58.1', + default_options: [ + 'c_std=c11', +- 'warning_level=3', ++ 'warning_level=2', ++ 'werror=true', + ], + ) + + add_project_arguments( + [ + '-DWLR_USE_UNSTABLE', +- '-Wall', + '-Wundef', + '-Wno-unused-parameter', + ], +@@ -34,14 +35,13 @@ if is_freebsd + ) + endif + +-wlroots = dependency('wlroots', version: '>= 0.14.0') ++wlroots = dependency('wlroots', version: '>= 0.16.0', fallback: ['wlroots', 'wlroots']) + wayland_protos = dependency('wayland-protocols', version: '>=1.14') + wayland_server = dependency('wayland-server') +-pixman = dependency('pixman-1') + xkbcommon = dependency('xkbcommon') + math = cc.find_library('m') + +-wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') ++wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') + wayland_scanner = find_program('wayland-scanner') + wayland_scanner_server = generator( + wayland_scanner, +@@ -65,12 +65,11 @@ server_protos = declare_dependency( + ) + + if get_option('xwayland') +- wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include ', dependencies: wlroots) == '1' ++ wlroots_has_xwayland = wlroots.get_variable(pkgconfig: 'have_xwayland', internal: 'have_xwayland') == 'true' + if not wlroots_has_xwayland + error('Cannot build Cage with XWayland support: wlroots has been built without it') +- else +- have_xwayland = true + endif ++ have_xwayland = true + else + have_xwayland = false + endif +@@ -78,8 +77,8 @@ 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']) +- git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD']) ++ git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false) ++ git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) + if git_commit.returncode() == 0 and git_branch.returncode() == 0 + version = '@0@-@1@ (branch \'@2@\')'.format( + meson.project_version(), +@@ -95,7 +94,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_pkgconfig_variable('scdoc'), native: true) ++ scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true) + sh = find_program('sh', native: true) + mandir = get_option('mandir') + man_files = [ +@@ -111,7 +110,7 @@ if scdoc.found() + input: filename, + output: output, + command: [ +- sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) ++ sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output) + ], + install: true, + install_dir: '@0@/man@1@'.format(mandir, section) +@@ -123,9 +122,7 @@ cage_sources = [ + 'cage.c', + 'idle_inhibit_v1.c', + 'output.c', +- 'render.c', + 'seat.c', +- 'util.c', + 'view.c', + 'xdg_shell.c', + ] +@@ -136,10 +133,8 @@ cage_headers = [ + configuration: conf_data), + 'idle_inhibit_v1.h', + 'output.h', +- 'render.h', + 'seat.h', + 'server.h', +- 'util.h', + 'view.h', + 'xdg_shell.h', + ] +@@ -157,7 +152,6 @@ executable( + wayland_server, + wlroots, + xkbcommon, +- pixman, + math, + ], + install: true, +diff --git a/output.c b/output.c +index d8da3b9..6eca0db 100644 +--- a/output.c ++++ b/output.c +@@ -1,7 +1,7 @@ + /* + * Cage: A Wayland kiosk. + * +- * Copyright (C) 2018-2020 Jente Hidskes ++ * Copyright (C) 2018-2021 Jente Hidskes + * Copyright (C) 2019 The Sway authors + * + * See the LICENSE file accompanying this file. +@@ -11,6 +11,7 @@ + + #include "config.h" + ++#include + #include + #include + #include +@@ -21,221 +22,25 @@ + #include + #endif + #include ++#include + #include + #include + #include + #include + #include +-#include ++#include + #include + #include + #include + + #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 + +-static void output_for_each_surface(struct cg_output *output, cg_surface_iterator_func_t iterator, void *user_data); +- +-struct surface_iterator_data { +- cg_surface_iterator_func_t user_iterator; +- void *user_data; +- +- struct cg_output *output; +- +- /* 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_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_popup_surface(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; +- } +- } +- +- struct cg_view *view = seat_get_focus(server->seat); +- if (!view || !view->wlr_surface) { +- return false; +- } +- +- 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 + output_enable(struct cg_output *output) + { +@@ -249,6 +54,9 @@ output_enable(struct cg_output *output) + wlr_output_layout_add_auto(output->server->output_layout, wlr_output); + wlr_output_enable(wlr_output, true); + wlr_output_commit(wlr_output); ++ ++ output->scene_output = wlr_scene_get_scene_output(output->server->scene, wlr_output); ++ assert(output->scene_output != NULL); + } + + static void +@@ -261,6 +69,8 @@ output_disable(struct cg_output *output) + return; + } + ++ output->scene_output = NULL; ++ + wlr_log(WLR_DEBUG, "Disabling output %s", wlr_output->name); + wlr_output_enable(wlr_output, false); + wlr_output_layout_remove(output->server->output_layout, wlr_output); +@@ -268,93 +78,19 @@ output_disable(struct cg_output *output) + } + + static void +-damage_surface_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box, void *user_data) ++handle_output_frame(struct wl_listener *listener, void *data) + { +- struct wlr_output *wlr_output = output->wlr_output; +- bool whole = *(bool *) user_data; +- +- scale_box(box, output->wlr_output->scale); +- +- 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); +- +- 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); +- } +-} ++ struct cg_output *output = wl_container_of(listener, output, frame); + +-void +-output_damage_surface(struct cg_output *output, struct wlr_surface *surface, double lx, double ly, bool whole) +-{ + if (!output->wlr_output->enabled) { +- wlr_log(WLR_DEBUG, "Not adding damage for disabled output %s", output->wlr_output->name); + return; + } + +- 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); +-} ++ wlr_scene_output_commit(output->scene_output); + +-static void +-handle_output_damage_frame(struct wl_listener *listener, void *data) +-{ +- 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; +- } +- +- /* 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); ++ struct timespec now = {0}; ++ clock_gettime(CLOCK_MONOTONIC, &now); ++ wlr_scene_output_send_frame_done(output->scene_output, &now); + } + + static void +@@ -395,11 +131,12 @@ output_destroy(struct cg_output *output) + { + struct cg_server *server = output->server; + ++ output->wlr_output->data = NULL; ++ + wl_list_remove(&output->destroy.link); + wl_list_remove(&output->commit.link); + wl_list_remove(&output->mode.link); +- wl_list_remove(&output->damage_frame.link); +- wl_list_remove(&output->damage_destroy.link); ++ wl_list_remove(&output->frame.link); + wl_list_remove(&output->link); + + wlr_output_layout_remove(server->output_layout, output->wlr_output); +@@ -421,18 +158,10 @@ output_destroy(struct cg_output *output) + } + } + +-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); + } + +@@ -442,6 +171,11 @@ 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"); +@@ -449,8 +183,9 @@ 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; +@@ -459,15 +194,29 @@ handle_new_output(struct wl_listener *listener, void *data) + wl_signal_add(&wlr_output->events.mode, &output->mode); + output->destroy.notify = handle_output_destroy; + wl_signal_add(&wlr_output->events.destroy, &output->destroy); +- 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); +- +- struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); +- if (preferred_mode) { +- wlr_output_set_mode(wlr_output, preferred_mode); ++ output->frame.notify = handle_output_frame; ++ wl_signal_add(&wlr_output->events.frame, &output->frame); ++ ++ if (!wl_list_empty(&wlr_output->modes)) { ++ struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); ++ if (preferred_mode) { ++ wlr_output_set_mode(wlr_output, preferred_mode); ++ } ++ if (!wlr_output_test(wlr_output)) { ++ struct wlr_output_mode *mode; ++ wl_list_for_each (mode, &wlr_output->modes, link) { ++ if (mode == preferred_mode) { ++ continue; ++ } ++ ++ wlr_output_set_mode(wlr_output, mode); ++ if (wlr_output_test(wlr_output)) { ++ break; ++ } ++ } ++ } + } ++ + wlr_output_set_transform(wlr_output, output->server->output_transform); + + if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST) { +diff --git a/output.h b/output.h +index b3fd3b4..ced06f6 100644 +--- a/output.h ++++ b/output.h +@@ -11,28 +11,17 @@ + struct cg_output { + struct cg_server *server; + struct wlr_output *wlr_output; +- struct wlr_output_damage *damage; ++ struct wlr_scene_output *scene_output; + + struct wl_listener commit; + struct wl_listener mode; + struct wl_listener destroy; +- struct wl_listener damage_frame; +- struct wl_listener damage_destroy; ++ struct wl_listener frame; + + struct wl_list link; // cg_server::outputs + }; + +-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_surface(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 +diff --git a/render.c b/render.c +deleted file mode 100644 +index 166a088..0000000 +--- a/render.c ++++ /dev/null +@@ -1,205 +0,0 @@ +-/* +- * Cage: A Wayland kiosk. +- * +- * Copyright (C) 2018-2020 Jente Hidskes +- * Copyright (C) 2019 The Sway authors +- * +- * See the LICENSE file accompanying this file. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#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_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_surface(output, view, render_surface_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"); +- } +-} +diff --git a/render.h b/render.h +deleted file mode 100644 +index 085b00b..0000000 +--- a/render.h ++++ /dev/null +@@ -1,8 +0,0 @@ +-#ifndef CG_RENDER_H +-#define CG_RENDER_H +- +-#include "output.h" +- +-void output_render(struct cg_output *output, pixman_region32_t *damage); +- +-#endif +diff --git a/seat.c b/seat.c +index 08f25a3..5d80e33 100644 +--- a/seat.c ++++ b/seat.c +@@ -6,10 +6,14 @@ + * See the LICENSE file accompanying this file. + */ + ++#define _POSIX_C_SOURCE 200809L ++ + #include "config.h" + ++#include + #include + #include ++#include + #include + #include + #include +@@ -18,9 +22,11 @@ + #include + #include + #include ++#include + #include +-#include + #include ++#include ++#include + #include + #include + #if CAGE_HAS_XWAYLAND +@@ -41,42 +47,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. */ +-static bool +-view_at(struct cg_view *view, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) ++ * coordinates relative to that surface's top-left corner. ++ * ++ * This function iterates over all of our surfaces and attempts to find one ++ * under the cursor. If desktop_view_at returns a view, there is also a ++ * surface. There cannot be a surface without a view, either. It's both or ++ * nothing. ++ */ ++static struct cg_view * ++desktop_view_at(struct cg_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) + { +- double view_sx = lx - view->lx; +- double view_sy = ly - view->ly; ++ 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; ++ } + +- 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; ++ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); ++ struct wlr_scene_surface *scene_surface = wlr_scene_surface_from_buffer(scene_buffer); ++ if (!scene_surface) { ++ return NULL; + } + +- return false; +-} ++ *surface = scene_surface->surface; + +-/* 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 cg_view *view; +- +- wl_list_for_each (view, &server->views, link) { +- if (view_at(view, lx, ly, surface, sx, sy)) { +- return view; ++ /* Walk up the tree until we find a node with a data pointer. When done, ++ * we've found the node representing the view. */ ++ while (!node->data) { ++ if (!node->parent) { ++ node = NULL; ++ break; + } ++ ++ node = &node->parent->node; + } + +- return NULL; ++ assert(node != NULL); ++ return node->data; + } + + static void +@@ -127,16 +133,16 @@ update_capabilities(struct cg_seat *seat) + } + + static void +-map_input_device_to_output(struct cg_seat *seat, struct wlr_input_device *device) ++map_input_device_to_output(struct cg_seat *seat, struct wlr_input_device *device, const char *output_name) + { +- if (!device->output_name) { ++ if (!output_name) { + wlr_log(WLR_INFO, "Input device %s cannot be mapped to an output device\n", device->name); + return; + } + + struct cg_output *output; + wl_list_for_each (output, &seat->server->outputs, link) { +- if (strcmp(device->output_name, output->wlr_output->name) == 0) { ++ if (strcmp(output_name, output->wlr_output->name) == 0) { + wlr_log(WLR_INFO, "Mapping input device %s to output device %s\n", device->name, + output->wlr_output->name); + wlr_cursor_map_input_to_output(seat->cursor, device, output->wlr_output); +@@ -154,7 +160,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->device); ++ wlr_cursor_detach_input_device(seat->cursor, &touch->touch->base); + wl_list_remove(&touch->destroy.link); + free(touch); + +@@ -162,7 +168,7 @@ handle_touch_destroy(struct wl_listener *listener, void *data) + } + + static void +-handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device) ++handle_new_touch(struct cg_seat *seat, struct wlr_touch *wlr_touch) + { + struct cg_touch *touch = calloc(1, sizeof(struct cg_touch)); + if (!touch) { +@@ -171,14 +177,14 @@ handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device) + } + + touch->seat = seat; +- touch->device = device; +- wlr_cursor_attach_input_device(seat->cursor, device); ++ touch->touch = wlr_touch; ++ wlr_cursor_attach_input_device(seat->cursor, &wlr_touch->base); + + wl_list_insert(&seat->touch, &touch->link); + touch->destroy.notify = handle_touch_destroy; +- wl_signal_add(&touch->device->events.destroy, &touch->destroy); ++ wl_signal_add(&wlr_touch->base.events.destroy, &touch->destroy); + +- map_input_device_to_output(seat, device); ++ map_input_device_to_output(seat, &wlr_touch->base, wlr_touch->output_name); + } + + static void +@@ -188,7 +194,7 @@ handle_pointer_destroy(struct wl_listener *listener, void *data) + struct cg_seat *seat = pointer->seat; + + wl_list_remove(&pointer->link); +- wlr_cursor_detach_input_device(seat->cursor, pointer->device); ++ wlr_cursor_detach_input_device(seat->cursor, &pointer->pointer->base); + wl_list_remove(&pointer->destroy.link); + free(pointer); + +@@ -196,7 +202,7 @@ handle_pointer_destroy(struct wl_listener *listener, void *data) + } + + static void +-handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device) ++handle_new_pointer(struct cg_seat *seat, struct wlr_pointer *wlr_pointer) + { + struct cg_pointer *pointer = calloc(1, sizeof(struct cg_pointer)); + if (!pointer) { +@@ -205,21 +211,42 @@ handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device) + } + + pointer->seat = seat; +- pointer->device = device; +- wlr_cursor_attach_input_device(seat->cursor, device); ++ pointer->pointer = wlr_pointer; ++ wlr_cursor_attach_input_device(seat->cursor, &wlr_pointer->base); + + wl_list_insert(&seat->pointers, &pointer->link); + pointer->destroy.notify = handle_pointer_destroy; +- wl_signal_add(&device->events.destroy, &pointer->destroy); ++ wl_signal_add(&wlr_pointer->base.events.destroy, &pointer->destroy); ++ ++ 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) ++{ ++ 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; ++ ++ /* 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_input_device *device, struct cg_seat *seat) ++handle_modifier_event(struct wlr_keyboard *keyboard, struct cg_seat *seat) + { +- wlr_seat_set_keyboard(seat->seat, device); +- wlr_seat_keyboard_notify_modifiers(seat->seat, &device->keyboard->modifiers); ++ wlr_seat_set_keyboard(seat->seat, keyboard); ++ wlr_seat_keyboard_notify_modifiers(seat->seat, &keyboard->modifiers); + + wlr_idle_notify_activity(seat->server->idle, seat->seat); + } +@@ -249,18 +276,18 @@ handle_keybinding(struct cg_server *server, xkb_keysym_t sym) + } + + static void +-handle_key_event(struct wlr_input_device *device, struct cg_seat *seat, void *data) ++handle_key_event(struct wlr_keyboard *keyboard, struct cg_seat *seat, void *data) + { +- struct wlr_event_keyboard_key *event = data; ++ struct wlr_keyboard_key_event *event = data; + + /* Translate from libinput keycode to an xkbcommon keycode. */ + xkb_keycode_t keycode = event->keycode + 8; + + const xkb_keysym_t *syms; +- int nsyms = xkb_state_key_get_syms(device->keyboard->xkb_state, keycode, &syms); ++ int nsyms = xkb_state_key_get_syms(keyboard->xkb_state, keycode, &syms); + + bool handled = false; +- uint32_t modifiers = wlr_keyboard_get_modifiers(device->keyboard); ++ uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); + if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + /* If Alt is held down and this button was pressed, we + * attempt to process it as a compositor +@@ -272,7 +299,7 @@ handle_key_event(struct wlr_input_device *device, struct cg_seat *seat, void *da + + if (!handled) { + /* Otherwise, we pass it along to the client. */ +- wlr_seat_set_keyboard(seat->seat, device); ++ wlr_seat_set_keyboard(seat->seat, keyboard); + wlr_seat_keyboard_notify_key(seat->seat, event->time_msec, event->keycode, event->state); + } + +@@ -283,27 +310,32 @@ 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->input_device, cg_group->seat, data); ++ handle_key_event(&cg_group->wlr_group->keyboard, 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->input_device, group->seat); ++ handle_modifier_event(&group->wlr_group->keyboard, group->seat); + } + + static void +-cg_keyboard_group_add(struct wlr_input_device *device, struct cg_seat *seat) ++cg_keyboard_group_add(struct wlr_keyboard *keyboard, struct cg_seat *seat, bool virtual) + { +- 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; ++ /* 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; ++ } + } + } + +@@ -315,6 +347,7 @@ cg_keyboard_group_add(struct wlr_input_device *device, struct cg_seat *seat) + 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."); +@@ -322,14 +355,14 @@ cg_keyboard_group_add(struct wlr_input_device *device, struct cg_seat *seat) + } + + cg_group->wlr_group->data = cg_group; +- wlr_keyboard_set_keymap(&cg_group->wlr_group->keyboard, device->keyboard->keymap); ++ wlr_keyboard_set_keymap(&cg_group->wlr_group->keyboard, keyboard->keymap); + +- wlr_keyboard_set_repeat_info(&cg_group->wlr_group->keyboard, wlr_keyboard->repeat_info.rate, +- wlr_keyboard->repeat_info.delay); ++ wlr_keyboard_set_repeat_info(&cg_group->wlr_group->keyboard, keyboard->repeat_info.rate, ++ keyboard->repeat_info.delay); + + wlr_log(WLR_DEBUG, "Created keyboard group"); + +- wlr_keyboard_group_add_keyboard(cg_group->wlr_group, wlr_keyboard); ++ wlr_keyboard_group_add_keyboard(cg_group->wlr_group, keyboard); + wl_list_insert(&seat->keyboard_groups, &cg_group->link); + + wl_signal_add(&cg_group->wlr_group->keyboard.events.key, &cg_group->key); +@@ -347,36 +380,45 @@ cleanup: + } + + static void +-handle_new_keyboard(struct cg_seat *seat, struct wlr_input_device *device) ++handle_new_keyboard(struct cg_seat *seat, struct wlr_keyboard *keyboard, bool virtual) + { + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!context) { +- wlr_log(WLR_ERROR, "Unable to create XBK context"); ++ wlr_log(WLR_ERROR, "Unable to create XKB context"); + return; + } + +- struct xkb_rule_names rules = {0}; +- rules.rules = getenv("XKB_DEFAULT_RULES"); +- rules.model = getenv("XKB_DEFAULT_MODEL"); +- rules.layout = getenv("XKB_DEFAULT_LAYOUT"); +- rules.variant = getenv("XKB_DEFAULT_VARIANT"); +- rules.options = getenv("XKB_DEFAULT_OPTIONS"); +- struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); ++ struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!keymap) { + wlr_log(WLR_ERROR, "Unable to configure keyboard: keymap does not exist"); + xkb_context_unref(context); + return; + } + +- wlr_keyboard_set_keymap(device->keyboard, keymap); ++ wlr_keyboard_set_keymap(keyboard, keymap); + + xkb_keymap_unref(keymap); + xkb_context_unref(context); +- wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); ++ wlr_keyboard_set_repeat_info(keyboard, 25, 600); ++ ++ cg_keyboard_group_add(keyboard, seat, virtual); ++ ++ wlr_seat_set_keyboard(seat->seat, keyboard); ++} + +- cg_keyboard_group_add(device, seat); ++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 */ + +- wlr_seat_set_keyboard(seat->seat, device); ++ handle_new_keyboard(seat, wlr_keyboard, true); ++ update_capabilities(seat); + } + + static void +@@ -387,13 +429,13 @@ handle_new_input(struct wl_listener *listener, void *data) + + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: +- handle_new_keyboard(seat, device); ++ handle_new_keyboard(seat, wlr_keyboard_from_input_device(device), false); + break; + case WLR_INPUT_DEVICE_POINTER: +- handle_new_pointer(seat, device); ++ handle_new_pointer(seat, wlr_pointer_from_input_device(device)); + break; + case WLR_INPUT_DEVICE_TOUCH: +- handle_new_touch(seat, device); ++ handle_new_touch(seat, wlr_touch_from_input_device(device)); + break; + case WLR_INPUT_DEVICE_SWITCH: + wlr_log(WLR_DEBUG, "Switch input is not implemented"); +@@ -448,10 +490,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_event_touch_down *event = data; ++ struct wlr_touch_down_event *event = data; + + double lx, ly; +- wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device, event->x, event->y, &lx, &ly); ++ wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->touch->base, event->x, event->y, &lx, &ly); + + double sx, sy; + struct wlr_surface *surface; +@@ -466,7 +508,7 @@ 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->device, event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED, lx, ly); ++ press_cursor_button(seat, &event->touch->base, event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED, lx, ly); + } + + wlr_idle_notify_activity(seat->server->idle, seat->seat); +@@ -476,14 +518,14 @@ static void + handle_touch_up(struct wl_listener *listener, void *data) + { + struct cg_seat *seat = wl_container_of(listener, seat, touch_up); +- struct wlr_event_touch_up *event = data; ++ struct wlr_touch_up_event *event = data; + + if (!wlr_seat_touch_get_point(seat->seat, event->touch_id)) { + return; + } + + if (wlr_seat_touch_num_points(seat->seat) == 1) { +- press_cursor_button(seat, event->device, event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED, ++ press_cursor_button(seat, &event->touch->base, event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED, + seat->touch_lx, seat->touch_ly); + } + +@@ -495,14 +537,14 @@ static void + handle_touch_motion(struct wl_listener *listener, void *data) + { + struct cg_seat *seat = wl_container_of(listener, seat, touch_motion); +- struct wlr_event_touch_motion *event = data; ++ struct wlr_touch_motion_event *event = data; + + if (!wlr_seat_touch_get_point(seat->seat, event->touch_id)) { + return; + } + + double lx, ly; +- wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device, event->x, event->y, &lx, &ly); ++ wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->touch->base, event->x, event->y, &lx, &ly); + + double sx, sy; + struct wlr_surface *surface; +@@ -536,7 +578,7 @@ static void + handle_cursor_axis(struct wl_listener *listener, void *data) + { + struct cg_seat *seat = wl_container_of(listener, seat, cursor_axis); +- struct wlr_event_pointer_axis *event = data; ++ struct wlr_pointer_axis_event *event = data; + + wlr_seat_pointer_notify_axis(seat->seat, event->time_msec, event->orientation, event->delta, + event->delta_discrete, event->source); +@@ -547,10 +589,10 @@ static void + handle_cursor_button(struct wl_listener *listener, void *data) + { + struct cg_seat *seat = wl_container_of(listener, seat, cursor_button); +- struct wlr_event_pointer_button *event = data; ++ struct wlr_pointer_button_event *event = data; + + wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button, event->state); +- press_cursor_button(seat, event->device, event->time_msec, event->button, event->state, seat->cursor->x, ++ press_cursor_button(seat, &event->pointer->base, event->time_msec, event->button, event->state, seat->cursor->x, + seat->cursor->y); + wlr_idle_notify_activity(seat->server->idle, seat->seat); + } +@@ -569,10 +611,7 @@ process_cursor_motion(struct cg_seat *seat, uint32_t time) + } else { + wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); + +- bool focus_changed = wlr_seat->pointer_state.focused_surface != surface; +- if (!focus_changed && time > 0) { +- wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy); +- } ++ wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy); + } + + struct cg_drag_icon *drag_icon; +@@ -587,9 +626,9 @@ static void + handle_cursor_motion_absolute(struct wl_listener *listener, void *data) + { + struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion_absolute); +- struct wlr_event_pointer_motion_absolute *event = data; ++ struct wlr_pointer_motion_absolute_event *event = data; + +- wlr_cursor_warp_absolute(seat->cursor, event->device, event->x, event->y); ++ wlr_cursor_warp_absolute(seat->cursor, &event->pointer->base, event->x, event->y); + process_cursor_motion(seat, event->time_msec); + wlr_idle_notify_activity(seat->server->idle, seat->seat); + } +@@ -598,22 +637,13 @@ static void + handle_cursor_motion(struct wl_listener *listener, void *data) + { + struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion); +- struct wlr_event_pointer_motion *event = data; ++ struct wlr_pointer_motion_event *event = data; + +- wlr_cursor_move(seat->cursor, event->device, event->delta_x, event->delta_y); ++ wlr_cursor_move(seat->cursor, &event->pointer->base, 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 + drag_icon_update_position(struct cg_drag_icon *drag_icon) + { +@@ -621,8 +651,6 @@ 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; +@@ -640,7 +668,7 @@ drag_icon_update_position(struct cg_drag_icon *drag_icon) + break; + } + +- drag_icon_damage(drag_icon); ++ wlr_scene_node_set_position(&drag_icon->scene_tree->node, drag_icon->lx, drag_icon->ly); + } + + static void +@@ -650,6 +678,7 @@ handle_drag_icon_destroy(struct wl_listener *listener, void *data) + + wl_list_remove(&drag_icon->link); + wl_list_remove(&drag_icon->destroy.link); ++ wlr_scene_node_destroy(&drag_icon->scene_tree->node); + free(drag_icon); + } + +@@ -692,6 +721,11 @@ handle_start_drag(struct wl_listener *listener, void *data) + } + drag_icon->seat = seat; + drag_icon->wlr_drag_icon = wlr_drag_icon; ++ drag_icon->scene_tree = wlr_scene_subsurface_tree_create(&seat->server->scene->tree, wlr_drag_icon->surface); ++ if (!drag_icon->scene_tree) { ++ free(drag_icon); ++ return; ++ } + + drag_icon->destroy.notify = handle_drag_icon_destroy; + wl_signal_add(&wlr_drag_icon->events.destroy, &drag_icon->destroy); +@@ -812,6 +846,9 @@ 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); +@@ -840,7 +877,10 @@ struct cg_view * + seat_get_focus(struct cg_seat *seat) + { + struct wlr_surface *prev_surface = seat->seat->keyboard_state.focused_surface; +- return view_from_wlr_surface(seat->server, prev_surface); ++ if (!prev_surface) { ++ return NULL; ++ } ++ return view_from_wlr_surface(prev_surface); + } + + void +diff --git a/seat.h b/seat.h +index 188543d..52cbee4 100644 +--- a/seat.h ++++ b/seat.h +@@ -55,12 +55,13 @@ 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_input_device *device; ++ struct wlr_pointer *pointer; + + struct wl_listener destroy; + }; +@@ -68,7 +69,7 @@ struct cg_pointer { + struct cg_touch { + struct wl_list link; // seat::touch + struct cg_seat *seat; +- struct wlr_input_device *device; ++ struct wlr_touch *touch; + + struct wl_listener destroy; + }; +@@ -77,6 +78,7 @@ 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; +diff --git a/server.h b/server.h +index 817637b..082a435 100644 +--- a/server.h ++++ b/server.h +@@ -12,10 +12,6 @@ + #include + #endif + +-#include "output.h" +-#include "seat.h" +-#include "view.h" +- + enum cg_multi_output_mode { + CAGE_MULTI_OUTPUT_MODE_EXTEND, + CAGE_MULTI_OUTPUT_MODE_LAST, +@@ -25,6 +21,8 @@ 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 cg_seat *seat; + struct wlr_idle *idle; +@@ -34,6 +32,7 @@ struct cg_server { + + enum cg_multi_output_mode output_mode; + struct wlr_output_layout *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 +@@ -41,6 +40,9 @@ struct cg_server { + + struct wl_listener xdg_toplevel_decoration; + struct wl_listener new_xdg_shell_surface; ++ ++ struct wl_listener new_virtual_keyboard; ++ struct wl_listener new_virtual_pointer; + #if CAGE_HAS_XWAYLAND + struct wl_listener new_xwayland_surface; + #endif +@@ -48,9 +50,6 @@ struct cg_server { + bool xdg_decoration; + bool allow_vt_switch; + enum wl_output_transform output_transform; +-#ifdef DEBUG +- bool debug_damage_tracking; +-#endif + }; + + #endif +diff --git a/util.c b/util.c +deleted file mode 100644 +index 95de499..0000000 +--- a/util.c ++++ /dev/null +@@ -1,36 +0,0 @@ +-/* +- * Cage: A Wayland kiosk. +- * +- * Copyright (C) 2019 The Sway authors +- * +- * See the LICENSE file accompanying this file. +- */ +- +-#include +- +-#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); +-} +diff --git a/util.h b/util.h +deleted file mode 100644 +index db6bc7d..0000000 +--- a/util.h ++++ /dev/null +@@ -1,11 +0,0 @@ +-#ifndef CG_UTIL_H +-#define CG_UTIL_H +- +-#include +- +-/** 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 +diff --git a/view.c b/view.c +index 3f3b0ed..b4a3eca 100644 +--- a/view.c ++++ b/view.c +@@ -1,20 +1,20 @@ + /* + * Cage: A Wayland kiosk. + * +- * Copyright (C) 2018-2020 Jente Hidskes ++ * Copyright (C) 2018-2021 Jente Hidskes + * + * See the LICENSE file accompanying this file. + */ + + #define _POSIX_C_SOURCE 200809L + ++#include + #include + #include + #include + #include +-#include + #include +-#include ++#include + + #include "output.h" + #include "seat.h" +@@ -24,96 +24,6 @@ + #include "xwayland.h" + #endif + +-static void +-view_child_handle_commit(struct wl_listener *listener, void *data) +-{ +- struct cg_view_child *child = wl_container_of(listener, child, commit); +- view_damage_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) + { +@@ -136,24 +46,6 @@ 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) + { +@@ -174,6 +66,9 @@ view_maximize(struct cg_view *view, struct wlr_box *layout_box) + { + view->lx = layout_box->x; + view->ly = layout_box->y; ++ ++ wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly); ++ + view->impl->maximize(view, layout_box->width, layout_box->height); + } + +@@ -185,33 +80,21 @@ 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; ++ ++ 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); ++ struct wlr_box layout_box; ++ wlr_output_layout_get_box(view->server->output_layout, NULL, &layout_box); + +- if (view_is_primary(view) || view_extends_output_layout(view, layout_box)) { +- view_maximize(view, layout_box); ++ if (view_is_primary(view) || view_extends_output_layout(view, &layout_box)) { ++ view_maximize(view, &layout_box); + } else { +- view_center(view, layout_box); +- } +-} +- +-void +-view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data) +-{ +- view->impl->for_each_surface(view, iterator, data); +-} +- +-void +-view_for_each_popup_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data) +-{ +- if (!view->impl->for_each_popup_surface) { +- return; ++ view_center(view, &layout_box); + } +- view->impl->for_each_popup_surface(view, iterator, data); + } + + void +@@ -219,31 +102,24 @@ view_unmap(struct cg_view *view) + { + wl_list_remove(&view->link); + +- wl_list_remove(&view->new_subsurface.link); +- +- struct cg_view_child *child, *tmp; +- wl_list_for_each_safe (child, tmp, &view->children, link) { +- child->destroy(child); +- } ++ wlr_scene_node_destroy(&view->scene_tree->node); + ++ view->wlr_surface->data = NULL; + view->wlr_surface = NULL; + } + + void + view_map(struct cg_view *view, struct wlr_surface *surface) + { +- view->wlr_surface = surface; +- +- struct wlr_subsurface *subsurface; +- wl_list_for_each (subsurface, &view->wlr_surface->subsurfaces_below, parent_link) { +- subsurface_create(view, subsurface); +- } +- wl_list_for_each (subsurface, &view->wlr_surface->subsurfaces_above, parent_link) { +- subsurface_create(view, subsurface); ++ view->scene_tree = wlr_scene_subsurface_tree_create(&view->server->scene->tree, surface); ++ if (!view->scene_tree) { ++ wl_resource_post_no_memory(surface->resource); ++ return; + } ++ view->scene_tree->node.data = view; + +- view->new_subsurface.notify = handle_new_subsurface; +- wl_signal_add(&view->wlr_surface->events.new_subsurface, &view->new_subsurface); ++ view->wlr_surface = surface; ++ surface->data = view; + + #if CAGE_HAS_XWAYLAND + /* We shouldn't position override-redirect windows. They set +@@ -283,24 +159,11 @@ 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 cg_server *server, struct wlr_surface *surface) +-{ +- struct cg_view *view; +- wl_list_for_each (view, &server->views, link) { +- if (view->wlr_surface == surface) { +- return view; +- } +- } +- return NULL; +-} +- +-struct wlr_surface * +-view_wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y) ++view_from_wlr_surface(struct wlr_surface *surface) + { +- return view->impl->wlr_surface_at(view, sx, sy, sub_x, sub_y); ++ assert(surface); ++ return surface->data; + } +diff --git a/view.h b/view.h +index cd16e42..5ae13dd 100644 +--- a/view.h ++++ b/view.h +@@ -5,9 +5,9 @@ + + #include + #include +-#include +-#include ++#include + #include ++#include + #if CAGE_HAS_XWAYLAND + #include + #endif +@@ -24,16 +24,14 @@ enum cg_view_type { + struct cg_view { + struct cg_server *server; + struct wl_list link; // server::views +- struct wl_list children; // cg_view_child::link + struct wlr_surface *wlr_surface; ++ 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 { +@@ -44,47 +42,18 @@ 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_surface)(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data); +- struct wlr_surface *(*wlr_surface_at)(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y); +-}; +- +-struct cg_view_child { +- struct cg_view *view; +- struct wlr_surface *wlr_surface; +- struct wl_list link; +- +- struct wl_listener commit; +- struct wl_listener new_subsurface; +- +- void (*destroy)(struct cg_view_child *child); +-}; +- +-struct cg_subsurface { +- struct cg_view_child view_child; +- struct wlr_subsurface *wlr_subsurface; +- +- struct wl_listener destroy; + }; + + char *view_get_title(struct cg_view *view); + bool view_is_primary(struct cg_view *view); + bool view_is_transient_for(struct cg_view *child, struct cg_view *parent); +-void view_damage_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_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data); +-void view_for_each_popup_surface(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 cg_server *server, struct wlr_surface *surface); +-struct wlr_surface *view_wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y); +- +-void view_child_finish(struct cg_view_child *child); +-void view_child_init(struct cg_view_child *child, struct cg_view *view, struct wlr_surface *wlr_surface); ++struct cg_view *view_from_wlr_surface(struct wlr_surface *surface); + + #endif +diff --git a/xdg_shell.c b/xdg_shell.c +index 2e42347..c577dc5 100644 +--- a/xdg_shell.c ++++ b/xdg_shell.c +@@ -6,10 +6,11 @@ + * See the LICENSE file accompanying this file. + */ + ++#include + #include + #include + #include +-#include ++#include + #include + #include + +@@ -41,105 +42,47 @@ xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data) + wlr_xdg_toplevel_decoration_v1_set_mode(xdg_decoration->wlr_decoration, mode); + } + +-static void +-xdg_popup_destroy(struct cg_view_child *child) +-{ +- 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) ++static struct cg_view * ++popup_get_view(struct wlr_xdg_popup *popup) + { +- 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); ++ while (true) { ++ if (popup->parent == NULL || !wlr_surface_is_xdg_surface(popup->parent)) { ++ return NULL; ++ } + +-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); ++ struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_from_wlr_surface(popup->parent); ++ 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 cg_xdg_popup *popup) ++popup_unconstrain(struct cg_view *view, struct wlr_xdg_popup *popup) + { +- struct cg_view *view = popup->view_child.view; + struct cg_server *server = view->server; +- struct wlr_box *popup_box = &popup->wlr_popup->geometry; ++ struct wlr_box *popup_box = &popup->current.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); ++ struct wlr_box output_box; ++ wlr_output_layout_get_box(output_layout, wlr_output, &output_box); + + struct wlr_box output_toplevel_box = { +- .x = output_box->x - view->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->wlr_popup, &output_toplevel_box); +-} +- +-static void +-xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup) +-{ +- struct cg_xdg_popup *popup = calloc(1, sizeof(struct cg_xdg_popup)); +- if (!popup) { +- return; +- } +- +- popup->wlr_popup = wlr_popup; +- view_child_init(&popup->view_child, view, wlr_popup->base->surface); +- popup->view_child.destroy = xdg_popup_destroy; +- popup->destroy.notify = handle_xdg_popup_destroy; +- wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); +- popup->map.notify = handle_xdg_popup_map; +- wl_signal_add(&wlr_popup->base->events.map, &popup->map); +- popup->unmap.notify = handle_xdg_popup_unmap; +- wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); +- popup->new_popup.notify = popup_handle_new_xdg_popup; +- wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); +- +- popup_unconstrain(popup); +-} +- +-static void +-handle_new_xdg_popup(struct wl_listener *listener, void *data) +-{ +- struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup); +- struct wlr_xdg_popup *wlr_popup = data; +- xdg_popup_create(&xdg_shell_view->view, wlr_popup); ++ wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box); + } + + static struct cg_xdg_shell_view * +@@ -152,7 +95,7 @@ static char * + get_title(struct cg_view *view) + { + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); +- return xdg_shell_view->xdg_surface->toplevel->title; ++ return xdg_shell_view->xdg_toplevel->title; + } + + static void +@@ -161,7 +104,7 @@ get_geometry(struct cg_view *view, int *width_out, int *height_out) + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + struct wlr_box geom; + +- wlr_xdg_surface_get_geometry(xdg_shell_view->xdg_surface, &geom); ++ wlr_xdg_surface_get_geometry(xdg_shell_view->xdg_toplevel->base, &geom); + *width_out = geom.width; + *height_out = geom.height; + } +@@ -170,9 +113,9 @@ static bool + is_primary(struct cg_view *view) + { + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); +- struct wlr_xdg_surface *parent = xdg_shell_view->xdg_surface->toplevel->parent; +- /* FIXME: role is 0? */ +- return parent == NULL; /*&& role == WLR_XDG_SURFACE_ROLE_TOPLEVEL */ ++ struct wlr_xdg_toplevel *parent = xdg_shell_view->xdg_toplevel->parent; ++ ++ return parent == NULL; + } + + static bool +@@ -182,14 +125,13 @@ is_transient_for(struct cg_view *child, struct cg_view *parent) + return false; + } + struct cg_xdg_shell_view *_child = xdg_shell_view_from_view(child); +- struct wlr_xdg_surface *xdg_surface = _child->xdg_surface; ++ struct wlr_xdg_toplevel *xdg_toplevel = _child->xdg_toplevel; + struct cg_xdg_shell_view *_parent = xdg_shell_view_from_view(parent); +- struct wlr_xdg_surface *parent_xdg_surface = _parent->xdg_surface; +- while (xdg_surface) { +- if (xdg_surface->toplevel->parent == parent_xdg_surface) { ++ while (xdg_toplevel) { ++ if (xdg_toplevel->parent == _parent->xdg_toplevel) { + return true; + } +- xdg_surface = xdg_surface->toplevel->parent; ++ xdg_toplevel = xdg_toplevel->parent; + } + return false; + } +@@ -198,15 +140,15 @@ static void + activate(struct cg_view *view, bool activate) + { + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); +- wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_surface, activate); ++ wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_toplevel, activate); + } + + static void + maximize(struct cg_view *view, int output_width, int output_height) + { + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); +- wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_surface, output_width, output_height); +- wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_surface, true); ++ wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_toplevel, output_width, output_height); ++ wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_toplevel, true); + } + + static void +@@ -216,41 +158,21 @@ destroy(struct cg_view *view) + free(xdg_shell_view); + } + +-static void +-for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data) +-{ +- struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); +- wlr_xdg_surface_for_each_surface(xdg_shell_view->xdg_surface, iterator, data); +-} +- +-static void +-for_each_popup_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data) +-{ +- struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); +- wlr_xdg_surface_for_each_popup_surface(xdg_shell_view->xdg_surface, iterator, data); +-} +- +-static struct wlr_surface * +-wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y) +-{ +- struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); +- return wlr_xdg_surface_surface_at(xdg_shell_view->xdg_surface, sx, sy, sub_x, sub_y); +-} +- + static void + handle_xdg_shell_surface_request_fullscreen(struct wl_listener *listener, void *data) + { + 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); ++ /** ++ * Certain clients do not like figuring out their own window geometry if they ++ * display in fullscreen mode, so we set it here. ++ */ ++ struct wlr_box layout_box; ++ wlr_output_layout_get_box(xdg_shell_view->view.server->output_layout, NULL, &layout_box); ++ wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_toplevel, layout_box.width, layout_box.height); ++ ++ wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_toplevel, ++ xdg_shell_view->xdg_toplevel->requested.fullscreen); + } + + static void +@@ -259,10 +181,6 @@ 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); + } + +@@ -272,12 +190,7 @@ 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; + +- xdg_shell_view->commit.notify = handle_xdg_shell_surface_commit; +- wl_signal_add(&xdg_shell_view->xdg_surface->surface->events.commit, &xdg_shell_view->commit); +- +- view_map(view, xdg_shell_view->xdg_surface->surface); +- +- view_damage_whole(view); ++ view_map(view, xdg_shell_view->xdg_toplevel->base->surface); + } + + static void +@@ -290,8 +203,7 @@ handle_xdg_shell_surface_destroy(struct wl_listener *listener, void *data) + wl_list_remove(&xdg_shell_view->unmap.link); + wl_list_remove(&xdg_shell_view->destroy.link); + wl_list_remove(&xdg_shell_view->request_fullscreen.link); +- wl_list_remove(&xdg_shell_view->new_popup.link); +- xdg_shell_view->xdg_surface = NULL; ++ xdg_shell_view->xdg_toplevel = NULL; + + view_destroy(view); + } +@@ -304,9 +216,6 @@ 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_surface = for_each_popup_surface, +- .wlr_surface_at = wlr_surface_at, + }; + + void +@@ -315,29 +224,64 @@ handle_xdg_shell_surface_new(struct wl_listener *listener, void *data) + struct cg_server *server = wl_container_of(listener, server, new_xdg_shell_surface); + struct wlr_xdg_surface *xdg_surface = data; + +- if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { +- return; +- } ++ switch (xdg_surface->role) { ++ case WLR_XDG_SURFACE_ROLE_TOPLEVEL:; ++ struct cg_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct cg_xdg_shell_view)); ++ if (!xdg_shell_view) { ++ wlr_log(WLR_ERROR, "Failed to allocate XDG Shell view"); ++ return; ++ } + +- struct cg_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct cg_xdg_shell_view)); +- if (!xdg_shell_view) { +- wlr_log(WLR_ERROR, "Failed to allocate XDG Shell view"); +- return; +- } ++ view_init(&xdg_shell_view->view, server, CAGE_XDG_SHELL_VIEW, &xdg_shell_view_impl); ++ xdg_shell_view->xdg_toplevel = xdg_surface->toplevel; ++ ++ xdg_shell_view->map.notify = handle_xdg_shell_surface_map; ++ wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); ++ xdg_shell_view->unmap.notify = handle_xdg_shell_surface_unmap; ++ wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); ++ xdg_shell_view->destroy.notify = handle_xdg_shell_surface_destroy; ++ wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); ++ xdg_shell_view->request_fullscreen.notify = handle_xdg_shell_surface_request_fullscreen; ++ wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen); ++ ++ xdg_surface->data = xdg_shell_view; ++ break; ++ case WLR_XDG_SURFACE_ROLE_POPUP:; ++ struct wlr_xdg_popup *popup = xdg_surface->popup; ++ struct cg_view *view = popup_get_view(popup); ++ if (view == NULL) { ++ return; ++ } ++ ++ struct wlr_scene_tree *parent_scene_tree = NULL; ++ struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface(popup->parent); ++ 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; ++ } + +- view_init(&xdg_shell_view->view, server, CAGE_XDG_SHELL_VIEW, &xdg_shell_view_impl); +- xdg_shell_view->xdg_surface = xdg_surface; +- +- xdg_shell_view->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); ++ struct wlr_scene_tree *popup_scene_tree = wlr_scene_xdg_surface_create(parent_scene_tree, xdg_surface); ++ if (popup_scene_tree == NULL) { ++ wlr_log(WLR_ERROR, "Failed to allocate scene-graph node for XDG popup"); ++ return; ++ } ++ ++ popup_unconstrain(view, popup); ++ ++ xdg_surface->data = popup_scene_tree; ++ break; ++ case WLR_XDG_SURFACE_ROLE_NONE: ++ assert(false); // unreachable ++ } + } + + void +diff --git a/xdg_shell.h b/xdg_shell.h +index 45d87db..2fd506a 100644 +--- a/xdg_shell.h ++++ b/xdg_shell.h +@@ -9,24 +9,12 @@ + + struct cg_xdg_shell_view { + struct cg_view view; +- struct wlr_xdg_surface *xdg_surface; ++ struct wlr_xdg_toplevel *xdg_toplevel; + + struct wl_listener destroy; + struct wl_listener unmap; + struct wl_listener map; +- struct wl_listener commit; + struct wl_listener request_fullscreen; +- struct wl_listener new_popup; +-}; +- +-struct cg_xdg_popup { +- struct cg_view_child view_child; +- struct wlr_xdg_popup *wlr_popup; +- +- struct wl_listener destroy; +- struct wl_listener map; +- struct wl_listener unmap; +- struct wl_listener new_popup; + }; + + struct cg_xdg_decoration { +diff --git a/xwayland.c b/xwayland.c +index 2aae0f9..ef37a49 100644 +--- a/xwayland.c ++++ b/xwayland.c +@@ -9,7 +9,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -96,18 +95,6 @@ destroy(struct cg_view *view) + free(xwayland_view); + } + +-static void +-for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data) +-{ +- wlr_surface_for_each_surface(view->wlr_surface, iterator, data); +-} +- +-static struct wlr_surface * +-wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y) +-{ +- return wlr_surface_surface_at(view->wlr_surface, sx, sy, sub_x, sub_y); +-} +- + static void + handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *data) + { +@@ -116,24 +103,12 @@ handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *d + wlr_xwayland_surface_set_fullscreen(xwayland_view->xwayland_surface, xwayland_surface->fullscreen); + } + +-static void +-handle_xwayland_surface_commit(struct wl_listener *listener, void *data) +-{ +- struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, commit); +- struct cg_view *view = &xwayland_view->view; +- view_damage_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); + } + +@@ -148,12 +123,7 @@ 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 +@@ -179,10 +149,6 @@ 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_surface = NULL, +- .wlr_surface_at = wlr_surface_at, + }; + + void +diff --git a/xwayland.h b/xwayland.h +index d257f57..31edb8f 100644 +--- a/xwayland.h ++++ b/xwayland.h +@@ -12,7 +12,6 @@ struct cg_xwayland_view { + struct wl_listener destroy; + struct wl_listener unmap; + struct wl_listener map; +- struct wl_listener commit; + struct wl_listener request_fullscreen; + }; + diff --git a/clonezilla-unstable/.SRCINFO b/clonezilla-unstable/.SRCINFO index d9dba78..a930449 100644 --- a/clonezilla-unstable/.SRCINFO +++ b/clonezilla-unstable/.SRCINFO @@ -1,6 +1,6 @@ pkgbase = clonezilla-unstable pkgdesc = ncurses partition and disk imaging/cloning program - pkgver = 5.6.22 + pkgver = 5.6.21 pkgrel = 1 url = https://clonezilla.org arch = any @@ -29,9 +29,9 @@ pkgbase = clonezilla-unstable provides = clonezilla conflicts = clonezilla backup = etc/drbl/drbl-ocs.conf - source = https://free.nchc.org.tw/drbl-core/src/unstable/clonezilla-5.6.22.tar.xz + source = https://free.nchc.org.tw/drbl-core/src/unstable/clonezilla-5.6.21.tar.xz source = usrbin.patch - sha512sums = e1b58bbde1cf6fe42b94e5788207b9e1fb88b48522a0bae90567dd60ff26167aadcab0dfde2a1fb84701e0409928f991c91f4c44070fa740425095efb25657cc + sha512sums = 3839a217e579e529783d4078124055c10e40ed7450d496a7be3aa951bb169c19f0fbe0aa88a92299ae47a4ba986c79bb1d9bee6843d4d436bedbdfaef2faf79d sha512sums = 39e63eaa18ffaed7fe9a2dde449c8b57d4a3dc313024213881bd620d0011671bb7f8a0346df7c247c2da94f8d82afb669057cb05dd9cefffafb680826544d6ac pkgname = clonezilla-unstable diff --git a/clonezilla-unstable/PKGBUILD b/clonezilla-unstable/PKGBUILD index df4f371..dd53299 100644 --- a/clonezilla-unstable/PKGBUILD +++ b/clonezilla-unstable/PKGBUILD @@ -2,7 +2,7 @@ pkgname=clonezilla-unstable _pkgname=clonezilla -pkgver=5.6.22 +pkgver=5.6.21 pkgrel=1 pkgdesc="ncurses partition and disk imaging/cloning program" arch=('any') @@ -16,7 +16,7 @@ conflicts=("${_pkgname}") provides=("${_pkgname}") source=("https://free.nchc.org.tw/drbl-core/src/unstable/${_pkgname}-${pkgver}.tar.xz" 'usrbin.patch') -sha512sums=('e1b58bbde1cf6fe42b94e5788207b9e1fb88b48522a0bae90567dd60ff26167aadcab0dfde2a1fb84701e0409928f991c91f4c44070fa740425095efb25657cc' +sha512sums=('3839a217e579e529783d4078124055c10e40ed7450d496a7be3aa951bb169c19f0fbe0aa88a92299ae47a4ba986c79bb1d9bee6843d4d436bedbdfaef2faf79d' '39e63eaa18ffaed7fe9a2dde449c8b57d4a3dc313024213881bd620d0011671bb7f8a0346df7c247c2da94f8d82afb669057cb05dd9cefffafb680826544d6ac') prepare() {