From 9eaa07a4b141bc80a46cb7ab2dc94048f126fa8c Mon Sep 17 00:00:00 2001 From: William McKinnon Date: Tue, 11 Jul 2023 00:44:26 -0400 Subject: added more scene dependencies, added tinywl --- README.md | 2 + flake.lock | 44 + flake.nix | 53 + include/meson.build | 1 + include/types/wlr_buffer.h | 84 + include/types/wlr_scene.h | 8 + include/util/array.h | 18 + include/util/env.h | 11 + include/util/time.h | 27 + meson.build | 174 ++ meson_options.txt | 8 + protocol/drm.xml | 189 +++ protocol/idle.xml | 49 + protocol/input-method-unstable-v2.xml | 494 ++++++ protocol/meson.build | 92 ++ protocol/server-decoration.xml | 94 ++ protocol/virtual-keyboard-unstable-v1.xml | 113 ++ protocol/wlr-data-control-unstable-v1.xml | 278 ++++ protocol/wlr-export-dmabuf-unstable-v1.xml | 203 +++ ...wlr-foreign-toplevel-management-unstable-v1.xml | 270 +++ protocol/wlr-gamma-control-unstable-v1.xml | 126 ++ protocol/wlr-input-inhibitor-unstable-v1.xml | 67 + protocol/wlr-layer-shell-unstable-v1.xml | 390 +++++ protocol/wlr-output-management-unstable-v1.xml | 601 +++++++ .../wlr-output-power-management-unstable-v1.xml | 128 ++ protocol/wlr-screencopy-unstable-v1.xml | 232 +++ protocol/wlr-virtual-pointer-unstable-v1.xml | 152 ++ src/layer_shell_v1.c | 186 --- src/output_layout.c | 157 -- src/subsurface_tree.c | 259 --- src/surface.c | 211 --- src/wlr_scene.c | 1728 -------------------- src/xdg_shell.c | 124 -- tinywl/meson.build | 2 +- tinywl/tinywl.c | 3 +- types/meson.build | 8 + types/scene/layer_shell_v1.c | 186 +++ types/scene/output_layout.c | 157 ++ types/scene/subsurface_tree.c | 259 +++ types/scene/surface.c | 211 +++ types/scene/wlr_scene.c | 1728 ++++++++++++++++++++ types/scene/xdg_shell.c | 124 ++ util/array.c | 40 + util/env.c | 38 + util/meson.build | 5 + util/time.c | 32 + 46 files changed, 6699 insertions(+), 2667 deletions(-) create mode 100644 README.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 include/meson.build create mode 100644 include/types/wlr_buffer.h create mode 100644 include/types/wlr_scene.h create mode 100644 include/util/array.h create mode 100644 include/util/env.h create mode 100644 include/util/time.h create mode 100644 meson.build create mode 100644 meson_options.txt create mode 100644 protocol/drm.xml create mode 100644 protocol/idle.xml create mode 100644 protocol/input-method-unstable-v2.xml create mode 100644 protocol/meson.build create mode 100644 protocol/server-decoration.xml create mode 100644 protocol/virtual-keyboard-unstable-v1.xml create mode 100644 protocol/wlr-data-control-unstable-v1.xml create mode 100644 protocol/wlr-export-dmabuf-unstable-v1.xml create mode 100644 protocol/wlr-foreign-toplevel-management-unstable-v1.xml create mode 100644 protocol/wlr-gamma-control-unstable-v1.xml create mode 100644 protocol/wlr-input-inhibitor-unstable-v1.xml create mode 100644 protocol/wlr-layer-shell-unstable-v1.xml create mode 100644 protocol/wlr-output-management-unstable-v1.xml create mode 100644 protocol/wlr-output-power-management-unstable-v1.xml create mode 100644 protocol/wlr-screencopy-unstable-v1.xml create mode 100644 protocol/wlr-virtual-pointer-unstable-v1.xml delete mode 100644 src/layer_shell_v1.c delete mode 100644 src/output_layout.c delete mode 100644 src/subsurface_tree.c delete mode 100644 src/surface.c delete mode 100644 src/wlr_scene.c delete mode 100644 src/xdg_shell.c create mode 100644 types/meson.build create mode 100644 types/scene/layer_shell_v1.c create mode 100644 types/scene/output_layout.c create mode 100644 types/scene/subsurface_tree.c create mode 100644 types/scene/surface.c create mode 100644 types/scene/wlr_scene.c create mode 100644 types/scene/xdg_shell.c create mode 100644 util/array.c create mode 100644 util/env.c create mode 100644 util/meson.build create mode 100644 util/time.c diff --git a/README.md b/README.md new file mode 100644 index 0000000..b1ec848 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# scenefx + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..8a8e486 --- /dev/null +++ b/flake.lock @@ -0,0 +1,44 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1688981480, + "narHash": "sha256-AYgIAotBA5C+55PjXKck8cpDgWYrUYsTMpMxH1bZ7/M=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "b9ebd80c7dbcdec2240c5baae334365eaf3d7230", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..a6b50a6 --- /dev/null +++ b/flake.nix @@ -0,0 +1,53 @@ +{ + description = "scenefx development environment"; + + inputs = { + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { self, nixpkgs, flake-compat, ... }: + let + pkgsFor = system: + import nixpkgs { + inherit system; + overlays = [ ]; + }; + + targetSystems = [ "aarch64-linux" "x86_64-linux" ]; + in + { + devShells = nixpkgs.lib.genAttrs targetSystems (system: + let + pkgs = pkgsFor system; + in + { + default = pkgs.mkShell { + name = "scenefx-shell"; + depsBuildBuild = with pkgs; [ pkg-config ]; + inputsFrom = [ pkgs.wlroots_0_16 ]; + + nativeBuildInputs = with pkgs; [ + cmake + meson + ninja + pkg-config + wayland-scanner + scdoc + hwdata + ]; + +# shellHook = with pkgs; ''( +# mkdir -p "$PWD/subprojects" +# cd "$PWD/subprojects" +# cp -R --no-preserve=mode,ownership ${wlroots_0_16.src} wlroots +# )''; + }; + }); + }; +} + diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 0000000..4c388f0 --- /dev/null +++ b/include/meson.build @@ -0,0 +1 @@ +exclude_files = ['meson.build', 'config.h.in', 'version.h.in'] diff --git a/include/types/wlr_buffer.h b/include/types/wlr_buffer.h new file mode 100644 index 0000000..59d78e9 --- /dev/null +++ b/include/types/wlr_buffer.h @@ -0,0 +1,84 @@ +#ifndef TYPES_WLR_BUFFER +#define TYPES_WLR_BUFFER + +#include + +struct wlr_shm_client_buffer { + struct wlr_buffer base; + + uint32_t format; + size_t stride; + + // The following fields are NULL if the client has destroyed the wl_buffer + struct wl_resource *resource; + struct wl_shm_buffer *shm_buffer; + + // This is used to keep the backing storage alive after the client has + // destroyed the wl_buffer + struct wl_shm_pool *saved_shm_pool; + void *saved_data; + + struct wl_listener resource_destroy; + struct wl_listener release; +}; + +struct wlr_shm_client_buffer *shm_client_buffer_get_or_create( + struct wl_resource *resource); + +/** + * A read-only buffer that holds a data pointer. + * + * This is suitable for passing raw pixel data to a function that accepts a + * wlr_buffer. + */ +struct wlr_readonly_data_buffer { + struct wlr_buffer base; + + const void *data; + uint32_t format; + size_t stride; + + void *saved_data; +}; + +/** + * Wraps a read-only data pointer into a wlr_buffer. The data pointer may be + * accessed until readonly_data_buffer_drop() is called. + */ +struct wlr_readonly_data_buffer *readonly_data_buffer_create(uint32_t format, + size_t stride, uint32_t width, uint32_t height, const void *data); +/** + * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and + * perform a copy of the data pointer if a consumer still has the buffer locked. + */ +bool readonly_data_buffer_drop(struct wlr_readonly_data_buffer *buffer); + +struct wlr_dmabuf_buffer { + struct wlr_buffer base; + struct wlr_dmabuf_attributes dmabuf; + bool saved; +}; + +/** + * Wraps a DMA-BUF into a wlr_buffer. The DMA-BUF may be accessed until + * dmabuf_buffer_drop() is called. + */ +struct wlr_dmabuf_buffer *dmabuf_buffer_create( + struct wlr_dmabuf_attributes *dmabuf); +/** + * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and + * takes a reference to the DMA-BUF (by dup'ing its file descriptors) if a + * consumer still has the buffer locked. + */ +bool dmabuf_buffer_drop(struct wlr_dmabuf_buffer *buffer); + +/** + * Check whether a buffer is fully opaque. + * + * When true is returned, the buffer is guaranteed to be fully opaque, but the + * reverse is not true: false may be returned in cases where the buffer is fully + * opaque. + */ +bool buffer_is_opaque(struct wlr_buffer *buffer); + +#endif diff --git a/include/types/wlr_scene.h b/include/types/wlr_scene.h new file mode 100644 index 0000000..64c11bc --- /dev/null +++ b/include/types/wlr_scene.h @@ -0,0 +1,8 @@ +#ifndef TYPES_WLR_SCENE_H +#define TYPES_WLR_SCENE_H + +#include + +struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node); + +#endif diff --git a/include/util/array.h b/include/util/array.h new file mode 100644 index 0000000..a51bdb6 --- /dev/null +++ b/include/util/array.h @@ -0,0 +1,18 @@ +#ifndef UTIL_ARRAY_H +#define UTIL_ARRAY_H + +#include +#include +#include + +/** + * Remove a chunk of memory of the specified size at the specified offset. + */ +void array_remove_at(struct wl_array *arr, size_t offset, size_t size); + +/** + * Grow or shrink the array to fit the specifized size. + */ +bool array_realloc(struct wl_array *arr, size_t size); + +#endif diff --git a/include/util/env.h b/include/util/env.h new file mode 100644 index 0000000..6720fa8 --- /dev/null +++ b/include/util/env.h @@ -0,0 +1,11 @@ +#ifndef UTIL_ENV_H +#define UTIL_ENV_H + +#include +#include + +bool env_parse_bool(const char *option); + +ssize_t env_parse_switch(const char *option, const char **switches); + +#endif diff --git a/include/util/time.h b/include/util/time.h new file mode 100644 index 0000000..287698d --- /dev/null +++ b/include/util/time.h @@ -0,0 +1,27 @@ +#ifndef UTIL_TIME_H +#define UTIL_TIME_H + +#include + +/** + * Get the current time, in milliseconds. + */ +uint32_t get_current_time_msec(void); + +/** + * Convert a timespec to milliseconds. + */ +int64_t timespec_to_msec(const struct timespec *a); + +/** + * Convert nanoseconds to a timespec. + */ +void timespec_from_nsec(struct timespec *r, int64_t nsec); + +/** + * Subtracts timespec `b` from timespec `a`, and stores the difference in `r`. + */ +void timespec_sub(struct timespec *r, const struct timespec *a, + const struct timespec *b); + +#endif diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..d94b871 --- /dev/null +++ b/meson.build @@ -0,0 +1,174 @@ +project( + 'scenefx', + 'c', + version: '0.1', + license: 'MIT', + meson_version: '>=0.59.0', + default_options: [ + 'c_std=c11', + 'warning_level=2', + 'werror=true', + ], +) + +# When doing a major or minor release, *always* increase soversion. This isn't +# necessary for bugfix releases. Increasing soversion is required because +# wlroots never guarantees ABI stability -- only API stability is guaranteed +# between minor releases. +soversion = 1 + +little_endian = target_machine.endian() == 'little' +big_endian = target_machine.endian() == 'big' + +add_project_arguments([ + '-DWLR_USE_UNSTABLE', + '-DWLR_LITTLE_ENDIAN=@0@'.format(little_endian.to_int()), + '-DWLR_BIG_ENDIAN=@0@'.format(big_endian.to_int()), +], language: 'c') + +cc = meson.get_compiler('c') + +add_project_arguments(cc.get_supported_arguments([ + '-Wundef', + '-Wlogical-op', + '-Wmissing-include-dirs', + '-Wold-style-definition', + '-Wpointer-arith', + '-Winit-self', + '-Wstrict-prototypes', + '-Wimplicit-fallthrough=2', + '-Wendif-labels', + '-Wstrict-aliasing=2', + '-Woverflow', + '-Wmissing-prototypes', + '-Walloca', + + '-Wno-missing-braces', + '-Wno-missing-field-initializers', + '-Wno-unused-parameter', +]), language: 'c') + +# Compute the relative path used by compiler invocations. +source_root = meson.current_source_dir().split('/') +build_root = meson.global_build_root().split('/') +relative_dir_parts = [] +i = 0 +in_prefix = true +foreach p : build_root + if i >= source_root.length() or not in_prefix or p != source_root[i] + in_prefix = false + relative_dir_parts += '..' + endif + i += 1 +endforeach +i = 0 +in_prefix = true +foreach p : source_root + if i >= build_root.length() or not in_prefix or build_root[i] != p + in_prefix = false + relative_dir_parts += p + endif + i += 1 +endforeach +relative_dir = join_paths(relative_dir_parts) + '/' + +# Strip relative path prefixes from the code if possible, otherwise hide them. +if cc.has_argument('-fmacro-prefix-map=/prefix/to/hide=') + add_project_arguments( + '-fmacro-prefix-map=@0@='.format(relative_dir), + language: 'c', + ) +else + add_project_arguments( + '-DWLR_REL_SRC_DIR="@0@"'.format(relative_dir), + language: 'c', + ) +endif + +features = { + 'drm-backend': false, + 'x11-backend': false, + 'libinput-backend': false, + 'xwayland': false, + 'gles2-renderer': false, + 'vulkan-renderer': false, + 'gbm-allocator': false, +} +internal_features = { + 'xcb-errors': false, + 'egl': false, +} + +wayland_project_options = ['tests=false', 'documentation=false'] +wayland_server = dependency('wayland-server', + version: '>=1.21', + fallback: 'wayland', + default_options: wayland_project_options, +) + +drm = dependency('libdrm', + version: '>=2.4.113', + fallback: 'libdrm', + default_options: [ + 'intel=disabled', + 'radeon=disabled', + 'amdgpu=disabled', + 'nouveau=disabled', + 'vmwgfx=disabled', + 'omap=disabled', + 'exynos=disabled', + 'freedreno=disabled', + 'tegra=disabled', + 'vc4=disabled', + 'etnaviv=disabled', + 'cairo-tests=disabled', + 'man-pages=disabled', + 'valgrind=disabled', + 'tests=false', + ], +) +xkbcommon = dependency('xkbcommon') +udev = dependency('libudev') +pixman = dependency('pixman-1') +math = cc.find_library('m') +rt = cc.find_library('rt') + +wlr_files = [] +wlr_deps = [ + wayland_server, + drm, + xkbcommon, + udev, + pixman, + math, + rt, +] + +subdir('protocol') + +subdir('types') +subdir('util') + +subdir('include') + +foreach name, have : internal_features + add_project_arguments( + '-DHAS_@0@=@1@'.format(name.underscorify().to_upper(), have.to_int()), + language: 'c', + ) +endforeach + +symbols_file = 'wlroots.syms' +symbols_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), symbols_file) + +wlr_vars = {} +foreach name, have : features + wlr_vars += { 'have_' + name.underscorify(): have.to_string() } +endforeach + +summary(features + internal_features, bool_yn: true) + +if get_option('examples') + # TODO: subdir('examples') + subdir('tinywl') +endif diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..1a439a4 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,8 @@ +option('xcb-errors', type: 'feature', value: 'auto', description: 'Use xcb-errors util library') +option('xwayland', type: 'feature', value: 'auto', yield: true, description: 'Enable support for X11 applications') +option('examples', type: 'boolean', value: true, description: 'Build example applications') +option('icon_directory', description: 'Location used to look for cursors (default: ${datadir}/icons)', type: 'string', value: '') +option('renderers', type: 'array', choices: ['auto', 'gles2', 'vulkan'], value: ['auto'], description: 'Select built-in renderers') +option('backends', type: 'array', choices: ['auto', 'drm', 'libinput', 'x11'], value: ['auto'], description: 'Select built-in backends') +option('allocators', type: 'array', choices: ['auto', 'gbm'], value: ['auto'], + description: 'Select built-in allocators') diff --git a/protocol/drm.xml b/protocol/drm.xml new file mode 100644 index 0000000..eaf2654 --- /dev/null +++ b/protocol/drm.xml @@ -0,0 +1,189 @@ + + + + + Copyright © 2008-2011 Kristian Høgsberg + Copyright © 2010-2011 Intel Corporation + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that\n the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bitmask of capabilities. + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/protocol/idle.xml b/protocol/idle.xml new file mode 100644 index 0000000..92d9989 --- /dev/null +++ b/protocol/idle.xml @@ -0,0 +1,49 @@ + + + . + ]]> + + + This interface allows to monitor user idle time on a given seat. The interface + allows to register timers which trigger after no user activity was registered + on the seat for a given interval. It notifies when user activity resumes. + + This is useful for applications wanting to perform actions when the user is not + interacting with the system, e.g. chat applications setting the user as away, power + management features to dim screen, etc.. + + + + + + + + + + + + + + + + + + + + + + diff --git a/protocol/input-method-unstable-v2.xml b/protocol/input-method-unstable-v2.xml new file mode 100644 index 0000000..51bccf2 --- /dev/null +++ b/protocol/input-method-unstable-v2.xml @@ -0,0 +1,494 @@ + + + + + Copyright © 2008-2011 Kristian Høgsberg + Copyright © 2010-2011 Intel Corporation + Copyright © 2012-2013 Collabora, Ltd. + Copyright © 2012, 2013 Intel Corporation + Copyright © 2015, 2016 Jan Arne Petersen + Copyright © 2017, 2018 Red Hat, Inc. + Copyright © 2018 Purism SPC + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol allows applications to act as input methods for compositors. + + An input method context is used to manage the state of the input method. + + Text strings are UTF-8 encoded, their indices and lengths are in bytes. + + This document adheres to the RFC 2119 when using words like "must", + "should", "may", etc. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + An input method object allows for clients to compose text. + + The objects connects the client to a text input in an application, and + lets the client to serve as an input method for a seat. + + The zwp_input_method_v2 object can occupy two distinct states: active and + inactive. In the active state, the object is associated to and + communicates with a text input. In the inactive state, there is no + associated text input, and the only communication is with the compositor. + Initially, the input method is in the inactive state. + + Requests issued in the inactive state must be accepted by the compositor. + Because of the serial mechanism, and the state reset on activate event, + they will not have any effect on the state of the next text input. + + There must be no more than one input method object per seat. + + + + + + + + + Notification that a text input focused on this seat requested the input + method to be activated. + + This event serves the purpose of providing the compositor with an + active input method. + + This event resets all state associated with previous enable, disable, + surrounding_text, text_change_cause, and content_type events, as well + as the state associated with set_preedit_string, commit_string, and + delete_surrounding_text requests. In addition, it marks the + zwp_input_method_v2 object as active, and makes any existing + zwp_input_popup_surface_v2 objects visible. + + The surrounding_text, and content_type events must follow before the + next done event if the text input supports the respective + functionality. + + State set with this event is double-buffered. It will get applied on + the next zwp_input_method_v2.done event, and stay valid until changed. + + + + + + Notification that no focused text input currently needs an active + input method on this seat. + + This event marks the zwp_input_method_v2 object as inactive. The + compositor must make all existing zwp_input_popup_surface_v2 objects + invisible until the next activate event. + + State set with this event is double-buffered. It will get applied on + the next zwp_input_method_v2.done event, and stay valid until changed. + + + + + + Updates the surrounding plain text around the cursor, excluding the + preedit text. + + If any preedit text is present, it is replaced with the cursor for the + purpose of this event. + + The argument text is a buffer containing the preedit string, and must + include the cursor position, and the complete selection. It should + contain additional characters before and after these. There is a + maximum length of wayland messages, so text can not be longer than 4000 + bytes. + + cursor is the byte offset of the cursor within the text buffer. + + anchor is the byte offset of the selection anchor within the text + buffer. If there is no selected text, anchor must be the same as + cursor. + + If this event does not arrive before the first done event, the input + method may assume that the text input does not support this + functionality and ignore following surrounding_text events. + + Values set with this event are double-buffered. They will get applied + and set to initial values on the next zwp_input_method_v2.done + event. + + The initial state for affected fields is empty, meaning that the text + input does not support sending surrounding text. If the empty values + get applied, subsequent attempts to change them may have no effect. + + + + + + + + + Tells the input method why the text surrounding the cursor changed. + + Whenever the client detects an external change in text, cursor, or + anchor position, it must issue this request to the compositor. This + request is intended to give the input method a chance to update the + preedit text in an appropriate way, e.g. by removing it when the user + starts typing with a keyboard. + + cause describes the source of the change. + + The value set with this event is double-buffered. It will get applied + and set to its initial value on the next zwp_input_method_v2.done + event. + + The initial value of cause is input_method. + + + + + + + Indicates the content type and hint for the current + zwp_input_method_v2 instance. + + Values set with this event are double-buffered. They will get applied + on the next zwp_input_method_v2.done event. + + The initial value for hint is none, and the initial value for purpose + is normal. + + + + + + + + Atomically applies state changes recently sent to the client. + + The done event establishes and updates the state of the client, and + must be issued after any changes to apply them. + + Text input state (content purpose, content hint, surrounding text, and + change cause) is conceptually double-buffered within an input method + context. + + Events modify the pending state, as opposed to the current state in use + by the input method. A done event atomically applies all pending state, + replacing the current state. After done, the new pending state is as + documented for each related request. + + Events must be applied in the order of arrival. + + Neither current nor pending state are modified unless noted otherwise. + + + + + + Send the commit string text for insertion to the application. + + Inserts a string at current cursor position (see commit event + sequence). The string to commit could be either just a single character + after a key press or the result of some composing. + + The argument text is a buffer containing the string to insert. There is + a maximum length of wayland messages, so text can not be longer than + 4000 bytes. + + Values set with this event are double-buffered. They must be applied + and reset to initial on the next zwp_text_input_v3.commit request. + + The initial value of text is an empty string. + + + + + + + Send the pre-edit string text to the application text input. + + Place a new composing text (pre-edit) at the current cursor position. + Any previously set composing text must be removed. Any previously + existing selected text must be removed. The cursor is moved to a new + position within the preedit string. + + The argument text is a buffer containing the preedit string. There is + a maximum length of wayland messages, so text can not be longer than + 4000 bytes. + + The arguments cursor_begin and cursor_end are counted in bytes relative + to the beginning of the submitted string buffer. Cursor should be + hidden by the text input when both are equal to -1. + + cursor_begin indicates the beginning of the cursor. cursor_end + indicates the end of the cursor. It may be equal or different than + cursor_begin. + + Values set with this event are double-buffered. They must be applied on + the next zwp_input_method_v2.commit event. + + The initial value of text is an empty string. The initial value of + cursor_begin, and cursor_end are both 0. + + + + + + + + + Remove the surrounding text. + + before_length and after_length are the number of bytes before and after + the current cursor index (excluding the preedit text) to delete. + + If any preedit text is present, it is replaced with the cursor for the + purpose of this event. In effect before_length is counted from the + beginning of preedit text, and after_length from its end (see commit + event sequence). + + Values set with this event are double-buffered. They must be applied + and reset to initial on the next zwp_input_method_v2.commit request. + + The initial values of both before_length and after_length are 0. + + + + + + + + Apply state changes from commit_string, set_preedit_string and + delete_surrounding_text requests. + + The state relating to these events is double-buffered, and each one + modifies the pending state. This request replaces the current state + with the pending state. + + The connected text input is expected to proceed by evaluating the + changes in the following order: + + 1. Replace existing preedit string with the cursor. + 2. Delete requested surrounding text. + 3. Insert commit string with the cursor at its end. + 4. Calculate surrounding text to send. + 5. Insert new preedit text in cursor position. + 6. Place cursor inside preedit text. + + The serial number reflects the last state of the zwp_input_method_v2 + object known to the client. The value of the serial argument must be + equal to the number of done events already issued by that object. When + the compositor receives a commit request with a serial different than + the number of past done events, it must proceed as normal, except it + should not change the current state of the zwp_input_method_v2 object. + + + + + + + Creates a new zwp_input_popup_surface_v2 object wrapping a given + surface. + + The surface gets assigned the "input_popup" role. If the surface + already has an assigned role, the compositor must issue a protocol + error. + + + + + + + + Allow an input method to receive hardware keyboard input and process + key events to generate text events (with pre-edit) over the wire. This + allows input methods which compose multiple key events for inputting + text like it is done for CJK languages. + + The compositor should send all keyboard events on the seat to the grab + holder via the returned wl_keyboard object. Nevertheless, the + compositor may decide not to forward any particular event. The + compositor must not further process any event after it has been + forwarded to the grab holder. + + Releasing the resulting wl_keyboard object releases the grab. + + + + + + + The input method ceased to be available. + + The compositor must issue this event as the only event on the object if + there was another input_method object associated with the same seat at + the time of its creation. + + The compositor must issue this request when the object is no longer + usable, e.g. due to seat removal. + + The input method context becomes inert and should be destroyed after + deactivation is handled. Any further requests and events except for the + destroy request must be ignored. + + + + + + Destroys the zwp_text_input_v2 object and any associated child + objects, i.e. zwp_input_popup_surface_v2 and + zwp_input_method_keyboard_grab_v2. + + + + + + + This interface marks a surface as a popup for interacting with an input + method. + + The compositor should place it near the active text input area. It must + be visible if and only if the input method is in the active state. + + The client must not destroy the underlying wl_surface while the + zwp_input_popup_surface_v2 object exists. + + + + + Notify about the position of the area of the text input expressed as a + rectangle in surface local coordinates. + + This is a hint to the input method telling it the relative position of + the text being entered. + + + + + + + + + + + + + + The zwp_input_method_keyboard_grab_v2 interface represents an exclusive + grab of the wl_keyboard interface associated with the seat. + + + + + This event provides a file descriptor to the client which can be + memory-mapped to provide a keyboard mapping description. + + + + + + + + + A key was pressed or released. + The time argument is a timestamp with millisecond granularity, with an + undefined base. + + + + + + + + + + Notifies clients that the modifier and/or group state has changed, and + it should update its local state. + + + + + + + + + + + + + + + Informs the client about the keyboard's repeat rate and delay. + + This event is sent as soon as the zwp_input_method_keyboard_grab_v2 + object has been created, and is guaranteed to be received by the + client before any key press event. + + Negative values for either rate or delay are illegal. A rate of zero + will disable any repeating (regardless of the value of delay). + + This event can be sent later on as well with a new value if necessary, + so clients should continue listening for the event past the creation + of zwp_input_method_keyboard_grab_v2. + + + + + + + + + The input method manager allows the client to become the input method on + a chosen seat. + + No more than one input method must be associated with any seat at any + given time. + + + + + Request a new input zwp_input_method_v2 object associated with a given + seat. + + + + + + + + Destroys the zwp_input_method_manager_v2 object. + + The zwp_input_method_v2 objects originating from it remain valid. + + + + diff --git a/protocol/meson.build b/protocol/meson.build new file mode 100644 index 0000000..4d56bbf --- /dev/null +++ b/protocol/meson.build @@ -0,0 +1,92 @@ +wayland_protos = dependency('wayland-protocols', + version: '>=1.27', + fallback: 'wayland-protocols', + default_options: ['tests=false'], +) +wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') + +wayland_scanner_dep = dependency('wayland-scanner', native: true) +wayland_scanner = find_program( + wayland_scanner_dep.get_variable('wayland_scanner'), + native: true, +) + +protocols = { + # Stable upstream protocols + 'presentation-time': wl_protocol_dir / 'stable/presentation-time/presentation-time.xml', + 'viewporter': wl_protocol_dir / 'stable/viewporter/viewporter.xml', + 'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', + + # Staging upstream protocols + 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', + 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml', + 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', + 'single-pixel-buffer-v1': wl_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml', + 'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml', + + # Unstable upstream protocols + 'fullscreen-shell-unstable-v1': wl_protocol_dir / 'unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml', + 'idle-inhibit-unstable-v1': wl_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml', + 'keyboard-shortcuts-inhibit-unstable-v1': wl_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml', + 'linux-dmabuf-unstable-v1': wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', + 'pointer-constraints-unstable-v1': wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', + 'pointer-gestures-unstable-v1': wl_protocol_dir / 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml', + 'primary-selection-unstable-v1': wl_protocol_dir / 'unstable/primary-selection/primary-selection-unstable-v1.xml', + 'relative-pointer-unstable-v1': wl_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml', + 'tablet-unstable-v2': wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml', + 'text-input-unstable-v3': wl_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml', + 'xdg-decoration-unstable-v1': wl_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml', + 'xdg-foreign-unstable-v1': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v1.xml', + 'xdg-foreign-unstable-v2': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v2.xml', + 'xdg-output-unstable-v1': wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', + + # Other protocols + 'drm': 'drm.xml', + 'input-method-unstable-v2': 'input-method-unstable-v2.xml', + 'kde-idle': 'idle.xml', + 'kde-server-decoration': 'server-decoration.xml', + 'virtual-keyboard-unstable-v1': 'virtual-keyboard-unstable-v1.xml', + 'wlr-data-control-unstable-v1': 'wlr-data-control-unstable-v1.xml', + 'wlr-export-dmabuf-unstable-v1': 'wlr-export-dmabuf-unstable-v1.xml', + 'wlr-foreign-toplevel-management-unstable-v1': 'wlr-foreign-toplevel-management-unstable-v1.xml', + 'wlr-gamma-control-unstable-v1': 'wlr-gamma-control-unstable-v1.xml', + 'wlr-input-inhibitor-unstable-v1': 'wlr-input-inhibitor-unstable-v1.xml', + 'wlr-layer-shell-unstable-v1': 'wlr-layer-shell-unstable-v1.xml', + 'wlr-output-management-unstable-v1': 'wlr-output-management-unstable-v1.xml', + 'wlr-output-power-management-unstable-v1': 'wlr-output-power-management-unstable-v1.xml', + 'wlr-screencopy-unstable-v1': 'wlr-screencopy-unstable-v1.xml', + 'wlr-virtual-pointer-unstable-v1': 'wlr-virtual-pointer-unstable-v1.xml', +} + +protocols_code = {} +protocols_server_header = {} +protocols_client_header = {} +foreach name, path : protocols + code = custom_target( + name.underscorify() + '_c', + input: path, + output: '@BASENAME@-protocol.c', + command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], + ) + wlr_files += code + + server_header = custom_target( + name.underscorify() + '_server_h', + input: path, + output: '@BASENAME@-protocol.h', + command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], + ) + wlr_files += server_header + + client_header = custom_target( + name.underscorify() + '_client_h', + input: path, + output: '@BASENAME@-client-protocol.h', + command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], + build_by_default: false, + ) + + protocols_code += { name: code } + protocols_server_header += { name: server_header } + protocols_client_header += { name: client_header } +endforeach diff --git a/protocol/server-decoration.xml b/protocol/server-decoration.xml new file mode 100644 index 0000000..45f1128 --- /dev/null +++ b/protocol/server-decoration.xml @@ -0,0 +1,94 @@ + + + . + ]]> + + + This interface allows to coordinate whether the server should create + a server-side window decoration around a wl_surface representing a + shell surface (wl_shell_surface or similar). By announcing support + for this interface the server indicates that it supports server + side decorations. + + + + When a client creates a server-side decoration object it indicates + that it supports the protocol. The client is supposed to tell the + server whether it wants server-side decorations or will provide + client-side decorations. + + If the client does not create a server-side decoration object for + a surface the server interprets this as lack of support for this + protocol and considers it as client-side decorated. Nevertheless a + client-side decorated surface should use this protocol to indicate + to the server that it does not want a server-side deco. + + + + + + + + + + + + + This event is emitted directly after binding the interface. It contains + the default mode for the decoration. When a new server decoration object + is created this new object will be in the default mode until the first + request_mode is requested. + + The server may change the default mode at any time. + + + + + + + + + + + + + + + + + + + + + This event is emitted directly after the decoration is created and + represents the base decoration policy by the server. E.g. a server + which wants all surfaces to be client-side decorated will send Client, + a server which wants server-side decoration will send Server. + + The client can request a different mode through the decoration request. + The server will acknowledge this by another event with the same mode. So + even if a server prefers server-side decoration it's possible to force a + client-side decoration. + + The server may emit this event at any time. In this case the client can + again request a different mode. It's the responsibility of the server to + prevent a feedback loop. + + + + + diff --git a/protocol/virtual-keyboard-unstable-v1.xml b/protocol/virtual-keyboard-unstable-v1.xml new file mode 100644 index 0000000..5095c91 --- /dev/null +++ b/protocol/virtual-keyboard-unstable-v1.xml @@ -0,0 +1,113 @@ + + + + Copyright © 2008-2011 Kristian Høgsberg + Copyright © 2010-2013 Intel Corporation + Copyright © 2012-2013 Collabora, Ltd. + Copyright © 2018 Purism SPC + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + The virtual keyboard provides an application with requests which emulate + the behaviour of a physical keyboard. + + This interface can be used by clients on its own to provide raw input + events, or it can accompany the input method protocol. + + + + + Provide a file descriptor to the compositor which can be + memory-mapped to provide a keyboard mapping description. + + Format carries a value from the keymap_format enumeration. + + + + + + + + + + + + + A key was pressed or released. + The time argument is a timestamp with millisecond granularity, with an + undefined base. All requests regarding a single object must share the + same clock. + + Keymap must be set before issuing this request. + + State carries a value from the key_state enumeration. + + + + + + + + + Notifies the compositor that the modifier and/or group state has + changed, and it should update state. + + The client should use wl_keyboard.modifiers event to synchronize its + internal state with seat state. + + Keymap must be set before issuing this request. + + + + + + + + + + + + + + + A virtual keyboard manager allows an application to provide keyboard + input events as if they came from a physical keyboard. + + + + + + + + + Creates a new virtual keyboard associated to a seat. + + If the compositor enables a keyboard to perform arbitrary actions, it + should present an error when an untrusted client requests a new + keyboard. + + + + + + diff --git a/protocol/wlr-data-control-unstable-v1.xml b/protocol/wlr-data-control-unstable-v1.xml new file mode 100644 index 0000000..75e8671 --- /dev/null +++ b/protocol/wlr-data-control-unstable-v1.xml @@ -0,0 +1,278 @@ + + + + Copyright © 2018 Simon Ser + Copyright © 2019 Ivan Molodetskikh + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + This protocol allows a privileged client to control data devices. In + particular, the client will be able to manage the current selection and take + the role of a clipboard manager. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows creating per-seat data device + controls. + + + + + Create a new data source. + + + + + + + Create a data device that can be used to manage a seat's selection. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This interface allows a client to manage a seat's selection. + + When the seat is destroyed, this object becomes inert. + + + + + This request asks the compositor to set the selection to the data from + the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the selection, set the source to NULL. + + + + + + + Destroys the data device object. + + + + + + The data_offer event introduces a new wlr_data_control_offer object, + which will subsequently be used in either the + wlr_data_control_device.selection event (for the regular clipboard + selections) or the wlr_data_control_device.primary_selection event (for + the primary clipboard selections). Immediately following the + wlr_data_control_device.data_offer event, the new data_offer object + will send out wlr_data_control_offer.offer events to describe the MIME + types it offers. + + + + + + + The selection event is sent out to notify the client of a new + wlr_data_control_offer for the selection for this device. The + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The selection event is sent to a client when a new + selection is set. The wlr_data_control_offer is valid until a new + wlr_data_control_offer or NULL is received. The client must destroy the + previous selection wlr_data_control_offer, if any, upon receiving this + event. + + The first selection event is sent upon binding the + wlr_data_control_device object. + + + + + + + This data control object is no longer valid and should be destroyed by + the client. + + + + + + + + The primary_selection event is sent out to notify the client of a new + wlr_data_control_offer for the primary selection for this device. The + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The primary_selection event is sent to a client when a + new primary selection is set. The wlr_data_control_offer is valid until + a new wlr_data_control_offer or NULL is received. The client must + destroy the previous primary selection wlr_data_control_offer, if any, + upon receiving this event. + + If the compositor supports primary selection, the first + primary_selection event is sent upon binding the + wlr_data_control_device object. + + + + + + + This request asks the compositor to set the primary selection to the + data from the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the primary selection, set the source to NULL. + + The compositor will ignore this request if it does not support primary + selection. + + + + + + + + + + + + The wlr_data_control_source object is the source side of a + wlr_data_control_offer. It is created by the source client in a data + transfer and provides a way to describe the offered data and a way to + respond to requests to transfer the data. + + + + + + + + + This request adds a MIME type to the set of MIME types advertised to + targets. Can be called several times to offer multiple types. + + Calling this after wlr_data_control_device.set_selection is a protocol + error. + + + + + + + Destroys the data source object. + + + + + + Request for data from the client. Send the data as the specified MIME + type over the passed file descriptor, then close it. + + + + + + + + This data source is no longer valid. The data source has been replaced + by another data source. + + The client should clean up and destroy this data source. + + + + + + + A wlr_data_control_offer represents a piece of data offered for transfer + by another client (the source client). The offer describes the different + MIME types that the data can be converted to and provides the mechanism + for transferring the data directly from the source client. + + + + + To transfer the offered data, the client issues this request and + indicates the MIME type it wants to receive. The transfer happens + through the passed file descriptor (typically created with the pipe + system call). The source client writes the data in the MIME type + representation requested and then closes the file descriptor. + + The receiving client reads from the read end of the pipe until EOF and + then closes its end, at which point the transfer is complete. + + This request may happen multiple times for different MIME types. + + + + + + + + Destroys the data offer object. + + + + + + Sent immediately after creating the wlr_data_control_offer object. + One event per offered MIME type. + + + + + diff --git a/protocol/wlr-export-dmabuf-unstable-v1.xml b/protocol/wlr-export-dmabuf-unstable-v1.xml new file mode 100644 index 0000000..751f7ef --- /dev/null +++ b/protocol/wlr-export-dmabuf-unstable-v1.xml @@ -0,0 +1,203 @@ + + + + Copyright © 2018 Rostislav Pehlivanov + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + An interface to capture surfaces in an efficient way by exporting DMA-BUFs. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This object is a manager with which to start capturing from sources. + + + + + Capture the next frame of a an entire output. + + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This object represents a single DMA-BUF frame. + + If the capture is successful, the compositor will first send a "frame" + event, followed by one or several "object". When the frame is available + for readout, the "ready" event is sent. + + If the capture failed, the "cancel" event is sent. This can happen anytime + before the "ready" event. + + Once either a "ready" or a "cancel" event is received, the client should + destroy the frame. Once an "object" event is received, the client is + responsible for closing the associated file descriptor. + + All frames are read-only and may not be written into or altered. + + + + + Special flags that should be respected by the client. + + + + + + + Main event supplying the client with information about the frame. If the + capture didn't fail, this event is always emitted first before any other + events. + + This event is followed by a number of "object" as specified by the + "num_objects" argument. + + + + + + + + + + + + + + + + Event which serves to supply the client with the file descriptors + containing the data for each object. + + After receiving this event, the client must always close the file + descriptor as soon as they're done with it and even if the frame fails. + + + + + + + + + + + + This event is sent as soon as the frame is presented, indicating it is + available for reading. This event includes the time at which + presentation happened at. + + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, + each component being an unsigned 32-bit value. Whole seconds are in + tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, + and the additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part + may have an arbitrary offset at start. + + After receiving this event, the client should destroy this object. + + + + + + + + + Indicates reason for cancelling the frame. + + + + + + + + + If the capture failed or if the frame is no longer valid after the + "frame" event has been emitted, this event will be used to inform the + client to scrap the frame. + + If the failure is temporary, the client may capture again the same + source. If the failure is permanent, any further attempts to capture the + same source will fail again. + + After receiving this event, the client should destroy this object. + + + + + + + Unreferences the frame. This request must be called as soon as its no + longer used. + + It can be called at any time by the client. The client will still have + to close any FDs it has been given. + + + + diff --git a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 0000000..1081337 --- /dev/null +++ b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,270 @@ + + + + Copyright © 2018 Ilia Bozhinov + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + The purpose of this protocol is to enable the creation of taskbars + and docks by providing them with a list of opened applications and + letting them request certain actions on them, like maximizing, etc. + + After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + toplevel window will be sent via the toplevel event + + + + + This event is emitted whenever a new toplevel window is created. It + is emitted for all toplevels, regardless of the app that has created + them. + + All initial details of the toplevel(title, app_id, states, etc.) will + be sent immediately after this event via the corresponding events in + zwlr_foreign_toplevel_handle_v1. + + + + + + + Indicates the client no longer wishes to receive events for new toplevels. + However the compositor may emit further toplevel_created events, until + the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending events to the + zwlr_foreign_toplevel_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + window. Each app may have multiple opened toplevels. + + Each toplevel has a list of outputs it is visible on, conveyed to the + client with the output_enter and output_leave events. + + + + + This event is emitted whenever the title of the toplevel changes. + + + + + + + This event is emitted whenever the app-id of the toplevel changes. + + + + + + + This event is emitted whenever the toplevel becomes visible on + the given output. A toplevel may be visible on multiple outputs. + + + + + + + This event is emitted whenever the toplevel stops being visible on + the given output. It is guaranteed that an entered-output event + with the same output has been emitted before this event. + + + + + + + Requests that the toplevel be maximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unmaximized. If the maximized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be minimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Requests that the toplevel be unminimized. If the minimized state actually + changes, this will be indicated by the state event. + + + + + + Request that this toplevel be activated on the given seat. + There is no guarantee the toplevel will be actually activated. + + + + + + + The different states that a toplevel can have. These have the same meaning + as the states with the same names defined in xdg-toplevel + + + + + + + + + + + This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 + is created and each time the toplevel state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + + This event is sent after all changes in the toplevel state have been + sent. + + This allows changes to the zwlr_foreign_toplevel_handle_v1 properties + to be seen as atomic, even if they happen via multiple events. + + + + + + Send a request to the toplevel to close itself. The compositor would + typically use a shell-specific method to carry out this request, for + example by sending the xdg_toplevel.close event. However, this gives + no guarantees the toplevel will actually be destroyed. If and when + this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + be emitted. + + + + + + The rectangle of the surface specified in this request corresponds to + the place where the app using this protocol represents the given toplevel. + It can be used by the compositor as a hint for some operations, e.g + minimizing. The client is however not required to set this, in which + case the compositor is free to decide some default value. + + If the client specifies more than one rectangle, only the last one is + considered. + + The dimensions are given in surface-local coordinates. + Setting width=height=0 removes the already-set rectangle. + + + + + + + + + + + + + + + + This event means the toplevel has been destroyed. It is guaranteed there + won't be any more events for this zwlr_foreign_toplevel_handle_v1. The + toplevel itself becomes inert so any requests will be ignored except the + destroy request. + + + + + + Destroys the zwlr_foreign_toplevel_handle_v1 object. + + This request should be called either when the client does not want to + use the toplevel anymore or after the closed event to finalize the + destruction of the object. + + + + + + + + Requests that the toplevel be fullscreened on the given output. If the + fullscreen state and/or the outputs the toplevel is visible on actually + change, this will be indicated by the state and output_enter/leave + events. + + The output parameter is only a hint to the compositor. Also, if output + is NULL, the compositor should decide which output the toplevel will be + fullscreened on, if at all. + + + + + + + Requests that the toplevel be unfullscreened. If the fullscreen state + actually changes, this will be indicated by the state event. + + + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + + + diff --git a/protocol/wlr-gamma-control-unstable-v1.xml b/protocol/wlr-gamma-control-unstable-v1.xml new file mode 100644 index 0000000..a9db762 --- /dev/null +++ b/protocol/wlr-gamma-control-unstable-v1.xml @@ -0,0 +1,126 @@ + + + + Copyright © 2015 Giulio camuffo + Copyright © 2018 Simon Ser + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + This protocol allows a privileged client to set the gamma tables for + outputs. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows creating per-output gamma + controls. + + + + + Create a gamma control that can be used to adjust gamma tables for the + provided output. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This interface allows a client to adjust gamma tables for a particular + output. + + The client will receive the gamma size, and will then be able to set gamma + tables. At any time the compositor can send a failed event indicating that + this object is no longer valid. + + There must always be at most one gamma control object per output, which + has exclusive access to this particular output. When the gamma control + object is destroyed, the gamma table is restored to its original value. + + + + + Advertise the size of each gamma ramp. + + This event is sent immediately when the gamma control object is created. + + + + + + + + + + + Set the gamma table. The file descriptor can be memory-mapped to provide + the raw gamma table, which contains successive gamma ramps for the red, + green and blue channels. Each gamma ramp is an array of 16-byte unsigned + integers which has the same length as the gamma size. + + The file descriptor data must have the same length as three times the + gamma size. + + + + + + + This event indicates that the gamma control is no longer valid. This + can happen for a number of reasons, including: + - The output doesn't support gamma tables + - Setting the gamma tables failed + - Another client already has exclusive gamma control for this output + - The compositor has transferred gamma control to another client + + Upon receiving this event, the client should destroy this object. + + + + + + Destroys the gamma control object. If the object is still valid, this + restores the original gamma tables. + + + + diff --git a/protocol/wlr-input-inhibitor-unstable-v1.xml b/protocol/wlr-input-inhibitor-unstable-v1.xml new file mode 100644 index 0000000..b62d1bb --- /dev/null +++ b/protocol/wlr-input-inhibitor-unstable-v1.xml @@ -0,0 +1,67 @@ + + + + Copyright © 2018 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to prevent input events from being sent to + any surfaces but its own, which is useful for example in lock screen + software. It is assumed that access to this interface will be locked down + to whitelisted clients by the compositor. + + + + + Activates the input inhibitor. As long as the inhibitor is active, the + compositor will not send input events to other clients. + + + + + + + + + + + + While this resource exists, input to clients other than the owner of the + inhibitor resource will not receive input events. The client that owns + this resource will receive all input events normally. The compositor will + also disable all of its own input processing (such as keyboard shortcuts) + while the inhibitor is active. + + The compositor may continue to send input events to selected clients, + such as an on-screen keyboard (via the input-method protocol). + + + + + Destroy the inhibitor and allow other clients to receive input. + + + + diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..d62fd51 --- /dev/null +++ b/protocol/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,390 @@ + + + + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + + + + + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + After creating a layer_surface object and setting it up, the client + must perform an initial commit without any buffer attached. + The compositor will reply with a layer_surface.configure event. + The client must acknowledge it and is then allowed to attach a buffer + to map the surface. + + You may pass NULL for output to allow the compositor to decide which + output to use. Generally this will be the one that the user most + recently interacted with. + + Clients can specify a namespace that defines the purpose of the layer + surface. + + + + + + + + + + + + + + + + + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + + + + + + + + + + + + + This request indicates that the client will not use the layer_shell + object any more. Objects that have been created through this instance + are not affected. + + + + + + + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (layer, size, anchor, exclusive zone, + margin, interactivity) is double-buffered, and will be applied at the + time wl_surface.commit of the corresponding wl_surface is called. + + Attaching a null buffer to a layer surface unmaps it. + + Unmapping a layer_surface means that the surface cannot be shown by the + compositor until it is explicitly mapped again. The layer_surface + returns to the state it had right after layer_shell.get_layer_surface. + The client can re-map the surface by performing a commit without any + buffer attached, waiting for a configure event and handling it as usual. + + + + + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + + + + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthogonal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + + + + + + + Requests that the compositor avoids occluding an area with other + surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to one + edge or an edge and both perpendicular edges. If the surface is not + anchored, anchored to only two perpendicular edges (a corner), anchored + to only two parallel edges or anchored to all edges, a positive value + will be treated the same as zero. + + A positive zone is the distance from the edge in surface-local + coordinates to consider exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive exclusive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + + + + + + + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + + + + + + + + + + Types of keyboard interaction possible for layer shell surfaces. The + rationale for this is twofold: (1) some applications are not interested + in keyboard events and not allowing them to be focused can improve the + desktop experience; (2) some applications will want to take exclusive + keyboard focus. + + + + + This value indicates that this surface is not interested in keyboard + events and the compositor should never assign it the keyboard focus. + + This is the default value, set for newly created layer shell surfaces. + + This is useful for e.g. desktop widgets that display information or + only have interaction with non-keyboard input devices. + + + + + Request exclusive keyboard focus if this surface is above the shell surface layer. + + For the top and overlay layers, the seat will always give + exclusive keyboard focus to the top-most layer which has keyboard + interactivity set to exclusive. If this layer contains multiple + surfaces with keyboard interactivity set to exclusive, the compositor + determines the one receiving keyboard events in an implementation- + defined manner. In this case, no guarantee is made when this surface + will receive keyboard focus (if ever). + + For the bottom and background layers, the compositor is allowed to use + normal focus semantics. + + This setting is mainly intended for applications that need to ensure + they receive all keyboard events, such as a lock screen or a password + prompt. + + + + + This requests the compositor to allow this surface to be focused and + unfocused by the user in an implementation-defined manner. The user + should be able to unfocus this surface even regardless of the layer + it is on. + + Typically, the compositor will want to use its normal mechanism to + manage keyboard focus between layer shell surfaces with this setting + and regular toplevels on the desktop layer (e.g. click to focus). + Nevertheless, it is possible for a compositor to require a special + interaction to focus or unfocus layer shell surfaces (e.g. requiring + a click even if focus follows the mouse normally, or providing a + keybinding to switch focus between layers). + + This setting is mainly intended for desktop shell components (e.g. + panels) that allow keyboard interaction. Using this option can allow + implementing a desktop shell that can be fully usable without the + mouse. + + + + + + + Set how keyboard events are delivered to this surface. By default, + layer shell surfaces do not receive keyboard events; this request can + be used to change this. + + This setting is inherited by child surfaces set by the get_popup + request. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Keyboard interactivity is double-buffered, see wl_surface.commit. + + + + + + + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + + + + + + This request destroys the layer surface. + + + + + + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + + + + + + + + + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + diff --git a/protocol/wlr-output-management-unstable-v1.xml b/protocol/wlr-output-management-unstable-v1.xml new file mode 100644 index 0000000..411e2f0 --- /dev/null +++ b/protocol/wlr-output-management-unstable-v1.xml @@ -0,0 +1,601 @@ + + + + Copyright © 2019 Purism SPC + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + This protocol exposes interfaces to obtain and modify output device + configuration. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows reading and writing the current + output device configuration. + + Output devices that display pixels (e.g. a physical monitor or a virtual + output in a window) are represented as heads. Heads cannot be created nor + destroyed by the client, but they can be enabled or disabled and their + properties can be changed. Each head may have one or more available modes. + + Whenever a head appears (e.g. a monitor is plugged in), it will be + advertised via the head event. Immediately after the output manager is + bound, all current heads are advertised. + + Whenever a head's properties change, the relevant wlr_output_head events + will be sent. Not all head properties will be sent: only properties that + have changed need to. + + Whenever a head disappears (e.g. a monitor is unplugged), a + wlr_output_head.finished event will be sent. + + After one or more heads appear, change or disappear, the done event will + be sent. It carries a serial which can be used in a create_configuration + request to update heads properties. + + The information obtained from this protocol should only be used for output + configuration purposes. This protocol is not designed to be a generic + output property advertisement protocol for regular clients. Instead, + protocols such as xdg-output should be used. + + + + + This event introduces a new head. This happens whenever a new head + appears (e.g. a monitor is plugged in) or after the output manager is + bound. + + + + + + + This event is sent after all information has been sent after binding to + the output manager object and after any subsequent changes. This applies + to child head and mode objects as well. In other words, this event is + sent whenever a head or mode is created or destroyed and whenever one of + their properties has been changed. Not all state is re-sent each time + the current configuration changes: only the actual changes are sent. + + This allows changes to the output configuration to be seen as atomic, + even if they happen via multiple events. + + A serial is sent to be used in a future create_configuration request. + + + + + + + Create a new output configuration object. This allows to update head + properties. + + + + + + + + Indicates the client no longer wishes to receive events for output + configuration changes. However the compositor may emit further events, + until the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + This event indicates that the compositor is done sending manager events. + The compositor will destroy the object immediately after sending this + event, so it will become invalid and the client should release any + resources associated with it. + + + + + + + A head is an output device. The difference between a wl_output object and + a head is that heads are advertised even if they are turned off. A head + object only advertises properties and cannot be used directly to change + them. + + A head has some read-only properties: modes, name, description and + physical_size. These cannot be changed by clients. + + Other properties can be updated via a wlr_output_configuration object. + + Properties sent via this interface are applied atomically via the + wlr_output_manager.done event. No guarantees are made regarding the order + in which properties are sent. + + + + + This event describes the head name. + + The naming convention is compositor defined, but limited to alphanumeric + characters and dashes (-). Each name is unique among all wlr_output_head + objects, but if a wlr_output_head object is destroyed the same name may + be reused later. The names will also remain consistent across sessions + with the same hardware and software configuration. + + Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do + not assume that the name is a reflection of an underlying DRM + connector, X11 connection, etc. + + If the compositor implements the xdg-output protocol and this head is + enabled, the xdg_output.name event must report the same name. + + The name event is sent after a wlr_output_head object is created. This + event is only sent once per object, and the name does not change over + the lifetime of the wlr_output_head object. + + + + + + + This event describes a human-readable description of the head. + + The description is a UTF-8 string with no convention defined for its + contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11 + output via :1'. However, do not assume that the name is a reflection of + the make, model, serial of the underlying DRM connector or the display + name of the underlying X11 connection, etc. + + If the compositor implements xdg-output and this head is enabled, + the xdg_output.description must report the same description. + + The description event is sent after a wlr_output_head object is created. + This event is only sent once per object, and the description does not + change over the lifetime of the wlr_output_head object. + + + + + + + This event describes the physical size of the head. This event is only + sent if the head has a physical size (e.g. is not a projector or a + virtual device). + + + + + + + + This event introduces a mode for this head. It is sent once per + supported mode. + + + + + + + This event describes whether the head is enabled. A disabled head is not + mapped to a region of the global compositor space. + + When a head is disabled, some properties (current_mode, position, + transform and scale) are irrelevant. + + + + + + + This event describes the mode currently in use for this head. It is only + sent if the output is enabled. + + + + + + + This events describes the position of the head in the global compositor + space. It is only sent if the output is enabled. + + + + + + + + This event describes the transformation currently applied to the head. + It is only sent if the output is enabled. + + + + + + + This events describes the scale of the head in the global compositor + space. It is only sent if the output is enabled. + + + + + + + This event indicates that the head is no longer available. The head + object becomes inert. Clients should send a destroy request and release + any resources associated with it. + + + + + + + + This event describes the manufacturer of the head. + + This must report the same make as the wl_output interface does in its + geometry event. + + Together with the model and serial_number events the purpose is to + allow clients to recognize heads from previous sessions and for example + load head-specific configurations back. + + It is not guaranteed this event will be ever sent. A reason for that + can be that the compositor does not have information about the make of + the head or the definition of a make is not sensible in the current + setup, for example in a virtual session. Clients can still try to + identify the head by available information from other events but should + be aware that there is an increased risk of false positives. + + It is not recommended to display the make string in UI to users. For + that the string provided by the description event should be preferred. + + + + + + + This event describes the model of the head. + + This must report the same model as the wl_output interface does in its + geometry event. + + Together with the make and serial_number events the purpose is to + allow clients to recognize heads from previous sessions and for example + load head-specific configurations back. + + It is not guaranteed this event will be ever sent. A reason for that + can be that the compositor does not have information about the model of + the head or the definition of a model is not sensible in the current + setup, for example in a virtual session. Clients can still try to + identify the head by available information from other events but should + be aware that there is an increased risk of false positives. + + It is not recommended to display the model string in UI to users. For + that the string provided by the description event should be preferred. + + + + + + + This event describes the serial number of the head. + + Together with the make and model events the purpose is to allow clients + to recognize heads from previous sessions and for example load head- + specific configurations back. + + It is not guaranteed this event will be ever sent. A reason for that + can be that the compositor does not have information about the serial + number of the head or the definition of a serial number is not sensible + in the current setup. Clients can still try to identify the head by + available information from other events but should be aware that there + is an increased risk of false positives. + + It is not recommended to display the serial_number string in UI to + users. For that the string provided by the description event should be + preferred. + + + + + + + + + This request indicates that the client will no longer use this head + object. + + + + + + + + + + + + + This event describes whether adaptive sync is currently enabled for + the head or not. Adaptive sync is also known as Variable Refresh + Rate or VRR. + + + + + + + + This object describes an output mode. + + Some heads don't support output modes, in which case modes won't be + advertised. + + Properties sent via this interface are applied atomically via the + wlr_output_manager.done event. No guarantees are made regarding the order + in which properties are sent. + + + + + This event describes the mode size. The size is given in physical + hardware units of the output device. This is not necessarily the same as + the output size in the global compositor space. For instance, the output + may be scaled or transformed. + + + + + + + + This event describes the mode's fixed vertical refresh rate. It is only + sent if the mode has a fixed refresh rate. + + + + + + + This event advertises this mode as preferred. + + + + + + This event indicates that the mode is no longer available. The mode + object becomes inert. Clients should send a destroy request and release + any resources associated with it. + + + + + + + + This request indicates that the client will no longer use this mode + object. + + + + + + + This object is used by the client to describe a full output configuration. + + First, the client needs to setup the output configuration. Each head can + be either enabled (and configured) or disabled. It is a protocol error to + send two enable_head or disable_head requests with the same head. It is a + protocol error to omit a head in a configuration. + + Then, the client can apply or test the configuration. The compositor will + then reply with a succeeded, failed or cancelled event. Finally the client + should destroy the configuration object. + + + + + + + + + + + Enable a head. This request creates a head configuration object that can + be used to change the head's properties. + + + + + + + + Disable a head. + + + + + + + Apply the new output configuration. + + In case the configuration is successfully applied, there is no guarantee + that the new output state matches completely the requested + configuration. For instance, a compositor might round the scale if it + doesn't support fractional scaling. + + After this request has been sent, the compositor must respond with an + succeeded, failed or cancelled event. Sending a request that isn't the + destructor is a protocol error. + + + + + + Test the new output configuration. The configuration won't be applied, + but will only be validated. + + Even if the compositor succeeds to test a configuration, applying it may + fail. + + After this request has been sent, the compositor must respond with an + succeeded, failed or cancelled event. Sending a request that isn't the + destructor is a protocol error. + + + + + + Sent after the compositor has successfully applied the changes or + tested them. + + Upon receiving this event, the client should destroy this object. + + If the current configuration has changed, events to describe the changes + will be sent followed by a wlr_output_manager.done event. + + + + + + Sent if the compositor rejects the changes or failed to apply them. The + compositor should revert any changes made by the apply request that + triggered this event. + + Upon receiving this event, the client should destroy this object. + + + + + + Sent if the compositor cancels the configuration because the state of an + output changed and the client has outdated information (e.g. after an + output has been hotplugged). + + The client can create a new configuration with a newer serial and try + again. + + Upon receiving this event, the client should destroy this object. + + + + + + Using this request a client can tell the compositor that it is not going + to use the configuration object anymore. Any changes to the outputs + that have not been applied will be discarded. + + This request also destroys wlr_output_configuration_head objects created + via this object. + + + + + + + This object is used by the client to update a single head's configuration. + + It is a protocol error to set the same property twice. + + + + + + + + + + + + + + This request sets the head's mode. + + + + + + + This request assigns a custom mode to the head. The size is given in + physical hardware units of the output device. If set to zero, the + refresh rate is unspecified. + + It is a protocol error to set both a mode and a custom mode. + + + + + + + + + This request sets the head's position in the global compositor space. + + + + + + + + This request sets the head's transform. + + + + + + + This request sets the head's scale. + + + + + + + + + This request enables/disables adaptive sync. Adaptive sync is also + known as Variable Refresh Rate or VRR. + + + + + diff --git a/protocol/wlr-output-power-management-unstable-v1.xml b/protocol/wlr-output-power-management-unstable-v1.xml new file mode 100644 index 0000000..a977839 --- /dev/null +++ b/protocol/wlr-output-power-management-unstable-v1.xml @@ -0,0 +1,128 @@ + + + + Copyright © 2019 Purism SPC + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol allows clients to control power management modes + of outputs that are currently part of the compositor space. The + intent is to allow special clients like desktop shells to power + down outputs when the system is idle. + + To modify outputs not currently part of the compositor space see + wlr-output-management. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This interface is a manager that allows creating per-output power + management mode controls. + + + + + Create a output power management mode control that can be used to + adjust the power management mode for a given output. + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This object offers requests to set the power management mode of + an output. + + + + + + + + + + + + + + Set an output's power save mode to the given mode. The mode change + is effective immediately. If the output does not support the given + mode a failed event is sent. + + + + + + + Report the power management mode change of an output. + + The mode event is sent after an output changed its power + management mode. The reason can be a client using set_mode or the + compositor deciding to change an output's mode. + This event is also sent immediately when the object is created + so the client is informed about the current power management mode. + + + + + + + This event indicates that the output power management mode control + is no longer valid. This can happen for a number of reasons, + including: + - The output doesn't support power management + - Another client already has exclusive power management mode control + for this output + - The output disappeared + + Upon receiving this event, the client should destroy this object. + + + + + + Destroys the output power management mode control object. + + + + diff --git a/protocol/wlr-screencopy-unstable-v1.xml b/protocol/wlr-screencopy-unstable-v1.xml new file mode 100644 index 0000000..50b1b7d --- /dev/null +++ b/protocol/wlr-screencopy-unstable-v1.xml @@ -0,0 +1,232 @@ + + + + Copyright © 2018 Simon Ser + Copyright © 2019 Andri Yngvason + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol allows clients to ask the compositor to copy part of the + screen content to a client buffer. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This object is a manager which offers requests to start capturing from a + source. + + + + + Capture the next frame of an entire output. + + + + + + + + + Capture the next frame of an output's region. + + The region is given in output logical coordinates, see + xdg_output.logical_size. The region will be clipped to the output's + extents. + + + + + + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + + + + This object represents a single frame. + + When created, a series of buffer events will be sent, each representing a + supported buffer type. The "buffer_done" event is sent afterwards to + indicate that all supported buffer types have been enumerated. The client + will then be able to send a "copy" request. If the capture is successful, + the compositor will send a "flags" followed by a "ready" event. + + For objects version 2 or lower, wl_shm buffers are always supported, ie. + the "buffer" event is guaranteed to be sent. + + If the capture failed, the "failed" event is sent. This can happen anytime + before the "ready" event. + + Once either a "ready" or a "failed" event is received, the client should + destroy the frame. + + + + + Provides information about wl_shm buffer parameters that need to be + used for this frame. This event is sent once after the frame is created + if wl_shm buffers are supported. + + + + + + + + + + Copy the frame to the supplied buffer. The buffer must have a the + correct size, see zwlr_screencopy_frame_v1.buffer and + zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a + supported format. + + If the frame is successfully copied, a "flags" and a "ready" events are + sent. Otherwise, a "failed" event is sent. + + + + + + + + + + + + + + + + Provides flags about the frame. This event is sent once before the + "ready" event. + + + + + + + Called as soon as the frame is copied, indicating it is available + for reading. This event includes the time at which presentation happened + at. + + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, + each component being an unsigned 32-bit value. Whole seconds are in + tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, + and the additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part + may have an arbitrary offset at start. + + After receiving this event, the client should destroy the object. + + + + + + + + + This event indicates that the attempted frame copy has failed. + + After receiving this event, the client should destroy the object. + + + + + + Destroys the frame. This request can be sent at any time by the client. + + + + + + + Same as copy, except it waits until there is damage to copy. + + + + + + + This event is sent right before the ready event when copy_with_damage is + requested. It may be generated multiple times for each copy_with_damage + request. + + The arguments describe a box around an area that has changed since the + last copy request that was derived from the current screencopy manager + instance. + + The union of all regions received between the call to copy_with_damage + and a ready event is the total damage since the prior ready event. + + + + + + + + + + + Provides information about linux-dmabuf buffer parameters that need to + be used for this frame. This event is sent once after the frame is + created if linux-dmabuf buffers are supported. + + + + + + + + + This event is sent once after all buffer events have been sent. + + The client should proceed to create a buffer of one of the supported + types, and send a "copy" request. + + + + diff --git a/protocol/wlr-virtual-pointer-unstable-v1.xml b/protocol/wlr-virtual-pointer-unstable-v1.xml new file mode 100644 index 0000000..ea243e7 --- /dev/null +++ b/protocol/wlr-virtual-pointer-unstable-v1.xml @@ -0,0 +1,152 @@ + + + + Copyright © 2019 Josef Gajdusek + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This protocol allows clients to emulate a physical pointer device. The + requests are mostly mirror opposites of those specified in wl_pointer. + + + + + + + + + + The pointer has moved by a relative amount to the previous request. + + Values are in the global compositor space. + + + + + + + + + The pointer has moved in an absolute coordinate frame. + + Value of x can range from 0 to x_extent, value of y can range from 0 + to y_extent. + + + + + + + + + + + A button was pressed or released. + + + + + + + + + Scroll and other axis requests. + + + + + + + + + Indicates the set of events that logically belong together. + + + + + + Source information for scroll and other axis. + + + + + + + Stop notification for scroll and other axes. + + + + + + + + Discrete step information for scroll and other axes. + + This event allows the client to extend data normally sent using the axis + event with discrete value. + + + + + + + + + + + + + + + This object allows clients to create individual virtual pointer objects. + + + + + Creates a new virtual pointer. The optional seat is a suggestion to the + compositor. + + + + + + + + + + + + + Creates a new virtual pointer. The seat and the output arguments are + optional. If the seat argument is set, the compositor should assign the + input device to the requested seat. If the output argument is set, the + compositor should map the input device to the requested output. + + + + + + + diff --git a/src/layer_shell_v1.c b/src/layer_shell_v1.c deleted file mode 100644 index 3ed616a..0000000 --- a/src/layer_shell_v1.c +++ /dev/null @@ -1,186 +0,0 @@ -#include -#include -#include - -static void scene_layer_surface_handle_tree_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, tree_destroy); - // tree and surface_node will be cleaned up by scene_node_finish - wl_list_remove(&scene_layer_surface->tree_destroy.link); - wl_list_remove(&scene_layer_surface->layer_surface_destroy.link); - wl_list_remove(&scene_layer_surface->layer_surface_map.link); - wl_list_remove(&scene_layer_surface->layer_surface_unmap.link); - free(scene_layer_surface); -} - -static void scene_layer_surface_handle_layer_surface_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, layer_surface_destroy); - wlr_scene_node_destroy(&scene_layer_surface->tree->node); -} - -static void scene_layer_surface_handle_layer_surface_map( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, layer_surface_map); - wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, true); -} - -static void scene_layer_surface_handle_layer_surface_unmap( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, layer_surface_unmap); - wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, false); -} - -static void layer_surface_exclusive_zone( - struct wlr_layer_surface_v1_state *state, - struct wlr_box *usable_area) { - switch (state->anchor) { - case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): - // Anchor top - usable_area->y += state->exclusive_zone + state->margin.top; - usable_area->height -= state->exclusive_zone + state->margin.top; - break; - case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): - // Anchor bottom - usable_area->height -= state->exclusive_zone + state->margin.bottom; - break; - case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT): - // Anchor left - usable_area->x += state->exclusive_zone + state->margin.left; - usable_area->width -= state->exclusive_zone + state->margin.left; - break; - case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): - // Anchor right - usable_area->width -= state->exclusive_zone + state->margin.right; - break; - } -} - -void wlr_scene_layer_surface_v1_configure( - struct wlr_scene_layer_surface_v1 *scene_layer_surface, - const struct wlr_box *full_area, struct wlr_box *usable_area) { - struct wlr_layer_surface_v1 *layer_surface = - scene_layer_surface->layer_surface; - struct wlr_layer_surface_v1_state *state = &layer_surface->current; - - // If the exclusive zone is set to -1, the layer surface will use the - // full area of the output, otherwise it is constrained to the - // remaining usable area. - struct wlr_box bounds; - if (state->exclusive_zone == -1) { - bounds = *full_area; - } else { - bounds = *usable_area; - } - - struct wlr_box box = { - .width = state->desired_width, - .height = state->desired_height, - }; - - // Horizontal positioning - if (box.width == 0) { - box.x = bounds.x + state->margin.left; - box.width = bounds.width - - (state->margin.left + state->margin.right); - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT && - state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { - box.x = bounds.x + bounds.width/2 -box.width/2; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { - box.x = bounds.x + state->margin.left; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { - box.x = bounds.x + bounds.width - box.width - state->margin.right; - } else { - box.x = bounds.x + bounds.width/2 - box.width/2; - } - - // Vertical positioning - if (box.height == 0) { - box.y = bounds.y + state->margin.top; - box.height = bounds.height - - (state->margin.top + state->margin.bottom); - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP && - state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { - box.y = bounds.y + bounds.height/2 - box.height/2; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { - box.y = bounds.y + state->margin.top; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { - box.y = bounds.y + bounds.height - box.height - state->margin.bottom; - } else { - box.y = bounds.y + bounds.height/2 - box.height/2; - } - - wlr_scene_node_set_position(&scene_layer_surface->tree->node, box.x, box.y); - wlr_layer_surface_v1_configure(layer_surface, box.width, box.height); - - if (layer_surface->mapped && state->exclusive_zone > 0) { - layer_surface_exclusive_zone(state, usable_area); - } -} - -struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( - struct wlr_scene_tree *parent, - struct wlr_layer_surface_v1 *layer_surface) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - calloc(1, sizeof(*scene_layer_surface)); - if (scene_layer_surface == NULL) { - return NULL; - } - - scene_layer_surface->layer_surface = layer_surface; - - scene_layer_surface->tree = wlr_scene_tree_create(parent); - if (scene_layer_surface->tree == NULL) { - free(scene_layer_surface); - return NULL; - } - - struct wlr_scene_tree *surface_tree = wlr_scene_subsurface_tree_create( - scene_layer_surface->tree, layer_surface->surface); - if (surface_tree == NULL) { - wlr_scene_node_destroy(&scene_layer_surface->tree->node); - free(scene_layer_surface); - return NULL; - } - - scene_layer_surface->tree_destroy.notify = - scene_layer_surface_handle_tree_destroy; - wl_signal_add(&scene_layer_surface->tree->node.events.destroy, - &scene_layer_surface->tree_destroy); - - scene_layer_surface->layer_surface_destroy.notify = - scene_layer_surface_handle_layer_surface_destroy; - wl_signal_add(&layer_surface->events.destroy, - &scene_layer_surface->layer_surface_destroy); - - scene_layer_surface->layer_surface_map.notify = - scene_layer_surface_handle_layer_surface_map; - wl_signal_add(&layer_surface->events.map, - &scene_layer_surface->layer_surface_map); - - scene_layer_surface->layer_surface_unmap.notify = - scene_layer_surface_handle_layer_surface_unmap; - wl_signal_add(&layer_surface->events.unmap, - &scene_layer_surface->layer_surface_unmap); - - wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, - layer_surface->mapped); - - return scene_layer_surface; -} diff --git a/src/output_layout.c b/src/output_layout.c deleted file mode 100644 index 1d1484a..0000000 --- a/src/output_layout.c +++ /dev/null @@ -1,157 +0,0 @@ -#include -#include -#include - -struct wlr_scene_output_layout { - struct wlr_output_layout *layout; - struct wlr_scene *scene; - - struct wl_list outputs; // wlr_scene_output_layout_output.link - - struct wl_listener layout_add; - struct wl_listener layout_change; - struct wl_listener layout_destroy; - struct wl_listener scene_destroy; -}; - -struct wlr_scene_output_layout_output { - struct wlr_output_layout_output *layout_output; - struct wlr_scene_output *scene_output; - - struct wl_list link; // wlr_scene_output_layout.outputs - - struct wl_listener layout_output_destroy; - struct wl_listener scene_output_destroy; -}; - -static void scene_output_layout_output_destroy( - struct wlr_scene_output_layout_output *solo) { - wl_list_remove(&solo->layout_output_destroy.link); - wl_list_remove(&solo->scene_output_destroy.link); - wl_list_remove(&solo->link); - wlr_scene_output_destroy(solo->scene_output); - free(solo); -} - -static void scene_output_layout_output_handle_layout_output_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout_output *solo = - wl_container_of(listener, solo, layout_output_destroy); - scene_output_layout_output_destroy(solo); -} - -static void scene_output_layout_output_handle_scene_output_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout_output *solo = - wl_container_of(listener, solo, scene_output_destroy); - solo->scene_output = NULL; - scene_output_layout_output_destroy(solo); -} - -static void scene_output_layout_destroy(struct wlr_scene_output_layout *sol) { - struct wlr_scene_output_layout_output *solo, *tmp; - wl_list_for_each_safe(solo, tmp, &sol->outputs, link) { - scene_output_layout_output_destroy(solo); - } - wl_list_remove(&sol->layout_add.link); - wl_list_remove(&sol->layout_change.link); - wl_list_remove(&sol->layout_destroy.link); - wl_list_remove(&sol->scene_destroy.link); - free(sol); -} - -static void scene_output_layout_handle_layout_change( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout *sol = - wl_container_of(listener, sol, layout_change); - - struct wlr_scene_output_layout_output *solo; - wl_list_for_each(solo, &sol->outputs, link) { - wlr_scene_output_set_position(solo->scene_output, - solo->layout_output->x, solo->layout_output->y); - } -} - -static void scene_output_layout_add(struct wlr_scene_output_layout *sol, - struct wlr_output_layout_output *lo) { - struct wlr_scene_output_layout_output *solo = calloc(1, sizeof(*solo)); - if (solo == NULL) { - return; - } - - solo->scene_output = wlr_scene_output_create(sol->scene, lo->output); - if (solo->scene_output == NULL) { - free(solo); - return; - } - - solo->layout_output = lo; - - solo->layout_output_destroy.notify = - scene_output_layout_output_handle_layout_output_destroy; - wl_signal_add(&lo->events.destroy, &solo->layout_output_destroy); - - solo->scene_output_destroy.notify = - scene_output_layout_output_handle_scene_output_destroy; - wl_signal_add(&solo->scene_output->events.destroy, - &solo->scene_output_destroy); - - wl_list_insert(&sol->outputs, &solo->link); - - wlr_scene_output_set_position(solo->scene_output, lo->x, lo->y); -} - -static void scene_output_layout_handle_layout_add( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout *sol = - wl_container_of(listener, sol, layout_add); - struct wlr_output_layout_output *lo = data; - - scene_output_layout_add(sol, lo); -} - -static void scene_output_layout_handle_layout_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout *sol = - wl_container_of(listener, sol, layout_destroy); - scene_output_layout_destroy(sol); -} - -static void scene_output_layout_handle_scene_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout *sol = - wl_container_of(listener, sol, scene_destroy); - scene_output_layout_destroy(sol); -} - -bool wlr_scene_attach_output_layout(struct wlr_scene *scene, - struct wlr_output_layout *output_layout) { - struct wlr_scene_output_layout *sol = calloc(1, sizeof(*sol)); - if (sol == NULL) { - return false; - } - - sol->scene = scene; - sol->layout = output_layout; - - wl_list_init(&sol->outputs); - - sol->layout_destroy.notify = scene_output_layout_handle_layout_destroy; - wl_signal_add(&output_layout->events.destroy, &sol->layout_destroy); - - sol->layout_change.notify = scene_output_layout_handle_layout_change; - wl_signal_add(&output_layout->events.change, &sol->layout_change); - - sol->layout_add.notify = scene_output_layout_handle_layout_add; - wl_signal_add(&output_layout->events.add, &sol->layout_add); - - sol->scene_destroy.notify = scene_output_layout_handle_scene_destroy; - wl_signal_add(&scene->tree.node.events.destroy, &sol->scene_destroy); - - struct wlr_output_layout_output *lo; - wl_list_for_each(lo, &output_layout->outputs, link) { - scene_output_layout_add(sol, lo); - } - - return true; -} diff --git a/src/subsurface_tree.c b/src/subsurface_tree.c deleted file mode 100644 index 35420ab..0000000 --- a/src/subsurface_tree.c +++ /dev/null @@ -1,259 +0,0 @@ -#include -#include -#include -#include -#include - -/** - * A tree for a surface and all of its child sub-surfaces. - * - * `tree` contains `scene_surface` and one node per sub-surface. - */ -struct wlr_scene_subsurface_tree { - struct wlr_scene_tree *tree; - struct wlr_surface *surface; - struct wlr_scene_surface *scene_surface; - - struct wl_listener tree_destroy; - struct wl_listener surface_destroy; - struct wl_listener surface_commit; - struct wl_listener surface_new_subsurface; - - struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface - - // Only valid if the surface is a sub-surface - - struct wlr_addon surface_addon; - - struct wl_listener subsurface_destroy; - struct wl_listener subsurface_map; - struct wl_listener subsurface_unmap; -}; - -static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, tree_destroy); - // tree and scene_surface will be cleaned up by scene_node_finish - if (subsurface_tree->parent) { - wlr_addon_finish(&subsurface_tree->surface_addon); - wl_list_remove(&subsurface_tree->subsurface_destroy.link); - wl_list_remove(&subsurface_tree->subsurface_map.link); - wl_list_remove(&subsurface_tree->subsurface_unmap.link); - } - wl_list_remove(&subsurface_tree->tree_destroy.link); - wl_list_remove(&subsurface_tree->surface_destroy.link); - wl_list_remove(&subsurface_tree->surface_commit.link); - wl_list_remove(&subsurface_tree->surface_new_subsurface.link); - free(subsurface_tree); -} - -static const struct wlr_addon_interface subsurface_tree_addon_impl; - -static struct wlr_scene_subsurface_tree *subsurface_tree_from_subsurface( - struct wlr_scene_subsurface_tree *parent, - struct wlr_subsurface *subsurface) { - struct wlr_addon *addon = wlr_addon_find(&subsurface->surface->addons, - parent, &subsurface_tree_addon_impl); - assert(addon != NULL); - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(addon, subsurface_tree, surface_addon); - return subsurface_tree; -} - -static void subsurface_tree_reconfigure( - struct wlr_scene_subsurface_tree *subsurface_tree) { - struct wlr_surface *surface = subsurface_tree->surface; - - struct wlr_scene_node *prev = NULL; - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - struct wlr_scene_subsurface_tree *child = - subsurface_tree_from_subsurface(subsurface_tree, subsurface); - if (prev != NULL) { - wlr_scene_node_place_above(&child->tree->node, prev); - } - prev = &child->tree->node; - - wlr_scene_node_set_position(&child->tree->node, - subsurface->current.x, subsurface->current.y); - } - - if (prev != NULL) { - wlr_scene_node_place_above(&subsurface_tree->scene_surface->buffer->node, prev); - } - prev = &subsurface_tree->scene_surface->buffer->node; - - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - struct wlr_scene_subsurface_tree *child = - subsurface_tree_from_subsurface(subsurface_tree, subsurface); - wlr_scene_node_place_above(&child->tree->node, prev); - prev = &child->tree->node; - - wlr_scene_node_set_position(&child->tree->node, - subsurface->current.x, subsurface->current.y); - } -} - -static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_destroy); - wlr_scene_node_destroy(&subsurface_tree->tree->node); -} - -static void subsurface_tree_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_commit); - - // TODO: only do this on subsurface order or position change - subsurface_tree_reconfigure(subsurface_tree); -} - -static void subsurface_tree_handle_subsurface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, subsurface_destroy); - wlr_scene_node_destroy(&subsurface_tree->tree->node); -} - -static void subsurface_tree_handle_subsurface_map(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, subsurface_map); - - wlr_scene_node_set_enabled(&subsurface_tree->tree->node, true); -} - -static void subsurface_tree_handle_subsurface_unmap(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, subsurface_unmap); - - wlr_scene_node_set_enabled(&subsurface_tree->tree->node, false); -} - -static void subsurface_tree_addon_destroy(struct wlr_addon *addon) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(addon, subsurface_tree, surface_addon); - wlr_scene_node_destroy(&subsurface_tree->tree->node); -} - -static const struct wlr_addon_interface subsurface_tree_addon_impl = { - .name = "wlr_scene_subsurface_tree", - .destroy = subsurface_tree_addon_destroy, -}; - -static struct wlr_scene_subsurface_tree *scene_surface_tree_create( - struct wlr_scene_tree *parent, struct wlr_surface *surface); - -static bool subsurface_tree_create_subsurface( - struct wlr_scene_subsurface_tree *parent, - struct wlr_subsurface *subsurface) { - struct wlr_scene_subsurface_tree *child = scene_surface_tree_create( - parent->tree, subsurface->surface); - if (child == NULL) { - return false; - } - - child->parent = parent; - wlr_scene_node_set_enabled(&child->tree->node, subsurface->mapped); - - wlr_addon_init(&child->surface_addon, &subsurface->surface->addons, - parent, &subsurface_tree_addon_impl); - - child->subsurface_destroy.notify = subsurface_tree_handle_subsurface_destroy; - wl_signal_add(&subsurface->events.destroy, &child->subsurface_destroy); - - child->subsurface_map.notify = subsurface_tree_handle_subsurface_map; - wl_signal_add(&subsurface->events.map, &child->subsurface_map); - - child->subsurface_unmap.notify = subsurface_tree_handle_subsurface_unmap; - wl_signal_add(&subsurface->events.unmap, &child->subsurface_unmap); - - return true; -} - -static void subsurface_tree_handle_surface_new_subsurface( - struct wl_listener *listener, void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_new_subsurface); - struct wlr_subsurface *subsurface = data; - if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { - wl_resource_post_no_memory(subsurface->resource); - } -} - -static struct wlr_scene_subsurface_tree *scene_surface_tree_create( - struct wlr_scene_tree *parent, struct wlr_surface *surface) { - struct wlr_scene_subsurface_tree *subsurface_tree = - calloc(1, sizeof(*subsurface_tree)); - if (subsurface_tree == NULL) { - return NULL; - } - - subsurface_tree->tree = wlr_scene_tree_create(parent); - if (subsurface_tree->tree == NULL) { - goto error_surface_tree; - } - - subsurface_tree->scene_surface = - wlr_scene_surface_create(subsurface_tree->tree, surface); - if (subsurface_tree->scene_surface == NULL) { - goto error_scene_surface; - } - - subsurface_tree->surface = surface; - - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { - goto error_scene_surface; - } - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { - goto error_scene_surface; - } - } - - subsurface_tree_reconfigure(subsurface_tree); - - subsurface_tree->tree_destroy.notify = subsurface_tree_handle_tree_destroy; - wl_signal_add(&subsurface_tree->tree->node.events.destroy, - &subsurface_tree->tree_destroy); - - subsurface_tree->surface_destroy.notify = subsurface_tree_handle_surface_destroy; - wl_signal_add(&surface->events.destroy, &subsurface_tree->surface_destroy); - - subsurface_tree->surface_commit.notify = subsurface_tree_handle_surface_commit; - wl_signal_add(&surface->events.commit, &subsurface_tree->surface_commit); - - subsurface_tree->surface_new_subsurface.notify = - subsurface_tree_handle_surface_new_subsurface; - wl_signal_add(&surface->events.new_subsurface, - &subsurface_tree->surface_new_subsurface); - - return subsurface_tree; - -error_scene_surface: - wlr_scene_node_destroy(&subsurface_tree->tree->node); -error_surface_tree: - free(subsurface_tree); - return NULL; -} - -struct wlr_scene_tree *wlr_scene_subsurface_tree_create( - struct wlr_scene_tree *parent, struct wlr_surface *surface) { - struct wlr_scene_subsurface_tree *subsurface_tree = - scene_surface_tree_create(parent, surface); - if (subsurface_tree == NULL) { - return NULL; - } - return subsurface_tree->tree; -} diff --git a/src/surface.c b/src/surface.c deleted file mode 100644 index 553cc42..0000000 --- a/src/surface.c +++ /dev/null @@ -1,211 +0,0 @@ -#include -#include -#include -#include -#include "types/wlr_scene.h" - -static void handle_scene_buffer_output_enter( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, output_enter); - struct wlr_scene_output *output = data; - - wlr_surface_send_enter(surface->surface, output->output); -} - -static void handle_scene_buffer_output_leave( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, output_leave); - struct wlr_scene_output *output = data; - - wlr_surface_send_leave(surface->surface, output->output); -} - -static void handle_scene_buffer_output_present( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, output_present); - struct wlr_scene_output *scene_output = data; - - if (surface->buffer->primary_output == scene_output) { - struct wlr_scene *root = scene_node_get_root(&surface->buffer->node); - struct wlr_presentation *presentation = root->presentation; - - if (presentation) { - wlr_presentation_surface_sampled_on_output( - presentation, surface->surface, scene_output->output); - } - } -} - -static void handle_scene_buffer_frame_done( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, frame_done); - struct timespec *now = data; - - wlr_surface_send_frame_done(surface->surface, now); -} - -static void scene_surface_handle_surface_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, surface_destroy); - - wlr_scene_node_destroy(&surface->buffer->node); -} - -// This is used for wlr_scene where it unconditionally locks buffers preventing -// reuse of the existing texture for shm clients. With the usage pattern of -// wlr_scene surface handling, we can mark its locked buffer as safe -// for mutation. -static void client_buffer_mark_next_can_damage(struct wlr_client_buffer *buffer) { - buffer->n_ignore_locks++; -} - -static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buffer) { - if (!scene_buffer->buffer) { - return; - } - - struct wlr_client_buffer *buffer = wlr_client_buffer_get(scene_buffer->buffer); - if (!buffer) { - return; - } - - assert(buffer->n_ignore_locks > 0); - buffer->n_ignore_locks--; -} - -static void set_buffer_with_surface_state(struct wlr_scene_buffer *scene_buffer, - struct wlr_surface *surface) { - struct wlr_surface_state *state = &surface->current; - - wlr_scene_buffer_set_opaque_region(scene_buffer, &surface->opaque_region); - - struct wlr_fbox src_box; - wlr_surface_get_buffer_source_box(surface, &src_box); - wlr_scene_buffer_set_source_box(scene_buffer, &src_box); - - wlr_scene_buffer_set_dest_size(scene_buffer, state->width, state->height); - wlr_scene_buffer_set_transform(scene_buffer, state->transform); - - scene_buffer_unmark_client_buffer(scene_buffer); - - if (surface->buffer) { - client_buffer_mark_next_can_damage(surface->buffer); - - wlr_scene_buffer_set_buffer_with_damage(scene_buffer, - &surface->buffer->base, &surface->buffer_damage); - } else { - wlr_scene_buffer_set_buffer(scene_buffer, NULL); - } -} - -static void handle_scene_surface_surface_commit( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, surface_commit); - struct wlr_scene_buffer *scene_buffer = surface->buffer; - - set_buffer_with_surface_state(scene_buffer, surface->surface); - - // If the surface has requested a frame done event, honour that. The - // frame_callback_list will be populated in this case. We should only - // schedule the frame however if the node is enabled and there is an - // output intersecting, otherwise the frame done events would never reach - // the surface anyway. - int lx, ly; - bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly); - - if (!wl_list_empty(&surface->surface->current.frame_callback_list) && - surface->buffer->primary_output != NULL && enabled) { - wlr_output_schedule_frame(surface->buffer->primary_output->output); - } -} - -static bool scene_buffer_point_accepts_input(struct wlr_scene_buffer *scene_buffer, - int sx, int sy) { - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_buffer(scene_buffer); - - return wlr_surface_point_accepts_input(scene_surface->surface, sx, sy); -} - -static void surface_addon_destroy(struct wlr_addon *addon) { - struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); - - scene_buffer_unmark_client_buffer(surface->buffer); - - wlr_addon_finish(&surface->addon); - - wl_list_remove(&surface->output_enter.link); - wl_list_remove(&surface->output_leave.link); - wl_list_remove(&surface->output_present.link); - wl_list_remove(&surface->frame_done.link); - wl_list_remove(&surface->surface_destroy.link); - wl_list_remove(&surface->surface_commit.link); - - free(surface); -} - -static const struct wlr_addon_interface surface_addon_impl = { - .name = "wlr_scene_surface", - .destroy = surface_addon_destroy, -}; - -struct wlr_scene_surface *wlr_scene_surface_from_buffer( - struct wlr_scene_buffer *scene_buffer) { - struct wlr_addon *addon = wlr_addon_find(&scene_buffer->node.addons, - scene_buffer, &surface_addon_impl); - if (!addon) { - return NULL; - } - - struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); - return surface; -} - -struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent, - struct wlr_surface *wlr_surface) { - struct wlr_scene_surface *surface = calloc(1, sizeof(*surface)); - if (surface == NULL) { - return NULL; - } - - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(parent, NULL); - if (!scene_buffer) { - free(surface); - return NULL; - } - - surface->buffer = scene_buffer; - surface->surface = wlr_surface; - scene_buffer->point_accepts_input = scene_buffer_point_accepts_input; - - surface->output_enter.notify = handle_scene_buffer_output_enter; - wl_signal_add(&scene_buffer->events.output_enter, &surface->output_enter); - - surface->output_leave.notify = handle_scene_buffer_output_leave; - wl_signal_add(&scene_buffer->events.output_leave, &surface->output_leave); - - surface->output_present.notify = handle_scene_buffer_output_present; - wl_signal_add(&scene_buffer->events.output_present, &surface->output_present); - - surface->frame_done.notify = handle_scene_buffer_frame_done; - wl_signal_add(&scene_buffer->events.frame_done, &surface->frame_done); - - surface->surface_destroy.notify = scene_surface_handle_surface_destroy; - wl_signal_add(&wlr_surface->events.destroy, &surface->surface_destroy); - - surface->surface_commit.notify = handle_scene_surface_surface_commit; - wl_signal_add(&wlr_surface->events.commit, &surface->surface_commit); - - wlr_addon_init(&surface->addon, &scene_buffer->node.addons, - scene_buffer, &surface_addon_impl); - - set_buffer_with_surface_state(scene_buffer, wlr_surface); - - return surface; -} diff --git a/src/wlr_scene.c b/src/wlr_scene.c deleted file mode 100644 index eb1ce3e..0000000 --- a/src/wlr_scene.c +++ /dev/null @@ -1,1728 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "types/wlr_buffer.h" -#include "types/wlr_scene.h" -#include "util/array.h" -#include "util/env.h" -#include "util/time.h" - -#define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 - -static struct wlr_scene_tree *scene_tree_from_node(struct wlr_scene_node *node) { - assert(node->type == WLR_SCENE_NODE_TREE); - struct wlr_scene_tree *tree = wl_container_of(node, tree, node); - return tree; -} - -static struct wlr_scene_rect *scene_rect_from_node( - struct wlr_scene_node *node) { - assert(node->type == WLR_SCENE_NODE_RECT); - struct wlr_scene_rect *rect = wl_container_of(node, rect, node); - return rect; -} - -struct wlr_scene_buffer *wlr_scene_buffer_from_node( - struct wlr_scene_node *node) { - assert(node->type == WLR_SCENE_NODE_BUFFER); - struct wlr_scene_buffer *buffer = wl_container_of(node, buffer, node); - return buffer; -} - -struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { - struct wlr_scene_tree *tree; - if (node->type == WLR_SCENE_NODE_TREE) { - tree = scene_tree_from_node(node); - } else { - tree = node->parent; - } - - while (tree->node.parent != NULL) { - tree = tree->node.parent; - } - return (struct wlr_scene *)tree; -} - -static void scene_node_init(struct wlr_scene_node *node, - enum wlr_scene_node_type type, struct wlr_scene_tree *parent) { - memset(node, 0, sizeof(*node)); - node->type = type; - node->parent = parent; - node->enabled = true; - - wl_list_init(&node->link); - - wl_signal_init(&node->events.destroy); - pixman_region32_init(&node->visible); - - if (parent != NULL) { - wl_list_insert(parent->children.prev, &node->link); - } - - wlr_addon_set_init(&node->addons); -} - -struct highlight_region { - pixman_region32_t region; - struct timespec when; - struct wl_list link; -}; - -void wlr_scene_node_destroy(struct wlr_scene_node *node) { - if (node == NULL) { - return; - } - - // We want to call the destroy listeners before we do anything else - // in case the destroy signal would like to remove children before they - // are recursively destroyed. - wl_signal_emit_mutable(&node->events.destroy, NULL); - wlr_addon_set_finish(&node->addons); - - wlr_scene_node_set_enabled(node, false); - - struct wlr_scene *scene = scene_node_get_root(node); - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - uint64_t active = scene_buffer->active_outputs; - if (active) { - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, &scene->outputs, link) { - if (active & (1ull << scene_output->index)) { - wl_signal_emit_mutable(&scene_buffer->events.output_leave, - scene_output); - } - } - } - - wlr_texture_destroy(scene_buffer->texture); - wlr_buffer_unlock(scene_buffer->buffer); - pixman_region32_fini(&scene_buffer->opaque_region); - } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); - - if (scene_tree == &scene->tree) { - assert(!node->parent); - struct wlr_scene_output *scene_output, *scene_output_tmp; - wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) { - wlr_scene_output_destroy(scene_output); - } - - wl_list_remove(&scene->presentation_destroy.link); - } else { - assert(node->parent); - } - - struct wlr_scene_node *child, *child_tmp; - wl_list_for_each_safe(child, child_tmp, - &scene_tree->children, link) { - wlr_scene_node_destroy(child); - } - } - - wl_list_remove(&node->link); - pixman_region32_fini(&node->visible); - free(node); -} - -static void scene_tree_init(struct wlr_scene_tree *tree, - struct wlr_scene_tree *parent) { - memset(tree, 0, sizeof(*tree)); - scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, parent); - wl_list_init(&tree->children); -} - -struct wlr_scene *wlr_scene_create(void) { - struct wlr_scene *scene = calloc(1, sizeof(struct wlr_scene)); - if (scene == NULL) { - return NULL; - } - - scene_tree_init(&scene->tree, NULL); - - wl_list_init(&scene->outputs); - wl_list_init(&scene->presentation_destroy.link); - - const char *debug_damage_options[] = { - "none", - "rerender", - "highlight", - NULL - }; - - scene->debug_damage_option = env_parse_switch("WLR_SCENE_DEBUG_DAMAGE", debug_damage_options); - scene->direct_scanout = !env_parse_bool("WLR_SCENE_DISABLE_DIRECT_SCANOUT"); - scene->calculate_visibility = !env_parse_bool("WLR_SCENE_DISABLE_VISIBILITY"); - - return scene; -} - -struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { - assert(parent); - - struct wlr_scene_tree *tree = calloc(1, sizeof(struct wlr_scene_tree)); - if (tree == NULL) { - return NULL; - } - - scene_tree_init(tree, parent); - return tree; -} - -static void scene_node_get_size(struct wlr_scene_node *node, int *lx, int *ly); - -typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node, - int sx, int sy, void *data); - -static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, - scene_node_box_iterator_func_t iterator, void *user_data, int lx, int ly) { - if (!node->enabled) { - return false; - } - - switch (node->type) { - case WLR_SCENE_NODE_TREE:; - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each_reverse(child, &scene_tree->children, link) { - if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) { - return true; - } - } - break; - case WLR_SCENE_NODE_RECT: - case WLR_SCENE_NODE_BUFFER:; - struct wlr_box node_box = { .x = lx, .y = ly }; - scene_node_get_size(node, &node_box.width, &node_box.height); - - if (wlr_box_intersection(&node_box, &node_box, box) && - iterator(node, lx, ly, user_data)) { - return true; - } - break; - } - - return false; -} - -static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, - scene_node_box_iterator_func_t iterator, void *user_data) { - int x, y; - wlr_scene_node_coords(node, &x, &y); - - return _scene_nodes_in_box(node, box, iterator, user_data, x, y); -} - -static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, - pixman_region32_t *opaque) { - if (node->type == WLR_SCENE_NODE_RECT) { - struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); - if (scene_rect->color[3] != 1) { - return; - } - } else if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - if (!scene_buffer->buffer) { - return; - } - - if (!buffer_is_opaque(scene_buffer->buffer)) { - pixman_region32_copy(opaque, &scene_buffer->opaque_region); - pixman_region32_translate(opaque, x, y); - return; - } - } - - int width, height; - scene_node_get_size(node, &width, &height); - pixman_region32_fini(opaque); - pixman_region32_init_rect(opaque, x, y, width, height); -} - -struct scene_update_data { - pixman_region32_t *visible; - pixman_region32_t *update_region; - struct wl_list *outputs; - bool calculate_visibility; -}; - -static uint32_t region_area(pixman_region32_t *region) { - uint32_t area = 0; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); - for (int i = 0; i < nrects; ++i) { - area += (rects[i].x2 - rects[i].x1) * (rects[i].y2 - rects[i].y1); - } - - return area; -} - -static void scale_output_damage(pixman_region32_t *damage, float scale) { - wlr_region_scale(damage, damage, scale); - - if (floor(scale) != scale) { - wlr_region_expand(damage, damage, 1); - } -} - -static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) { - if (!pixman_region32_not_empty(damage)) { - return; - } - - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, &scene->outputs, link) { - pixman_region32_t output_damage; - pixman_region32_init(&output_damage); - pixman_region32_copy(&output_damage, damage); - pixman_region32_translate(&output_damage, - -scene_output->x, -scene_output->y); - scale_output_damage(&output_damage, scene_output->output->scale); - if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { - wlr_output_schedule_frame(scene_output->output); - } - pixman_region32_fini(&output_damage); - } -} - -static void update_node_update_outputs(struct wlr_scene_node *node, - struct wl_list *outputs, struct wlr_scene_output *ignore) { - if (node->type != WLR_SCENE_NODE_BUFFER) { - return; - } - - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - uint32_t largest_overlap = 0; - scene_buffer->primary_output = NULL; - - uint64_t active_outputs = 0; - - // let's update the outputs in two steps: - // - the primary outputs - // - the enter/leave signals - // This ensures that the enter/leave signals can rely on the primary output - // to have a reasonable value. Otherwise, they may get a value that's in - // the middle of a calculation. - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, outputs, link) { - if (scene_output == ignore) { - continue; - } - - if (!scene_output->output->enabled) { - continue; - } - - struct wlr_box output_box = { - .x = scene_output->x, - .y = scene_output->y, - }; - wlr_output_effective_resolution(scene_output->output, - &output_box.width, &output_box.height); - - pixman_region32_t intersection; - pixman_region32_init(&intersection); - pixman_region32_intersect_rect(&intersection, &node->visible, - output_box.x, output_box.y, output_box.width, output_box.height); - - if (pixman_region32_not_empty(&intersection)) { - uint32_t overlap = region_area(&intersection); - if (overlap >= largest_overlap) { - largest_overlap = overlap; - scene_buffer->primary_output = scene_output; - } - - active_outputs |= 1ull << scene_output->index; - } - - pixman_region32_fini(&intersection); - } - - uint64_t old_active = scene_buffer->active_outputs; - scene_buffer->active_outputs = active_outputs; - - wl_list_for_each(scene_output, outputs, link) { - uint64_t mask = 1ull << scene_output->index; - bool intersects = active_outputs & mask; - bool intersects_before = old_active & mask; - - if (intersects && !intersects_before) { - wl_signal_emit_mutable(&scene_buffer->events.output_enter, scene_output); - } else if (!intersects && intersects_before) { - wl_signal_emit_mutable(&scene_buffer->events.output_leave, scene_output); - } - } - - // if there are active outputs on this node, we should always have a primary - // output - assert(!scene_buffer->active_outputs || scene_buffer->primary_output); -} - -static bool scene_node_update_iterator(struct wlr_scene_node *node, - int lx, int ly, void *_data) { - struct scene_update_data *data = _data; - - struct wlr_box box = { .x = lx, .y = ly }; - scene_node_get_size(node, &box.width, &box.height); - - pixman_region32_subtract(&node->visible, &node->visible, data->update_region); - pixman_region32_union(&node->visible, &node->visible, data->visible); - pixman_region32_intersect_rect(&node->visible, &node->visible, - lx, ly, box.width, box.height); - - if (data->calculate_visibility) { - pixman_region32_t opaque; - pixman_region32_init(&opaque); - scene_node_opaque_region(node, lx, ly, &opaque); - pixman_region32_subtract(data->visible, data->visible, &opaque); - pixman_region32_fini(&opaque); - } - - update_node_update_outputs(node, data->outputs, NULL); - - return false; -} - -static void scene_node_visibility(struct wlr_scene_node *node, - pixman_region32_t *visible) { - if (!node->enabled) { - return; - } - - if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_visibility(child, visible); - } - return; - } - - pixman_region32_union(visible, visible, &node->visible); -} - -static void scene_node_bounds(struct wlr_scene_node *node, - int x, int y, pixman_region32_t *visible) { - if (!node->enabled) { - return; - } - - if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_bounds(child, x + child->x, y + child->y, visible); - } - return; - } - - int width, height; - scene_node_get_size(node, &width, &height); - pixman_region32_union_rect(visible, visible, x, y, width, height); -} - -static void scene_update_region(struct wlr_scene *scene, - pixman_region32_t *update_region) { - pixman_region32_t visible; - pixman_region32_init(&visible); - pixman_region32_copy(&visible, update_region); - - struct scene_update_data data = { - .visible = &visible, - .update_region = update_region, - .outputs = &scene->outputs, - .calculate_visibility = scene->calculate_visibility, - }; - - struct pixman_box32 *region_box = pixman_region32_extents(update_region); - struct wlr_box box = { - .x = region_box->x1, - .y = region_box->y1, - .width = region_box->x2 - region_box->x1, - .height = region_box->y2 - region_box->y1, - }; - - // update node visibility and output enter/leave events - scene_nodes_in_box(&scene->tree.node, &box, scene_node_update_iterator, &data); - - pixman_region32_fini(&visible); -} - -static void scene_node_update(struct wlr_scene_node *node, - pixman_region32_t *damage) { - struct wlr_scene *scene = scene_node_get_root(node); - - int x, y; - if (!wlr_scene_node_coords(node, &x, &y)) { - if (damage) { - scene_update_region(scene, damage); - scene_damage_outputs(scene, damage); - pixman_region32_fini(damage); - } - - return; - } - - pixman_region32_t visible; - if (!damage) { - pixman_region32_init(&visible); - scene_node_visibility(node, &visible); - damage = &visible; - } - - pixman_region32_t update_region; - pixman_region32_init(&update_region); - pixman_region32_copy(&update_region, damage); - scene_node_bounds(node, x, y, &update_region); - - scene_update_region(scene, &update_region); - pixman_region32_fini(&update_region); - - scene_node_visibility(node, damage); - scene_damage_outputs(scene, damage); - pixman_region32_fini(damage); -} - -struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, - int width, int height, const float color[static 4]) { - struct wlr_scene_rect *scene_rect = - calloc(1, sizeof(struct wlr_scene_rect)); - if (scene_rect == NULL) { - return NULL; - } - assert(parent); - scene_node_init(&scene_rect->node, WLR_SCENE_NODE_RECT, parent); - - scene_rect->width = width; - scene_rect->height = height; - memcpy(scene_rect->color, color, sizeof(scene_rect->color)); - - scene_node_update(&scene_rect->node, NULL); - - return scene_rect; -} - -void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) { - if (rect->width == width && rect->height == height) { - return; - } - - rect->width = width; - rect->height = height; - scene_node_update(&rect->node, NULL); -} - -void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]) { - if (memcmp(rect->color, color, sizeof(rect->color)) == 0) { - return; - } - - memcpy(rect->color, color, sizeof(rect->color)); - scene_node_update(&rect->node, NULL); -} - -struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, - struct wlr_buffer *buffer) { - struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); - if (scene_buffer == NULL) { - return NULL; - } - assert(parent); - scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent); - - if (buffer) { - scene_buffer->buffer = wlr_buffer_lock(buffer); - } - - wl_signal_init(&scene_buffer->events.output_enter); - wl_signal_init(&scene_buffer->events.output_leave); - wl_signal_init(&scene_buffer->events.output_present); - wl_signal_init(&scene_buffer->events.frame_done); - pixman_region32_init(&scene_buffer->opaque_region); - - scene_node_update(&scene_buffer->node, NULL); - - return scene_buffer; -} - -void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer, pixman_region32_t *damage) { - // specifying a region for a NULL buffer doesn't make sense. We need to know - // about the buffer to scale the buffer local coordinates down to scene - // coordinates. - assert(buffer || !damage); - - bool update = false; - wlr_buffer_unlock(scene_buffer->buffer); - - wlr_texture_destroy(scene_buffer->texture); - scene_buffer->texture = NULL; - - if (buffer) { - // if this node used to not be mapped or its previous displayed - // buffer region will be different from what the new buffer would - // produce we need to update the node. - update = !scene_buffer->buffer || - (scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0 && - (scene_buffer->buffer->width != buffer->width || - scene_buffer->buffer->height != buffer->height)); - - scene_buffer->buffer = wlr_buffer_lock(buffer); - } else { - update = true; - scene_buffer->buffer = NULL; - } - - if (update) { - scene_node_update(&scene_buffer->node, NULL); - // updating the node will already damage the whole node for us. Return - // early to not damage again - return; - } - - int lx, ly; - if (!wlr_scene_node_coords(&scene_buffer->node, &lx, &ly)) { - return; - } - - pixman_region32_t fallback_damage; - pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height); - if (!damage) { - damage = &fallback_damage; - } - - struct wlr_fbox box = scene_buffer->src_box; - if (wlr_fbox_empty(&box)) { - box.x = 0; - box.y = 0; - box.width = buffer->width; - box.height = buffer->height; - } - - wlr_fbox_transform(&box, &box, scene_buffer->transform, - buffer->width, buffer->height); - - float scale_x, scale_y; - if (scene_buffer->dst_width || scene_buffer->dst_height) { - scale_x = scene_buffer->dst_width / box.width; - scale_y = scene_buffer->dst_height / box.height; - } else { - scale_x = buffer->width / box.width; - scale_y = buffer->height / box.height; - } - - pixman_region32_t trans_damage; - pixman_region32_init(&trans_damage); - wlr_region_transform(&trans_damage, damage, - scene_buffer->transform, buffer->width, buffer->height); - pixman_region32_intersect_rect(&trans_damage, &trans_damage, - box.x, box.y, box.width, box.height); - pixman_region32_translate(&trans_damage, -box.x, -box.y); - - struct wlr_scene *scene = scene_node_get_root(&scene_buffer->node); - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, &scene->outputs, link) { - float output_scale = scene_output->output->scale; - float output_scale_x = output_scale * scale_x; - float output_scale_y = output_scale * scale_y; - pixman_region32_t output_damage; - pixman_region32_init(&output_damage); - wlr_region_scale_xy(&output_damage, &trans_damage, - output_scale_x, output_scale_y); - - // One output pixel will match (buffer_scale_x)x(buffer_scale_y) buffer pixels. - // If the buffer is upscaled on the given axis (output_scale_* > 1.0, - // buffer_scale_* < 1.0), its contents will bleed into adjacent - // (ceil(output_scale_* / 2)) output pixels because of linear filtering. - // Additionally, if the buffer is downscaled (output_scale_* < 1.0, - // buffer_scale_* > 1.0), and one output pixel matches a non-integer number of - // buffer pixels, its contents will bleed into neighboring output pixels. - // Handle both cases by computing buffer_scale_{x,y} and checking if they are - // integer numbers; ceilf() is used to ensure that the distance is at least 1. - float buffer_scale_x = 1.0f / output_scale_x; - float buffer_scale_y = 1.0f / output_scale_y; - int dist_x = floor(buffer_scale_x) != buffer_scale_x ? - (int)ceilf(output_scale_x / 2.0f) : 0; - int dist_y = floor(buffer_scale_y) != buffer_scale_y ? - (int)ceilf(output_scale_y / 2.0f) : 0; - // TODO: expand with per-axis distances - wlr_region_expand(&output_damage, &output_damage, - dist_x >= dist_y ? dist_x : dist_y); - - pixman_region32_t cull_region; - pixman_region32_init(&cull_region); - pixman_region32_copy(&cull_region, &scene_buffer->node.visible); - scale_output_damage(&cull_region, output_scale); - pixman_region32_translate(&cull_region, -lx * output_scale, -ly * output_scale); - pixman_region32_intersect(&output_damage, &output_damage, &cull_region); - pixman_region32_fini(&cull_region); - - pixman_region32_translate(&output_damage, - (lx - scene_output->x) * output_scale, - (ly - scene_output->y) * output_scale); - if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { - wlr_output_schedule_frame(scene_output->output); - } - pixman_region32_fini(&output_damage); - } - - pixman_region32_fini(&trans_damage); - pixman_region32_fini(&fallback_damage); -} - -void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer) { - wlr_scene_buffer_set_buffer_with_damage(scene_buffer, buffer, NULL); -} - -void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, - pixman_region32_t *region) { - if (pixman_region32_equal(&scene_buffer->opaque_region, region)) { - return; - } - - pixman_region32_copy(&scene_buffer->opaque_region, region); - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, - const struct wlr_fbox *box) { - struct wlr_fbox *cur = &scene_buffer->src_box; - if ((wlr_fbox_empty(box) && wlr_fbox_empty(cur)) || - (box != NULL && wlr_fbox_equal(cur, box))) { - return; - } - - if (box != NULL) { - memcpy(cur, box, sizeof(*box)); - } else { - memset(cur, 0, sizeof(*cur)); - } - - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, - int width, int height) { - if (scene_buffer->dst_width == width && scene_buffer->dst_height == height) { - return; - } - - scene_buffer->dst_width = width; - scene_buffer->dst_height = height; - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, - enum wl_output_transform transform) { - if (scene_buffer->transform == transform) { - return; - } - - scene_buffer->transform = transform; - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, - struct timespec *now) { - if (pixman_region32_not_empty(&scene_buffer->node.visible)) { - wl_signal_emit_mutable(&scene_buffer->events.frame_done, now); - } -} - -static struct wlr_texture *scene_buffer_get_texture( - struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { - struct wlr_client_buffer *client_buffer = - wlr_client_buffer_get(scene_buffer->buffer); - if (client_buffer != NULL) { - return client_buffer->texture; - } - - if (scene_buffer->texture != NULL) { - return scene_buffer->texture; - } - - scene_buffer->texture = - wlr_texture_from_buffer(renderer, scene_buffer->buffer); - return scene_buffer->texture; -} - -static void scene_node_get_size(struct wlr_scene_node *node, - int *width, int *height) { - *width = 0; - *height = 0; - - switch (node->type) { - case WLR_SCENE_NODE_TREE: - return; - case WLR_SCENE_NODE_RECT:; - struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); - *width = scene_rect->width; - *height = scene_rect->height; - break; - case WLR_SCENE_NODE_BUFFER:; - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) { - *width = scene_buffer->dst_width; - *height = scene_buffer->dst_height; - } else if (scene_buffer->buffer) { - if (scene_buffer->transform & WL_OUTPUT_TRANSFORM_90) { - *height = scene_buffer->buffer->width; - *width = scene_buffer->buffer->height; - } else { - *width = scene_buffer->buffer->width; - *height = scene_buffer->buffer->height; - } - } - break; - } -} - -static int scale_length(int length, int offset, float scale) { - return round((offset + length) * scale) - round(offset * scale); -} - -static 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); -} - -void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { - if (node->enabled == enabled) { - return; - } - - int x, y; - pixman_region32_t visible; - pixman_region32_init(&visible); - if (wlr_scene_node_coords(node, &x, &y)) { - scene_node_visibility(node, &visible); - } - - node->enabled = enabled; - - scene_node_update(node, &visible); -} - -void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { - if (node->x == x && node->y == y) { - return; - } - - node->x = x; - node->y = y; - scene_node_update(node, NULL); -} - -void wlr_scene_node_place_above(struct wlr_scene_node *node, - struct wlr_scene_node *sibling) { - assert(node != sibling); - assert(node->parent == sibling->parent); - - if (node->link.prev == &sibling->link) { - return; - } - - wl_list_remove(&node->link); - wl_list_insert(&sibling->link, &node->link); - scene_node_update(node, NULL); -} - -void wlr_scene_node_place_below(struct wlr_scene_node *node, - struct wlr_scene_node *sibling) { - assert(node != sibling); - assert(node->parent == sibling->parent); - - if (node->link.next == &sibling->link) { - return; - } - - wl_list_remove(&node->link); - wl_list_insert(sibling->link.prev, &node->link); - scene_node_update(node, NULL); -} - -void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) { - struct wlr_scene_node *current_top = wl_container_of( - node->parent->children.prev, current_top, link); - if (node == current_top) { - return; - } - wlr_scene_node_place_above(node, current_top); -} - -void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) { - struct wlr_scene_node *current_bottom = wl_container_of( - node->parent->children.next, current_bottom, link); - if (node == current_bottom) { - return; - } - wlr_scene_node_place_below(node, current_bottom); -} - -void wlr_scene_node_reparent(struct wlr_scene_node *node, - struct wlr_scene_tree *new_parent) { - assert(new_parent != NULL); - - if (node->parent == new_parent) { - return; - } - - /* Ensure that a node cannot become its own ancestor */ - for (struct wlr_scene_tree *ancestor = new_parent; ancestor != NULL; - ancestor = ancestor->node.parent) { - assert(&ancestor->node != node); - } - - int x, y; - pixman_region32_t visible; - pixman_region32_init(&visible); - if (wlr_scene_node_coords(node, &x, &y)) { - scene_node_visibility(node, &visible); - } - - wl_list_remove(&node->link); - node->parent = new_parent; - wl_list_insert(new_parent->children.prev, &node->link); - scene_node_update(node, &visible); -} - -bool wlr_scene_node_coords(struct wlr_scene_node *node, - int *lx_ptr, int *ly_ptr) { - assert(node); - - int lx = 0, ly = 0; - bool enabled = true; - while (true) { - lx += node->x; - ly += node->y; - enabled = enabled && node->enabled; - if (node->parent == NULL) { - break; - } - - node = &node->parent->node; - } - - *lx_ptr = lx; - *ly_ptr = ly; - return enabled; -} - -static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node, - int lx, int ly, wlr_scene_buffer_iterator_func_t user_iterator, - void *user_data) { - if (!node->enabled) { - return; - } - - lx += node->x; - ly += node->y; - - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - user_iterator(scene_buffer, lx, ly, user_data); - } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data); - } - } -} - -void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node, - wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { - scene_node_for_each_scene_buffer(node, 0, 0, user_iterator, user_data); -} - -struct node_at_data { - double lx, ly; - double rx, ry; - struct wlr_scene_node *node; -}; - -static bool scene_node_at_iterator(struct wlr_scene_node *node, - int lx, int ly, void *data) { - struct node_at_data *at_data = data; - - double rx = at_data->lx - lx; - double ry = at_data->ly - ly; - - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - if (scene_buffer->point_accepts_input && - !scene_buffer->point_accepts_input(scene_buffer, rx, ry)) { - return false; - } - } - - at_data->rx = rx; - at_data->ry = ry; - at_data->node = node; - return true; -} - -struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, - double lx, double ly, double *nx, double *ny) { - struct wlr_box box = { - .x = floor(lx), - .y = floor(ly), - .width = 1, - .height = 1 - }; - - struct node_at_data data = { - .lx = lx, - .ly = ly - }; - - if (scene_nodes_in_box(node, &box, scene_node_at_iterator, &data)) { - if (nx) { - *nx = data.rx; - } - if (ny) { - *ny = data.ry; - } - return data.node; - } - - return NULL; -} - -static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { - struct wlr_renderer *renderer = output->renderer; - assert(renderer); - - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - - int ow, oh; - wlr_output_transformed_resolution(output, &ow, &oh); - - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_box_transform(&box, &box, transform, ow, oh); - - wlr_renderer_scissor(renderer, &box); -} - -static void render_rect(struct wlr_output *output, - pixman_region32_t *damage, const float color[static 4], - const struct wlr_box *box, const float matrix[static 9]) { - struct wlr_renderer *renderer = output->renderer; - assert(renderer); - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(output, &rects[i]); - wlr_render_rect(renderer, box, color, matrix); - } -} - -static void render_texture(struct wlr_output *output, - pixman_region32_t *damage, struct wlr_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - const float matrix[static 9]) { - struct wlr_renderer *renderer = output->renderer; - assert(renderer); - - struct wlr_fbox default_src_box = {0}; - if (wlr_fbox_empty(src_box)) { - default_src_box.width = texture->width; - default_src_box.height = texture->height; - src_box = &default_src_box; - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(output, &rects[i]); - wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, 1.0); - } -} - -static void scene_node_render(struct wlr_scene_node *node, - struct wlr_scene_output *scene_output, pixman_region32_t *damage) { - int x, y; - wlr_scene_node_coords(node, &x, &y); - x -= scene_output->x; - y -= scene_output->y; - - struct wlr_output *output = scene_output->output; - - pixman_region32_t render_region; - pixman_region32_init(&render_region); - pixman_region32_copy(&render_region, &node->visible); - pixman_region32_translate(&render_region, -scene_output->x, -scene_output->y); - scale_output_damage(&render_region, output->scale); - pixman_region32_intersect(&render_region, &render_region, damage); - if (!pixman_region32_not_empty(&render_region)) { - pixman_region32_fini(&render_region); - return; - } - - struct wlr_box dst_box = { - .x = x, - .y = y, - }; - scene_node_get_size(node, &dst_box.width, &dst_box.height); - scale_box(&dst_box, output->scale); - - struct wlr_texture *texture; - float matrix[9]; - enum wl_output_transform transform; - switch (node->type) { - case WLR_SCENE_NODE_TREE: - assert(false); - break; - case WLR_SCENE_NODE_RECT:; - struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); - - render_rect(output, &render_region, scene_rect->color, &dst_box, - output->transform_matrix); - break; - case WLR_SCENE_NODE_BUFFER:; - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - assert(scene_buffer->buffer); - - struct wlr_renderer *renderer = output->renderer; - texture = scene_buffer_get_texture(scene_buffer, renderer); - if (texture == NULL) { - break; - } - - transform = wlr_output_transform_invert(scene_buffer->transform); - wlr_matrix_project_box(matrix, &dst_box, transform, 0.0, - output->transform_matrix); - - render_texture(output, &render_region, texture, &scene_buffer->src_box, - &dst_box, matrix); - - wl_signal_emit_mutable(&scene_buffer->events.output_present, scene_output); - break; - } - - pixman_region32_fini(&render_region); -} - -static void scene_handle_presentation_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene *scene = - wl_container_of(listener, scene, presentation_destroy); - wl_list_remove(&scene->presentation_destroy.link); - wl_list_init(&scene->presentation_destroy.link); - scene->presentation = NULL; -} - -void wlr_scene_set_presentation(struct wlr_scene *scene, - struct wlr_presentation *presentation) { - assert(scene->presentation == NULL); - scene->presentation = presentation; - scene->presentation_destroy.notify = scene_handle_presentation_destroy; - wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); -} - -static void scene_output_handle_destroy(struct wlr_addon *addon) { - struct wlr_scene_output *scene_output = - wl_container_of(addon, scene_output, addon); - wlr_scene_output_destroy(scene_output); -} - -static const struct wlr_addon_interface output_addon_impl = { - .name = "wlr_scene_output", - .destroy = scene_output_handle_destroy, -}; - -static void scene_node_output_update(struct wlr_scene_node *node, - struct wl_list *outputs, struct wlr_scene_output *ignore) { - if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_output_update(child, outputs, ignore); - } - return; - } - - update_node_update_outputs(node, outputs, ignore); -} - -static void scene_output_update_geometry(struct wlr_scene_output *scene_output) { - int width, height; - wlr_output_transformed_resolution(scene_output->output, &width, &height); - wlr_damage_ring_set_bounds(&scene_output->damage_ring, width, height); - wlr_output_schedule_frame(scene_output->output); - - scene_node_output_update(&scene_output->scene->tree.node, - &scene_output->scene->outputs, NULL); -} - -static void scene_output_handle_commit(struct wl_listener *listener, void *data) { - struct wlr_scene_output *scene_output = wl_container_of(listener, - scene_output, output_commit); - struct wlr_output_event_commit *event = data; - - if (event->committed & (WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM | - WLR_OUTPUT_STATE_SCALE | - WLR_OUTPUT_STATE_ENABLED)) { - scene_output_update_geometry(scene_output); - } -} - -static void scene_output_handle_mode(struct wl_listener *listener, void *data) { - struct wlr_scene_output *scene_output = wl_container_of(listener, - scene_output, output_mode); - scene_output_update_geometry(scene_output); -} - -static void scene_output_handle_damage(struct wl_listener *listener, void *data) { - struct wlr_scene_output *scene_output = wl_container_of(listener, - scene_output, output_damage); - struct wlr_output_event_damage *event = data; - if (wlr_damage_ring_add(&scene_output->damage_ring, event->damage)) { - wlr_output_schedule_frame(scene_output->output); - } -} - -static void scene_output_handle_needs_frame(struct wl_listener *listener, void *data) { - struct wlr_scene_output *scene_output = wl_container_of(listener, - scene_output, output_needs_frame); - wlr_output_schedule_frame(scene_output->output); -} - -struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, - struct wlr_output *output) { - struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output)); - if (scene_output == NULL) { - return NULL; - } - - scene_output->output = output; - scene_output->scene = scene; - wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl); - - wlr_damage_ring_init(&scene_output->damage_ring); - wl_list_init(&scene_output->damage_highlight_regions); - - int prev_output_index = -1; - struct wl_list *prev_output_link = &scene->outputs; - - struct wlr_scene_output *current_output; - wl_list_for_each(current_output, &scene->outputs, link) { - if (prev_output_index + 1 != current_output->index) { - break; - } - - prev_output_index = current_output->index; - prev_output_link = ¤t_output->link; - } - - scene_output->index = prev_output_index + 1; - assert(scene_output->index < 64); - wl_list_insert(prev_output_link, &scene_output->link); - - wl_signal_init(&scene_output->events.destroy); - - scene_output->output_commit.notify = scene_output_handle_commit; - wl_signal_add(&output->events.commit, &scene_output->output_commit); - - scene_output->output_mode.notify = scene_output_handle_mode; - wl_signal_add(&output->events.mode, &scene_output->output_mode); - - scene_output->output_damage.notify = scene_output_handle_damage; - wl_signal_add(&output->events.damage, &scene_output->output_damage); - - scene_output->output_needs_frame.notify = scene_output_handle_needs_frame; - wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame); - - scene_output_update_geometry(scene_output); - - return scene_output; -} - -static void highlight_region_destroy(struct highlight_region *damage) { - wl_list_remove(&damage->link); - pixman_region32_fini(&damage->region); - free(damage); -} - -void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { - if (scene_output == NULL) { - return; - } - - wl_signal_emit_mutable(&scene_output->events.destroy, NULL); - - scene_node_output_update(&scene_output->scene->tree.node, - &scene_output->scene->outputs, scene_output); - - struct highlight_region *damage, *tmp_damage; - wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) { - highlight_region_destroy(damage); - } - - wlr_addon_finish(&scene_output->addon); - wlr_damage_ring_finish(&scene_output->damage_ring); - wl_list_remove(&scene_output->link); - wl_list_remove(&scene_output->output_commit.link); - wl_list_remove(&scene_output->output_mode.link); - wl_list_remove(&scene_output->output_damage.link); - wl_list_remove(&scene_output->output_needs_frame.link); - - wl_array_release(&scene_output->render_list); - free(scene_output); -} - -struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, - struct wlr_output *output) { - struct wlr_addon *addon = - wlr_addon_find(&output->addons, scene, &output_addon_impl); - if (addon == NULL) { - return NULL; - } - struct wlr_scene_output *scene_output = - wl_container_of(addon, scene_output, addon); - return scene_output; -} - -void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, - int lx, int ly) { - if (scene_output->x == lx && scene_output->y == ly) { - return; - } - - scene_output->x = lx; - scene_output->y = ly; - - scene_output_update_geometry(scene_output); -} - -static bool scene_node_invisible(struct wlr_scene_node *node) { - if (node->type == WLR_SCENE_NODE_TREE) { - return true; - } else if (node->type == WLR_SCENE_NODE_RECT) { - struct wlr_scene_rect *rect = scene_rect_from_node(node); - - return rect->color[3] == 0.f; - } else if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - - return buffer->buffer == NULL; - } - - return false; -} - -struct render_list_constructor_data { - struct wlr_box box; - struct wl_array *render_list; - bool calculate_visibility; -}; - -static bool construct_render_list_iterator(struct wlr_scene_node *node, - int lx, int ly, void *_data) { - struct render_list_constructor_data *data = _data; - - if (scene_node_invisible(node)) { - return false; - } - - // while rendering, the background should always be black. - // If we see a black rect, we can ignore rendering everything under the rect - // and even the rect itself. - if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) { - struct wlr_scene_rect *rect = scene_rect_from_node(node); - float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; - - if (memcmp(rect->color, black, sizeof(float) * 4) == 0) { - return false; - } - } - - pixman_region32_t intersection; - pixman_region32_init(&intersection); - pixman_region32_intersect_rect(&intersection, &node->visible, - data->box.x, data->box.y, - data->box.width, data->box.height); - if (!pixman_region32_not_empty(&intersection)) { - pixman_region32_fini(&intersection); - return false; - } - - pixman_region32_fini(&intersection); - - struct wlr_scene_node **entry = wl_array_add(data->render_list, - sizeof(struct wlr_scene_node *)); - if (entry) { - *entry = node; - } - return false; -} - -static bool scene_node_try_direct_scanout(struct wlr_scene_node *node, - struct wlr_scene_output *scene_output, struct wlr_box *box) { - if (!scene_output->scene->direct_scanout) { - return false; - } - - if (scene_output->scene->debug_damage_option == - WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { - // We don't want to enter direct scan out if we have highlight regions - // enabled. Otherwise, we won't be able to render the damage regions. - return false; - } - - if (node->type != WLR_SCENE_NODE_BUFFER) { - return false; - } - - struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - struct wlr_output *output = scene_output->output; - - struct wlr_fbox default_box = {0}; - if (buffer->transform & WL_OUTPUT_TRANSFORM_90) { - default_box.width = buffer->buffer->height; - default_box.height = buffer->buffer->width; - } else { - default_box.width = buffer->buffer->width; - default_box.height = buffer->buffer->height; - } - - if (!wlr_fbox_empty(&buffer->src_box) && - !wlr_fbox_equal(&buffer->src_box, &default_box)) { - return false; - } - - if (buffer->transform != output->transform) { - return false; - } - - struct wlr_box node_box; - wlr_scene_node_coords(node, &node_box.x, &node_box.y); - scene_node_get_size(node, &node_box.width, &node_box.height); - - if (!wlr_box_equal(box, &node_box)) { - return false; - } - - wlr_output_attach_buffer(output, buffer->buffer); - if (!wlr_output_test(output)) { - wlr_output_rollback(output); - return false; - } - - return wlr_output_commit(output); -} - -bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { - struct wlr_output *output = scene_output->output; - enum wlr_scene_debug_damage_option debug_damage = - scene_output->scene->debug_damage_option; - - struct wlr_renderer *renderer = output->renderer; - assert(renderer != NULL); - - struct render_list_constructor_data list_con = { - .box = { .x = scene_output->x, .y = scene_output->y }, - .render_list = &scene_output->render_list, - .calculate_visibility = scene_output->scene->calculate_visibility, - }; - wlr_output_effective_resolution(output, - &list_con.box.width, &list_con.box.height); - - list_con.render_list->size = 0; - scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box, - construct_render_list_iterator, &list_con); - array_realloc(list_con.render_list, list_con.render_list->size); - - int list_len = list_con.render_list->size / sizeof(struct wlr_scene_node *); - struct wlr_scene_node **list_data = list_con.render_list->data; - - // if there is only one thing to render let's see if that thing can be - // directly scanned out - bool scanout = false; - if (list_len == 1) { - struct wlr_scene_node *node = list_data[0]; - scanout = scene_node_try_direct_scanout(node, scene_output, &list_con.box); - } - - if (scene_output->prev_scanout != scanout) { - scene_output->prev_scanout = scanout; - wlr_log(WLR_DEBUG, "Direct scan-out %s", - scanout ? "enabled" : "disabled"); - // When exiting direct scan-out, damage everything - wlr_damage_ring_add_whole(&scene_output->damage_ring); - } - - if (scanout) { - struct wlr_scene_node *node = list_data[0]; - - assert(node->type == WLR_SCENE_NODE_BUFFER); - struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - wl_signal_emit_mutable(&buffer->events.output_present, scene_output); - return true; - } - - if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) { - wlr_damage_ring_add_whole(&scene_output->damage_ring); - } - - struct timespec now; - if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { - struct wl_list *regions = &scene_output->damage_highlight_regions; - clock_gettime(CLOCK_MONOTONIC, &now); - - // add the current frame's damage if there is damage - if (pixman_region32_not_empty(&scene_output->damage_ring.current)) { - struct highlight_region *current_damage = - calloc(1, sizeof(*current_damage)); - if (current_damage) { - pixman_region32_init(¤t_damage->region); - pixman_region32_copy(¤t_damage->region, - &scene_output->damage_ring.current); - current_damage->when = now; - wl_list_insert(regions, ¤t_damage->link); - } - } - - pixman_region32_t acc_damage; - pixman_region32_init(&acc_damage); - struct highlight_region *damage, *tmp_damage; - wl_list_for_each_safe(damage, tmp_damage, regions, link) { - // remove overlaping damage regions - pixman_region32_subtract(&damage->region, &damage->region, &acc_damage); - pixman_region32_union(&acc_damage, &acc_damage, &damage->region); - - // if this damage is too old or has nothing in it, get rid of it - struct timespec time_diff; - timespec_sub(&time_diff, &now, &damage->when); - if (timespec_to_msec(&time_diff) >= HIGHLIGHT_DAMAGE_FADEOUT_TIME || - !pixman_region32_not_empty(&damage->region)) { - highlight_region_destroy(damage); - } - } - - wlr_damage_ring_add(&scene_output->damage_ring, &acc_damage); - pixman_region32_fini(&acc_damage); - } - - int buffer_age; - if (!wlr_output_attach_render(output, &buffer_age)) { - return false; - } - - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, - buffer_age, &damage); - if (!output->needs_frame && !pixman_region32_not_empty( - &scene_output->damage_ring.current)) { - pixman_region32_fini(&damage); - wlr_output_rollback(output); - return true; - } - - wlr_renderer_begin(renderer, output->width, output->height); - - pixman_region32_t background; - pixman_region32_init(&background); - pixman_region32_copy(&background, &damage); - - // Cull areas of the background that are occluded by opaque regions of - // scene nodes above. Those scene nodes will just render atop having us - // never see the background. - if (scene_output->scene->calculate_visibility) { - float output_scale = scene_output->output->scale; - - for (int i = list_len - 1; i >= 0; i--) { - struct wlr_scene_node *node = list_data[i]; - int x, y; - wlr_scene_node_coords(node, &x, &y); - - // We must only cull opaque regions that are visible by the node. - // The node's visibility will have the knowledge of a black rect - // that may have been omitted from the render list via the black - // rect optimization. In order to ensure we don't cull background - // rendering in that black rect region, consider the node's visibility. - pixman_region32_t opaque; - pixman_region32_init(&opaque); - scene_node_opaque_region(node, x, y, &opaque); - pixman_region32_intersect(&opaque, &opaque, &node->visible); - - pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); - wlr_region_scale(&opaque, &opaque, output_scale); - pixman_region32_subtract(&background, &background, &opaque); - pixman_region32_fini(&opaque); - } - - if (floor(output_scale) != output_scale) { - wlr_region_expand(&background, &background, 1); - - // reintersect with the damage because we never want to render - // outside of the damage region - pixman_region32_intersect(&background, &background, &damage); - } - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&background, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(output, &rects[i]); - wlr_renderer_clear(renderer, (float[4]){ 0.0, 0.0, 0.0, 1.0 }); - } - pixman_region32_fini(&background); - - for (int i = list_len - 1; i >= 0; i--) { - struct wlr_scene_node *node = list_data[i]; - scene_node_render(node, scene_output, &damage); - } - - wlr_renderer_scissor(renderer, NULL); - - if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { - struct highlight_region *damage; - wl_list_for_each(damage, &scene_output->damage_highlight_regions, link) { - struct timespec time_diff; - timespec_sub(&time_diff, &now, &damage->when); - int64_t time_diff_ms = timespec_to_msec(&time_diff); - float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage->region, &nrects); - for (int i = 0; i < nrects; ++i) { - struct wlr_box box = { - .x = rects[i].x1, - .y = rects[i].y1, - .width = rects[i].x2 - rects[i].x1, - .height = rects[i].y2 - rects[i].y1, - }; - - float color[4] = { alpha * .5, 0.0, 0.0, alpha * .5 }; - wlr_render_rect(renderer, &box, color, output->transform_matrix); - } - } - } - - wlr_output_render_software_cursors(output, &damage); - - wlr_renderer_end(renderer); - pixman_region32_fini(&damage); - - int tr_width, tr_height; - wlr_output_transformed_resolution(output, &tr_width, &tr_height); - - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - wlr_region_transform(&frame_damage, - &scene_output->damage_ring.current, - transform, tr_width, tr_height); - wlr_output_set_damage(output, &frame_damage); - pixman_region32_fini(&frame_damage); - - bool success = wlr_output_commit(output); - - if (success) { - wlr_damage_ring_rotate(&scene_output->damage_ring); - } - - if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && - !wl_list_empty(&scene_output->damage_highlight_regions)) { - wlr_output_schedule_frame(scene_output->output); - } - - return success; -} - -static void scene_node_send_frame_done(struct wlr_scene_node *node, - struct wlr_scene_output *scene_output, struct timespec *now) { - if (!node->enabled) { - return; - } - - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = - wlr_scene_buffer_from_node(node); - - if (scene_buffer->primary_output == scene_output) { - wlr_scene_buffer_send_frame_done(scene_buffer, now); - } - } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_send_frame_done(child, scene_output, now); - } - } -} - -void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, - struct timespec *now) { - scene_node_send_frame_done(&scene_output->scene->tree.node, - scene_output, now); -} - -static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, - struct wlr_scene_node *node, int lx, int ly, - wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { - if (!node->enabled) { - return; - } - - lx += node->x; - ly += node->y; - - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_box node_box = { .x = lx, .y = ly }; - scene_node_get_size(node, &node_box.width, &node_box.height); - - struct wlr_box intersection; - if (wlr_box_intersection(&intersection, output_box, &node_box)) { - struct wlr_scene_buffer *scene_buffer = - wlr_scene_buffer_from_node(node); - user_iterator(scene_buffer, lx, ly, user_data); - } - } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_output_for_each_scene_buffer(output_box, child, lx, ly, - user_iterator, user_data); - } - } -} - -void wlr_scene_output_for_each_buffer(struct wlr_scene_output *scene_output, - wlr_scene_buffer_iterator_func_t iterator, void *user_data) { - struct wlr_box box = { .x = scene_output->x, .y = scene_output->y }; - wlr_output_effective_resolution(scene_output->output, - &box.width, &box.height); - scene_output_for_each_scene_buffer(&box, &scene_output->scene->tree.node, 0, 0, - iterator, user_data); -} diff --git a/src/xdg_shell.c b/src/xdg_shell.c deleted file mode 100644 index 1688348..0000000 --- a/src/xdg_shell.c +++ /dev/null @@ -1,124 +0,0 @@ -#include -#include -#include - -struct wlr_scene_xdg_surface { - struct wlr_scene_tree *tree; - struct wlr_xdg_surface *xdg_surface; - struct wlr_scene_tree *surface_tree; - - struct wl_listener tree_destroy; - struct wl_listener xdg_surface_destroy; - struct wl_listener xdg_surface_map; - struct wl_listener xdg_surface_unmap; - struct wl_listener xdg_surface_commit; -}; - -static void scene_xdg_surface_handle_tree_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, tree_destroy); - // tree and surface_node will be cleaned up by scene_node_finish - wl_list_remove(&scene_xdg_surface->tree_destroy.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_destroy.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_map.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_unmap.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_commit.link); - free(scene_xdg_surface); -} - -static void scene_xdg_surface_handle_xdg_surface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_destroy); - wlr_scene_node_destroy(&scene_xdg_surface->tree->node); -} - -static void scene_xdg_surface_handle_xdg_surface_map(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_map); - wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, true); -} - -static void scene_xdg_surface_handle_xdg_surface_unmap(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_unmap); - wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, false); -} - -static void scene_xdg_surface_update_position( - struct wlr_scene_xdg_surface *scene_xdg_surface) { - struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface; - - struct wlr_box geo = {0}; - wlr_xdg_surface_get_geometry(xdg_surface, &geo); - wlr_scene_node_set_position(&scene_xdg_surface->surface_tree->node, - -geo.x, -geo.y); - - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - struct wlr_xdg_popup *popup = xdg_surface->popup; - wlr_scene_node_set_position(&scene_xdg_surface->tree->node, - popup->current.geometry.x, popup->current.geometry.y); - } -} - -static void scene_xdg_surface_handle_xdg_surface_commit(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_commit); - scene_xdg_surface_update_position(scene_xdg_surface); -} - -struct wlr_scene_tree *wlr_scene_xdg_surface_create( - struct wlr_scene_tree *parent, struct wlr_xdg_surface *xdg_surface) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - calloc(1, sizeof(*scene_xdg_surface)); - if (scene_xdg_surface == NULL) { - return NULL; - } - - scene_xdg_surface->xdg_surface = xdg_surface; - - scene_xdg_surface->tree = wlr_scene_tree_create(parent); - if (scene_xdg_surface->tree == NULL) { - free(scene_xdg_surface); - return NULL; - } - - scene_xdg_surface->surface_tree = wlr_scene_subsurface_tree_create( - scene_xdg_surface->tree, xdg_surface->surface); - if (scene_xdg_surface->surface_tree == NULL) { - wlr_scene_node_destroy(&scene_xdg_surface->tree->node); - free(scene_xdg_surface); - return NULL; - } - - scene_xdg_surface->tree_destroy.notify = - scene_xdg_surface_handle_tree_destroy; - wl_signal_add(&scene_xdg_surface->tree->node.events.destroy, - &scene_xdg_surface->tree_destroy); - - scene_xdg_surface->xdg_surface_destroy.notify = - scene_xdg_surface_handle_xdg_surface_destroy; - wl_signal_add(&xdg_surface->events.destroy, &scene_xdg_surface->xdg_surface_destroy); - - scene_xdg_surface->xdg_surface_map.notify = - scene_xdg_surface_handle_xdg_surface_map; - wl_signal_add(&xdg_surface->events.map, &scene_xdg_surface->xdg_surface_map); - - scene_xdg_surface->xdg_surface_unmap.notify = - scene_xdg_surface_handle_xdg_surface_unmap; - wl_signal_add(&xdg_surface->events.unmap, &scene_xdg_surface->xdg_surface_unmap); - - scene_xdg_surface->xdg_surface_commit.notify = - scene_xdg_surface_handle_xdg_surface_commit; - wl_signal_add(&xdg_surface->surface->events.commit, - &scene_xdg_surface->xdg_surface_commit); - - wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, xdg_surface->mapped); - scene_xdg_surface_update_position(scene_xdg_surface); - - return scene_xdg_surface->tree; -} diff --git a/tinywl/meson.build b/tinywl/meson.build index 82d31d2..74fe731 100644 --- a/tinywl/meson.build +++ b/tinywl/meson.build @@ -1,5 +1,5 @@ executable( 'tinywl', ['tinywl.c', protocols_client_header['xdg-shell']], - dependencies: wlroots, +# dependencies: wlroots, ) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 7e3c8c1..80eb6bd 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -26,6 +25,8 @@ #include #include +#include "types/wlr_scene.h" + /* For brevity's sake, struct members are annotated where they are used. */ enum tinywl_cursor_mode { TINYWL_CURSOR_PASSTHROUGH, diff --git a/types/meson.build b/types/meson.build new file mode 100644 index 0000000..0654669 --- /dev/null +++ b/types/meson.build @@ -0,0 +1,8 @@ +wlr_files += files( + 'scene/subsurface_tree.c', + 'scene/surface.c', + 'scene/wlr_scene.c', + 'scene/output_layout.c', + 'scene/xdg_shell.c', + 'scene/layer_shell_v1.c', +) diff --git a/types/scene/layer_shell_v1.c b/types/scene/layer_shell_v1.c new file mode 100644 index 0000000..3ed616a --- /dev/null +++ b/types/scene/layer_shell_v1.c @@ -0,0 +1,186 @@ +#include +#include +#include + +static void scene_layer_surface_handle_tree_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, tree_destroy); + // tree and surface_node will be cleaned up by scene_node_finish + wl_list_remove(&scene_layer_surface->tree_destroy.link); + wl_list_remove(&scene_layer_surface->layer_surface_destroy.link); + wl_list_remove(&scene_layer_surface->layer_surface_map.link); + wl_list_remove(&scene_layer_surface->layer_surface_unmap.link); + free(scene_layer_surface); +} + +static void scene_layer_surface_handle_layer_surface_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, layer_surface_destroy); + wlr_scene_node_destroy(&scene_layer_surface->tree->node); +} + +static void scene_layer_surface_handle_layer_surface_map( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, layer_surface_map); + wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, true); +} + +static void scene_layer_surface_handle_layer_surface_unmap( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, layer_surface_unmap); + wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, false); +} + +static void layer_surface_exclusive_zone( + struct wlr_layer_surface_v1_state *state, + struct wlr_box *usable_area) { + switch (state->anchor) { + case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP: + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): + // Anchor top + usable_area->y += state->exclusive_zone + state->margin.top; + usable_area->height -= state->exclusive_zone + state->margin.top; + break; + case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM: + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): + // Anchor bottom + usable_area->height -= state->exclusive_zone + state->margin.bottom; + break; + case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT: + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT): + // Anchor left + usable_area->x += state->exclusive_zone + state->margin.left; + usable_area->width -= state->exclusive_zone + state->margin.left; + break; + case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT: + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): + // Anchor right + usable_area->width -= state->exclusive_zone + state->margin.right; + break; + } +} + +void wlr_scene_layer_surface_v1_configure( + struct wlr_scene_layer_surface_v1 *scene_layer_surface, + const struct wlr_box *full_area, struct wlr_box *usable_area) { + struct wlr_layer_surface_v1 *layer_surface = + scene_layer_surface->layer_surface; + struct wlr_layer_surface_v1_state *state = &layer_surface->current; + + // If the exclusive zone is set to -1, the layer surface will use the + // full area of the output, otherwise it is constrained to the + // remaining usable area. + struct wlr_box bounds; + if (state->exclusive_zone == -1) { + bounds = *full_area; + } else { + bounds = *usable_area; + } + + struct wlr_box box = { + .width = state->desired_width, + .height = state->desired_height, + }; + + // Horizontal positioning + if (box.width == 0) { + box.x = bounds.x + state->margin.left; + box.width = bounds.width - + (state->margin.left + state->margin.right); + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT && + state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x = bounds.x + bounds.width/2 -box.width/2; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { + box.x = bounds.x + state->margin.left; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x = bounds.x + bounds.width - box.width - state->margin.right; + } else { + box.x = bounds.x + bounds.width/2 - box.width/2; + } + + // Vertical positioning + if (box.height == 0) { + box.y = bounds.y + state->margin.top; + box.height = bounds.height - + (state->margin.top + state->margin.bottom); + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP && + state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y = bounds.y + bounds.height/2 - box.height/2; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { + box.y = bounds.y + state->margin.top; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y = bounds.y + bounds.height - box.height - state->margin.bottom; + } else { + box.y = bounds.y + bounds.height/2 - box.height/2; + } + + wlr_scene_node_set_position(&scene_layer_surface->tree->node, box.x, box.y); + wlr_layer_surface_v1_configure(layer_surface, box.width, box.height); + + if (layer_surface->mapped && state->exclusive_zone > 0) { + layer_surface_exclusive_zone(state, usable_area); + } +} + +struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( + struct wlr_scene_tree *parent, + struct wlr_layer_surface_v1 *layer_surface) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + calloc(1, sizeof(*scene_layer_surface)); + if (scene_layer_surface == NULL) { + return NULL; + } + + scene_layer_surface->layer_surface = layer_surface; + + scene_layer_surface->tree = wlr_scene_tree_create(parent); + if (scene_layer_surface->tree == NULL) { + free(scene_layer_surface); + return NULL; + } + + struct wlr_scene_tree *surface_tree = wlr_scene_subsurface_tree_create( + scene_layer_surface->tree, layer_surface->surface); + if (surface_tree == NULL) { + wlr_scene_node_destroy(&scene_layer_surface->tree->node); + free(scene_layer_surface); + return NULL; + } + + scene_layer_surface->tree_destroy.notify = + scene_layer_surface_handle_tree_destroy; + wl_signal_add(&scene_layer_surface->tree->node.events.destroy, + &scene_layer_surface->tree_destroy); + + scene_layer_surface->layer_surface_destroy.notify = + scene_layer_surface_handle_layer_surface_destroy; + wl_signal_add(&layer_surface->events.destroy, + &scene_layer_surface->layer_surface_destroy); + + scene_layer_surface->layer_surface_map.notify = + scene_layer_surface_handle_layer_surface_map; + wl_signal_add(&layer_surface->events.map, + &scene_layer_surface->layer_surface_map); + + scene_layer_surface->layer_surface_unmap.notify = + scene_layer_surface_handle_layer_surface_unmap; + wl_signal_add(&layer_surface->events.unmap, + &scene_layer_surface->layer_surface_unmap); + + wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, + layer_surface->mapped); + + return scene_layer_surface; +} diff --git a/types/scene/output_layout.c b/types/scene/output_layout.c new file mode 100644 index 0000000..1d1484a --- /dev/null +++ b/types/scene/output_layout.c @@ -0,0 +1,157 @@ +#include +#include +#include + +struct wlr_scene_output_layout { + struct wlr_output_layout *layout; + struct wlr_scene *scene; + + struct wl_list outputs; // wlr_scene_output_layout_output.link + + struct wl_listener layout_add; + struct wl_listener layout_change; + struct wl_listener layout_destroy; + struct wl_listener scene_destroy; +}; + +struct wlr_scene_output_layout_output { + struct wlr_output_layout_output *layout_output; + struct wlr_scene_output *scene_output; + + struct wl_list link; // wlr_scene_output_layout.outputs + + struct wl_listener layout_output_destroy; + struct wl_listener scene_output_destroy; +}; + +static void scene_output_layout_output_destroy( + struct wlr_scene_output_layout_output *solo) { + wl_list_remove(&solo->layout_output_destroy.link); + wl_list_remove(&solo->scene_output_destroy.link); + wl_list_remove(&solo->link); + wlr_scene_output_destroy(solo->scene_output); + free(solo); +} + +static void scene_output_layout_output_handle_layout_output_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_output_layout_output *solo = + wl_container_of(listener, solo, layout_output_destroy); + scene_output_layout_output_destroy(solo); +} + +static void scene_output_layout_output_handle_scene_output_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_output_layout_output *solo = + wl_container_of(listener, solo, scene_output_destroy); + solo->scene_output = NULL; + scene_output_layout_output_destroy(solo); +} + +static void scene_output_layout_destroy(struct wlr_scene_output_layout *sol) { + struct wlr_scene_output_layout_output *solo, *tmp; + wl_list_for_each_safe(solo, tmp, &sol->outputs, link) { + scene_output_layout_output_destroy(solo); + } + wl_list_remove(&sol->layout_add.link); + wl_list_remove(&sol->layout_change.link); + wl_list_remove(&sol->layout_destroy.link); + wl_list_remove(&sol->scene_destroy.link); + free(sol); +} + +static void scene_output_layout_handle_layout_change( + struct wl_listener *listener, void *data) { + struct wlr_scene_output_layout *sol = + wl_container_of(listener, sol, layout_change); + + struct wlr_scene_output_layout_output *solo; + wl_list_for_each(solo, &sol->outputs, link) { + wlr_scene_output_set_position(solo->scene_output, + solo->layout_output->x, solo->layout_output->y); + } +} + +static void scene_output_layout_add(struct wlr_scene_output_layout *sol, + struct wlr_output_layout_output *lo) { + struct wlr_scene_output_layout_output *solo = calloc(1, sizeof(*solo)); + if (solo == NULL) { + return; + } + + solo->scene_output = wlr_scene_output_create(sol->scene, lo->output); + if (solo->scene_output == NULL) { + free(solo); + return; + } + + solo->layout_output = lo; + + solo->layout_output_destroy.notify = + scene_output_layout_output_handle_layout_output_destroy; + wl_signal_add(&lo->events.destroy, &solo->layout_output_destroy); + + solo->scene_output_destroy.notify = + scene_output_layout_output_handle_scene_output_destroy; + wl_signal_add(&solo->scene_output->events.destroy, + &solo->scene_output_destroy); + + wl_list_insert(&sol->outputs, &solo->link); + + wlr_scene_output_set_position(solo->scene_output, lo->x, lo->y); +} + +static void scene_output_layout_handle_layout_add( + struct wl_listener *listener, void *data) { + struct wlr_scene_output_layout *sol = + wl_container_of(listener, sol, layout_add); + struct wlr_output_layout_output *lo = data; + + scene_output_layout_add(sol, lo); +} + +static void scene_output_layout_handle_layout_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_output_layout *sol = + wl_container_of(listener, sol, layout_destroy); + scene_output_layout_destroy(sol); +} + +static void scene_output_layout_handle_scene_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_output_layout *sol = + wl_container_of(listener, sol, scene_destroy); + scene_output_layout_destroy(sol); +} + +bool wlr_scene_attach_output_layout(struct wlr_scene *scene, + struct wlr_output_layout *output_layout) { + struct wlr_scene_output_layout *sol = calloc(1, sizeof(*sol)); + if (sol == NULL) { + return false; + } + + sol->scene = scene; + sol->layout = output_layout; + + wl_list_init(&sol->outputs); + + sol->layout_destroy.notify = scene_output_layout_handle_layout_destroy; + wl_signal_add(&output_layout->events.destroy, &sol->layout_destroy); + + sol->layout_change.notify = scene_output_layout_handle_layout_change; + wl_signal_add(&output_layout->events.change, &sol->layout_change); + + sol->layout_add.notify = scene_output_layout_handle_layout_add; + wl_signal_add(&output_layout->events.add, &sol->layout_add); + + sol->scene_destroy.notify = scene_output_layout_handle_scene_destroy; + wl_signal_add(&scene->tree.node.events.destroy, &sol->scene_destroy); + + struct wlr_output_layout_output *lo; + wl_list_for_each(lo, &output_layout->outputs, link) { + scene_output_layout_add(sol, lo); + } + + return true; +} diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c new file mode 100644 index 0000000..35420ab --- /dev/null +++ b/types/scene/subsurface_tree.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include + +/** + * A tree for a surface and all of its child sub-surfaces. + * + * `tree` contains `scene_surface` and one node per sub-surface. + */ +struct wlr_scene_subsurface_tree { + struct wlr_scene_tree *tree; + struct wlr_surface *surface; + struct wlr_scene_surface *scene_surface; + + struct wl_listener tree_destroy; + struct wl_listener surface_destroy; + struct wl_listener surface_commit; + struct wl_listener surface_new_subsurface; + + struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface + + // Only valid if the surface is a sub-surface + + struct wlr_addon surface_addon; + + struct wl_listener subsurface_destroy; + struct wl_listener subsurface_map; + struct wl_listener subsurface_unmap; +}; + +static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, tree_destroy); + // tree and scene_surface will be cleaned up by scene_node_finish + if (subsurface_tree->parent) { + wlr_addon_finish(&subsurface_tree->surface_addon); + wl_list_remove(&subsurface_tree->subsurface_destroy.link); + wl_list_remove(&subsurface_tree->subsurface_map.link); + wl_list_remove(&subsurface_tree->subsurface_unmap.link); + } + wl_list_remove(&subsurface_tree->tree_destroy.link); + wl_list_remove(&subsurface_tree->surface_destroy.link); + wl_list_remove(&subsurface_tree->surface_commit.link); + wl_list_remove(&subsurface_tree->surface_new_subsurface.link); + free(subsurface_tree); +} + +static const struct wlr_addon_interface subsurface_tree_addon_impl; + +static struct wlr_scene_subsurface_tree *subsurface_tree_from_subsurface( + struct wlr_scene_subsurface_tree *parent, + struct wlr_subsurface *subsurface) { + struct wlr_addon *addon = wlr_addon_find(&subsurface->surface->addons, + parent, &subsurface_tree_addon_impl); + assert(addon != NULL); + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(addon, subsurface_tree, surface_addon); + return subsurface_tree; +} + +static void subsurface_tree_reconfigure( + struct wlr_scene_subsurface_tree *subsurface_tree) { + struct wlr_surface *surface = subsurface_tree->surface; + + struct wlr_scene_node *prev = NULL; + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &surface->current.subsurfaces_below, + current.link) { + struct wlr_scene_subsurface_tree *child = + subsurface_tree_from_subsurface(subsurface_tree, subsurface); + if (prev != NULL) { + wlr_scene_node_place_above(&child->tree->node, prev); + } + prev = &child->tree->node; + + wlr_scene_node_set_position(&child->tree->node, + subsurface->current.x, subsurface->current.y); + } + + if (prev != NULL) { + wlr_scene_node_place_above(&subsurface_tree->scene_surface->buffer->node, prev); + } + prev = &subsurface_tree->scene_surface->buffer->node; + + wl_list_for_each(subsurface, &surface->current.subsurfaces_above, + current.link) { + struct wlr_scene_subsurface_tree *child = + subsurface_tree_from_subsurface(subsurface_tree, subsurface); + wlr_scene_node_place_above(&child->tree->node, prev); + prev = &child->tree->node; + + wlr_scene_node_set_position(&child->tree->node, + subsurface->current.x, subsurface->current.y); + } +} + +static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, surface_destroy); + wlr_scene_node_destroy(&subsurface_tree->tree->node); +} + +static void subsurface_tree_handle_surface_commit(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, surface_commit); + + // TODO: only do this on subsurface order or position change + subsurface_tree_reconfigure(subsurface_tree); +} + +static void subsurface_tree_handle_subsurface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, subsurface_destroy); + wlr_scene_node_destroy(&subsurface_tree->tree->node); +} + +static void subsurface_tree_handle_subsurface_map(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, subsurface_map); + + wlr_scene_node_set_enabled(&subsurface_tree->tree->node, true); +} + +static void subsurface_tree_handle_subsurface_unmap(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, subsurface_unmap); + + wlr_scene_node_set_enabled(&subsurface_tree->tree->node, false); +} + +static void subsurface_tree_addon_destroy(struct wlr_addon *addon) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(addon, subsurface_tree, surface_addon); + wlr_scene_node_destroy(&subsurface_tree->tree->node); +} + +static const struct wlr_addon_interface subsurface_tree_addon_impl = { + .name = "wlr_scene_subsurface_tree", + .destroy = subsurface_tree_addon_destroy, +}; + +static struct wlr_scene_subsurface_tree *scene_surface_tree_create( + struct wlr_scene_tree *parent, struct wlr_surface *surface); + +static bool subsurface_tree_create_subsurface( + struct wlr_scene_subsurface_tree *parent, + struct wlr_subsurface *subsurface) { + struct wlr_scene_subsurface_tree *child = scene_surface_tree_create( + parent->tree, subsurface->surface); + if (child == NULL) { + return false; + } + + child->parent = parent; + wlr_scene_node_set_enabled(&child->tree->node, subsurface->mapped); + + wlr_addon_init(&child->surface_addon, &subsurface->surface->addons, + parent, &subsurface_tree_addon_impl); + + child->subsurface_destroy.notify = subsurface_tree_handle_subsurface_destroy; + wl_signal_add(&subsurface->events.destroy, &child->subsurface_destroy); + + child->subsurface_map.notify = subsurface_tree_handle_subsurface_map; + wl_signal_add(&subsurface->events.map, &child->subsurface_map); + + child->subsurface_unmap.notify = subsurface_tree_handle_subsurface_unmap; + wl_signal_add(&subsurface->events.unmap, &child->subsurface_unmap); + + return true; +} + +static void subsurface_tree_handle_surface_new_subsurface( + struct wl_listener *listener, void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, surface_new_subsurface); + struct wlr_subsurface *subsurface = data; + if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { + wl_resource_post_no_memory(subsurface->resource); + } +} + +static struct wlr_scene_subsurface_tree *scene_surface_tree_create( + struct wlr_scene_tree *parent, struct wlr_surface *surface) { + struct wlr_scene_subsurface_tree *subsurface_tree = + calloc(1, sizeof(*subsurface_tree)); + if (subsurface_tree == NULL) { + return NULL; + } + + subsurface_tree->tree = wlr_scene_tree_create(parent); + if (subsurface_tree->tree == NULL) { + goto error_surface_tree; + } + + subsurface_tree->scene_surface = + wlr_scene_surface_create(subsurface_tree->tree, surface); + if (subsurface_tree->scene_surface == NULL) { + goto error_scene_surface; + } + + subsurface_tree->surface = surface; + + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &surface->current.subsurfaces_below, + current.link) { + if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { + goto error_scene_surface; + } + } + wl_list_for_each(subsurface, &surface->current.subsurfaces_above, + current.link) { + if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { + goto error_scene_surface; + } + } + + subsurface_tree_reconfigure(subsurface_tree); + + subsurface_tree->tree_destroy.notify = subsurface_tree_handle_tree_destroy; + wl_signal_add(&subsurface_tree->tree->node.events.destroy, + &subsurface_tree->tree_destroy); + + subsurface_tree->surface_destroy.notify = subsurface_tree_handle_surface_destroy; + wl_signal_add(&surface->events.destroy, &subsurface_tree->surface_destroy); + + subsurface_tree->surface_commit.notify = subsurface_tree_handle_surface_commit; + wl_signal_add(&surface->events.commit, &subsurface_tree->surface_commit); + + subsurface_tree->surface_new_subsurface.notify = + subsurface_tree_handle_surface_new_subsurface; + wl_signal_add(&surface->events.new_subsurface, + &subsurface_tree->surface_new_subsurface); + + return subsurface_tree; + +error_scene_surface: + wlr_scene_node_destroy(&subsurface_tree->tree->node); +error_surface_tree: + free(subsurface_tree); + return NULL; +} + +struct wlr_scene_tree *wlr_scene_subsurface_tree_create( + struct wlr_scene_tree *parent, struct wlr_surface *surface) { + struct wlr_scene_subsurface_tree *subsurface_tree = + scene_surface_tree_create(parent, surface); + if (subsurface_tree == NULL) { + return NULL; + } + return subsurface_tree->tree; +} diff --git a/types/scene/surface.c b/types/scene/surface.c new file mode 100644 index 0000000..553cc42 --- /dev/null +++ b/types/scene/surface.c @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include "types/wlr_scene.h" + +static void handle_scene_buffer_output_enter( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, output_enter); + struct wlr_scene_output *output = data; + + wlr_surface_send_enter(surface->surface, output->output); +} + +static void handle_scene_buffer_output_leave( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, output_leave); + struct wlr_scene_output *output = data; + + wlr_surface_send_leave(surface->surface, output->output); +} + +static void handle_scene_buffer_output_present( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, output_present); + struct wlr_scene_output *scene_output = data; + + if (surface->buffer->primary_output == scene_output) { + struct wlr_scene *root = scene_node_get_root(&surface->buffer->node); + struct wlr_presentation *presentation = root->presentation; + + if (presentation) { + wlr_presentation_surface_sampled_on_output( + presentation, surface->surface, scene_output->output); + } + } +} + +static void handle_scene_buffer_frame_done( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, frame_done); + struct timespec *now = data; + + wlr_surface_send_frame_done(surface->surface, now); +} + +static void scene_surface_handle_surface_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, surface_destroy); + + wlr_scene_node_destroy(&surface->buffer->node); +} + +// This is used for wlr_scene where it unconditionally locks buffers preventing +// reuse of the existing texture for shm clients. With the usage pattern of +// wlr_scene surface handling, we can mark its locked buffer as safe +// for mutation. +static void client_buffer_mark_next_can_damage(struct wlr_client_buffer *buffer) { + buffer->n_ignore_locks++; +} + +static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buffer) { + if (!scene_buffer->buffer) { + return; + } + + struct wlr_client_buffer *buffer = wlr_client_buffer_get(scene_buffer->buffer); + if (!buffer) { + return; + } + + assert(buffer->n_ignore_locks > 0); + buffer->n_ignore_locks--; +} + +static void set_buffer_with_surface_state(struct wlr_scene_buffer *scene_buffer, + struct wlr_surface *surface) { + struct wlr_surface_state *state = &surface->current; + + wlr_scene_buffer_set_opaque_region(scene_buffer, &surface->opaque_region); + + struct wlr_fbox src_box; + wlr_surface_get_buffer_source_box(surface, &src_box); + wlr_scene_buffer_set_source_box(scene_buffer, &src_box); + + wlr_scene_buffer_set_dest_size(scene_buffer, state->width, state->height); + wlr_scene_buffer_set_transform(scene_buffer, state->transform); + + scene_buffer_unmark_client_buffer(scene_buffer); + + if (surface->buffer) { + client_buffer_mark_next_can_damage(surface->buffer); + + wlr_scene_buffer_set_buffer_with_damage(scene_buffer, + &surface->buffer->base, &surface->buffer_damage); + } else { + wlr_scene_buffer_set_buffer(scene_buffer, NULL); + } +} + +static void handle_scene_surface_surface_commit( + struct wl_listener *listener, void *data) { + struct wlr_scene_surface *surface = + wl_container_of(listener, surface, surface_commit); + struct wlr_scene_buffer *scene_buffer = surface->buffer; + + set_buffer_with_surface_state(scene_buffer, surface->surface); + + // If the surface has requested a frame done event, honour that. The + // frame_callback_list will be populated in this case. We should only + // schedule the frame however if the node is enabled and there is an + // output intersecting, otherwise the frame done events would never reach + // the surface anyway. + int lx, ly; + bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly); + + if (!wl_list_empty(&surface->surface->current.frame_callback_list) && + surface->buffer->primary_output != NULL && enabled) { + wlr_output_schedule_frame(surface->buffer->primary_output->output); + } +} + +static bool scene_buffer_point_accepts_input(struct wlr_scene_buffer *scene_buffer, + int sx, int sy) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_buffer(scene_buffer); + + return wlr_surface_point_accepts_input(scene_surface->surface, sx, sy); +} + +static void surface_addon_destroy(struct wlr_addon *addon) { + struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); + + scene_buffer_unmark_client_buffer(surface->buffer); + + wlr_addon_finish(&surface->addon); + + wl_list_remove(&surface->output_enter.link); + wl_list_remove(&surface->output_leave.link); + wl_list_remove(&surface->output_present.link); + wl_list_remove(&surface->frame_done.link); + wl_list_remove(&surface->surface_destroy.link); + wl_list_remove(&surface->surface_commit.link); + + free(surface); +} + +static const struct wlr_addon_interface surface_addon_impl = { + .name = "wlr_scene_surface", + .destroy = surface_addon_destroy, +}; + +struct wlr_scene_surface *wlr_scene_surface_from_buffer( + struct wlr_scene_buffer *scene_buffer) { + struct wlr_addon *addon = wlr_addon_find(&scene_buffer->node.addons, + scene_buffer, &surface_addon_impl); + if (!addon) { + return NULL; + } + + struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); + return surface; +} + +struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent, + struct wlr_surface *wlr_surface) { + struct wlr_scene_surface *surface = calloc(1, sizeof(*surface)); + if (surface == NULL) { + return NULL; + } + + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(parent, NULL); + if (!scene_buffer) { + free(surface); + return NULL; + } + + surface->buffer = scene_buffer; + surface->surface = wlr_surface; + scene_buffer->point_accepts_input = scene_buffer_point_accepts_input; + + surface->output_enter.notify = handle_scene_buffer_output_enter; + wl_signal_add(&scene_buffer->events.output_enter, &surface->output_enter); + + surface->output_leave.notify = handle_scene_buffer_output_leave; + wl_signal_add(&scene_buffer->events.output_leave, &surface->output_leave); + + surface->output_present.notify = handle_scene_buffer_output_present; + wl_signal_add(&scene_buffer->events.output_present, &surface->output_present); + + surface->frame_done.notify = handle_scene_buffer_frame_done; + wl_signal_add(&scene_buffer->events.frame_done, &surface->frame_done); + + surface->surface_destroy.notify = scene_surface_handle_surface_destroy; + wl_signal_add(&wlr_surface->events.destroy, &surface->surface_destroy); + + surface->surface_commit.notify = handle_scene_surface_surface_commit; + wl_signal_add(&wlr_surface->events.commit, &surface->surface_commit); + + wlr_addon_init(&surface->addon, &scene_buffer->node.addons, + scene_buffer, &surface_addon_impl); + + set_buffer_with_surface_state(scene_buffer, wlr_surface); + + return surface; +} diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c new file mode 100644 index 0000000..eb1ce3e --- /dev/null +++ b/types/scene/wlr_scene.c @@ -0,0 +1,1728 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "types/wlr_buffer.h" +#include "types/wlr_scene.h" +#include "util/array.h" +#include "util/env.h" +#include "util/time.h" + +#define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 + +static struct wlr_scene_tree *scene_tree_from_node(struct wlr_scene_node *node) { + assert(node->type == WLR_SCENE_NODE_TREE); + struct wlr_scene_tree *tree = wl_container_of(node, tree, node); + return tree; +} + +static struct wlr_scene_rect *scene_rect_from_node( + struct wlr_scene_node *node) { + assert(node->type == WLR_SCENE_NODE_RECT); + struct wlr_scene_rect *rect = wl_container_of(node, rect, node); + return rect; +} + +struct wlr_scene_buffer *wlr_scene_buffer_from_node( + struct wlr_scene_node *node) { + assert(node->type == WLR_SCENE_NODE_BUFFER); + struct wlr_scene_buffer *buffer = wl_container_of(node, buffer, node); + return buffer; +} + +struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { + struct wlr_scene_tree *tree; + if (node->type == WLR_SCENE_NODE_TREE) { + tree = scene_tree_from_node(node); + } else { + tree = node->parent; + } + + while (tree->node.parent != NULL) { + tree = tree->node.parent; + } + return (struct wlr_scene *)tree; +} + +static void scene_node_init(struct wlr_scene_node *node, + enum wlr_scene_node_type type, struct wlr_scene_tree *parent) { + memset(node, 0, sizeof(*node)); + node->type = type; + node->parent = parent; + node->enabled = true; + + wl_list_init(&node->link); + + wl_signal_init(&node->events.destroy); + pixman_region32_init(&node->visible); + + if (parent != NULL) { + wl_list_insert(parent->children.prev, &node->link); + } + + wlr_addon_set_init(&node->addons); +} + +struct highlight_region { + pixman_region32_t region; + struct timespec when; + struct wl_list link; +}; + +void wlr_scene_node_destroy(struct wlr_scene_node *node) { + if (node == NULL) { + return; + } + + // We want to call the destroy listeners before we do anything else + // in case the destroy signal would like to remove children before they + // are recursively destroyed. + wl_signal_emit_mutable(&node->events.destroy, NULL); + wlr_addon_set_finish(&node->addons); + + wlr_scene_node_set_enabled(node, false); + + struct wlr_scene *scene = scene_node_get_root(node); + if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + uint64_t active = scene_buffer->active_outputs; + if (active) { + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, &scene->outputs, link) { + if (active & (1ull << scene_output->index)) { + wl_signal_emit_mutable(&scene_buffer->events.output_leave, + scene_output); + } + } + } + + wlr_texture_destroy(scene_buffer->texture); + wlr_buffer_unlock(scene_buffer->buffer); + pixman_region32_fini(&scene_buffer->opaque_region); + } else if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + + if (scene_tree == &scene->tree) { + assert(!node->parent); + struct wlr_scene_output *scene_output, *scene_output_tmp; + wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) { + wlr_scene_output_destroy(scene_output); + } + + wl_list_remove(&scene->presentation_destroy.link); + } else { + assert(node->parent); + } + + struct wlr_scene_node *child, *child_tmp; + wl_list_for_each_safe(child, child_tmp, + &scene_tree->children, link) { + wlr_scene_node_destroy(child); + } + } + + wl_list_remove(&node->link); + pixman_region32_fini(&node->visible); + free(node); +} + +static void scene_tree_init(struct wlr_scene_tree *tree, + struct wlr_scene_tree *parent) { + memset(tree, 0, sizeof(*tree)); + scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, parent); + wl_list_init(&tree->children); +} + +struct wlr_scene *wlr_scene_create(void) { + struct wlr_scene *scene = calloc(1, sizeof(struct wlr_scene)); + if (scene == NULL) { + return NULL; + } + + scene_tree_init(&scene->tree, NULL); + + wl_list_init(&scene->outputs); + wl_list_init(&scene->presentation_destroy.link); + + const char *debug_damage_options[] = { + "none", + "rerender", + "highlight", + NULL + }; + + scene->debug_damage_option = env_parse_switch("WLR_SCENE_DEBUG_DAMAGE", debug_damage_options); + scene->direct_scanout = !env_parse_bool("WLR_SCENE_DISABLE_DIRECT_SCANOUT"); + scene->calculate_visibility = !env_parse_bool("WLR_SCENE_DISABLE_VISIBILITY"); + + return scene; +} + +struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { + assert(parent); + + struct wlr_scene_tree *tree = calloc(1, sizeof(struct wlr_scene_tree)); + if (tree == NULL) { + return NULL; + } + + scene_tree_init(tree, parent); + return tree; +} + +static void scene_node_get_size(struct wlr_scene_node *node, int *lx, int *ly); + +typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node, + int sx, int sy, void *data); + +static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data, int lx, int ly) { + if (!node->enabled) { + return false; + } + + switch (node->type) { + case WLR_SCENE_NODE_TREE:; + struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each_reverse(child, &scene_tree->children, link) { + if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) { + return true; + } + } + break; + case WLR_SCENE_NODE_RECT: + case WLR_SCENE_NODE_BUFFER:; + struct wlr_box node_box = { .x = lx, .y = ly }; + scene_node_get_size(node, &node_box.width, &node_box.height); + + if (wlr_box_intersection(&node_box, &node_box, box) && + iterator(node, lx, ly, user_data)) { + return true; + } + break; + } + + return false; +} + +static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data) { + int x, y; + wlr_scene_node_coords(node, &x, &y); + + return _scene_nodes_in_box(node, box, iterator, user_data, x, y); +} + +static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, + pixman_region32_t *opaque) { + if (node->type == WLR_SCENE_NODE_RECT) { + struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); + if (scene_rect->color[3] != 1) { + return; + } + } else if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + if (!scene_buffer->buffer) { + return; + } + + if (!buffer_is_opaque(scene_buffer->buffer)) { + pixman_region32_copy(opaque, &scene_buffer->opaque_region); + pixman_region32_translate(opaque, x, y); + return; + } + } + + int width, height; + scene_node_get_size(node, &width, &height); + pixman_region32_fini(opaque); + pixman_region32_init_rect(opaque, x, y, width, height); +} + +struct scene_update_data { + pixman_region32_t *visible; + pixman_region32_t *update_region; + struct wl_list *outputs; + bool calculate_visibility; +}; + +static uint32_t region_area(pixman_region32_t *region) { + uint32_t area = 0; + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); + for (int i = 0; i < nrects; ++i) { + area += (rects[i].x2 - rects[i].x1) * (rects[i].y2 - rects[i].y1); + } + + return area; +} + +static void scale_output_damage(pixman_region32_t *damage, float scale) { + wlr_region_scale(damage, damage, scale); + + if (floor(scale) != scale) { + wlr_region_expand(damage, damage, 1); + } +} + +static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) { + if (!pixman_region32_not_empty(damage)) { + return; + } + + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, &scene->outputs, link) { + pixman_region32_t output_damage; + pixman_region32_init(&output_damage); + pixman_region32_copy(&output_damage, damage); + pixman_region32_translate(&output_damage, + -scene_output->x, -scene_output->y); + scale_output_damage(&output_damage, scene_output->output->scale); + if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { + wlr_output_schedule_frame(scene_output->output); + } + pixman_region32_fini(&output_damage); + } +} + +static void update_node_update_outputs(struct wlr_scene_node *node, + struct wl_list *outputs, struct wlr_scene_output *ignore) { + if (node->type != WLR_SCENE_NODE_BUFFER) { + return; + } + + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + uint32_t largest_overlap = 0; + scene_buffer->primary_output = NULL; + + uint64_t active_outputs = 0; + + // let's update the outputs in two steps: + // - the primary outputs + // - the enter/leave signals + // This ensures that the enter/leave signals can rely on the primary output + // to have a reasonable value. Otherwise, they may get a value that's in + // the middle of a calculation. + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, outputs, link) { + if (scene_output == ignore) { + continue; + } + + if (!scene_output->output->enabled) { + continue; + } + + struct wlr_box output_box = { + .x = scene_output->x, + .y = scene_output->y, + }; + wlr_output_effective_resolution(scene_output->output, + &output_box.width, &output_box.height); + + pixman_region32_t intersection; + pixman_region32_init(&intersection); + pixman_region32_intersect_rect(&intersection, &node->visible, + output_box.x, output_box.y, output_box.width, output_box.height); + + if (pixman_region32_not_empty(&intersection)) { + uint32_t overlap = region_area(&intersection); + if (overlap >= largest_overlap) { + largest_overlap = overlap; + scene_buffer->primary_output = scene_output; + } + + active_outputs |= 1ull << scene_output->index; + } + + pixman_region32_fini(&intersection); + } + + uint64_t old_active = scene_buffer->active_outputs; + scene_buffer->active_outputs = active_outputs; + + wl_list_for_each(scene_output, outputs, link) { + uint64_t mask = 1ull << scene_output->index; + bool intersects = active_outputs & mask; + bool intersects_before = old_active & mask; + + if (intersects && !intersects_before) { + wl_signal_emit_mutable(&scene_buffer->events.output_enter, scene_output); + } else if (!intersects && intersects_before) { + wl_signal_emit_mutable(&scene_buffer->events.output_leave, scene_output); + } + } + + // if there are active outputs on this node, we should always have a primary + // output + assert(!scene_buffer->active_outputs || scene_buffer->primary_output); +} + +static bool scene_node_update_iterator(struct wlr_scene_node *node, + int lx, int ly, void *_data) { + struct scene_update_data *data = _data; + + struct wlr_box box = { .x = lx, .y = ly }; + scene_node_get_size(node, &box.width, &box.height); + + pixman_region32_subtract(&node->visible, &node->visible, data->update_region); + pixman_region32_union(&node->visible, &node->visible, data->visible); + pixman_region32_intersect_rect(&node->visible, &node->visible, + lx, ly, box.width, box.height); + + if (data->calculate_visibility) { + pixman_region32_t opaque; + pixman_region32_init(&opaque); + scene_node_opaque_region(node, lx, ly, &opaque); + pixman_region32_subtract(data->visible, data->visible, &opaque); + pixman_region32_fini(&opaque); + } + + update_node_update_outputs(node, data->outputs, NULL); + + return false; +} + +static void scene_node_visibility(struct wlr_scene_node *node, + pixman_region32_t *visible) { + if (!node->enabled) { + return; + } + + if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + scene_node_visibility(child, visible); + } + return; + } + + pixman_region32_union(visible, visible, &node->visible); +} + +static void scene_node_bounds(struct wlr_scene_node *node, + int x, int y, pixman_region32_t *visible) { + if (!node->enabled) { + return; + } + + if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + scene_node_bounds(child, x + child->x, y + child->y, visible); + } + return; + } + + int width, height; + scene_node_get_size(node, &width, &height); + pixman_region32_union_rect(visible, visible, x, y, width, height); +} + +static void scene_update_region(struct wlr_scene *scene, + pixman_region32_t *update_region) { + pixman_region32_t visible; + pixman_region32_init(&visible); + pixman_region32_copy(&visible, update_region); + + struct scene_update_data data = { + .visible = &visible, + .update_region = update_region, + .outputs = &scene->outputs, + .calculate_visibility = scene->calculate_visibility, + }; + + struct pixman_box32 *region_box = pixman_region32_extents(update_region); + struct wlr_box box = { + .x = region_box->x1, + .y = region_box->y1, + .width = region_box->x2 - region_box->x1, + .height = region_box->y2 - region_box->y1, + }; + + // update node visibility and output enter/leave events + scene_nodes_in_box(&scene->tree.node, &box, scene_node_update_iterator, &data); + + pixman_region32_fini(&visible); +} + +static void scene_node_update(struct wlr_scene_node *node, + pixman_region32_t *damage) { + struct wlr_scene *scene = scene_node_get_root(node); + + int x, y; + if (!wlr_scene_node_coords(node, &x, &y)) { + if (damage) { + scene_update_region(scene, damage); + scene_damage_outputs(scene, damage); + pixman_region32_fini(damage); + } + + return; + } + + pixman_region32_t visible; + if (!damage) { + pixman_region32_init(&visible); + scene_node_visibility(node, &visible); + damage = &visible; + } + + pixman_region32_t update_region; + pixman_region32_init(&update_region); + pixman_region32_copy(&update_region, damage); + scene_node_bounds(node, x, y, &update_region); + + scene_update_region(scene, &update_region); + pixman_region32_fini(&update_region); + + scene_node_visibility(node, damage); + scene_damage_outputs(scene, damage); + pixman_region32_fini(damage); +} + +struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, + int width, int height, const float color[static 4]) { + struct wlr_scene_rect *scene_rect = + calloc(1, sizeof(struct wlr_scene_rect)); + if (scene_rect == NULL) { + return NULL; + } + assert(parent); + scene_node_init(&scene_rect->node, WLR_SCENE_NODE_RECT, parent); + + scene_rect->width = width; + scene_rect->height = height; + memcpy(scene_rect->color, color, sizeof(scene_rect->color)); + + scene_node_update(&scene_rect->node, NULL); + + return scene_rect; +} + +void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) { + if (rect->width == width && rect->height == height) { + return; + } + + rect->width = width; + rect->height = height; + scene_node_update(&rect->node, NULL); +} + +void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]) { + if (memcmp(rect->color, color, sizeof(rect->color)) == 0) { + return; + } + + memcpy(rect->color, color, sizeof(rect->color)); + scene_node_update(&rect->node, NULL); +} + +struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, + struct wlr_buffer *buffer) { + struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); + if (scene_buffer == NULL) { + return NULL; + } + assert(parent); + scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent); + + if (buffer) { + scene_buffer->buffer = wlr_buffer_lock(buffer); + } + + wl_signal_init(&scene_buffer->events.output_enter); + wl_signal_init(&scene_buffer->events.output_leave); + wl_signal_init(&scene_buffer->events.output_present); + wl_signal_init(&scene_buffer->events.frame_done); + pixman_region32_init(&scene_buffer->opaque_region); + + scene_node_update(&scene_buffer->node, NULL); + + return scene_buffer; +} + +void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer, pixman_region32_t *damage) { + // specifying a region for a NULL buffer doesn't make sense. We need to know + // about the buffer to scale the buffer local coordinates down to scene + // coordinates. + assert(buffer || !damage); + + bool update = false; + wlr_buffer_unlock(scene_buffer->buffer); + + wlr_texture_destroy(scene_buffer->texture); + scene_buffer->texture = NULL; + + if (buffer) { + // if this node used to not be mapped or its previous displayed + // buffer region will be different from what the new buffer would + // produce we need to update the node. + update = !scene_buffer->buffer || + (scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0 && + (scene_buffer->buffer->width != buffer->width || + scene_buffer->buffer->height != buffer->height)); + + scene_buffer->buffer = wlr_buffer_lock(buffer); + } else { + update = true; + scene_buffer->buffer = NULL; + } + + if (update) { + scene_node_update(&scene_buffer->node, NULL); + // updating the node will already damage the whole node for us. Return + // early to not damage again + return; + } + + int lx, ly; + if (!wlr_scene_node_coords(&scene_buffer->node, &lx, &ly)) { + return; + } + + pixman_region32_t fallback_damage; + pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height); + if (!damage) { + damage = &fallback_damage; + } + + struct wlr_fbox box = scene_buffer->src_box; + if (wlr_fbox_empty(&box)) { + box.x = 0; + box.y = 0; + box.width = buffer->width; + box.height = buffer->height; + } + + wlr_fbox_transform(&box, &box, scene_buffer->transform, + buffer->width, buffer->height); + + float scale_x, scale_y; + if (scene_buffer->dst_width || scene_buffer->dst_height) { + scale_x = scene_buffer->dst_width / box.width; + scale_y = scene_buffer->dst_height / box.height; + } else { + scale_x = buffer->width / box.width; + scale_y = buffer->height / box.height; + } + + pixman_region32_t trans_damage; + pixman_region32_init(&trans_damage); + wlr_region_transform(&trans_damage, damage, + scene_buffer->transform, buffer->width, buffer->height); + pixman_region32_intersect_rect(&trans_damage, &trans_damage, + box.x, box.y, box.width, box.height); + pixman_region32_translate(&trans_damage, -box.x, -box.y); + + struct wlr_scene *scene = scene_node_get_root(&scene_buffer->node); + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, &scene->outputs, link) { + float output_scale = scene_output->output->scale; + float output_scale_x = output_scale * scale_x; + float output_scale_y = output_scale * scale_y; + pixman_region32_t output_damage; + pixman_region32_init(&output_damage); + wlr_region_scale_xy(&output_damage, &trans_damage, + output_scale_x, output_scale_y); + + // One output pixel will match (buffer_scale_x)x(buffer_scale_y) buffer pixels. + // If the buffer is upscaled on the given axis (output_scale_* > 1.0, + // buffer_scale_* < 1.0), its contents will bleed into adjacent + // (ceil(output_scale_* / 2)) output pixels because of linear filtering. + // Additionally, if the buffer is downscaled (output_scale_* < 1.0, + // buffer_scale_* > 1.0), and one output pixel matches a non-integer number of + // buffer pixels, its contents will bleed into neighboring output pixels. + // Handle both cases by computing buffer_scale_{x,y} and checking if they are + // integer numbers; ceilf() is used to ensure that the distance is at least 1. + float buffer_scale_x = 1.0f / output_scale_x; + float buffer_scale_y = 1.0f / output_scale_y; + int dist_x = floor(buffer_scale_x) != buffer_scale_x ? + (int)ceilf(output_scale_x / 2.0f) : 0; + int dist_y = floor(buffer_scale_y) != buffer_scale_y ? + (int)ceilf(output_scale_y / 2.0f) : 0; + // TODO: expand with per-axis distances + wlr_region_expand(&output_damage, &output_damage, + dist_x >= dist_y ? dist_x : dist_y); + + pixman_region32_t cull_region; + pixman_region32_init(&cull_region); + pixman_region32_copy(&cull_region, &scene_buffer->node.visible); + scale_output_damage(&cull_region, output_scale); + pixman_region32_translate(&cull_region, -lx * output_scale, -ly * output_scale); + pixman_region32_intersect(&output_damage, &output_damage, &cull_region); + pixman_region32_fini(&cull_region); + + pixman_region32_translate(&output_damage, + (lx - scene_output->x) * output_scale, + (ly - scene_output->y) * output_scale); + if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { + wlr_output_schedule_frame(scene_output->output); + } + pixman_region32_fini(&output_damage); + } + + pixman_region32_fini(&trans_damage); + pixman_region32_fini(&fallback_damage); +} + +void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer) { + wlr_scene_buffer_set_buffer_with_damage(scene_buffer, buffer, NULL); +} + +void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, + pixman_region32_t *region) { + if (pixman_region32_equal(&scene_buffer->opaque_region, region)) { + return; + } + + pixman_region32_copy(&scene_buffer->opaque_region, region); + scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, + const struct wlr_fbox *box) { + struct wlr_fbox *cur = &scene_buffer->src_box; + if ((wlr_fbox_empty(box) && wlr_fbox_empty(cur)) || + (box != NULL && wlr_fbox_equal(cur, box))) { + return; + } + + if (box != NULL) { + memcpy(cur, box, sizeof(*box)); + } else { + memset(cur, 0, sizeof(*cur)); + } + + scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, + int width, int height) { + if (scene_buffer->dst_width == width && scene_buffer->dst_height == height) { + return; + } + + scene_buffer->dst_width = width; + scene_buffer->dst_height = height; + scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, + enum wl_output_transform transform) { + if (scene_buffer->transform == transform) { + return; + } + + scene_buffer->transform = transform; + scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, + struct timespec *now) { + if (pixman_region32_not_empty(&scene_buffer->node.visible)) { + wl_signal_emit_mutable(&scene_buffer->events.frame_done, now); + } +} + +static struct wlr_texture *scene_buffer_get_texture( + struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { + struct wlr_client_buffer *client_buffer = + wlr_client_buffer_get(scene_buffer->buffer); + if (client_buffer != NULL) { + return client_buffer->texture; + } + + if (scene_buffer->texture != NULL) { + return scene_buffer->texture; + } + + scene_buffer->texture = + wlr_texture_from_buffer(renderer, scene_buffer->buffer); + return scene_buffer->texture; +} + +static void scene_node_get_size(struct wlr_scene_node *node, + int *width, int *height) { + *width = 0; + *height = 0; + + switch (node->type) { + case WLR_SCENE_NODE_TREE: + return; + case WLR_SCENE_NODE_RECT:; + struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); + *width = scene_rect->width; + *height = scene_rect->height; + break; + case WLR_SCENE_NODE_BUFFER:; + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) { + *width = scene_buffer->dst_width; + *height = scene_buffer->dst_height; + } else if (scene_buffer->buffer) { + if (scene_buffer->transform & WL_OUTPUT_TRANSFORM_90) { + *height = scene_buffer->buffer->width; + *width = scene_buffer->buffer->height; + } else { + *width = scene_buffer->buffer->width; + *height = scene_buffer->buffer->height; + } + } + break; + } +} + +static int scale_length(int length, int offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + +static 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); +} + +void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { + if (node->enabled == enabled) { + return; + } + + int x, y; + pixman_region32_t visible; + pixman_region32_init(&visible); + if (wlr_scene_node_coords(node, &x, &y)) { + scene_node_visibility(node, &visible); + } + + node->enabled = enabled; + + scene_node_update(node, &visible); +} + +void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { + if (node->x == x && node->y == y) { + return; + } + + node->x = x; + node->y = y; + scene_node_update(node, NULL); +} + +void wlr_scene_node_place_above(struct wlr_scene_node *node, + struct wlr_scene_node *sibling) { + assert(node != sibling); + assert(node->parent == sibling->parent); + + if (node->link.prev == &sibling->link) { + return; + } + + wl_list_remove(&node->link); + wl_list_insert(&sibling->link, &node->link); + scene_node_update(node, NULL); +} + +void wlr_scene_node_place_below(struct wlr_scene_node *node, + struct wlr_scene_node *sibling) { + assert(node != sibling); + assert(node->parent == sibling->parent); + + if (node->link.next == &sibling->link) { + return; + } + + wl_list_remove(&node->link); + wl_list_insert(sibling->link.prev, &node->link); + scene_node_update(node, NULL); +} + +void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) { + struct wlr_scene_node *current_top = wl_container_of( + node->parent->children.prev, current_top, link); + if (node == current_top) { + return; + } + wlr_scene_node_place_above(node, current_top); +} + +void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) { + struct wlr_scene_node *current_bottom = wl_container_of( + node->parent->children.next, current_bottom, link); + if (node == current_bottom) { + return; + } + wlr_scene_node_place_below(node, current_bottom); +} + +void wlr_scene_node_reparent(struct wlr_scene_node *node, + struct wlr_scene_tree *new_parent) { + assert(new_parent != NULL); + + if (node->parent == new_parent) { + return; + } + + /* Ensure that a node cannot become its own ancestor */ + for (struct wlr_scene_tree *ancestor = new_parent; ancestor != NULL; + ancestor = ancestor->node.parent) { + assert(&ancestor->node != node); + } + + int x, y; + pixman_region32_t visible; + pixman_region32_init(&visible); + if (wlr_scene_node_coords(node, &x, &y)) { + scene_node_visibility(node, &visible); + } + + wl_list_remove(&node->link); + node->parent = new_parent; + wl_list_insert(new_parent->children.prev, &node->link); + scene_node_update(node, &visible); +} + +bool wlr_scene_node_coords(struct wlr_scene_node *node, + int *lx_ptr, int *ly_ptr) { + assert(node); + + int lx = 0, ly = 0; + bool enabled = true; + while (true) { + lx += node->x; + ly += node->y; + enabled = enabled && node->enabled; + if (node->parent == NULL) { + break; + } + + node = &node->parent->node; + } + + *lx_ptr = lx; + *ly_ptr = ly; + return enabled; +} + +static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node, + int lx, int ly, wlr_scene_buffer_iterator_func_t user_iterator, + void *user_data) { + if (!node->enabled) { + return; + } + + lx += node->x; + ly += node->y; + + if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + user_iterator(scene_buffer, lx, ly, user_data); + } else if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data); + } + } +} + +void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node, + wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { + scene_node_for_each_scene_buffer(node, 0, 0, user_iterator, user_data); +} + +struct node_at_data { + double lx, ly; + double rx, ry; + struct wlr_scene_node *node; +}; + +static bool scene_node_at_iterator(struct wlr_scene_node *node, + int lx, int ly, void *data) { + struct node_at_data *at_data = data; + + double rx = at_data->lx - lx; + double ry = at_data->ly - ly; + + if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + if (scene_buffer->point_accepts_input && + !scene_buffer->point_accepts_input(scene_buffer, rx, ry)) { + return false; + } + } + + at_data->rx = rx; + at_data->ry = ry; + at_data->node = node; + return true; +} + +struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, + double lx, double ly, double *nx, double *ny) { + struct wlr_box box = { + .x = floor(lx), + .y = floor(ly), + .width = 1, + .height = 1 + }; + + struct node_at_data data = { + .lx = lx, + .ly = ly + }; + + if (scene_nodes_in_box(node, &box, scene_node_at_iterator, &data)) { + if (nx) { + *nx = data.rx; + } + if (ny) { + *ny = data.ry; + } + return data.node; + } + + return NULL; +} + +static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { + struct wlr_renderer *renderer = output->renderer; + assert(renderer); + + struct wlr_box box = { + .x = rect->x1, + .y = rect->y1, + .width = rect->x2 - rect->x1, + .height = rect->y2 - rect->y1, + }; + + int ow, oh; + wlr_output_transformed_resolution(output, &ow, &oh); + + enum wl_output_transform transform = + wlr_output_transform_invert(output->transform); + wlr_box_transform(&box, &box, transform, ow, oh); + + wlr_renderer_scissor(renderer, &box); +} + +static void render_rect(struct wlr_output *output, + pixman_region32_t *damage, const float color[static 4], + const struct wlr_box *box, const float matrix[static 9]) { + struct wlr_renderer *renderer = output->renderer; + assert(renderer); + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(output, &rects[i]); + wlr_render_rect(renderer, box, color, matrix); + } +} + +static void render_texture(struct wlr_output *output, + pixman_region32_t *damage, struct wlr_texture *texture, + const struct wlr_fbox *src_box, const struct wlr_box *dst_box, + const float matrix[static 9]) { + struct wlr_renderer *renderer = output->renderer; + assert(renderer); + + struct wlr_fbox default_src_box = {0}; + if (wlr_fbox_empty(src_box)) { + default_src_box.width = texture->width; + default_src_box.height = texture->height; + src_box = &default_src_box; + } + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(output, &rects[i]); + wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, 1.0); + } +} + +static void scene_node_render(struct wlr_scene_node *node, + struct wlr_scene_output *scene_output, pixman_region32_t *damage) { + int x, y; + wlr_scene_node_coords(node, &x, &y); + x -= scene_output->x; + y -= scene_output->y; + + struct wlr_output *output = scene_output->output; + + pixman_region32_t render_region; + pixman_region32_init(&render_region); + pixman_region32_copy(&render_region, &node->visible); + pixman_region32_translate(&render_region, -scene_output->x, -scene_output->y); + scale_output_damage(&render_region, output->scale); + pixman_region32_intersect(&render_region, &render_region, damage); + if (!pixman_region32_not_empty(&render_region)) { + pixman_region32_fini(&render_region); + return; + } + + struct wlr_box dst_box = { + .x = x, + .y = y, + }; + scene_node_get_size(node, &dst_box.width, &dst_box.height); + scale_box(&dst_box, output->scale); + + struct wlr_texture *texture; + float matrix[9]; + enum wl_output_transform transform; + switch (node->type) { + case WLR_SCENE_NODE_TREE: + assert(false); + break; + case WLR_SCENE_NODE_RECT:; + struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); + + render_rect(output, &render_region, scene_rect->color, &dst_box, + output->transform_matrix); + break; + case WLR_SCENE_NODE_BUFFER:; + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + assert(scene_buffer->buffer); + + struct wlr_renderer *renderer = output->renderer; + texture = scene_buffer_get_texture(scene_buffer, renderer); + if (texture == NULL) { + break; + } + + transform = wlr_output_transform_invert(scene_buffer->transform); + wlr_matrix_project_box(matrix, &dst_box, transform, 0.0, + output->transform_matrix); + + render_texture(output, &render_region, texture, &scene_buffer->src_box, + &dst_box, matrix); + + wl_signal_emit_mutable(&scene_buffer->events.output_present, scene_output); + break; + } + + pixman_region32_fini(&render_region); +} + +static void scene_handle_presentation_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene *scene = + wl_container_of(listener, scene, presentation_destroy); + wl_list_remove(&scene->presentation_destroy.link); + wl_list_init(&scene->presentation_destroy.link); + scene->presentation = NULL; +} + +void wlr_scene_set_presentation(struct wlr_scene *scene, + struct wlr_presentation *presentation) { + assert(scene->presentation == NULL); + scene->presentation = presentation; + scene->presentation_destroy.notify = scene_handle_presentation_destroy; + wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); +} + +static void scene_output_handle_destroy(struct wlr_addon *addon) { + struct wlr_scene_output *scene_output = + wl_container_of(addon, scene_output, addon); + wlr_scene_output_destroy(scene_output); +} + +static const struct wlr_addon_interface output_addon_impl = { + .name = "wlr_scene_output", + .destroy = scene_output_handle_destroy, +}; + +static void scene_node_output_update(struct wlr_scene_node *node, + struct wl_list *outputs, struct wlr_scene_output *ignore) { + if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + scene_node_output_update(child, outputs, ignore); + } + return; + } + + update_node_update_outputs(node, outputs, ignore); +} + +static void scene_output_update_geometry(struct wlr_scene_output *scene_output) { + int width, height; + wlr_output_transformed_resolution(scene_output->output, &width, &height); + wlr_damage_ring_set_bounds(&scene_output->damage_ring, width, height); + wlr_output_schedule_frame(scene_output->output); + + scene_node_output_update(&scene_output->scene->tree.node, + &scene_output->scene->outputs, NULL); +} + +static void scene_output_handle_commit(struct wl_listener *listener, void *data) { + struct wlr_scene_output *scene_output = wl_container_of(listener, + scene_output, output_commit); + struct wlr_output_event_commit *event = data; + + if (event->committed & (WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_TRANSFORM | + WLR_OUTPUT_STATE_SCALE | + WLR_OUTPUT_STATE_ENABLED)) { + scene_output_update_geometry(scene_output); + } +} + +static void scene_output_handle_mode(struct wl_listener *listener, void *data) { + struct wlr_scene_output *scene_output = wl_container_of(listener, + scene_output, output_mode); + scene_output_update_geometry(scene_output); +} + +static void scene_output_handle_damage(struct wl_listener *listener, void *data) { + struct wlr_scene_output *scene_output = wl_container_of(listener, + scene_output, output_damage); + struct wlr_output_event_damage *event = data; + if (wlr_damage_ring_add(&scene_output->damage_ring, event->damage)) { + wlr_output_schedule_frame(scene_output->output); + } +} + +static void scene_output_handle_needs_frame(struct wl_listener *listener, void *data) { + struct wlr_scene_output *scene_output = wl_container_of(listener, + scene_output, output_needs_frame); + wlr_output_schedule_frame(scene_output->output); +} + +struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, + struct wlr_output *output) { + struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output)); + if (scene_output == NULL) { + return NULL; + } + + scene_output->output = output; + scene_output->scene = scene; + wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl); + + wlr_damage_ring_init(&scene_output->damage_ring); + wl_list_init(&scene_output->damage_highlight_regions); + + int prev_output_index = -1; + struct wl_list *prev_output_link = &scene->outputs; + + struct wlr_scene_output *current_output; + wl_list_for_each(current_output, &scene->outputs, link) { + if (prev_output_index + 1 != current_output->index) { + break; + } + + prev_output_index = current_output->index; + prev_output_link = ¤t_output->link; + } + + scene_output->index = prev_output_index + 1; + assert(scene_output->index < 64); + wl_list_insert(prev_output_link, &scene_output->link); + + wl_signal_init(&scene_output->events.destroy); + + scene_output->output_commit.notify = scene_output_handle_commit; + wl_signal_add(&output->events.commit, &scene_output->output_commit); + + scene_output->output_mode.notify = scene_output_handle_mode; + wl_signal_add(&output->events.mode, &scene_output->output_mode); + + scene_output->output_damage.notify = scene_output_handle_damage; + wl_signal_add(&output->events.damage, &scene_output->output_damage); + + scene_output->output_needs_frame.notify = scene_output_handle_needs_frame; + wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame); + + scene_output_update_geometry(scene_output); + + return scene_output; +} + +static void highlight_region_destroy(struct highlight_region *damage) { + wl_list_remove(&damage->link); + pixman_region32_fini(&damage->region); + free(damage); +} + +void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { + if (scene_output == NULL) { + return; + } + + wl_signal_emit_mutable(&scene_output->events.destroy, NULL); + + scene_node_output_update(&scene_output->scene->tree.node, + &scene_output->scene->outputs, scene_output); + + struct highlight_region *damage, *tmp_damage; + wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) { + highlight_region_destroy(damage); + } + + wlr_addon_finish(&scene_output->addon); + wlr_damage_ring_finish(&scene_output->damage_ring); + wl_list_remove(&scene_output->link); + wl_list_remove(&scene_output->output_commit.link); + wl_list_remove(&scene_output->output_mode.link); + wl_list_remove(&scene_output->output_damage.link); + wl_list_remove(&scene_output->output_needs_frame.link); + + wl_array_release(&scene_output->render_list); + free(scene_output); +} + +struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, + struct wlr_output *output) { + struct wlr_addon *addon = + wlr_addon_find(&output->addons, scene, &output_addon_impl); + if (addon == NULL) { + return NULL; + } + struct wlr_scene_output *scene_output = + wl_container_of(addon, scene_output, addon); + return scene_output; +} + +void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, + int lx, int ly) { + if (scene_output->x == lx && scene_output->y == ly) { + return; + } + + scene_output->x = lx; + scene_output->y = ly; + + scene_output_update_geometry(scene_output); +} + +static bool scene_node_invisible(struct wlr_scene_node *node) { + if (node->type == WLR_SCENE_NODE_TREE) { + return true; + } else if (node->type == WLR_SCENE_NODE_RECT) { + struct wlr_scene_rect *rect = scene_rect_from_node(node); + + return rect->color[3] == 0.f; + } else if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + + return buffer->buffer == NULL; + } + + return false; +} + +struct render_list_constructor_data { + struct wlr_box box; + struct wl_array *render_list; + bool calculate_visibility; +}; + +static bool construct_render_list_iterator(struct wlr_scene_node *node, + int lx, int ly, void *_data) { + struct render_list_constructor_data *data = _data; + + if (scene_node_invisible(node)) { + return false; + } + + // while rendering, the background should always be black. + // If we see a black rect, we can ignore rendering everything under the rect + // and even the rect itself. + if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) { + struct wlr_scene_rect *rect = scene_rect_from_node(node); + float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; + + if (memcmp(rect->color, black, sizeof(float) * 4) == 0) { + return false; + } + } + + pixman_region32_t intersection; + pixman_region32_init(&intersection); + pixman_region32_intersect_rect(&intersection, &node->visible, + data->box.x, data->box.y, + data->box.width, data->box.height); + if (!pixman_region32_not_empty(&intersection)) { + pixman_region32_fini(&intersection); + return false; + } + + pixman_region32_fini(&intersection); + + struct wlr_scene_node **entry = wl_array_add(data->render_list, + sizeof(struct wlr_scene_node *)); + if (entry) { + *entry = node; + } + return false; +} + +static bool scene_node_try_direct_scanout(struct wlr_scene_node *node, + struct wlr_scene_output *scene_output, struct wlr_box *box) { + if (!scene_output->scene->direct_scanout) { + return false; + } + + if (scene_output->scene->debug_damage_option == + WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { + // We don't want to enter direct scan out if we have highlight regions + // enabled. Otherwise, we won't be able to render the damage regions. + return false; + } + + if (node->type != WLR_SCENE_NODE_BUFFER) { + return false; + } + + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + struct wlr_output *output = scene_output->output; + + struct wlr_fbox default_box = {0}; + if (buffer->transform & WL_OUTPUT_TRANSFORM_90) { + default_box.width = buffer->buffer->height; + default_box.height = buffer->buffer->width; + } else { + default_box.width = buffer->buffer->width; + default_box.height = buffer->buffer->height; + } + + if (!wlr_fbox_empty(&buffer->src_box) && + !wlr_fbox_equal(&buffer->src_box, &default_box)) { + return false; + } + + if (buffer->transform != output->transform) { + return false; + } + + struct wlr_box node_box; + wlr_scene_node_coords(node, &node_box.x, &node_box.y); + scene_node_get_size(node, &node_box.width, &node_box.height); + + if (!wlr_box_equal(box, &node_box)) { + return false; + } + + wlr_output_attach_buffer(output, buffer->buffer); + if (!wlr_output_test(output)) { + wlr_output_rollback(output); + return false; + } + + return wlr_output_commit(output); +} + +bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { + struct wlr_output *output = scene_output->output; + enum wlr_scene_debug_damage_option debug_damage = + scene_output->scene->debug_damage_option; + + struct wlr_renderer *renderer = output->renderer; + assert(renderer != NULL); + + struct render_list_constructor_data list_con = { + .box = { .x = scene_output->x, .y = scene_output->y }, + .render_list = &scene_output->render_list, + .calculate_visibility = scene_output->scene->calculate_visibility, + }; + wlr_output_effective_resolution(output, + &list_con.box.width, &list_con.box.height); + + list_con.render_list->size = 0; + scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box, + construct_render_list_iterator, &list_con); + array_realloc(list_con.render_list, list_con.render_list->size); + + int list_len = list_con.render_list->size / sizeof(struct wlr_scene_node *); + struct wlr_scene_node **list_data = list_con.render_list->data; + + // if there is only one thing to render let's see if that thing can be + // directly scanned out + bool scanout = false; + if (list_len == 1) { + struct wlr_scene_node *node = list_data[0]; + scanout = scene_node_try_direct_scanout(node, scene_output, &list_con.box); + } + + if (scene_output->prev_scanout != scanout) { + scene_output->prev_scanout = scanout; + wlr_log(WLR_DEBUG, "Direct scan-out %s", + scanout ? "enabled" : "disabled"); + // When exiting direct scan-out, damage everything + wlr_damage_ring_add_whole(&scene_output->damage_ring); + } + + if (scanout) { + struct wlr_scene_node *node = list_data[0]; + + assert(node->type == WLR_SCENE_NODE_BUFFER); + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + wl_signal_emit_mutable(&buffer->events.output_present, scene_output); + return true; + } + + if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) { + wlr_damage_ring_add_whole(&scene_output->damage_ring); + } + + struct timespec now; + if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { + struct wl_list *regions = &scene_output->damage_highlight_regions; + clock_gettime(CLOCK_MONOTONIC, &now); + + // add the current frame's damage if there is damage + if (pixman_region32_not_empty(&scene_output->damage_ring.current)) { + struct highlight_region *current_damage = + calloc(1, sizeof(*current_damage)); + if (current_damage) { + pixman_region32_init(¤t_damage->region); + pixman_region32_copy(¤t_damage->region, + &scene_output->damage_ring.current); + current_damage->when = now; + wl_list_insert(regions, ¤t_damage->link); + } + } + + pixman_region32_t acc_damage; + pixman_region32_init(&acc_damage); + struct highlight_region *damage, *tmp_damage; + wl_list_for_each_safe(damage, tmp_damage, regions, link) { + // remove overlaping damage regions + pixman_region32_subtract(&damage->region, &damage->region, &acc_damage); + pixman_region32_union(&acc_damage, &acc_damage, &damage->region); + + // if this damage is too old or has nothing in it, get rid of it + struct timespec time_diff; + timespec_sub(&time_diff, &now, &damage->when); + if (timespec_to_msec(&time_diff) >= HIGHLIGHT_DAMAGE_FADEOUT_TIME || + !pixman_region32_not_empty(&damage->region)) { + highlight_region_destroy(damage); + } + } + + wlr_damage_ring_add(&scene_output->damage_ring, &acc_damage); + pixman_region32_fini(&acc_damage); + } + + int buffer_age; + if (!wlr_output_attach_render(output, &buffer_age)) { + return false; + } + + pixman_region32_t damage; + pixman_region32_init(&damage); + wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, + buffer_age, &damage); + if (!output->needs_frame && !pixman_region32_not_empty( + &scene_output->damage_ring.current)) { + pixman_region32_fini(&damage); + wlr_output_rollback(output); + return true; + } + + wlr_renderer_begin(renderer, output->width, output->height); + + pixman_region32_t background; + pixman_region32_init(&background); + pixman_region32_copy(&background, &damage); + + // Cull areas of the background that are occluded by opaque regions of + // scene nodes above. Those scene nodes will just render atop having us + // never see the background. + if (scene_output->scene->calculate_visibility) { + float output_scale = scene_output->output->scale; + + for (int i = list_len - 1; i >= 0; i--) { + struct wlr_scene_node *node = list_data[i]; + int x, y; + wlr_scene_node_coords(node, &x, &y); + + // We must only cull opaque regions that are visible by the node. + // The node's visibility will have the knowledge of a black rect + // that may have been omitted from the render list via the black + // rect optimization. In order to ensure we don't cull background + // rendering in that black rect region, consider the node's visibility. + pixman_region32_t opaque; + pixman_region32_init(&opaque); + scene_node_opaque_region(node, x, y, &opaque); + pixman_region32_intersect(&opaque, &opaque, &node->visible); + + pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); + wlr_region_scale(&opaque, &opaque, output_scale); + pixman_region32_subtract(&background, &background, &opaque); + pixman_region32_fini(&opaque); + } + + if (floor(output_scale) != output_scale) { + wlr_region_expand(&background, &background, 1); + + // reintersect with the damage because we never want to render + // outside of the damage region + pixman_region32_intersect(&background, &background, &damage); + } + } + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&background, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(output, &rects[i]); + wlr_renderer_clear(renderer, (float[4]){ 0.0, 0.0, 0.0, 1.0 }); + } + pixman_region32_fini(&background); + + for (int i = list_len - 1; i >= 0; i--) { + struct wlr_scene_node *node = list_data[i]; + scene_node_render(node, scene_output, &damage); + } + + wlr_renderer_scissor(renderer, NULL); + + if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { + struct highlight_region *damage; + wl_list_for_each(damage, &scene_output->damage_highlight_regions, link) { + struct timespec time_diff; + timespec_sub(&time_diff, &now, &damage->when); + int64_t time_diff_ms = timespec_to_msec(&time_diff); + float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME; + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage->region, &nrects); + for (int i = 0; i < nrects; ++i) { + struct wlr_box box = { + .x = rects[i].x1, + .y = rects[i].y1, + .width = rects[i].x2 - rects[i].x1, + .height = rects[i].y2 - rects[i].y1, + }; + + float color[4] = { alpha * .5, 0.0, 0.0, alpha * .5 }; + wlr_render_rect(renderer, &box, color, output->transform_matrix); + } + } + } + + wlr_output_render_software_cursors(output, &damage); + + wlr_renderer_end(renderer); + pixman_region32_fini(&damage); + + int tr_width, tr_height; + wlr_output_transformed_resolution(output, &tr_width, &tr_height); + + enum wl_output_transform transform = + wlr_output_transform_invert(output->transform); + + pixman_region32_t frame_damage; + pixman_region32_init(&frame_damage); + wlr_region_transform(&frame_damage, + &scene_output->damage_ring.current, + transform, tr_width, tr_height); + wlr_output_set_damage(output, &frame_damage); + pixman_region32_fini(&frame_damage); + + bool success = wlr_output_commit(output); + + if (success) { + wlr_damage_ring_rotate(&scene_output->damage_ring); + } + + if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && + !wl_list_empty(&scene_output->damage_highlight_regions)) { + wlr_output_schedule_frame(scene_output->output); + } + + return success; +} + +static void scene_node_send_frame_done(struct wlr_scene_node *node, + struct wlr_scene_output *scene_output, struct timespec *now) { + if (!node->enabled) { + return; + } + + if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_from_node(node); + + if (scene_buffer->primary_output == scene_output) { + wlr_scene_buffer_send_frame_done(scene_buffer, now); + } + } else if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + scene_node_send_frame_done(child, scene_output, now); + } + } +} + +void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, + struct timespec *now) { + scene_node_send_frame_done(&scene_output->scene->tree.node, + scene_output, now); +} + +static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, + struct wlr_scene_node *node, int lx, int ly, + wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { + if (!node->enabled) { + return; + } + + lx += node->x; + ly += node->y; + + if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_box node_box = { .x = lx, .y = ly }; + scene_node_get_size(node, &node_box.width, &node_box.height); + + struct wlr_box intersection; + if (wlr_box_intersection(&intersection, output_box, &node_box)) { + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_from_node(node); + user_iterator(scene_buffer, lx, ly, user_data); + } + } else if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + scene_output_for_each_scene_buffer(output_box, child, lx, ly, + user_iterator, user_data); + } + } +} + +void wlr_scene_output_for_each_buffer(struct wlr_scene_output *scene_output, + wlr_scene_buffer_iterator_func_t iterator, void *user_data) { + struct wlr_box box = { .x = scene_output->x, .y = scene_output->y }; + wlr_output_effective_resolution(scene_output->output, + &box.width, &box.height); + scene_output_for_each_scene_buffer(&box, &scene_output->scene->tree.node, 0, 0, + iterator, user_data); +} diff --git a/types/scene/xdg_shell.c b/types/scene/xdg_shell.c new file mode 100644 index 0000000..1688348 --- /dev/null +++ b/types/scene/xdg_shell.c @@ -0,0 +1,124 @@ +#include +#include +#include + +struct wlr_scene_xdg_surface { + struct wlr_scene_tree *tree; + struct wlr_xdg_surface *xdg_surface; + struct wlr_scene_tree *surface_tree; + + struct wl_listener tree_destroy; + struct wl_listener xdg_surface_destroy; + struct wl_listener xdg_surface_map; + struct wl_listener xdg_surface_unmap; + struct wl_listener xdg_surface_commit; +}; + +static void scene_xdg_surface_handle_tree_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, tree_destroy); + // tree and surface_node will be cleaned up by scene_node_finish + wl_list_remove(&scene_xdg_surface->tree_destroy.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_destroy.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_map.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_unmap.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_commit.link); + free(scene_xdg_surface); +} + +static void scene_xdg_surface_handle_xdg_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_destroy); + wlr_scene_node_destroy(&scene_xdg_surface->tree->node); +} + +static void scene_xdg_surface_handle_xdg_surface_map(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_map); + wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, true); +} + +static void scene_xdg_surface_handle_xdg_surface_unmap(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_unmap); + wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, false); +} + +static void scene_xdg_surface_update_position( + struct wlr_scene_xdg_surface *scene_xdg_surface) { + struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface; + + struct wlr_box geo = {0}; + wlr_xdg_surface_get_geometry(xdg_surface, &geo); + wlr_scene_node_set_position(&scene_xdg_surface->surface_tree->node, + -geo.x, -geo.y); + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_popup *popup = xdg_surface->popup; + wlr_scene_node_set_position(&scene_xdg_surface->tree->node, + popup->current.geometry.x, popup->current.geometry.y); + } +} + +static void scene_xdg_surface_handle_xdg_surface_commit(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_commit); + scene_xdg_surface_update_position(scene_xdg_surface); +} + +struct wlr_scene_tree *wlr_scene_xdg_surface_create( + struct wlr_scene_tree *parent, struct wlr_xdg_surface *xdg_surface) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + calloc(1, sizeof(*scene_xdg_surface)); + if (scene_xdg_surface == NULL) { + return NULL; + } + + scene_xdg_surface->xdg_surface = xdg_surface; + + scene_xdg_surface->tree = wlr_scene_tree_create(parent); + if (scene_xdg_surface->tree == NULL) { + free(scene_xdg_surface); + return NULL; + } + + scene_xdg_surface->surface_tree = wlr_scene_subsurface_tree_create( + scene_xdg_surface->tree, xdg_surface->surface); + if (scene_xdg_surface->surface_tree == NULL) { + wlr_scene_node_destroy(&scene_xdg_surface->tree->node); + free(scene_xdg_surface); + return NULL; + } + + scene_xdg_surface->tree_destroy.notify = + scene_xdg_surface_handle_tree_destroy; + wl_signal_add(&scene_xdg_surface->tree->node.events.destroy, + &scene_xdg_surface->tree_destroy); + + scene_xdg_surface->xdg_surface_destroy.notify = + scene_xdg_surface_handle_xdg_surface_destroy; + wl_signal_add(&xdg_surface->events.destroy, &scene_xdg_surface->xdg_surface_destroy); + + scene_xdg_surface->xdg_surface_map.notify = + scene_xdg_surface_handle_xdg_surface_map; + wl_signal_add(&xdg_surface->events.map, &scene_xdg_surface->xdg_surface_map); + + scene_xdg_surface->xdg_surface_unmap.notify = + scene_xdg_surface_handle_xdg_surface_unmap; + wl_signal_add(&xdg_surface->events.unmap, &scene_xdg_surface->xdg_surface_unmap); + + scene_xdg_surface->xdg_surface_commit.notify = + scene_xdg_surface_handle_xdg_surface_commit; + wl_signal_add(&xdg_surface->surface->events.commit, + &scene_xdg_surface->xdg_surface_commit); + + wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, xdg_surface->mapped); + scene_xdg_surface_update_position(scene_xdg_surface); + + return scene_xdg_surface->tree; +} diff --git a/util/array.c b/util/array.c new file mode 100644 index 0000000..ec16a7b --- /dev/null +++ b/util/array.c @@ -0,0 +1,40 @@ +#include "util/array.h" +#include +#include + +void array_remove_at(struct wl_array *arr, size_t offset, size_t size) { + assert(arr->size >= offset + size); + + char *data = arr->data; + memmove(&data[offset], &data[offset + size], arr->size - offset - size); + arr->size -= size; +} + +bool array_realloc(struct wl_array *arr, size_t size) { + // If the size is less than 1/4th of the allocation size, we shrink it. + // 1/4th is picked to provide hysteresis, without which an array with size + // arr->alloc would constantly reallocate if an element is added and then + // removed continously. + size_t alloc; + if (arr->alloc > 0 && size > arr->alloc / 4) { + alloc = arr->alloc; + } else { + alloc = 16; + } + + while (alloc < size) { + alloc *= 2; + } + + if (alloc == arr->alloc) { + return true; + } + + void *data = realloc(arr->data, alloc); + if (data == NULL) { + return false; + } + arr->data = data; + arr->alloc = alloc; + return true; +} diff --git a/util/env.c b/util/env.c new file mode 100644 index 0000000..b0a9efd --- /dev/null +++ b/util/env.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include "util/env.h" + +bool env_parse_bool(const char *option) { + const char *env = getenv(option); + if (env) { + wlr_log(WLR_INFO, "Loading %s option: %s", option, env); + } + + if (!env || strcmp(env, "0") == 0) { + return false; + } else if (strcmp(env, "1") == 0) { + return true; + } + + wlr_log(WLR_ERROR, "Unknown %s option: %s", option, env); + return false; +} + +ssize_t env_parse_switch(const char *option, const char **switches) { + const char *env = getenv(option); + if (env) { + wlr_log(WLR_INFO, "Loading %s option: %s", option, env); + } else { + return 0; + } + + for (ssize_t i = 0; switches[i]; i++) { + if (strcmp(env, switches[i]) == 0) { + return i; + } + } + + wlr_log(WLR_ERROR, "Unknown %s option: %s", option, env); + return 0; +} diff --git a/util/meson.build b/util/meson.build new file mode 100644 index 0000000..b54835c --- /dev/null +++ b/util/meson.build @@ -0,0 +1,5 @@ +wlr_files += files( + 'array.c', + 'env.c', + 'time.c', +) diff --git a/util/time.c b/util/time.c new file mode 100644 index 0000000..06e42b4 --- /dev/null +++ b/util/time.c @@ -0,0 +1,32 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include + +#include "util/time.h" + +static const long NSEC_PER_SEC = 1000000000; + +int64_t timespec_to_msec(const struct timespec *a) { + return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; +} + +void timespec_from_nsec(struct timespec *r, int64_t nsec) { + r->tv_sec = nsec / NSEC_PER_SEC; + r->tv_nsec = nsec % NSEC_PER_SEC; +} + +uint32_t get_current_time_msec(void) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return timespec_to_msec(&now); +} + +void timespec_sub(struct timespec *r, const struct timespec *a, + const struct timespec *b) { + r->tv_sec = a->tv_sec - b->tv_sec; + r->tv_nsec = a->tv_nsec - b->tv_nsec; + if (r->tv_nsec < 0) { + r->tv_sec--; + r->tv_nsec += NSEC_PER_SEC; + } +} -- cgit v1.2.3