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..5e95347 --- /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 + + - 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.15.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 + - name: Fetch wlroots as a subproject + run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b 0.15.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 + - name: Fetch wlroots as a subproject + run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b 0.15.0 + - name: Run scan-build + run: | + meson build-scan-build -Dxwayland=true + ninja -C build-scan-build scan-build diff --git a/cage.c b/cage.c index 5392535..95f0cc7 100644 --- a/cage.c +++ b/cage.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,8 +28,11 @@ #include #include #include +#include +#include #include #include +#include #if CAGE_HAS_XWAYLAND #include #endif @@ -185,9 +189,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 +204,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,7 +253,6 @@ 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_data_device_manager *data_device_manager = NULL; struct wlr_server_decoration_manager *server_decoration_manager = NULL; @@ -270,6 +261,8 @@ main(int argc, char *argv[]) struct wlr_screencopy_manager_v1 *screencopy_manager = NULL; struct wlr_xdg_output_manager_v1 *output_manager = NULL; struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL; + struct wlr_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 +309,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,7 +335,16 @@ 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; @@ -401,6 +416,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"); diff --git a/meson.build b/meson.build index 3a84794..5a8b28c 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.15.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..c33bbe1 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 @@ -26,216 +27,20 @@ #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..4dce511 100644 --- a/seat.c +++ b/seat.c @@ -8,6 +8,7 @@ #include "config.h" +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -41,42 +43,31 @@ static void drag_icon_update_position(struct cg_drag_icon *drag_icon); * menus or tooltips. This function tests if any of those are underneath the * coordinates lx and ly (in output Layout Coordinates). If so, it sets the * surface pointer to that wlr_surface and the sx and sy coordinates to the - * coordinates relative to that surface's top-left corner. */ -static bool -view_at(struct cg_view *view, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) -{ - double view_sx = lx - view->lx; - double view_sy = ly - view->ly; - - double _sx, _sy; - struct wlr_surface *_surface = view_wlr_surface_at(view, view_sx, view_sy, &_sx, &_sy); - if (_surface != NULL) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return true; - } - - return false; -} - -/* This iterates over all of our surfaces and attempts to find one - * under the cursor. This relies on server->views being ordered from - * top-to-bottom. If desktop_view_at returns a view, there is also a - * surface. There cannot be a surface without a view, either. It's - * both or nothing. */ + * 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) { - struct cg_view *view; + struct wlr_scene_node *node = wlr_scene_node_at(&server->scene->node, lx, ly, sx, sy); + if (node == NULL || node->type != WLR_SCENE_NODE_SURFACE) { + return NULL; + } - wl_list_for_each (view, &server->views, link) { - if (view_at(view, lx, ly, surface, sx, sy)) { - return view; - } + *surface = wlr_scene_surface_from_node(node)->surface; + + /* Walk up the tree until we find a node with a data pointer. When done, + * we've found the node representing the view. */ + while (node != NULL && node->data == NULL) { + node = node->parent; } + assert(node != NULL); - return NULL; + return node->data; } static void @@ -351,17 +342,11 @@ handle_new_keyboard(struct cg_seat *seat, struct wlr_input_device *device) { struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) { - wlr_log(WLR_ERROR, "Unable to create 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); @@ -569,10 +554,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; @@ -605,15 +587,6 @@ handle_cursor_motion(struct wl_listener *listener, void *data) 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 +594,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 +611,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_node, drag_icon->lx, drag_icon->ly); } static void @@ -650,6 +621,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_node); free(drag_icon); } @@ -692,6 +664,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_node = wlr_scene_subsurface_tree_create(&seat->server->scene->node, wlr_drag_icon->surface); + if (!drag_icon->scene_node) { + free(drag_icon); + return; + } drag_icon->destroy.notify = handle_drag_icon_destroy; wl_signal_add(&wlr_drag_icon->events.destroy, &drag_icon->destroy); @@ -840,7 +817,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..5fb2db3 100644 --- a/seat.h +++ b/seat.h @@ -77,6 +77,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_node *scene_node; /* The drag icon has a position in layout coordinates. */ double lx, ly; diff --git a/server.h b/server.h index 817637b..bb7f3c1 100644 --- a/server.h +++ b/server.h @@ -25,6 +25,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 +36,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 @@ -48,9 +51,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..6551142 100644 --- a/view.c +++ b/view.c @@ -1,19 +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" @@ -24,96 +25,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 +47,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 +67,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_node, view->lx, view->ly); + view->impl->maximize(view, layout_box->width, layout_box->height); } @@ -185,6 +81,8 @@ 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_node, view->lx, view->ly); } void @@ -199,51 +97,29 @@ view_position(struct cg_view *view) } } -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->impl->for_each_popup_surface(view, iterator, data); -} - void 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_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_node = wlr_scene_subsurface_tree_create(&view->server->scene->node, surface); + if (!view->scene_node) { + wl_resource_post_no_memory(surface->resource); + return; } + view->scene_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..677a949 100644 --- a/view.h +++ b/view.h @@ -5,9 +5,9 @@ #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_node *scene_node; /* 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..ede71f4 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,60 +42,32 @@ 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) +static struct cg_view * +popup_get_view(struct wlr_xdg_popup *popup) { - struct cg_xdg_popup *popup = wl_container_of(listener, popup, map); - view_damage_whole(popup->view_child.view); -} - -static void -handle_xdg_popup_unmap(struct wl_listener *listener, void *data) -{ - struct cg_xdg_popup *popup = wl_container_of(listener, popup, unmap); - view_damage_whole(popup->view_child.view); -} - -static void -handle_xdg_popup_destroy(struct wl_listener *listener, void *data) -{ - struct cg_xdg_popup *popup = wl_container_of(listener, popup, destroy); - struct cg_view_child *view_child = (struct cg_view_child *) popup; - xdg_popup_destroy(view_child); -} - -static void xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup); + 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->geometry; struct wlr_output_layout *output_layout = server->output_layout; struct wlr_output *wlr_output = @@ -108,38 +81,7 @@ popup_unconstrain(struct cg_xdg_popup *popup) .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 * @@ -170,9 +112,12 @@ 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_surface *xdg_surface = xdg_shell_view->xdg_surface; + + struct wlr_xdg_surface *parent = xdg_surface->toplevel->parent; + enum wlr_xdg_surface_role role = xdg_surface->role; + + return parent == NULL && role == WLR_XDG_SURFACE_ROLE_TOPLEVEL; } static bool @@ -216,41 +161,20 @@ 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); + wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_surface, layout_box->width, layout_box->height); + + wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_surface, event->fullscreen); } static void @@ -259,10 +183,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 +192,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); } static void @@ -290,7 +205,6 @@ 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; view_destroy(view); @@ -304,9 +218,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 +226,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_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_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_node *parent_scene_node = 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_node = view->scene_node; + break; + case WLR_XDG_SURFACE_ROLE_POPUP: + parent_scene_node = parent->data; + break; + case WLR_XDG_SURFACE_ROLE_NONE: + break; + } + if (parent_scene_node == 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_node *popup_scene_node = wlr_scene_xdg_surface_create(parent_scene_node, xdg_surface); + if (popup_scene_node == NULL) { + wlr_log(WLR_ERROR, "Failed to allocate scene-graph node for XDG popup"); + return; + } + + popup_unconstrain(view, popup); + + xdg_surface->data = popup_scene_node; + break; + case WLR_XDG_SURFACE_ROLE_NONE: + assert(false); // unreachable + } } void diff --git a/xdg_shell.h b/xdg_shell.h index 45d87db..9a101c6 100644 --- a/xdg_shell.h +++ b/xdg_shell.h @@ -14,19 +14,7 @@ struct cg_xdg_shell_view { 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; };