diff --git a/.clang-tidy b/.clang-tidy index 360eab1f..eba4f37e 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -12,6 +12,9 @@ Checks: " cppcoreguidelines-*, -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-macro-usage, google-*, @@ -19,6 +22,9 @@ Checks: " -misc-no-recursion, modernize-*, + -modernize-use-nodiscard, + -modernize-use-trailing-return-type, + performance-*, portability-*, @@ -28,5 +34,8 @@ Checks: " *-cloexec-*, " +CheckOptions: +- key: 'cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor' + value: 'true' WarningsAsErrors: '*' FormatStyle: file diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index db9a0ce4..f53e4e58 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -25,14 +25,22 @@ jobs: steps: - name: Install dependencies run: | - echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main" | sudo tee -a /etc/apt/sources.list - echo "deb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main" | sudo tee -a /etc/apt/sources.list - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt-get update sudo apt-get install \ libwayland-dev libwlroots-dev libpixman-1-dev \ libxkbcommon-dev libglu1-mesa-dev libglew-dev \ - meson libfreetype-dev librsvg2-dev libcglm-dev clang-15 + meson libfreetype-dev librsvg2-dev libcglm-dev + + - name: Install clang15 + if: matrix.clang-version == 15 + run: | + echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main" | sudo tee -a /etc/apt/sources.list + echo "deb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main" | sudo tee -a /etc/apt/sources.list + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo apt-get update + sudo apt-get install clang-15 clang-format-15 clang-tidy-15 + sudo ln -sf /usr/bin/clang-tidy-15 /usr/bin/clang-tidy + sudo ln -sf /usr/bin/clang-format-15 /usr/bin/clang-format - uses: actions/setup-python@v1 with: @@ -46,12 +54,25 @@ jobs: unzip grpc-dev.zip working-directory: ./grpc-dev + - name: Clone zen-protocols + uses: actions/checkout@v2 + with: + repository: zwin-project/zen-protocols + path: zen-protocols + + - name: Install zen-protocols + working-directory: ./zen-protocols + run: | + meson build + sudo ninja -C build install + - name: Clone zen-remote uses: actions/checkout@v2 with: repository: zwin-project/zen-remote path: zen-remote submodules: recursive + ref: 'v0.2.0' - name: Build zen-remote working-directory: ./zen-remote @@ -65,6 +86,7 @@ jobs: with: repository: zwin-project/zwin path: zwin + ref: 'v0.2.0' - name: Build zwin working-directory: ./zwin diff --git a/LICENSE b/LICENSE index 308dad92..2ce7cd3b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Akihiro Kiuchi & Taishi Eguchi +Copyright (c) 2023 Zwin Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/common/include/zen-common/cpp-util.h b/common/include/zen-common/cpp-util.h new file mode 100644 index 00000000..0022ecb6 --- /dev/null +++ b/common/include/zen-common/cpp-util.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus + +// NOLINTBEGIN(bugprone-macro-parentheses) +#define DISABLE_MOVE_AND_COPY(Class) \ + Class(const Class &) = delete; \ + Class(Class &&) = delete; \ + Class &operator=(const Class &) = delete; \ + Class &operator=(Class &&) = delete +// NOLINTEND(bugprone-macro-parentheses) + +#endif diff --git a/common/include/zen-common/delay-signal.h b/common/include/zen-common/delay-signal.h new file mode 100644 index 00000000..e7c47305 --- /dev/null +++ b/common/include/zen-common/delay-signal.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +struct zn_delay_signal { + struct wl_signal signal; + struct wl_event_source *idle; // @nonnull, @ref +}; + +/// The signal handlers will be called at the next idle time. +/// No user data is allowed as it's hard to manage the ownership of the data. +/// In there is already a scheduled signal, this signal scheduling will be +/// ignored. +void zn_delay_signal_schedule( + struct zn_delay_signal *self, struct wl_display *display); + +void zn_delay_signal_add( + struct zn_delay_signal *self, struct wl_listener *listener); + +void zn_delay_signal_init(struct zn_delay_signal *self); + +void zn_delay_signal_release(struct zn_delay_signal *self); diff --git a/common/include/zen-common/log.h b/common/include/zen-common/log.h index e3fb5e07..fb22e803 100644 --- a/common/include/zen-common/log.h +++ b/common/include/zen-common/log.h @@ -5,6 +5,8 @@ #include #include +#include "util.h" + #ifdef __cplusplus extern "C" { #endif @@ -36,7 +38,8 @@ void zn_vlog_(zn_log_importance_t verbosity, const char *format, va_list args) void zn_abort_(const char *format, ...) ATTRIB_PRINTF(1, 2); -bool zn_assert_(bool condition, const char *format, ...) ATTRIB_PRINTF(2, 3); +bool zn_assert_handler_(const char *format, ...) + ATTRIB_PRINTF(1, 2) CLANG_ANALYZER_NORETURN; #define zn_log(verb, fmt, ...) \ zn_log_(verb, "[zen] [%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__) @@ -59,9 +62,9 @@ bool zn_assert_(bool condition, const char *format, ...) ATTRIB_PRINTF(2, 3); #define zn_abort(fmt, ...) \ zn_abort_("[zen] [%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__) -#define zn_assert(cond, fmt, ...) \ - zn_assert_(cond, "[zen] [%s:%d] %s: " fmt, __FILE__, __LINE__, __func__, \ - ##__VA_ARGS__) +#define zn_assert(cond, fmt, ...) \ + (cond || zn_assert_handler_( \ + "[zen] [%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)) #define zn_warn_once(fmt, ...) \ { \ diff --git a/common/include/zen-common/util.h b/common/include/zen-common/util.h index 8fa52bea..32916af8 100644 --- a/common/include/zen-common/util.h +++ b/common/include/zen-common/util.h @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include // NOLINT(modernize-deprecated-headers) +#include // NOLINT(modernize-deprecated-headers) #ifdef __cplusplus extern "C" { @@ -28,11 +28,19 @@ extern "C" { #endif #endif +#ifndef CLANG_ANALYZER_NORETURN +#if __has_feature(attribute_analyzer_noreturn) +#define CLANG_ANALYZER_NORETURN __attribute__((analyzer_noreturn)) +#else +#define CLANG_ANALYZER_NORETURN +#endif +#endif + /** Allocate memory and set to zero */ UNUSED static inline void * zalloc(size_t size) { - return calloc(1, size); + return calloc(1, size); // NOLINT(cppcoreguidelines-*) } #define ZN_MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -42,16 +50,6 @@ zalloc(size_t size) #define zn_container_of(ptr, sample, member) \ (__typeof__(sample))((char *)(ptr)-offsetof(__typeof__(*(sample)), member)) -#ifdef __cplusplus - -#define DISABLE_MOVE_AND_COPY(Class) \ - Class(const Class &) = delete; \ - Class(Class &&) = delete; \ - Class &operator=(const Class &) = delete; \ - Class &operator=(Class &&) = delete - -#endif - #ifdef __cplusplus } #endif diff --git a/common/include/zen-common/wl-array.h b/common/include/zen-common/wl-array.h new file mode 100644 index 00000000..a2f7e17c --- /dev/null +++ b/common/include/zen-common/wl-array.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// @return true if successful, false otherwise +bool zn_wl_array_to_int64_t(struct wl_array *array, int64_t *value); + +/// @return true if successful, false otherwise +bool zn_wl_array_to_uint64_t(struct wl_array *array, uint64_t *value); + +/// @return true if successful, false otherwise +/// @param array must be initialized +bool zn_wl_array_from_vec3(struct wl_array *array, vec3 value); + +/// @return true if successful, false otherwise +bool zn_wl_array_to_vec3(struct wl_array *array, vec3 value); + +#ifdef __cplusplus +} +#endif diff --git a/common/meson.build b/common/meson.build index 6cdd2058..64f84872 100644 --- a/common/meson.build +++ b/common/meson.build @@ -1,8 +1,10 @@ _zen_common_srcs = [ 'src/cairo/rounded.c', + 'src/delay-signal.c', 'src/log.c', 'src/signal.c', 'src/terminate.c', + 'src/wl-array.c', 'src/wlr/box.c', ] diff --git a/common/src/delay-signal.c b/common/src/delay-signal.c new file mode 100644 index 00000000..0b24103a --- /dev/null +++ b/common/src/delay-signal.c @@ -0,0 +1,49 @@ +#include "zen-common/delay-signal.h" + +#include "zen-common/signal.h" +#include "zen-common/util.h" + +static void +zn_delay_signal_idle_timer(void *data) +{ + struct zn_delay_signal *self = data; + + self->idle = NULL; + + zn_signal_emit_mutable(&self->signal, NULL); +} + +void +zn_delay_signal_schedule( + struct zn_delay_signal *self, struct wl_display *display) +{ + if (self->idle) { + return; + } + + struct wl_event_loop *loop = wl_display_get_event_loop(display); + + self->idle = wl_event_loop_add_idle(loop, zn_delay_signal_idle_timer, self); +} + +void +zn_delay_signal_add(struct zn_delay_signal *self, struct wl_listener *listener) +{ + wl_signal_add(&self->signal, listener); +} + +void +zn_delay_signal_init(struct zn_delay_signal *self) +{ + wl_signal_init(&self->signal); + self->idle = NULL; +} + +void +zn_delay_signal_release(struct zn_delay_signal *self) +{ + wl_list_remove(&self->signal.listener_list); + if (self->idle) { + wl_event_source_remove(self->idle); + } +} diff --git a/common/src/log.c b/common/src/log.c index ef7fe7df..07cb9527 100644 --- a/common/src/log.c +++ b/common/src/log.c @@ -121,12 +121,8 @@ zn_abort_(const char *format, ...) } bool -zn_assert_(bool condition, const char *format, ...) +zn_assert_handler_(const char *format, ...) { - if (condition) { - return true; - } - va_list args; va_start(args, format); zn_vlog_(ZEN_ERROR, format, args); diff --git a/common/src/wl-array.c b/common/src/wl-array.c new file mode 100644 index 00000000..564d113b --- /dev/null +++ b/common/src/wl-array.c @@ -0,0 +1,52 @@ +#include "zen-common/wl-array.h" + +#include + +bool +zn_wl_array_to_int64_t(struct wl_array *array, int64_t *value) +{ + if (array->size != sizeof(*value)) { + return false; + } + + memcpy(value, array->data, array->size); + + return true; +} + +bool +zn_wl_array_to_uint64_t(struct wl_array *array, uint64_t *value) +{ + if (array->size != sizeof(*value)) { + return false; + } + + memcpy(value, array->data, array->size); + + return true; +} + +bool +zn_wl_array_from_vec3(struct wl_array *array, vec3 value) +{ + void *data = wl_array_add(array, sizeof(vec3)); + if (data == NULL) { + return false; + } + + memcpy(data, value, sizeof(vec3)); + + return true; +} + +bool +zn_wl_array_to_vec3(struct wl_array *array, vec3 value) +{ + if (array->size != sizeof(vec3)) { + return false; + } + + memcpy(value, array->data, array->size); + + return true; +} diff --git a/desktop/include/zen-desktop/shell.h b/desktop/include/zen-desktop/shell.h index 50f45fd9..ca7df436 100644 --- a/desktop/include/zen-desktop/shell.h +++ b/desktop/include/zen-desktop/shell.h @@ -5,6 +5,11 @@ struct zn_cursor_grab; struct zn_theme; +enum zn_desktop_shell_display_mode { + ZN_DESKTOP_SHELL_DISPLAY_MODE_SCREEN, + ZN_DESKTOP_SHELL_DISPLAY_MODE_IMMERSIVE, +}; + struct zn_desktop_shell { struct zn_screen_layout *screen_layout; // @nonnull, @owning @@ -12,6 +17,10 @@ struct zn_desktop_shell { struct zn_theme *theme; // @nonnull, @owning + enum zn_desktop_shell_display_mode mode; + + struct wl_listener xr_system_changed_listener; + struct wl_listener new_xr_system_listener; struct wl_listener new_screen_listener; struct wl_listener seat_capabilities_listener; struct wl_listener pointer_motion_listener; @@ -19,6 +28,7 @@ struct zn_desktop_shell { struct wl_listener pointer_axis_listener; struct wl_listener pointer_frame_listener; struct wl_listener view_mapped_listener; + struct wl_listener bounded_mapped_listener; }; /// Ownership of `grab` moves to `self` diff --git a/desktop/include/zen-desktop/xr-system.h b/desktop/include/zen-desktop/xr-system.h new file mode 100644 index 00000000..6078053d --- /dev/null +++ b/desktop/include/zen-desktop/xr-system.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +struct zn_xr_system; +struct zn_desktop_server_xr_system; + +struct zn_desktop_xr_system { + struct zn_xr_system *zn_xr_system; // @nonnull, @outlive + + struct wl_global *global; // @nonnull, @owning + struct wl_list resource_list; // wl_resource::link of zen_xr_system + + enum zen_xr_system_status status; + + struct wl_listener zn_xr_system_destroy_listener; + struct wl_listener session_state_changed_listener; +}; + +struct zn_desktop_xr_system *zn_desktop_xr_system_create( + struct zn_xr_system *zn_xr_system, struct wl_display *display); diff --git a/desktop/main.c b/desktop/main.c index c549359a..00dc79f8 100644 --- a/desktop/main.c +++ b/desktop/main.c @@ -9,6 +9,7 @@ #include "zen-common/terminate.h" #include "zen-common/util.h" #include "zen-desktop/shell.h" +#include "zen/config.h" #include "zen/include/zen/server.h" static void @@ -76,10 +77,16 @@ main(int argc UNUSED, const char *argv[] UNUSED) goto err_signal; } - struct zn_server *server = zn_server_create(display, NULL); + struct zn_config *config = zn_config_create(); + if (config == NULL) { + zn_error("Failed to create a zn_config"); + goto err_signal; + } + + struct zn_server *server = zn_server_create(display, NULL, config); if (server == NULL) { zn_error("Failed to create a zn_server"); - goto err_signal; + goto err_config; } struct zn_desktop_shell *shell = zn_desktop_shell_create(); @@ -95,6 +102,9 @@ main(int argc UNUSED, const char *argv[] UNUSED) err_server: zn_server_destroy(server); +err_config: + zn_config_destroy(config); + err_signal: for (int i = ARRAY_LENGTH(signal_sources) - 1; i >= 0; i--) { if (signal_sources[i]) { diff --git a/desktop/meson.build b/desktop/meson.build index 86c5bfdf..0a1de69c 100644 --- a/desktop/meson.build +++ b/desktop/meson.build @@ -20,6 +20,10 @@ _zen_desktop_srcs = [ 'src/ui/decoration/shadow.c', 'src/view.c', + 'src/xr-system.c', + + protocols_code['zen-desktop'], + protocols_server_header['zen-desktop'], ] _zen_desktop_inc = include_directories('include') diff --git a/desktop/src/cursor-grab.c b/desktop/src/cursor-grab.c index 525b4477..a43b7312 100644 --- a/desktop/src/cursor-grab.c +++ b/desktop/src/cursor-grab.c @@ -17,24 +17,20 @@ zn_cursor_grab_move_relative(vec2 delta) struct zn_server *server = zn_server_get_singleton(); struct zn_desktop_shell *shell = zn_desktop_shell_get_singleton(); struct zn_cursor *cursor = server->seat->cursor; + struct zn_screen *screen = zn_screen_from_snode_root(cursor->snode->root); + struct zn_desktop_screen *desktop_screen = - cursor->snode->screen ? zn_desktop_screen_get(cursor->snode->screen) - : NULL; - vec2 position; + screen ? zn_desktop_screen_get(screen) : NULL; if (desktop_screen == NULL) { - desktop_screen = zn_screen_layout_get_main_screen(shell->screen_layout); + return; + } - if (!desktop_screen) { - return; - } + vec2 position; - glm_vec2_divs(desktop_screen->screen->size, 2, position); - } else { - glm_vec2_add(cursor->snode->position, delta, position); - zn_screen_layout_get_closest_position(shell->screen_layout, desktop_screen, - position, &desktop_screen, position); - } + glm_vec2_add(cursor->snode->position, delta, position); + zn_screen_layout_get_closest_position(shell->screen_layout, desktop_screen, + position, &desktop_screen, position); zn_snode_set_position(cursor->snode, desktop_screen->cursor_layer, position); } diff --git a/desktop/src/cursor-grab/default.c b/desktop/src/cursor-grab/default.c index 4956d75d..577d6133 100644 --- a/desktop/src/cursor-grab/default.c +++ b/desktop/src/cursor-grab/default.c @@ -9,6 +9,7 @@ #include "zen/screen.h" #include "zen/seat.h" #include "zen/server.h" +#include "zen/snode-root.h" #include "zen/snode.h" static void zn_cursor_default_grab_destroy(struct zn_cursor_default_grab *self); @@ -26,16 +27,17 @@ zn_cursor_default_grab_handle_motion_relative( { struct zn_server *server = zn_server_get_singleton(); struct zn_cursor *cursor = server->seat->cursor; + struct zn_screen *screen = zn_screen_from_snode_root(cursor->snode->root); zn_cursor_grab_move_relative(delta); - if (cursor->snode->screen == NULL) { + if (screen == NULL) { return; } vec2 point = GLM_VEC2_ZERO_INIT; struct zn_snode *snode = zn_snode_get_snode_at( - cursor->snode->screen->snode_root, cursor->snode->position, point); + screen->snode_root->node, cursor->snode->position, point); zn_seat_pointer_enter(server->seat, snode, point); @@ -48,13 +50,13 @@ zn_cursor_default_grab_handle_rebase(struct zn_cursor_grab *grab UNUSED) struct zn_server *server = zn_server_get_singleton(); struct zn_cursor *cursor = server->seat->cursor; - if (cursor->snode->screen == NULL) { + if (cursor->snode->root == NULL) { return; } vec2 point = GLM_VEC2_ZERO_INIT; struct zn_snode *snode = zn_snode_get_snode_at( - cursor->snode->screen->snode_root, cursor->snode->position, point); + cursor->snode->root->node, cursor->snode->position, point); zn_seat_pointer_enter(server->seat, snode, point); } diff --git a/desktop/src/cursor-grab/down.c b/desktop/src/cursor-grab/down.c index 707173ff..9eb70d9f 100644 --- a/desktop/src/cursor-grab/down.c +++ b/desktop/src/cursor-grab/down.c @@ -30,7 +30,8 @@ zn_cursor_down_grab_handle_motion_relative( zn_cursor_grab_move_relative(delta); - if (focus == NULL || focus->screen == NULL || cursor->snode->screen == NULL) { + if (focus == NULL || zn_screen_from_snode_root(focus->root) == NULL || + zn_screen_from_snode_root(cursor->snode->root) == NULL) { return; } diff --git a/desktop/src/cursor-grab/move.c b/desktop/src/cursor-grab/move.c index e97c5ea7..cb701f9c 100644 --- a/desktop/src/cursor-grab/move.c +++ b/desktop/src/cursor-grab/move.c @@ -8,8 +8,10 @@ #include "zen-desktop/shell.h" #include "zen-desktop/view.h" #include "zen/cursor.h" +#include "zen/screen.h" #include "zen/seat.h" #include "zen/server.h" +#include "zen/snode-root.h" #include "zen/snode.h" static void zn_cursor_move_grab_destroy(struct zn_cursor_move_grab *self); @@ -27,23 +29,24 @@ zn_cursor_move_grab_handle_motion_relative( { struct zn_server *server = zn_server_get_singleton(); struct zn_cursor *cursor = server->seat->cursor; + struct zn_screen *screen = zn_screen_from_snode_root(cursor->snode->root); struct zn_cursor_move_grab *self = zn_cursor_move_grab_get(grab); vec2 new_position; zn_cursor_grab_move_relative(delta); - if (cursor->snode->screen == NULL) { + if (screen == NULL) { return; } - struct zn_desktop_screen *screen = - zn_desktop_screen_get(cursor->snode->screen); + struct zn_desktop_screen *desktop_screen = zn_desktop_screen_get(screen); glm_vec2_add(cursor->snode->position, self->initial_view_cursor_position, new_position); - zn_snode_set_position(self->view->snode, screen->view_layer, new_position); + zn_snode_set_position( + self->view->snode, desktop_screen->view_layer, new_position); } static void diff --git a/desktop/src/screen.c b/desktop/src/screen.c index 1a686615..c32ca64b 100644 --- a/desktop/src/screen.c +++ b/desktop/src/screen.c @@ -7,6 +7,7 @@ #include "zen-desktop/screen-layout.h" #include "zen-desktop/shell.h" #include "zen/screen.h" +#include "zen/snode-root.h" #include "zen/snode.h" static void zn_desktop_screen_destroy(struct zn_desktop_screen *self); @@ -77,15 +78,17 @@ zn_desktop_screen_create(struct zn_screen *screen) } zn_snode_set_position(screen->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - screen->snode_root, GLM_VEC2_ZERO); + screen->snode_root->node, GLM_VEC2_ZERO); zn_snode_set_position(screen->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - screen->snode_root, GLM_VEC2_ZERO); - zn_snode_set_position(self->view_layer, screen->snode_root, GLM_VEC2_ZERO); + screen->snode_root->node, GLM_VEC2_ZERO); + zn_snode_set_position( + self->view_layer, screen->snode_root->node, GLM_VEC2_ZERO); zn_snode_set_position(screen->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - screen->snode_root, GLM_VEC2_ZERO); + screen->snode_root->node, GLM_VEC2_ZERO); zn_snode_set_position(screen->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - screen->snode_root, GLM_VEC2_ZERO); - zn_snode_set_position(self->cursor_layer, screen->snode_root, GLM_VEC2_ZERO); + screen->snode_root->node, GLM_VEC2_ZERO); + zn_snode_set_position( + self->cursor_layer, screen->snode_root->node, GLM_VEC2_ZERO); self->screen_destroy_listener.notify = zn_desktop_screen_handle_screen_destroy; diff --git a/desktop/src/shell.c b/desktop/src/shell.c index b4b788ae..62a61c8a 100644 --- a/desktop/src/shell.c +++ b/desktop/src/shell.c @@ -1,5 +1,6 @@ #include "zen-desktop/shell.h" +#include #include #include @@ -10,16 +11,60 @@ #include "zen-desktop/screen.h" #include "zen-desktop/theme.h" #include "zen-desktop/view.h" +#include "zen-desktop/xr-system.h" #include "zen/backend.h" +#include "zen/bounded.h" +#include "zen/cursor.h" +#include "zen/inode.h" #include "zen/screen.h" #include "zen/seat.h" #include "zen/server.h" #include "zen/snode.h" #include "zen/view.h" +#include "zen/virtual-object.h" // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static struct zn_desktop_shell *desktop_shell_singleton = NULL; +static void +zn_desktop_shell_handle_xr_system_changed( + struct wl_listener *listener, void *data) +{ + struct zn_desktop_shell *self = + zn_container_of(listener, self, xr_system_changed_listener); + struct zn_xr_system *xr_system = data; + struct zn_server *server = zn_server_get_singleton(); + + enum zn_desktop_shell_display_mode mode = + xr_system ? ZN_DESKTOP_SHELL_DISPLAY_MODE_IMMERSIVE + : ZN_DESKTOP_SHELL_DISPLAY_MODE_SCREEN; + + if (self->mode == mode) { + return; + } + + self->mode = mode; + + if (self->mode == ZN_DESKTOP_SHELL_DISPLAY_MODE_SCREEN) { + zn_inode_unmap(server->inode_root); + zn_info("Switch to screen display mode"); + } else { + zn_inode_map(server->inode_root); + zn_info("Switch to immersive display mode"); + } +} + +static void +zn_desktop_shell_handle_new_xr_system( + struct wl_listener *listener UNUSED, void *data) +{ + struct zn_server *server = zn_server_get_singleton(); + + struct zn_xr_system *zn_xr_system = data; + + zn_desktop_xr_system_create(zn_xr_system, server->display); +} + static void zn_desktop_shell_handle_new_screen(struct wl_listener *listener, void *data) { @@ -29,6 +74,20 @@ zn_desktop_shell_handle_new_screen(struct wl_listener *listener, void *data) struct zn_desktop_screen *desktop_screen = zn_desktop_screen_create(screen); zn_screen_layout_add(self->screen_layout, desktop_screen); + + struct zn_server *server = zn_server_get_singleton(); + struct zn_cursor *cursor = server->seat->cursor; + + // FIXME(@Aki-7): when it's immersive display system, the cursor should not + // appear on the screen + if (cursor->snode->root == NULL) { + vec2 position; + + glm_vec2_divs(desktop_screen->screen->size, 2.0F, position); + + zn_snode_set_position( + cursor->snode, desktop_screen->cursor_layer, position); + } } static void @@ -43,10 +102,31 @@ zn_desktop_shell_handle_view_mapped(struct wl_listener *listener, void *data) struct zn_desktop_screen *desktop_screen = zn_screen_layout_get_main_screen(self->screen_layout); + vec2 initial_position; + glm_vec2_sub( + desktop_screen->screen->size, view->zn_view->size, initial_position); + glm_vec2_scale(initial_position, 0.5F, initial_position); + if (initial_position[1] < 40) { + initial_position[1] = 40; + } + if (desktop_screen) { zn_snode_set_position( - view->snode, desktop_screen->view_layer, (vec2){0, 0}); + view->snode, desktop_screen->view_layer, initial_position); } + + struct zn_server *server = zn_server_get_singleton(); + + zn_seat_set_focus(server->seat, view->snode); +} + +static void +zn_desktop_shell_handle_bounded_mapped( + struct wl_listener *listener UNUSED, void *data) +{ + struct zn_bounded *bounded = data; + + zn_virtual_object_change_visibility(bounded->virtual_object, true); } static void @@ -154,6 +234,8 @@ zn_desktop_shell_create(void) desktop_shell_singleton = self; + self->mode = ZN_DESKTOP_SHELL_DISPLAY_MODE_SCREEN; + self->theme = zn_theme_create(); if (self->theme == NULL) { zn_error("Failed to create theme"); @@ -174,6 +256,15 @@ zn_desktop_shell_create(void) } self->cursor_grab = &cursor_default_grab->base; + self->xr_system_changed_listener.notify = + zn_desktop_shell_handle_xr_system_changed; + wl_signal_add(&server->backend->events.xr_system_changed, + &self->xr_system_changed_listener); + + self->new_xr_system_listener.notify = zn_desktop_shell_handle_new_xr_system; + wl_signal_add( + &server->backend->events.new_xr_system, &self->new_xr_system_listener); + self->new_screen_listener.notify = zn_desktop_shell_handle_new_screen; wl_signal_add( &server->backend->events.new_screen, &self->new_screen_listener); @@ -203,6 +294,10 @@ zn_desktop_shell_create(void) wl_signal_add( &server->backend->events.view_mapped, &self->view_mapped_listener); + self->bounded_mapped_listener.notify = zn_desktop_shell_handle_bounded_mapped; + wl_signal_add( + &server->backend->events.bounded_mapped, &self->bounded_mapped_listener); + return self; err_screen_layout: @@ -222,6 +317,7 @@ zn_desktop_shell_create(void) void zn_desktop_shell_destroy(struct zn_desktop_shell *self) { + wl_list_remove(&self->bounded_mapped_listener.link); wl_list_remove(&self->view_mapped_listener.link); wl_list_remove(&self->pointer_frame_listener.link); wl_list_remove(&self->pointer_axis_listener.link); @@ -229,6 +325,8 @@ zn_desktop_shell_destroy(struct zn_desktop_shell *self) wl_list_remove(&self->pointer_motion_listener.link); wl_list_remove(&self->seat_capabilities_listener.link); wl_list_remove(&self->new_screen_listener.link); + wl_list_remove(&self->new_xr_system_listener.link); + wl_list_remove(&self->xr_system_changed_listener.link); zn_cursor_grab_destroy(self->cursor_grab); zn_screen_layout_destroy(self->screen_layout); zn_theme_destroy(self->theme); diff --git a/desktop/src/ui/decoration/header-bar.c b/desktop/src/ui/decoration/header-bar.c index 8e7a5bd1..6dcc2998 100644 --- a/desktop/src/ui/decoration/header-bar.c +++ b/desktop/src/ui/decoration/header-bar.c @@ -118,14 +118,17 @@ zn_ui_header_bar_update_texture(struct zn_ui_header_bar *self) if (self->texture) { zn_snode_damage_whole(self->snode); wlr_texture_destroy(self->texture); + self->texture = NULL; } unsigned char *data = cairo_image_surface_get_data(surface); int stride = cairo_image_surface_get_stride(surface); int width = cairo_image_surface_get_width(surface); int height = cairo_image_surface_get_height(surface); - self->texture = zn_backend_create_wlr_texture_from_pixels( - server->backend, DRM_FORMAT_ARGB8888, stride, width, height, data); + if (width > 0 && height > 0) { + self->texture = zn_backend_create_wlr_texture_from_pixels( + server->backend, DRM_FORMAT_ARGB8888, stride, width, height, data); + } zn_snode_damage_whole(self->snode); diff --git a/desktop/src/view.c b/desktop/src/view.c index fa36df76..c651fd48 100644 --- a/desktop/src/view.c +++ b/desktop/src/view.c @@ -133,7 +133,7 @@ zn_desktop_view_handle_zn_view_resized(struct wl_listener *listener, void *data) if (position_changed) { vec2 new_position; glm_vec2_add(self->snode->position, position_diff, new_position); - zn_snode_set_position(self->snode, self->snode->parent, new_position); + zn_snode_change_position(self->snode, new_position); } zn_desktop_view_update_decoration(self); diff --git a/desktop/src/xr-system.c b/desktop/src/xr-system.c new file mode 100644 index 00000000..1da1905c --- /dev/null +++ b/desktop/src/xr-system.c @@ -0,0 +1,152 @@ +#include "zen-desktop/xr-system.h" + +#include + +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/backend.h" +#include "zen/server.h" +#include "zen/xr-system.h" + +static void zn_desktop_xr_system_destroy(struct zn_desktop_xr_system *self); + +static void +zn_desktop_xr_system_handle_destroy(struct wl_resource *resource) +{ + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +zn_desktop_xr_system_protocol_connect( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + struct zn_desktop_xr_system *self = wl_resource_get_user_data(resource); + + zn_xr_system_connect(self->zn_xr_system); +} + +static const struct zen_xr_system_interface implementation = { + .connect = zn_desktop_xr_system_protocol_connect, +}; + +static void +zn_desktop_xr_system_bind( + struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct zn_desktop_xr_system *self = data; + + struct wl_resource *resource = + wl_resource_create(client, &zen_xr_system_interface, (int)version, id); + if (resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation( + resource, &implementation, self, zn_desktop_xr_system_handle_destroy); + + wl_list_insert(&self->resource_list, wl_resource_get_link(resource)); + + zen_xr_system_send_status(resource, self->status); +} + +static void +zn_desktop_xr_system_handle_xr_system_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_desktop_xr_system *self = + zn_container_of(listener, self, zn_xr_system_destroy_listener); + + zn_desktop_xr_system_destroy(self); +} + +static void +zn_desktop_xr_system_handle_session_state_changed( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_server *server = zn_server_get_singleton(); + + struct zn_desktop_xr_system *self = + zn_container_of(listener, self, session_state_changed_listener); + + struct wl_resource *resource = NULL; + + // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) + enum zen_xr_system_status status = self->status; + + switch (self->zn_xr_system->state) { + case ZN_XR_SYSTEM_SESSION_STATE_AVAILABLE: + status = ZEN_XR_SYSTEM_STATUS_AVAILABLE; + break; + case ZN_XR_SYSTEM_SESSION_STATE_SYNCHRONIZED: + if (!zn_backend_get_xr_system(server->backend)) { + zn_backend_set_xr_system(server->backend, self->zn_xr_system); + } + status = ZEN_XR_SYSTEM_STATUS_CONNECTED; + break; + case ZN_XR_SYSTEM_SESSION_STATE_DEAD: + status = ZEN_XR_SYSTEM_STATUS_UNAVAILABLE; + break; + case ZN_XR_SYSTEM_SESSION_STATE_VISIBLE: // fallthrough + case ZN_XR_SYSTEM_SESSION_STATE_FOCUS: + return; + } + + if (self->status != status) { + self->status = status; + wl_resource_for_each (resource, &self->resource_list) { + zen_xr_system_send_status(resource, status); + } + } +} + +struct zn_desktop_xr_system * +zn_desktop_xr_system_create( + struct zn_xr_system *zn_xr_system, struct wl_display *display) +{ + struct zn_desktop_xr_system *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->zn_xr_system = zn_xr_system; + self->status = ZEN_XR_SYSTEM_STATUS_AVAILABLE; + wl_list_init(&self->resource_list); + + self->global = wl_global_create( + display, &zen_xr_system_interface, 1, self, zn_desktop_xr_system_bind); + if (self->global == NULL) { + zn_error("Failed to create a wl_global"); + goto err_free; + } + + self->zn_xr_system_destroy_listener.notify = + zn_desktop_xr_system_handle_xr_system_destroy; + wl_signal_add( + &zn_xr_system->events.destroy, &self->zn_xr_system_destroy_listener); + + self->session_state_changed_listener.notify = + zn_desktop_xr_system_handle_session_state_changed; + wl_signal_add(&zn_xr_system->events.session_state_changed, + &self->session_state_changed_listener); + + return self; + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_desktop_xr_system_destroy(struct zn_desktop_xr_system *self) +{ + wl_list_remove(&self->session_state_changed_listener.link); + wl_list_remove(&self->zn_xr_system_destroy_listener.link); + wl_global_destroy(self->global); + wl_list_remove(&self->resource_list); + free(self); +} diff --git a/desktop/tests/screen-layout/setup.h b/desktop/tests/screen-layout/setup.h index 460d294b..8cf2fc4e 100644 --- a/desktop/tests/screen-layout/setup.h +++ b/desktop/tests/screen-layout/setup.h @@ -4,13 +4,15 @@ #include "mock/output.h" #include "zen-common/util.h" #include "zen-desktop/shell.h" +#include "zen/config.h" #include "zen/server.h" UNUSED static void setup(struct wl_display *display) { struct zn_mock_backend *backend = zn_mock_backend_create(); - zn_server_create(display, &backend->base); + struct zn_config *config = zn_config_create(); + zn_server_create(display, &backend->base, config); zn_desktop_shell_create(); } @@ -19,6 +21,8 @@ teardown(void) { struct zn_server *server = zn_server_get_singleton(); struct zn_desktop_shell *shell = zn_desktop_shell_get_singleton(); + struct zn_config *config = server->config; zn_desktop_shell_destroy(shell); zn_server_destroy(server); + zn_config_destroy(config); } diff --git a/meson.build b/meson.build index ae1df0e6..0595eef0 100644 --- a/meson.build +++ b/meson.build @@ -120,6 +120,9 @@ configure_file( wayland_req = '>= 1.18.0' wayland_protocols_req = '>= 1.24' wlroots_req = ['>= 0.15', '< 0.16'] +zen_protocols_req = '0.1.0' +zen_remote_server_req = '0.2.0' +zwin_protocols_req = '0.1.0' glew_proj = subproject( @@ -142,6 +145,9 @@ wayland_protocols_dep = dependency('wayland-protocols', version: wayland_protoco wayland_scanner_dep = dependency('wayland-scanner', native: true) wayland_server_dep = dependency('wayland-server', version: wayland_req) xkbcommon_dep = dependency('xkbcommon') +zen_protocols_dep = dependency('zen-protocols', version: zen_protocols_req) +zen_remote_server_dep = dependency('zen-remote-server', version: zen_remote_server_req) +zwin_protocols_dep = dependency('zwin-protocols', version: zwin_protocols_req) subdir('protocol') wlroots_dep = declare_dependency( diff --git a/protocol/meson.build b/protocol/meson.build index f2856347..c1718219 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,4 +1,6 @@ wl_protocol_dir = wayland_protocols_dep.get_variable('pkgdatadir') +zen_protocol_dir = zen_protocols_dep.get_variable('pkgdatadir') +zwin_protocol_dir = zwin_protocols_dep.get_variable('pkgdatadir') wayland_scanner = find_program( wayland_scanner_dep.get_variable('wayland_scanner'), @@ -8,11 +10,15 @@ wayland_scanner = find_program( protocols = { 'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', + 'zen-desktop': zen_protocol_dir / 'unstable/zen-desktop.xml', + + 'zwin': zwin_protocol_dir / 'zwin.xml', + 'zwin-gl': zwin_protocol_dir / 'zwin-gl.xml', + 'zwin-shell': zwin_protocol_dir / 'zwin-shell.xml', + 'wlr-layer-shell-unstable-v1': 'wlr-layer-shell-unstable-v1.xml', } -wlroots_srcs = [] - protocols_code = {} protocols_server_header = {} protocols_client_header = {} @@ -24,8 +30,6 @@ foreach name, path : protocols command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], ) - wlroots_srcs += code - server_header = custom_target( name.underscorify() + '_server_h', input: path, @@ -33,8 +37,6 @@ foreach name, path : protocols command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], ) - wlroots_srcs += server_header - client_header = custom_target( name.underscorify() + '_client_h', input: path, @@ -47,3 +49,10 @@ foreach name, path : protocols protocols_server_header += { name: server_header } protocols_client_header += { name: client_header } endforeach + +wlroots_srcs = [ + protocols_code['wlr-layer-shell-unstable-v1'], + protocols_code['xdg-shell'], + protocols_server_header['wlr-layer-shell-unstable-v1'], + protocols_server_header['xdg-shell'], +] diff --git a/test-harness/include/test-harness.h b/test-harness/include/test-harness.h index d65f7b91..018659ac 100644 --- a/test-harness/include/test-harness.h +++ b/test-harness/include/test-harness.h @@ -26,6 +26,14 @@ struct test { \ static void name##_test(void) +#define FAIL_TEST(name) \ + static void name##_test(void); \ + \ + const struct test test##name __attribute__(( \ + used, section("test_section"))) = {#name, name##_test, 1}; \ + \ + static void name##_test(void) + // Assertions void assert_equal_string_( @@ -72,3 +80,6 @@ void assert_equal_vec_(const char *actual_expression, const float *expected, #define ASSERT_EQUAL_VEC2(expected, actual) \ assert_equal_vec_(#actual, expected, actual, 2) + +#define ASSERT_EQUAL_VEC3(expected, actual) \ + assert_equal_vec_(#actual, expected, actual, 3) diff --git a/zen/include/zen/backend.h b/zen/include/zen/backend.h index 8fc6031e..43252cd3 100644 --- a/zen/include/zen/backend.h +++ b/zen/include/zen/backend.h @@ -5,6 +5,7 @@ #include "zen-common/util.h" struct zn_backend; +struct zn_xr_system; /// Within the lifetime of zn_backend, the start and stop methods can be called /// in this order only once each. @@ -12,10 +13,21 @@ struct zn_backend_interface { struct wlr_texture *(*create_wlr_texture_from_pixels)(struct zn_backend *self, uint32_t format, uint32_t stride, uint32_t width, uint32_t height, const void *data); + + /// @param xr_system is nullable. + /// If not null, xr_system must be connected state + void (*set_xr_system)( + struct zn_backend *self, struct zn_xr_system *xr_system); + + /// @return value can be NULL + struct zn_xr_system *(*get_xr_system)(struct zn_backend *self); + /// Starts monitoring the connection or disconnection of input/output devices. bool (*start)(struct zn_backend *self); + /// Destroy input/output devices void (*stop)(struct zn_backend *self); + void (*destroy)(struct zn_backend *self); }; @@ -23,9 +35,12 @@ struct zn_backend { const struct zn_backend_interface *impl; struct { - struct wl_signal new_screen; // (struct zn_screen *) - struct wl_signal view_mapped; // (struct zn_view *) - struct wl_signal destroy; // (NULL) + struct wl_signal new_screen; // (struct zn_screen *) + struct wl_signal view_mapped; // (struct zn_view *) + struct wl_signal bounded_mapped; // (struct zn_bounded *) + struct wl_signal destroy; // (NULL) + struct wl_signal new_xr_system; // (struct zn_xr_system *) + struct wl_signal xr_system_changed; // (struct zn_xr_system *) } events; }; @@ -55,3 +70,17 @@ zn_backend_create_wlr_texture_from_pixels(struct zn_backend *self, return self->impl->create_wlr_texture_from_pixels( self, format, stride, width, height, data); } + +UNUSED static inline void +zn_backend_set_xr_system( + struct zn_backend *self, struct zn_xr_system *xr_system) +{ + self->impl->set_xr_system(self, xr_system); +} + +/// @return value can be NULL +UNUSED static inline struct zn_xr_system * +zn_backend_get_xr_system(struct zn_backend *self) +{ + return self->impl->get_xr_system(self); +} diff --git a/zen/include/zen/binding.h b/zen/include/zen/binding.h new file mode 100644 index 00000000..4d2f43bd --- /dev/null +++ b/zen/include/zen/binding.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "zen/config.h" + +struct zn_binding; + +/// @return true if the key is used and should not be used any more +typedef bool (*zn_binding_handler_t)( + const char *name, uint32_t key, uint32_t modifiers, void *user_data); + +/// @param name must be a string literal +void zn_binding_add(struct zn_binding *self, const char *name, + zn_binding_handler_t handler, void *user_data); diff --git a/zen/include/zen/bounded-configure.h b/zen/include/zen/bounded-configure.h new file mode 100644 index 00000000..2b8a907c --- /dev/null +++ b/zen/include/zen/bounded-configure.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +struct zn_bounded_configure { + uint32_t serial; + vec3 half_size; + struct wl_list link; // zn_bounded::configure_list +}; diff --git a/zen/include/zen/bounded.h b/zen/include/zen/bounded.h new file mode 100644 index 00000000..17a03ae9 --- /dev/null +++ b/zen/include/zen/bounded.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +struct zn_virtual_object; + +struct zn_bounded { + struct wl_resource *resource; // @nonnull, @outlive + + struct zn_virtual_object *virtual_object; // @nonnull, @outlive + + /// configure_list.prev is the latest + struct wl_list configure_list; // zn_bounded_configure::link, @owning items + + struct wl_listener virtual_object_destroy_listener; + struct wl_listener commit_listener; + + struct { + uint32_t serial; + vec3 half_size; + struct wl_event_source *idle; // @nullable, @owning + } scheduled_config; + + bool added; + bool configured; + bool mapped; + + struct { + vec3 half_size; + } pending; + + struct { + vec3 half_size; + } current; + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; diff --git a/zen/include/zen/buffer.h b/zen/include/zen/buffer.h new file mode 100644 index 00000000..7d5faa51 --- /dev/null +++ b/zen/include/zen/buffer.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_buffer; + +/// The initial refcount must be 1 +struct zn_buffer_interface { + void (*ref)(struct zn_buffer *self); + + void (*unref)(struct zn_buffer *self); + + void *(*begin_access)(struct zn_buffer *self); + /// @return false if errors occur during the access + bool (*end_access)(struct zn_buffer *self); + + ssize_t (*get_size)(struct zn_buffer *self); +}; + +/// The initial refcount is 1 +struct zn_buffer { + void *impl_data; // @nullable, @outlive + const struct zn_buffer_interface *impl; // @nonnull, @outlive +}; + +struct zn_buffer *zn_buffer_create( + void *impl_data, const struct zn_buffer_interface *implementation); + +void zn_buffer_destroy(struct zn_buffer *self); + +UNUSED static inline void +zn_buffer_ref(struct zn_buffer *self) +{ + self->impl->ref(self); +} + +UNUSED static inline void +zn_buffer_unref(struct zn_buffer *self) +{ + self->impl->unref(self); +} + +UNUSED static inline void * +zn_buffer_begin_access(struct zn_buffer *self) +{ + return self->impl->begin_access(self); +} + +UNUSED static inline bool +zn_buffer_end_access(struct zn_buffer *self) +{ + return self->impl->end_access(self); +} + +UNUSED static inline ssize_t +zn_buffer_get_size(struct zn_buffer *self) +{ + return self->impl->get_size(self); +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/config.h b/zen/include/zen/config.h index 5edd58fe..6664f504 100644 --- a/zen/include/zen/config.h +++ b/zen/include/zen/config.h @@ -4,34 +4,16 @@ #include #include -struct zn_config_section { - /// This must be called once in zn_config_add_section - /// @param table is nullable - /// @return true if successful, false otherwise - bool (*load)(struct zn_config_section *self, toml_table_t *table); - - /// This must be called once when zn_config_destroy - void (*destroy)(struct zn_config_section *self); -}; - -struct zn_config_section_info { - char *name; // @nonnull, @owning - struct zn_config_section *section; // @nonnull, @owning -}; - struct zn_config { - toml_table_t *root_table; // @nullable, @owning - struct wl_array sections; // [struct zn_config_section_info] + toml_table_t *root_table; // @nullable, @owning + struct wl_array reserved_sections; // [const char *] }; -/// @returns true if successful, false otherwise -/// @param section's ownership will be moved to the zn_config -bool zn_config_add_section(struct zn_config *self, const char *name, - struct zn_config_section *section); - -/// @return value is nullable -struct zn_config_section *zn_config_get_section( - struct zn_config *self, const char *name); +/// @returns true if no other component has been reserved the section yet. +/// +/// This doesn't check if the section exist or not +bool zn_config_reserve_section( + struct zn_config *self, const char *section_name); struct zn_config *zn_config_create(void); diff --git a/zen/include/zen/gl-base-technique.h b/zen/include/zen/gl-base-technique.h new file mode 100644 index 00000000..d1d08e70 --- /dev/null +++ b/zen/include/zen/gl-base-technique.h @@ -0,0 +1,91 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_gl_base_technique; +struct zn_gl_buffer; +struct zn_gl_program; +struct zn_gl_vertex_array; + +struct zn_gl_base_technique_interface { + void (*bind_program)( + struct zn_gl_base_technique *self, struct zn_gl_program *gl_program); + void (*bind_vertex_array)(struct zn_gl_base_technique *self, + struct zn_gl_vertex_array *gl_vertex_array); + void (*uniform_vector)(struct zn_gl_base_technique *self, uint32_t location, + const char *name, uint32_t type, uint32_t size, uint32_t count, + void *value); + void (*uniform_matrix)(struct zn_gl_base_technique *self, uint32_t location, + const char *name, uint32_t col, uint32_t row, uint32_t count, + bool transpose, void *value); + void (*draw_arrays)(struct zn_gl_base_technique *self, uint32_t mode, + int32_t first, uint32_t count); + void (*draw_elements)(struct zn_gl_base_technique *self, uint32_t mode, + uint32_t count, uint32_t type, uint64_t offset, + struct zn_gl_buffer *gl_buffer); +}; + +struct zn_gl_base_technique { + void *impl_data; // @nullable, @outlive + const struct zn_gl_base_technique_interface *impl; // @nonnull, @outlive + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +UNUSED static inline void +zn_gl_base_technique_bind_program( + struct zn_gl_base_technique *self, struct zn_gl_program *gl_program) +{ + self->impl->bind_program(self, gl_program); +} + +UNUSED static inline void +zn_gl_base_technique_bind_vertex_array(struct zn_gl_base_technique *self, + struct zn_gl_vertex_array *gl_vertex_array) +{ + self->impl->bind_vertex_array(self, gl_vertex_array); +} + +UNUSED static inline void +zn_gl_base_technique_uniform_vector(struct zn_gl_base_technique *self, + uint32_t location, const char *name, uint32_t type, uint32_t size, + uint32_t count, void *value) +{ + self->impl->uniform_vector(self, location, name, type, size, count, value); +} + +UNUSED static inline void +zn_gl_base_technique_uniform_matrix(struct zn_gl_base_technique *self, + uint32_t location, const char *name, uint32_t col, uint32_t row, + uint32_t count, bool transpose, void *value) +{ + self->impl->uniform_matrix( + self, location, name, col, row, count, transpose, value); +} + +UNUSED static inline void +zn_gl_base_technique_draw_arrays(struct zn_gl_base_technique *self, + uint32_t mode, int32_t first, uint32_t count) +{ + self->impl->draw_arrays(self, mode, first, count); +} + +UNUSED static inline void +zn_gl_base_technique_draw_elements(struct zn_gl_base_technique *self, + uint32_t mode, uint32_t count, uint32_t type, uint64_t offset, + struct zn_gl_buffer *gl_buffer) +{ + self->impl->draw_elements(self, mode, count, type, offset, gl_buffer); +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/gl-buffer.h b/zen/include/zen/gl-buffer.h new file mode 100644 index 00000000..057a1138 --- /dev/null +++ b/zen/include/zen/gl-buffer.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_buffer; +struct zn_gl_buffer; + +struct zn_gl_buffer_interface { + void (*data)(struct zn_gl_buffer *self, uint32_t target, + struct zn_buffer *buffer, uint32_t usage); +}; + +struct zn_gl_buffer { + void *impl_data; // @nullable, @outlive + const struct zn_gl_buffer_interface *impl; // @nonnull, @outlive + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +UNUSED static inline void +zn_gl_buffer_data(struct zn_gl_buffer *self, uint32_t target, + struct zn_buffer *buffer, uint32_t usage) +{ + self->impl->data(self, target, buffer, usage); +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/gl-program.h b/zen/include/zen/gl-program.h new file mode 100644 index 00000000..5123613d --- /dev/null +++ b/zen/include/zen/gl-program.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_gl_program; +struct zn_gl_shader; + +struct zn_gl_program_interface { + void (*attach_shader)( + struct zn_gl_program *self, struct zn_gl_shader *gl_shader); + void (*link)(struct zn_gl_program *self); +}; + +struct zn_gl_program { + void *impl_data; // @nullable, @outlive + const struct zn_gl_program_interface *impl; // @nonnull, @outlive + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +UNUSED static inline void +zn_gl_program_attach_shader( + struct zn_gl_program *self, struct zn_gl_shader *gl_shader) +{ + self->impl->attach_shader(self, gl_shader); +} + +UNUSED static inline void +zn_gl_program_link(struct zn_gl_program *self) +{ + self->impl->link(self); +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/gl-rendering-unit.h b/zen/include/zen/gl-rendering-unit.h new file mode 100644 index 00000000..d3a583f9 --- /dev/null +++ b/zen/include/zen/gl-rendering-unit.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_gl_rendering_unit; + +struct zn_gl_rendering_unit_interface { + void (*change_visibility)(struct zn_gl_rendering_unit *self, bool visible); +}; + +struct zn_gl_rendering_unit { + void *impl_data; // @nullable, @outlive + const struct zn_gl_rendering_unit_interface *impl; // @nonnull, @outlive + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +UNUSED static inline void +zn_gl_rendering_unit_change_visibility( + struct zn_gl_rendering_unit *self, bool visible) +{ + self->impl->change_visibility(self, visible); +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/gl-shader.h b/zen/include/zen/gl-shader.h new file mode 100644 index 00000000..bafe5802 --- /dev/null +++ b/zen/include/zen/gl-shader.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_gl_shader { + void *impl_data; // @nullable, @outlive + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/gl-texture.h b/zen/include/zen/gl-texture.h new file mode 100644 index 00000000..32c4c47e --- /dev/null +++ b/zen/include/zen/gl-texture.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_gl_texture; +struct zn_buffer; + +struct zn_gl_texture_interface { + void (*image_2d)(struct zn_gl_texture *self, uint32_t target, int32_t level, + int32_t internal_format, uint32_t width, uint32_t height, int32_t border, + uint32_t format, uint32_t type, struct zn_buffer *data); + void (*generate_mipmap)(struct zn_gl_texture *self, uint32_t target); +}; + +struct zn_gl_texture { + void *impl_data; // @nullable, @outlive + const struct zn_gl_texture_interface *impl; // @nonnull, @outlive + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +UNUSED static inline void +zn_gl_texture_image_2d(struct zn_gl_texture *self, uint32_t target, + int32_t level, int32_t internal_format, uint32_t width, uint32_t height, + int32_t border, uint32_t format, uint32_t type, struct zn_buffer *data) +{ + self->impl->image_2d(self, target, level, internal_format, width, height, + border, format, type, data); +} + +UNUSED static inline void +zn_gl_texture_generate_mipmap(struct zn_gl_texture *self, uint32_t target) +{ + self->impl->generate_mipmap(self, target); +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/gl-vertex-array.h b/zen/include/zen/gl-vertex-array.h new file mode 100644 index 00000000..a60ced8b --- /dev/null +++ b/zen/include/zen/gl-vertex-array.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_gl_buffer; +struct zn_gl_vertex_array; + +struct zn_gl_vertex_array_interface { + void (*enable_vertex_attrib_array)( + struct zn_gl_vertex_array *self, uint32_t index); + void (*disable_vertex_attrib_array)( + struct zn_gl_vertex_array *self, uint32_t index); + void (*vertex_attrib_pointer)(struct zn_gl_vertex_array *self, uint32_t index, + int32_t size, uint32_t type, bool normalized, int32_t stride, + uint64_t offset, struct zn_gl_buffer *gl_buffer); +}; + +struct zn_gl_vertex_array { + void *impl_data; // @nullable, @outlive + const struct zn_gl_vertex_array_interface *impl; // @nonnull, @outlive + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +UNUSED static inline void +zn_gl_vertex_array_enable_vertex_attrib_array( + struct zn_gl_vertex_array *self, uint32_t index) +{ + self->impl->enable_vertex_attrib_array(self, index); +} + +UNUSED static inline void +zn_gl_vertex_array_disable_vertex_attrib_array( + struct zn_gl_vertex_array *self, uint32_t index) +{ + self->impl->disable_vertex_attrib_array(self, index); +} + +UNUSED static inline void +zn_gl_vertex_array_vertex_attrib_pointer(struct zn_gl_vertex_array *self, + uint32_t index, int32_t size, uint32_t type, bool normalized, + int32_t stride, uint64_t offset, struct zn_gl_buffer *gl_buffer) +{ + self->impl->vertex_attrib_pointer( + self, index, size, type, normalized, stride, offset, gl_buffer); +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/inode.h b/zen/include/zen/inode.h new file mode 100644 index 00000000..cc8f7ef7 --- /dev/null +++ b/zen/include/zen/inode.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +#include "zen-common/util.h" + +struct zn_xr_system; + +struct zn_inode_interface { + void (*mapped)(void *impl_data); + void (*unmapped)(void *impl_data); + void (*activated)(void *impl_data); + void (*deactivated)(void *impl_data); + void (*moved)(void *impl_data); +}; + +void zn_inode_noop_mapped(void *impl_data); + +void zn_inode_noop_unmapped(void *impl_data); + +void zn_inode_noop_activated(void *impl_data); + +void zn_inode_noop_deactivated(void *impl_data); + +void zn_inode_noop_moved(void *impl_data); + +extern const struct zn_inode_interface zn_inode_noop_implementation; + +/// inode is a root node when self->parent == NULL +struct zn_inode { + void *impl_data; // @nullable, @outlive + const struct zn_inode_interface *impl; // @nonnull, @outlive + + struct zn_xr_system *xr_system; // @nullable, @ref + + bool mapped; + + // local coords + vec3 position; + versor quaternion; + + // root node local coords, affine matrix + mat4 transform_abs; + + struct zn_inode *parent; // @nullable, @ref + struct wl_list link; // zn_inode::child_list + struct wl_list child_list; // zn_inode::link +}; + +UNUSED static inline bool +zn_inode_is_mapped(struct zn_inode *self) +{ + return self->mapped; +} + +UNUSED static inline bool +zn_inode_is_active(struct zn_inode *self) +{ + return self->xr_system != NULL; +} + +void zn_inode_move(struct zn_inode *self, struct zn_inode *parent, + vec3 position, versor quaternion); + +/// @param self must be root +void zn_inode_unmap(struct zn_inode *self); + +/// @param self must be root +void zn_inode_map(struct zn_inode *self); + +struct zn_inode *zn_inode_create( + void *impl_data, const struct zn_inode_interface *implementation); + +/// Destroy child inodes before the parent inode +void zn_inode_destroy(struct zn_inode *self); diff --git a/zen/include/zen/screen.h b/zen/include/zen/screen.h index d3b7089e..f0a78c1f 100644 --- a/zen/include/zen/screen.h +++ b/zen/include/zen/screen.h @@ -22,7 +22,7 @@ struct zn_screen { void *user_data; // @nonnull, @outlive if exists - struct zn_snode *snode_root; // @nonnull, @owning + struct zn_snode_root *snode_root; // @nonnull, @owning // These layers do not have a parent at first, user must set a parent to show // them. User must not add children to these layers. @@ -41,22 +41,11 @@ struct zn_screen { } events; }; +/// @return value can be NULL +struct zn_screen *zn_screen_from_snode_root(struct zn_snode_root *snode_root); + void zn_screen_set_layout_position( struct zn_screen *self, vec2 layout_position); /// @param fbox : Effective coordinate system void zn_screen_damage(struct zn_screen *self, struct wlr_fbox *fbox); - -/// Called by the impl object -/// @param size : effective coords -void zn_screen_notify_resize(struct zn_screen *self, vec2 size); - -/// Called by the impl object -void zn_screen_notify_frame(struct zn_screen *self, struct timespec *when); - -/// Called by the impl object -struct zn_screen *zn_screen_create( - void *impl_data, const struct zn_screen_interface *implementation); - -/// Called by the impl object -void zn_screen_destroy(struct zn_screen *self); diff --git a/zen/include/zen/server.h b/zen/include/zen/server.h index 58ae2825..24d06a88 100644 --- a/zen/include/zen/server.h +++ b/zen/include/zen/server.h @@ -5,12 +5,21 @@ struct zn_backend; struct zn_seat; +struct zn_binding; +struct zn_inode; struct zn_server { struct wl_display *display; // @nonnull, @outlive struct zn_backend *backend; // @nonnull, @owning struct zn_seat *seat; // @nonnull, @owning + struct zn_binding *binding; // @nonnull, @owning + struct zn_config *config; // @nonnull, @outlive + + // zn_inode_set_xr_system for these root inodes is called in the backend + // implementation + struct zn_inode *inode_root; // @nonnull, @owning + struct zn_inode *inode_invisible_root; // @nonnull, @owning bool running; int exit_status; @@ -29,7 +38,8 @@ int zn_server_run(struct zn_server *self); void zn_server_terminate(struct zn_server *self, int exit_status); /// @param backend is nullable, ownership will be moved -struct zn_server *zn_server_create( - struct wl_display *display, struct zn_backend *backend); +/// @param config must not be null +struct zn_server *zn_server_create(struct wl_display *display, + struct zn_backend *backend, struct zn_config *config); void zn_server_destroy(struct zn_server *self); diff --git a/zen/include/zen/snode-root.h b/zen/include/zen/snode-root.h new file mode 100644 index 00000000..6f67a82a --- /dev/null +++ b/zen/include/zen/snode-root.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include "zen-common/util.h" + +struct zn_snode_root_interface { + void (*damage)(void *user_data, struct wlr_fbox *fbox); + void (*layout_position)(void *user_data, vec2 position); +}; + +struct zn_snode_root { + void *user_data; // @nullable, @outlive if exists + const struct zn_snode_root_interface *impl; // @nonnull, @outlive + + struct zn_snode *node; // @nonnull, @owning +}; + +UNUSED static inline void +zn_snode_root_damage(struct zn_snode_root *self, struct wlr_fbox *fbox) +{ + self->impl->damage(self->user_data, fbox); +} + +UNUSED static inline void +zn_snode_root_layout_position(struct zn_snode_root *self, vec2 position) +{ + self->impl->layout_position(self->user_data, position); +} + +struct zn_snode_root *zn_snode_root_create( + void *user_data, const struct zn_snode_root_interface *implementation); + +void zn_snode_root_destroy(struct zn_snode_root *self); diff --git a/zen/include/zen/snode.h b/zen/include/zen/snode.h index 9ec13036..60447820 100644 --- a/zen/include/zen/snode.h +++ b/zen/include/zen/snode.h @@ -8,6 +8,8 @@ #include "zen-common/util.h" +struct zn_snode_root; + enum zn_snode_flag { ZN_SNODE_FLAG_FOCUSABLE = 1 << 0, }; @@ -80,10 +82,9 @@ struct zn_snode { uint32_t flags; // a bitfield of enum zn_snode_flag - // When the screen is destroyed, the root snode is destroyed and - // `position_changed` signal handler will set this NULL. - // For root snode, this is @outlive - struct zn_screen *screen; // @nullable, @ref + // When the root is destroyed, `position_changed` signal handler will set this + // NULL. + struct zn_snode_root *root; // @nullable, @ref struct wl_list child_node_list; // zn_snode::link, sorted from back to front struct wl_list link; // zn_snode::child_node_list @@ -169,9 +170,26 @@ void zn_snode_notify_frame(struct zn_snode *self, const struct timespec *when); /// @param parent is nullable /// @param position is in the parent local coords +/// The node is placed on top. void zn_snode_set_position( struct zn_snode *self, struct zn_snode *parent, vec2 position); +/// @param position is in the parent local coords +/// Only the position changes, the order of the siblings remains the same. +void zn_snode_change_position(struct zn_snode *self, vec2 position); + +/// @param sibling must not be NULL +/// self and sibling must have the same direct nonnull parent. +/// self is placed directly above the sibling. +/// Position doesn't change +void zn_snode_place_above(struct zn_snode *self, struct zn_snode *sibling); + +/// @param sibling must not be NULL +/// self and sibling must have the same direct nonnull parent. +/// self is placed directly below the sibling. +/// Position doesn't change +void zn_snode_place_below(struct zn_snode *self, struct zn_snode *sibling); + /// @return value is nullable struct wlr_texture *zn_snode_get_texture(struct zn_snode *self); @@ -188,6 +206,4 @@ struct zn_snode *zn_snode_create( struct zn_snode *zn_snode_create_focusable( void *user_data, const struct zn_snode_interface *implementation); -struct zn_snode *zn_snode_create_root(struct zn_screen *screen); - void zn_snode_destroy(struct zn_snode *self); diff --git a/zen/include/zen/view.h b/zen/include/zen/view.h index 9b05c4f7..d35e4d7f 100644 --- a/zen/include/zen/view.h +++ b/zen/include/zen/view.h @@ -54,27 +54,3 @@ void zn_view_set_focus(struct zn_view *self, bool focused); void zn_view_configure_size(struct zn_view *self, vec2 size); void zn_view_close(struct zn_view *self); - -/// Called by the impl object -void zn_view_notify_resized(struct zn_view *self, vec2 size); - -/// Called by the impl object -void zn_view_notify_decoration( - struct zn_view *self, enum zn_view_decoration_mode mode); - -/// Called by the impl object -void zn_view_notify_move_request(struct zn_view *self); - -/// Called by the impl object -void zn_view_notify_resize_request( - struct zn_view *self, struct zn_view_resize_event *event); - -/// Called by the impl object -void zn_view_notify_unmap(struct zn_view *self); - -/// Called by the impl object -struct zn_view *zn_view_create( - void *impl_data, const struct zn_view_interface *implementation); - -/// Called by the impl object -void zn_view_destroy(struct zn_view *self); diff --git a/zen/include/zen/virtual-object.h b/zen/include/zen/virtual-object.h new file mode 100644 index 00000000..f8a2c91e --- /dev/null +++ b/zen/include/zen/virtual-object.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_bounded; +struct zn_gl_virtual_object; +struct zn_inode; + +enum zn_virtual_object_role { + ZN_VIRTUAL_OBJECT_ROLE_NONE = 0, + ZN_VIRTUAL_OBJECT_ROLE_BOUNDED, +}; + +struct zn_virtual_object { + struct zn_inode *inode; // @nonnull, @owning + + struct zn_gl_virtual_object *gl_virtual_object; // @nullable, @ref + + union { + void *role_object; // @nullable, @ref + struct zn_bounded *bounded; // when role == BOUNDED + }; + enum zn_virtual_object_role role; + + struct wl_listener role_object_destroy_listener; + + struct { + struct wl_signal destroy; // (NULL) + struct wl_signal commit; // (NULL) + } events; +}; + +void zn_virtual_object_commit(struct zn_virtual_object *self); + +void zn_virtual_object_change_visibility( + struct zn_virtual_object *self, bool visible); + +/// @return true if successful, false otherwise +bool zn_virtual_object_set_role(struct zn_virtual_object *self, + enum zn_virtual_object_role role, void *role_object); + +struct zn_virtual_object *zn_virtual_object_create(void); + +void zn_virtual_object_destroy(struct zn_virtual_object *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/xr-dispatcher.h b/zen/include/zen/xr-dispatcher.h new file mode 100644 index 00000000..b470960b --- /dev/null +++ b/zen/include/zen/xr-dispatcher.h @@ -0,0 +1,181 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_buffer; +struct zn_gl_base_technique; +struct zn_gl_buffer; +struct zn_gl_program; +struct zn_gl_rendering_unit; +struct zn_gl_shader; +struct zn_gl_texture; +struct zn_gl_vertex_array; +struct zn_gl_virtual_object; +struct zn_xr_dispatcher; + +struct zn_xr_dispatcher_interface { + struct zn_gl_virtual_object *(*get_new_gl_virtual_object)( + struct zn_xr_dispatcher *self); + void (*destroy_gl_virtual_object)(struct zn_xr_dispatcher *self, + struct zn_gl_virtual_object *gl_virtual_object); + + struct zn_gl_rendering_unit *(*get_new_gl_rendering_unit)( + struct zn_xr_dispatcher *self, + struct zn_gl_virtual_object *gl_virtual_object); + void (*destroy_gl_rendering_unit)(struct zn_xr_dispatcher *self, + struct zn_gl_rendering_unit *gl_rendering_unit); + + struct zn_gl_base_technique *(*get_new_gl_base_technique)( + struct zn_xr_dispatcher *self, + struct zn_gl_rendering_unit *gl_rendering_unit); + void (*destroy_gl_base_technique)(struct zn_xr_dispatcher *self, + struct zn_gl_base_technique *gl_base_technique); + + struct zn_gl_buffer *(*get_new_gl_buffer)(struct zn_xr_dispatcher *self); + void (*destroy_gl_buffer)( + struct zn_xr_dispatcher *self, struct zn_gl_buffer *gl_buffer); + + struct zn_gl_shader *(*get_new_gl_shader)( + struct zn_xr_dispatcher *self, struct zn_buffer *buffer, uint32_t type); + void (*destroy_gl_shader)( + struct zn_xr_dispatcher *self, struct zn_gl_shader *gl_shader); + + struct zn_gl_program *(*get_new_gl_program)(struct zn_xr_dispatcher *self); + void (*destroy_gl_program)( + struct zn_xr_dispatcher *self, struct zn_gl_program *gl_program); + + struct zn_gl_texture *(*get_new_gl_texture)(struct zn_xr_dispatcher *self); + void (*destroy_gl_texture)( + struct zn_xr_dispatcher *self, struct zn_gl_texture *gl_texture); + + struct zn_gl_vertex_array *(*get_new_gl_vertex_array)( + struct zn_xr_dispatcher *self); + void (*destroy_gl_vertex_array)(struct zn_xr_dispatcher *self, + struct zn_gl_vertex_array *gl_vertex_array); +}; + +struct zn_xr_dispatcher { + void *impl_data; + const struct zn_xr_dispatcher_interface *impl; + + struct { + struct wl_signal destroy; + } events; +}; + +UNUSED static inline struct zn_gl_virtual_object * +zn_xr_dispatcher_get_new_gl_virtual_object(struct zn_xr_dispatcher *self) +{ + return self->impl->get_new_gl_virtual_object(self); +} + +UNUSED static inline void +zn_xr_dispatcher_destroy_gl_virtual_object(struct zn_xr_dispatcher *self, + struct zn_gl_virtual_object *gl_virtual_object) +{ + self->impl->destroy_gl_virtual_object(self, gl_virtual_object); +} + +UNUSED static inline struct zn_gl_rendering_unit * +zn_xr_dispatcher_get_new_gl_rendering_unit(struct zn_xr_dispatcher *self, + struct zn_gl_virtual_object *gl_virtual_object) +{ + return self->impl->get_new_gl_rendering_unit(self, gl_virtual_object); +} + +UNUSED static inline void +zn_xr_dispatcher_destroy_gl_rendering_unit(struct zn_xr_dispatcher *self, + struct zn_gl_rendering_unit *gl_rendering_unit) +{ + self->impl->destroy_gl_rendering_unit(self, gl_rendering_unit); +} + +UNUSED static inline struct zn_gl_base_technique * +zn_xr_dispatcher_get_new_gl_base_technique(struct zn_xr_dispatcher *self, + struct zn_gl_rendering_unit *gl_rendering_unit) +{ + return self->impl->get_new_gl_base_technique(self, gl_rendering_unit); +} + +UNUSED static inline void +zn_xr_dispatcher_destroy_gl_base_technique(struct zn_xr_dispatcher *self, + struct zn_gl_base_technique *gl_base_technique) +{ + self->impl->destroy_gl_base_technique(self, gl_base_technique); +} + +UNUSED static inline struct zn_gl_buffer * +zn_xr_dispatcher_get_new_gl_buffer(struct zn_xr_dispatcher *self) +{ + return self->impl->get_new_gl_buffer(self); +} + +UNUSED static inline void +zn_xr_dispatcher_destroy_gl_buffer( + struct zn_xr_dispatcher *self, struct zn_gl_buffer *gl_buffer) +{ + self->impl->destroy_gl_buffer(self, gl_buffer); +} + +UNUSED static inline struct zn_gl_shader * +zn_xr_dispatcher_get_new_gl_shader( + struct zn_xr_dispatcher *self, struct zn_buffer *buffer, uint32_t type) +{ + return self->impl->get_new_gl_shader(self, buffer, type); +} + +UNUSED static inline void +zn_xr_dispatcher_destroy_gl_shader( + struct zn_xr_dispatcher *self, struct zn_gl_shader *gl_shader) +{ + self->impl->destroy_gl_shader(self, gl_shader); +} + +UNUSED static inline struct zn_gl_program * +zn_xr_dispatcher_get_new_gl_program(struct zn_xr_dispatcher *self) +{ + return self->impl->get_new_gl_program(self); +} + +UNUSED static inline void +zn_xr_dispatcher_destroy_gl_program( + struct zn_xr_dispatcher *self, struct zn_gl_program *gl_program) +{ + self->impl->destroy_gl_program(self, gl_program); +} + +UNUSED static inline struct zn_gl_texture * +zn_xr_dispatcher_get_new_gl_texture(struct zn_xr_dispatcher *self) +{ + return self->impl->get_new_gl_texture(self); +} + +UNUSED static inline void +zn_xr_dispatcher_destroy_gl_texture( + struct zn_xr_dispatcher *self, struct zn_gl_texture *gl_texture) +{ + self->impl->destroy_gl_texture(self, gl_texture); +} + +UNUSED static inline struct zn_gl_vertex_array * +zn_xr_dispatcher_get_new_gl_vertex_array(struct zn_xr_dispatcher *self) +{ + return self->impl->get_new_gl_vertex_array(self); +} + +UNUSED static inline void +zn_xr_dispatcher_destroy_gl_vertex_array( + struct zn_xr_dispatcher *self, struct zn_gl_vertex_array *gl_vertex_array) +{ + self->impl->destroy_gl_vertex_array(self, gl_vertex_array); +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/include/zen/xr-system.h b/zen/include/zen/xr-system.h new file mode 100644 index 00000000..3d4a284a --- /dev/null +++ b/zen/include/zen/xr-system.h @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_xr_system; +struct zn_xr_dispatcher; + +struct zn_xr_system_interface { + void (*connect)(struct zn_xr_system *self); +}; + +/// +/// create +/// ↓ +/// available --→ dead → destroy +/// ↓↑ ↑ +/// synchronized ---+ +/// ↓↑ +/// visible +/// ↓↑ +/// focus +/// +/// connected = synchronized | visible | focus +/// +enum zn_xr_system_session_state { + ZN_XR_SYSTEM_SESSION_STATE_AVAILABLE, + ZN_XR_SYSTEM_SESSION_STATE_SYNCHRONIZED, + ZN_XR_SYSTEM_SESSION_STATE_VISIBLE, + ZN_XR_SYSTEM_SESSION_STATE_FOCUS, + ZN_XR_SYSTEM_SESSION_STATE_DEAD, +}; + +struct zn_xr_system { + void *impl_data; // @outlive + const struct zn_xr_system_interface *impl; // @nonnull, @outlive + + enum zn_xr_system_session_state state; + + /// These dispatchers are not null when connected, null otherwise. + struct zn_xr_dispatcher *high_priority_dispatcher; // @owning + struct zn_xr_dispatcher *default_dispatcher; // @owning + + struct { + struct wl_signal session_state_changed; // (NULL) + struct wl_signal destroy; + } events; +}; + +UNUSED static inline void +zn_xr_system_connect(struct zn_xr_system *self) +{ + self->impl->connect(self); +} + +UNUSED static inline bool +zn_xr_system_is_connected(struct zn_xr_system *self) +{ + const uint32_t connected = ZN_XR_SYSTEM_SESSION_STATE_SYNCHRONIZED | + ZN_XR_SYSTEM_SESSION_STATE_VISIBLE | + ZN_XR_SYSTEM_SESSION_STATE_FOCUS; + return (self->state & connected) != 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/meson.build b/zen/meson.build index 4756bdfa..dc81b4e6 100644 --- a/zen/meson.build +++ b/zen/meson.build @@ -1,11 +1,25 @@ _zen_srcs = [ + 'src/binding.c', + 'src/buffer.c', 'src/config.c', 'src/cursor.c', + 'src/gl-base-technique.c', + 'src/gl-buffer.c', + 'src/gl-program.c', + 'src/gl-rendering-unit.c', + 'src/gl-shader.c', + 'src/gl-texture.c', + 'src/gl-vertex-array.c', + 'src/gl-virtual-object.c', + 'src/inode.c', 'src/screen.c', 'src/seat.c', 'src/server.c', 'src/snode.c', + 'src/snode-root.c', 'src/view.c', + 'src/virtual-object.c', + 'src/xr-dispatcher.c', ] zen_inc = include_directories('include') diff --git a/zen/src/backend/backend.c b/zen/src/backend/default-backend.c similarity index 65% rename from zen/src/backend/backend.c rename to zen/src/backend/default-backend.c index 303a78fb..c41cb9a1 100644 --- a/zen/src/backend/backend.c +++ b/zen/src/backend/default-backend.c @@ -1,4 +1,4 @@ -#include "backend.h" +#include "default-backend.h" #include #include @@ -7,16 +7,22 @@ #include #include -#include "compositor.h" -#include "keyboard.h" -#include "output.h" -#include "pointer.h" +#include "immersive/gl-context.h" +#include "immersive/shm.h" +#include "immersive/xr-system-manager.h" +#include "immersive/xr.h" +#include "inode.h" +#include "screen/compositor.h" +#include "screen/keyboard.h" +#include "screen/output.h" +#include "screen/pointer.h" #include "seat.h" #include "zen-common/log.h" #include "zen-common/signal.h" #include "zen-common/util.h" #include "zen/server.h" #include "zen/wlr/render/glew.h" +#include "zen/xr-system.h" static void zn_default_backend_destroy(struct zn_default_backend *self); @@ -60,6 +66,63 @@ zn_default_backend_notify_view_mapped( wl_signal_emit(&self->base.events.view_mapped, view); } +void +zn_default_backend_notify_bounded_mapped( + struct zn_default_backend *self, struct zn_bounded *bounded) +{ + wl_signal_emit(&self->base.events.bounded_mapped, bounded); +} + +/// @param xr_system is nullable +static void +zn_default_backend_set_xr_system( + struct zn_default_backend *self, struct zn_xr_system *xr_system) +{ + struct zn_xr_system *current_xr_system = + zn_backend_get_xr_system(&self->base); + if (current_xr_system == xr_system) { + return; + } + + struct zn_server *server = zn_server_get_singleton(); + + if (self->gl_context) { + wl_list_remove(&self->gl_context_destroy_listener.link); + wl_list_init(&self->gl_context_destroy_listener.link); + zn_gl_context_destroy(self->gl_context); + self->gl_context = NULL; + } + + if (xr_system) { + self->gl_context = zn_gl_context_create(self->display, xr_system); + wl_signal_add( + &self->gl_context->events.destroy, &self->gl_context_destroy_listener); + } + + zn_inode_set_xr_system(server->inode_root, xr_system); + zn_inode_set_xr_system(server->inode_invisible_root, xr_system); + + zn_signal_emit_mutable(&self->base.events.xr_system_changed, xr_system); +} + +static void +zn_default_backend_handle_gl_context_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_default_backend *self = + zn_container_of(listener, self, gl_context_destroy_listener); + struct zn_server *server = zn_server_get_singleton(); + + wl_list_remove(&self->gl_context_destroy_listener.link); + wl_list_init(&self->gl_context_destroy_listener.link); + self->gl_context = NULL; + + zn_inode_set_xr_system(server->inode_root, NULL); + zn_inode_set_xr_system(server->inode_invisible_root, NULL); + + zn_signal_emit_mutable(&self->base.events.xr_system_changed, NULL); +} + static void zn_default_backend_handle_new_input(struct wl_listener *listener, void *data) { @@ -130,6 +193,17 @@ zn_default_backend_handle_new_output(struct wl_listener *listener, void *data) wl_signal_emit(&self->base.events.new_screen, output->screen); } +static void +zn_default_backend_handle_new_xr_system( + struct wl_listener *listener, void *data) +{ + struct zn_default_backend *self = + zn_container_of(listener, self, new_xr_system_listener); + struct zn_xr_system *xr_system = data; + + wl_signal_emit(&self->base.events.new_xr_system, xr_system); +} + static struct wlr_texture * zn_default_backend_create_wlr_texture_from_pixels(struct zn_backend *base, uint32_t format, uint32_t stride, uint32_t width, uint32_t height, @@ -144,6 +218,22 @@ zn_default_backend_create_wlr_texture_from_pixels(struct zn_backend *base, self->wlr_renderer, format, stride, width, height, data); } +static void +zn_default_backend_handle_set_xr_system( + struct zn_backend *base, struct zn_xr_system *xr_system) +{ + struct zn_default_backend *self = zn_container_of(base, self, base); + + zn_default_backend_set_xr_system(self, xr_system); +} + +static struct zn_xr_system * +zn_default_backend_handle_get_xr_system(struct zn_backend *base) +{ + struct zn_default_backend *self = zn_container_of(base, self, base); + return self->gl_context ? self->gl_context->xr_system : NULL; +} + static bool zn_default_backend_start(struct zn_backend *base) { @@ -169,6 +259,8 @@ zn_default_backend_handle_destroy(struct zn_backend *base) static const struct zn_backend_interface implementation = { .create_wlr_texture_from_pixels = zn_default_backend_create_wlr_texture_from_pixels, + .set_xr_system = zn_default_backend_handle_set_xr_system, + .get_xr_system = zn_default_backend_handle_get_xr_system, .start = zn_default_backend_start, .stop = zn_default_backend_stop, .destroy = zn_default_backend_handle_destroy, @@ -185,9 +277,13 @@ zn_default_backend_create(struct wl_display *display, struct zn_seat *zn_seat) wl_signal_init(&self->base.events.new_screen); wl_signal_init(&self->base.events.view_mapped); + wl_signal_init(&self->base.events.bounded_mapped); wl_signal_init(&self->base.events.destroy); + wl_signal_init(&self->base.events.new_xr_system); + wl_signal_init(&self->base.events.xr_system_changed); self->base.impl = &implementation; self->display = display; + self->gl_context = NULL; wl_list_init(&self->input_device_list); self->wlr_backend = wlr_backend_autocreate(display); @@ -226,6 +322,14 @@ zn_default_backend_create(struct wl_display *display, struct zn_seat *zn_seat) goto err_wlr_allocator; } + self->xr = zn_xr_create(display); + if (self->xr == NULL) { + zn_error("Failed to create a xr instance"); + goto err_compositor; + } + + zn_shm_init(display); + wlr_xwayland_set_seat(self->compositor->xwayland, zn_seat->wlr_seat); self->new_output_listener.notify = zn_default_backend_handle_new_output; @@ -236,8 +340,19 @@ zn_default_backend_create(struct wl_display *display, struct zn_seat *zn_seat) wl_signal_add( &self->wlr_backend->events.new_input, &self->new_input_listener); + self->new_xr_system_listener.notify = zn_default_backend_handle_new_xr_system; + wl_signal_add(&self->xr->xr_system_manager->events.new_system, + &self->new_xr_system_listener); + + self->gl_context_destroy_listener.notify = + zn_default_backend_handle_gl_context_destroy; + wl_list_init(&self->gl_context_destroy_listener.link); + return &self->base; +err_compositor: + zn_compositor_destroy(self->compositor); + err_wlr_allocator: wlr_allocator_destroy(self->wlr_allocator); @@ -259,8 +374,16 @@ zn_default_backend_destroy(struct zn_default_backend *self) { zn_signal_emit_mutable(&self->base.events.destroy, NULL); + if (self->gl_context) { + zn_gl_context_destroy(self->gl_context); + self->gl_context = NULL; + } + + wl_list_remove(&self->gl_context_destroy_listener.link); + wl_list_remove(&self->new_xr_system_listener.link); wl_list_remove(&self->new_input_listener.link); wl_list_remove(&self->new_output_listener.link); + zn_xr_destroy(self->xr); zn_compositor_destroy(self->compositor); wlr_allocator_destroy(self->wlr_allocator); wlr_renderer_destroy(self->wlr_renderer); @@ -268,7 +391,10 @@ zn_default_backend_destroy(struct zn_default_backend *self) wlr_backend_destroy(self->wlr_backend); } wl_list_remove(&self->input_device_list); + wl_list_remove(&self->base.events.xr_system_changed.listener_list); + wl_list_remove(&self->base.events.new_xr_system.listener_list); wl_list_remove(&self->base.events.destroy.listener_list); + wl_list_remove(&self->base.events.bounded_mapped.listener_list); wl_list_remove(&self->base.events.view_mapped.listener_list); wl_list_remove(&self->base.events.new_screen.listener_list); free(self); diff --git a/zen/src/backend/backend.h b/zen/src/backend/default-backend.h similarity index 65% rename from zen/src/backend/backend.h rename to zen/src/backend/default-backend.h index 8ed0b42c..449e92f5 100644 --- a/zen/src/backend/backend.h +++ b/zen/src/backend/default-backend.h @@ -2,8 +2,11 @@ #include "zen/backend.h" +struct zn_bounded; struct zn_compositor; +struct zn_gl_context; struct zn_view; +struct zn_xr; struct zn_default_backend { struct zn_backend base; @@ -18,10 +21,18 @@ struct zn_default_backend { struct zn_compositor *compositor; // @nonnull, @owning + // Basically owned by zn_default_backend, but can be destroyed when given + // xr_system is disconnected or destroyed. + struct zn_gl_context *gl_context; // @nullable + + struct zn_xr *xr; // @nonnull, @owing + struct wl_list input_device_list; // zn_input_device_base::link struct wl_listener new_input_listener; struct wl_listener new_output_listener; + struct wl_listener new_xr_system_listener; + struct wl_listener gl_context_destroy_listener; }; struct zn_default_backend *zn_default_backend_get(struct zn_backend *base); @@ -30,3 +41,6 @@ void zn_default_backend_update_capabilities(struct zn_default_backend *self); void zn_default_backend_notify_view_mapped( struct zn_default_backend *self, struct zn_view *view); + +void zn_default_backend_notify_bounded_mapped( + struct zn_default_backend *self, struct zn_bounded *bounded); diff --git a/zen/src/backend/immersive/client-virtual-object.c b/zen/src/backend/immersive/client-virtual-object.c new file mode 100644 index 00000000..0506bfbe --- /dev/null +++ b/zen/src/backend/immersive/client-virtual-object.c @@ -0,0 +1,119 @@ +#include "client-virtual-object.h" + +#include + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" +#include "zen/virtual-object.h" + +static void zn_client_virtual_object_destroy( + struct zn_client_virtual_object *self); + +static void +zn_client_virtual_object_handle_destroy(struct wl_resource *resource) +{ + struct zn_client_virtual_object *self = + zn_client_virtual_object_get(resource); + + if (self->zn_virtual_object->role_object != NULL) { + wl_resource_post_error(self->resource, + ZWN_VIRTUAL_OBJECT_ERROR_DEFUNCT_ROLE_OBJECT, + "Virtual object is destroyed before its role object"); + } + + zn_client_virtual_object_destroy(self); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_virtual_object_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_virtual_object_protocol_commit( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + struct zn_client_virtual_object *self = + zn_client_virtual_object_get(resource); + + if (self == NULL) { + return; + } + + zn_virtual_object_commit(self->zn_virtual_object); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_virtual_object_protocol_frame(struct wl_client *client UNUSED, + struct wl_resource *resource UNUSED, uint32_t callback UNUSED) +{} + +static const struct zwn_virtual_object_interface implementation = { + .destroy = zn_client_virtual_object_protocol_destroy, + .commit = zn_client_virtual_object_protocol_commit, + .frame = zn_client_virtual_object_protocol_frame, +}; + +struct zn_client_virtual_object * +zn_client_virtual_object_get(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +struct zn_client_virtual_object * +zn_client_virtual_object_create(struct wl_client *client, uint32_t id) +{ + struct zn_client_virtual_object *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + wl_signal_init(&self->events.destroy); + + self->zn_virtual_object = zn_virtual_object_create(); + if (self->zn_virtual_object == NULL) { + zn_error("Failed to get new zn_virtual_object"); + wl_client_post_no_memory(client); + goto err_free; + } + + self->resource = + wl_resource_create(client, &zwn_virtual_object_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_zn_virtual_object; + } + + wl_resource_set_implementation(self->resource, &implementation, self, + &zn_client_virtual_object_handle_destroy); + + return self; + +err_zn_virtual_object: + zn_virtual_object_destroy(self->zn_virtual_object); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_client_virtual_object_destroy(struct zn_client_virtual_object *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + zn_virtual_object_destroy(self->zn_virtual_object); + wl_resource_set_implementation(self->resource, &implementation, NULL, NULL); + free(self); +} diff --git a/zen/src/backend/immersive/client-virtual-object.h b/zen/src/backend/immersive/client-virtual-object.h new file mode 100644 index 00000000..e84ac06f --- /dev/null +++ b/zen/src/backend/immersive/client-virtual-object.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +struct zn_virtual_object; +struct zn_xr_dispatcher; + +struct zn_client_virtual_object { + struct wl_resource *resource; // @nonnull, @outlive + + struct zn_virtual_object *zn_virtual_object; // @nonnull, @owning + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +struct zn_client_virtual_object *zn_client_virtual_object_create( + struct wl_client *client, uint32_t id); + +struct zn_client_virtual_object *zn_client_virtual_object_get( + struct wl_resource *resource); diff --git a/zen/src/backend/immersive/gl-context.h b/zen/src/backend/immersive/gl-context.h new file mode 100644 index 00000000..f93fa57e --- /dev/null +++ b/zen/src/backend/immersive/gl-context.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +struct zn_xr_system; + +struct zn_gl_context { + struct wl_global *global; // @nonnull, @owning + + struct wl_list resource_list; // wl_resource::link of zwn_gl_context + + // must be connected state + struct zn_xr_system *xr_system; // @nonnull, @outlive + + struct wl_listener xr_system_session_state_changed_listener; + + bool destroying; + + struct { + struct wl_signal destroy; + } events; +}; + +struct zn_gl_context *zn_gl_context_create( + struct wl_display *display, struct zn_xr_system *xr_system); + +void zn_gl_context_destroy(struct zn_gl_context *self); diff --git a/zen/src/backend/immersive/gl/client-gl-base-technique.c b/zen/src/backend/immersive/gl/client-gl-base-technique.c new file mode 100644 index 00000000..ed2e0e3d --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-base-technique.c @@ -0,0 +1,312 @@ +#include "client-gl-base-technique.h" + +#include +#include + +#include "backend/immersive/client-virtual-object.h" +#include "client-gl-buffer.h" +#include "client-gl-program.h" +#include "client-gl-rendering-unit.h" +#include "client-gl-vertex-array.h" +#include "gl-virtual-object.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen-common/wl-array.h" +#include "zen/gl-base-technique.h" +#include "zen/virtual-object.h" +#include "zen/xr-dispatcher.h" + +static void zn_client_gl_base_technique_destroy( + struct zn_client_gl_base_technique *self); + +static void +zn_client_gl_base_technique_handle_destroy(struct wl_resource *resource) +{ + struct zn_client_gl_base_technique *self = + zn_client_gl_base_technique_get(resource); + + zn_client_gl_base_technique_destroy(self); +} + +static void +zn_client_gl_base_technique_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_base_technique_protocol_bind_program( + struct wl_client *client UNUSED, struct wl_resource *resource, + struct wl_resource *program_resource) +{ + struct zn_client_gl_base_technique *self = + zn_client_gl_base_technique_get(resource); + + struct zn_client_gl_program *program = + zn_client_gl_program_get(program_resource); + + if (self == NULL || program == NULL) { + return; + } + + zn_gl_base_technique_bind_program( + self->zn_gl_base_technique, program->zn_gl_program); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_base_technique_protocol_bind_vertex_array( + struct wl_client *client UNUSED, struct wl_resource *resource, + struct wl_resource *vertex_array_resource) +{ + struct zn_client_gl_base_technique *self = + zn_client_gl_base_technique_get(resource); + + struct zn_client_gl_vertex_array *gl_vertex_array = + zn_client_gl_vertex_array_get(vertex_array_resource); + + if (self == NULL || gl_vertex_array == NULL) { + return; + } + + zn_gl_base_technique_bind_vertex_array( + self->zn_gl_base_technique, gl_vertex_array->zn_gl_vertex_array); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_base_technique_protocol_bind_texture( + struct wl_client *client UNUSED, struct wl_resource *resource UNUSED, + uint32_t binding UNUSED, const char *name UNUSED, + struct wl_resource *texture UNUSED, uint32_t target UNUSED, + struct wl_resource *sampler UNUSED) +{} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_base_technique_protocol_uniform_vector( + struct wl_client *client UNUSED, struct wl_resource *resource, + int32_t location, const char *name, uint32_t type, uint32_t size, + uint32_t count, struct wl_array *value) +{ + struct zn_client_gl_base_technique *self = + zn_client_gl_base_technique_get(resource); + + if (self == NULL) { + return; + } + + size_t expected_size = 0; + + switch ((enum zwn_gl_base_technique_uniform_variable_type)type) { + case ZWN_GL_BASE_TECHNIQUE_UNIFORM_VARIABLE_TYPE_INT: + expected_size = sizeof(int32_t) * size * count; + break; + case ZWN_GL_BASE_TECHNIQUE_UNIFORM_VARIABLE_TYPE_UINT: + expected_size = sizeof(uint32_t) * size * count; + break; + case ZWN_GL_BASE_TECHNIQUE_UNIFORM_VARIABLE_TYPE_FLOAT: + expected_size = sizeof(float) * size * count; + break; + } + + if (expected_size != value->size) { + wl_resource_post_error(resource, ZWN_COMPOSITOR_ERROR_INVALID_WL_ARRAY_SIZE, + "invalid wl_array size"); + return; + } + + zn_gl_base_technique_uniform_vector(self->zn_gl_base_technique, location, + name, type, size, count, value->data); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_base_technique_protocol_uniform_matrix( + struct wl_client *client UNUSED, struct wl_resource *resource UNUSED, + int32_t location UNUSED, const char *name UNUSED, uint32_t col UNUSED, + uint32_t row UNUSED, uint32_t count UNUSED, uint32_t transpose UNUSED, + struct wl_array *value UNUSED) +{ + struct zn_client_gl_base_technique *self = + zn_client_gl_base_technique_get(resource); + + if (self == NULL) { + return; + } + + size_t expected_size = sizeof(float) * col * row * count; + + if (expected_size != value->size) { + wl_resource_post_error(resource, ZWN_COMPOSITOR_ERROR_INVALID_WL_ARRAY_SIZE, + "invalid wl_array size"); + return; + } + + zn_gl_base_technique_uniform_matrix(self->zn_gl_base_technique, location, + name, col, row, count, transpose, value->data); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_base_technique_protocol_draw_arrays( + struct wl_client *client UNUSED, struct wl_resource *resource, + uint32_t mode, int32_t first, uint32_t count) +{ + struct zn_client_gl_base_technique *self = + zn_client_gl_base_technique_get(resource); + + if (self == NULL) { + return; + } + + zn_gl_base_technique_draw_arrays( + self->zn_gl_base_technique, mode, first, count); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_base_technique_protocol_draw_elements( + struct wl_client *client UNUSED, struct wl_resource *resource, + uint32_t mode, uint32_t count, uint32_t type, struct wl_array *offset_array, + struct wl_resource *element_array_buffer_resource) +{ + struct zn_client_gl_base_technique *self = + zn_client_gl_base_technique_get(resource); + + struct zn_client_gl_buffer *element_array_buffer = + zn_client_gl_buffer_get(element_array_buffer_resource); + + if (self == NULL || element_array_buffer_resource == NULL) { + return; + } + + uint64_t offset = 0; + + if (!zn_wl_array_to_uint64_t(offset_array, &offset)) { + wl_resource_post_error(resource, ZWN_COMPOSITOR_ERROR_INVALID_WL_ARRAY_SIZE, + "invalid wl_array size"); + return; + } + + zn_gl_base_technique_draw_elements(self->zn_gl_base_technique, mode, count, + type, offset, element_array_buffer->zn_gl_buffer); +} + +static const struct zwn_gl_base_technique_interface implementation = { + .destroy = zn_client_gl_base_technique_protocol_destroy, + .bind_program = zn_client_gl_base_technique_protocol_bind_program, + .bind_vertex_array = zn_client_gl_base_technique_protocol_bind_vertex_array, + .bind_texture = zn_client_gl_base_technique_protocol_bind_texture, + .uniform_vector = zn_client_gl_base_technique_protocol_uniform_vector, + .uniform_matrix = zn_client_gl_base_technique_protocol_uniform_matrix, + .draw_arrays = zn_client_gl_base_technique_protocol_draw_arrays, + .draw_elements = zn_client_gl_base_technique_protocol_draw_elements, +}; + +struct zn_client_gl_base_technique * +zn_client_gl_base_technique_get(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +static void +zn_client_gl_base_technique_handle_rendering_unit_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_client_gl_base_technique *self = + zn_container_of(listener, self, rendering_unit_destroy_listener); + + zn_client_gl_base_technique_destroy(self); +} + +static void +zn_client_gl_base_technique_handle_zn_gl_base_technique_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_client_gl_base_technique *self = + zn_container_of(listener, self, zn_gl_base_technique_destroy_listener); + + zn_client_gl_base_technique_destroy(self); +} + +struct zn_client_gl_base_technique * +zn_client_gl_base_technique_create(struct wl_client *client, uint32_t id, + struct zn_client_gl_rendering_unit *rendering_unit) +{ + struct zn_gl_virtual_object *gl_virtual_object = + rendering_unit->virtual_object->zn_virtual_object->gl_virtual_object; + if (gl_virtual_object == NULL) { + zn_error("Failed to get gl_virtual_object"); + return NULL; + } + + struct zn_client_gl_base_technique *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + wl_client_post_no_memory(client); + goto err; + } + + self->rendering_unit = rendering_unit; + + self->zn_gl_base_technique = zn_xr_dispatcher_get_new_gl_base_technique( + gl_virtual_object->dispatcher, rendering_unit->zn_gl_rendering_unit); + if (self->zn_gl_base_technique == NULL) { + zn_error("Failed to get new zn_gl_base_technique"); + wl_client_post_no_memory(client); + goto err_free; + } + + self->resource = + wl_resource_create(client, &zwn_gl_base_technique_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_gl_base_technique; + } + + wl_resource_set_implementation(self->resource, &implementation, self, + zn_client_gl_base_technique_handle_destroy); + + self->rendering_unit_destroy_listener.notify = + zn_client_gl_base_technique_handle_rendering_unit_destroy; + wl_signal_add( + &rendering_unit->events.destroy, &self->rendering_unit_destroy_listener); + + self->zn_gl_base_technique_destroy_listener.notify = + zn_client_gl_base_technique_handle_zn_gl_base_technique_destroy; + wl_signal_add(&self->zn_gl_base_technique->events.destroy, + &self->zn_gl_base_technique_destroy_listener); + + return self; + +err_gl_base_technique: + zn_xr_dispatcher_destroy_gl_base_technique( + gl_virtual_object->dispatcher, self->zn_gl_base_technique); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_client_gl_base_technique_destroy(struct zn_client_gl_base_technique *self) +{ + struct zn_gl_virtual_object *gl_virtual_object = + self->rendering_unit->virtual_object->zn_virtual_object + ->gl_virtual_object; + + wl_resource_set_implementation(self->resource, &implementation, NULL, NULL); + wl_list_remove(&self->rendering_unit_destroy_listener.link); + wl_list_remove(&self->zn_gl_base_technique_destroy_listener.link); + if (gl_virtual_object) { + zn_xr_dispatcher_destroy_gl_base_technique( + gl_virtual_object->dispatcher, self->zn_gl_base_technique); + } + free(self); +} diff --git a/zen/src/backend/immersive/gl/client-gl-base-technique.h b/zen/src/backend/immersive/gl/client-gl-base-technique.h new file mode 100644 index 00000000..980e7c62 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-base-technique.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +struct zn_client_gl_rendering_unit; +struct zn_gl_base_technique; + +struct zn_client_gl_base_technique { + struct zn_client_gl_rendering_unit *rendering_unit; // @nonnull @outlive + + struct wl_resource *resource; // @nonnull, @outlive + + struct zn_gl_base_technique *zn_gl_base_technique; // @nonnull, @outlive + + struct wl_listener rendering_unit_destroy_listener; + struct wl_listener zn_gl_base_technique_destroy_listener; +}; + +struct zn_client_gl_base_technique *zn_client_gl_base_technique_create( + struct wl_client *client, uint32_t id, + struct zn_client_gl_rendering_unit *rendering_unit); + +/// @return value is nullable +struct zn_client_gl_base_technique *zn_client_gl_base_technique_get( + struct wl_resource *resource); diff --git a/zen/src/backend/immersive/gl/client-gl-buffer.c b/zen/src/backend/immersive/gl/client-gl-buffer.c new file mode 100644 index 00000000..610735c0 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-buffer.c @@ -0,0 +1,138 @@ +#include "client-gl-buffer.h" + +#include + +#include "backend/immersive/shm-buffer.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/backend.h" +#include "zen/gl-buffer.h" +#include "zen/server.h" +#include "zen/xr-dispatcher.h" +#include "zen/xr-system.h" + +static void zn_client_gl_buffer_destroy(struct zn_client_gl_buffer *self); + +static void +zn_client_gl_buffer_handle_destroy(struct wl_resource *resource) +{ + struct zn_client_gl_buffer *self = zn_client_gl_buffer_get(resource); + + zn_client_gl_buffer_destroy(self); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_buffer_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_buffer_protocol_data(struct wl_client *client UNUSED, + struct wl_resource *resource, uint32_t target, + struct wl_resource *data_resource, uint32_t usage) +{ + struct zn_client_gl_buffer *self = zn_client_gl_buffer_get(resource); + struct zn_shm_buffer *shm_buffer = zn_shm_buffer_get(data_resource); + + if (self == NULL || shm_buffer == NULL) { + return; + } + + zn_gl_buffer_data(self->zn_gl_buffer, target, shm_buffer->zn_buffer, usage); +} + +static const struct zwn_gl_buffer_interface implementation = { + .destroy = zn_client_gl_buffer_protocol_destroy, + .data = zn_client_gl_buffer_protocol_data, +}; + +struct zn_client_gl_buffer * +zn_client_gl_buffer_get(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +static void +zn_client_gl_buffer_handle_zn_gl_buffer_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_client_gl_buffer *self = + zn_container_of(listener, self, zn_gl_buffer_destroy_listener); + + zn_client_gl_buffer_destroy(self); +} + +struct zn_client_gl_buffer * +zn_client_gl_buffer_create(struct wl_client *client, uint32_t id) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + if (xr_system == NULL || !zn_xr_system_is_connected(xr_system)) { + zn_error("Failed to get xr_system"); + goto err; + } + + struct zn_client_gl_buffer *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + wl_client_post_no_memory(client); + goto err; + } + + self->zn_gl_buffer = + zn_xr_dispatcher_get_new_gl_buffer(xr_system->default_dispatcher); + if (self->zn_gl_buffer == NULL) { + zn_error("Failed to get new zn_gl_buffer"); + wl_client_post_no_memory(client); + goto err_free; + } + + self->resource = wl_resource_create(client, &zwn_gl_buffer_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_gl_buffer; + } + + wl_resource_set_implementation(self->resource, &implementation, self, + zn_client_gl_buffer_handle_destroy); + + self->zn_gl_buffer_destroy_listener.notify = + zn_client_gl_buffer_handle_zn_gl_buffer_destroy; + wl_signal_add(&self->zn_gl_buffer->events.destroy, + &self->zn_gl_buffer_destroy_listener); + + return self; + +err_gl_buffer: + zn_xr_dispatcher_destroy_gl_buffer( + xr_system->default_dispatcher, self->zn_gl_buffer); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_client_gl_buffer_destroy(struct zn_client_gl_buffer *self) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + wl_resource_set_implementation(self->resource, &implementation, NULL, NULL); + wl_list_remove(&self->zn_gl_buffer_destroy_listener.link); + + if (xr_system != NULL && zn_xr_system_is_connected(xr_system)) { + zn_xr_dispatcher_destroy_gl_buffer( + xr_system->default_dispatcher, self->zn_gl_buffer); + } + + free(self); +} diff --git a/zen/src/backend/immersive/gl/client-gl-buffer.h b/zen/src/backend/immersive/gl/client-gl-buffer.h new file mode 100644 index 00000000..a9dd7e4e --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-buffer.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +struct zn_gl_buffer; + +struct zn_client_gl_buffer { + struct wl_resource *resource; // @nonnull, @outlive + + struct zn_gl_buffer *zn_gl_buffer; // @nonnull, @outlive + + struct wl_listener zn_gl_buffer_destroy_listener; +}; + +struct zn_client_gl_buffer *zn_client_gl_buffer_create( + struct wl_client *client, uint32_t id); + +/// @return value is nullable +struct zn_client_gl_buffer *zn_client_gl_buffer_get( + struct wl_resource *resource); diff --git a/zen/src/backend/immersive/gl/client-gl-program.c b/zen/src/backend/immersive/gl/client-gl-program.c new file mode 100644 index 00000000..7c86cb12 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-program.c @@ -0,0 +1,152 @@ +#include "client-gl-program.h" + +#include + +#include "client-gl-shader.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/backend.h" +#include "zen/gl-program.h" +#include "zen/server.h" +#include "zen/xr-dispatcher.h" +#include "zen/xr-system.h" + +static void zn_client_gl_program_destroy(struct zn_client_gl_program *self); + +static void +zn_client_gl_program_handle_destroy(struct wl_resource *resource) +{ + struct zn_client_gl_program *self = zn_client_gl_program_get(resource); + + zn_client_gl_program_destroy(self); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_program_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_program_protocol_attach_shader(struct wl_client *client UNUSED, + struct wl_resource *resource, struct wl_resource *shader_resource) +{ + struct zn_client_gl_program *self = zn_client_gl_program_get(resource); + struct zn_client_gl_shader *shader = zn_client_gl_shader_get(shader_resource); + + if (self == NULL || shader == NULL) { + return; + } + + zn_gl_program_attach_shader(self->zn_gl_program, shader->zn_gl_shader); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_program_protocol_link( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + struct zn_client_gl_program *self = zn_client_gl_program_get(resource); + + if (self == NULL) { + return; + } + + zn_gl_program_link(self->zn_gl_program); +} + +static const struct zwn_gl_program_interface implementation = { + .destroy = zn_client_gl_program_protocol_destroy, + .attach_shader = zn_client_gl_program_protocol_attach_shader, + .link = zn_client_gl_program_protocol_link, +}; + +struct zn_client_gl_program * +zn_client_gl_program_get(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +static void +zn_client_gl_program_handle_zn_gl_program_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_client_gl_program *self = + zn_container_of(listener, self, zn_gl_program_destroy_listener); + + zn_client_gl_program_destroy(self); +} + +struct zn_client_gl_program * +zn_client_gl_program_create(struct wl_client *client, uint32_t id) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + if (xr_system == NULL || !zn_xr_system_is_connected(xr_system)) { + zn_error("Failed to get xr_system"); + goto err; + } + + struct zn_client_gl_program *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + wl_client_post_no_memory(client); + goto err; + } + + self->zn_gl_program = + zn_xr_dispatcher_get_new_gl_program(xr_system->default_dispatcher); + if (self->zn_gl_program == NULL) { + zn_error("Failed to get new gl_program"); + wl_client_post_no_memory(client); + goto err_free; + } + + self->resource = wl_resource_create(client, &zwn_gl_program_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_gl_program; + } + + wl_resource_set_implementation(self->resource, &implementation, self, + zn_client_gl_program_handle_destroy); + + self->zn_gl_program_destroy_listener.notify = + zn_client_gl_program_handle_zn_gl_program_destroy; + wl_signal_add(&self->zn_gl_program->events.destroy, + &self->zn_gl_program_destroy_listener); + + return self; + +err_gl_program: + zn_xr_dispatcher_destroy_gl_program( + xr_system->default_dispatcher, self->zn_gl_program); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_client_gl_program_destroy(struct zn_client_gl_program *self) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + wl_resource_set_implementation(self->resource, &implementation, NULL, NULL); + wl_list_remove(&self->zn_gl_program_destroy_listener.link); + + if (xr_system != NULL && zn_xr_system_is_connected(xr_system)) { + zn_xr_dispatcher_destroy_gl_program( + xr_system->default_dispatcher, self->zn_gl_program); + } + + free(self); +} diff --git a/zen/src/backend/immersive/gl/client-gl-program.h b/zen/src/backend/immersive/gl/client-gl-program.h new file mode 100644 index 00000000..97e62499 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-program.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +struct zn_gl_program; + +struct zn_client_gl_program { + struct wl_resource *resource; // @nonnull, @outlive + + struct zn_gl_program *zn_gl_program; // @nonnull, @outlive + + struct wl_listener zn_gl_program_destroy_listener; +}; + +struct zn_client_gl_program *zn_client_gl_program_create( + struct wl_client *client, uint32_t id); + +/// @return value is nullable +struct zn_client_gl_program *zn_client_gl_program_get( + struct wl_resource *resource); diff --git a/zen/src/backend/immersive/gl/client-gl-rendering-unit.c b/zen/src/backend/immersive/gl/client-gl-rendering-unit.c new file mode 100644 index 00000000..8d52bdb7 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-rendering-unit.c @@ -0,0 +1,160 @@ +#include "client-gl-rendering-unit.h" + +#include + +#include "backend/immersive/client-virtual-object.h" +#include "gl-virtual-object.h" +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" +#include "zen/gl-rendering-unit.h" +#include "zen/virtual-object.h" +#include "zen/xr-dispatcher.h" + +static void zn_client_gl_rendering_unit_destroy( + struct zn_client_gl_rendering_unit *self); + +static void +zn_client_gl_rendering_unit_handle_destroy(struct wl_resource *resource) +{ + struct zn_client_gl_rendering_unit *self = + zn_client_gl_rendering_unit_get(resource); + + zn_client_gl_rendering_unit_destroy(self); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_rendering_unit_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_rendering_unit_protocol_change_visibility( + struct wl_client *client UNUSED, struct wl_resource *resource, + uint32_t visible) +{ + struct zn_client_gl_rendering_unit *self = + wl_resource_get_user_data(resource); + if (self == NULL) { + return; + } + + zn_gl_rendering_unit_change_visibility(self->zn_gl_rendering_unit, visible); +} + +static const struct zwn_gl_rendering_unit_interface implementation = { + .destroy = zn_client_gl_rendering_unit_protocol_destroy, + .change_visibility = zn_client_gl_rendering_unit_protocol_change_visibility, +}; + +struct zn_client_gl_rendering_unit * +zn_client_gl_rendering_unit_get(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +static void +zn_client_gl_rendering_unit_handle_virtual_object_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_client_gl_rendering_unit *self = + zn_container_of(listener, self, virtual_object_destroy_listener); + + zn_client_gl_rendering_unit_destroy(self); +} + +static void +zn_client_gl_rendering_unit_handle_zn_gl_rendering_unit_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_client_gl_rendering_unit *self = + zn_container_of(listener, self, zn_gl_rendering_unit_destroy_listener); + + zn_client_gl_rendering_unit_destroy(self); +} + +struct zn_client_gl_rendering_unit * +zn_client_gl_rendering_unit_create(struct wl_client *client, uint32_t id, + struct zn_client_virtual_object *virtual_object) +{ + struct zn_gl_virtual_object *gl_virtual_object = + virtual_object->zn_virtual_object->gl_virtual_object; + if (gl_virtual_object == NULL) { + zn_error("Failed to get gl_virtual_object"); + goto err; + } + + struct zn_client_gl_rendering_unit *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + wl_client_post_no_memory(client); + goto err; + } + + self->virtual_object = virtual_object; + wl_signal_init(&self->events.destroy); + + self->zn_gl_rendering_unit = zn_xr_dispatcher_get_new_gl_rendering_unit( + gl_virtual_object->dispatcher, gl_virtual_object); + if (self->zn_gl_rendering_unit == NULL) { + zn_error("Failed to get new zn_gl_rendering_unit"); + wl_client_post_no_memory(client); + goto err_free; + } + + self->resource = + wl_resource_create(client, &zwn_gl_rendering_unit_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_gl_rendering_unit; + } + + wl_resource_set_implementation(self->resource, &implementation, self, + zn_client_gl_rendering_unit_handle_destroy); + + self->virtual_object_destroy_listener.notify = + zn_client_gl_rendering_unit_handle_virtual_object_destroy; + wl_signal_add( + &virtual_object->events.destroy, &self->virtual_object_destroy_listener); + + self->zn_gl_rendering_unit_destroy_listener.notify = + zn_client_gl_rendering_unit_handle_zn_gl_rendering_unit_destroy; + wl_signal_add(&self->zn_gl_rendering_unit->events.destroy, + &self->zn_gl_rendering_unit_destroy_listener); + + return self; + +err_gl_rendering_unit: + zn_xr_dispatcher_destroy_gl_rendering_unit( + gl_virtual_object->dispatcher, self->zn_gl_rendering_unit); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_client_gl_rendering_unit_destroy(struct zn_client_gl_rendering_unit *self) +{ + struct zn_gl_virtual_object *gl_virtual_object = + self->virtual_object->zn_virtual_object->gl_virtual_object; + + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_resource_set_implementation(self->resource, &implementation, NULL, NULL); + wl_list_remove(&self->virtual_object_destroy_listener.link); + wl_list_remove(&self->zn_gl_rendering_unit_destroy_listener.link); + wl_list_remove(&self->events.destroy.listener_list); + if (gl_virtual_object) { + zn_xr_dispatcher_destroy_gl_rendering_unit( + gl_virtual_object->dispatcher, self->zn_gl_rendering_unit); + } + free(self); +} diff --git a/zen/src/backend/immersive/gl/client-gl-rendering-unit.h b/zen/src/backend/immersive/gl/client-gl-rendering-unit.h new file mode 100644 index 00000000..8744e829 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-rendering-unit.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +struct zn_client_virtual_object; +struct zn_gl_rendering_unit; + +struct zn_client_gl_rendering_unit { + struct zn_client_virtual_object *virtual_object; // @nonnull, @outlive + + struct wl_resource *resource; // @nonnull, @outlive + + struct zn_gl_rendering_unit *zn_gl_rendering_unit; // @nonnull, @outlive + + struct wl_listener virtual_object_destroy_listener; + struct wl_listener zn_gl_rendering_unit_destroy_listener; + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +struct zn_client_gl_rendering_unit *zn_client_gl_rendering_unit_create( + struct wl_client *client, uint32_t id, + struct zn_client_virtual_object *virtual_object); + +struct zn_client_gl_rendering_unit *zn_client_gl_rendering_unit_get( + struct wl_resource *resource); diff --git a/zen/src/backend/immersive/gl/client-gl-shader.c b/zen/src/backend/immersive/gl/client-gl-shader.c new file mode 100644 index 00000000..1edc3594 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-shader.c @@ -0,0 +1,123 @@ +#include "client-gl-shader.h" + +#include + +#include "backend/immersive/shm-buffer.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/backend.h" +#include "zen/buffer.h" +#include "zen/gl-shader.h" +#include "zen/server.h" +#include "zen/xr-dispatcher.h" +#include "zen/xr-system.h" + +static void zn_client_gl_shader_destroy(struct zn_client_gl_shader *self); + +static void +zn_client_gl_shader_handle_destroy(struct wl_resource *resource) +{ + struct zn_client_gl_shader *self = zn_client_gl_shader_get(resource); + + zn_client_gl_shader_destroy(self); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_shader_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zwn_gl_shader_interface implementation = { + .destroy = zn_client_gl_shader_protocol_destroy, +}; + +struct zn_client_gl_shader * +zn_client_gl_shader_get(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +static void +zn_client_gl_shader_handle_zn_gl_shader_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_client_gl_shader *self = + zn_container_of(listener, self, zn_gl_shader_destroy_listener); + + zn_client_gl_shader_destroy(self); +} + +struct zn_client_gl_shader * +zn_client_gl_shader_create(struct wl_client *client, uint32_t id, + struct zn_buffer *buffer, uint32_t type) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + if (xr_system == NULL || !zn_xr_system_is_connected(xr_system)) { + zn_error("Failed to get xr_system"); + goto err; + } + + struct zn_client_gl_shader *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + wl_client_post_no_memory(client); + goto err; + } + + self->zn_gl_shader = zn_xr_dispatcher_get_new_gl_shader( + xr_system->default_dispatcher, buffer, type); + if (self->zn_gl_shader == NULL) { + zn_error("Failed to get new gl_shader"); + wl_client_post_no_memory(client); + goto err_free; + } + + self->resource = wl_resource_create(client, &zwn_gl_shader_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_gl_shader; + } + + wl_resource_set_implementation(self->resource, &implementation, self, + zn_client_gl_shader_handle_destroy); + + self->zn_gl_shader_destroy_listener.notify = + zn_client_gl_shader_handle_zn_gl_shader_destroy; + wl_signal_add(&self->zn_gl_shader->events.destroy, + &self->zn_gl_shader_destroy_listener); + + return self; + +err_gl_shader: + zn_xr_dispatcher_destroy_gl_shader( + xr_system->default_dispatcher, self->zn_gl_shader); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_client_gl_shader_destroy(struct zn_client_gl_shader *self) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + wl_resource_set_implementation(self->resource, &implementation, NULL, NULL); + wl_list_remove(&self->zn_gl_shader_destroy_listener.link); + + if (xr_system != NULL && zn_xr_system_is_connected(xr_system)) { + zn_xr_dispatcher_destroy_gl_shader( + xr_system->default_dispatcher, self->zn_gl_shader); + } + + free(self); +} diff --git a/zen/src/backend/immersive/gl/client-gl-shader.h b/zen/src/backend/immersive/gl/client-gl-shader.h new file mode 100644 index 00000000..880edbc9 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-shader.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +struct zn_buffer; +struct zn_gl_shader; + +struct zn_client_gl_shader { + struct wl_resource *resource; // @nonnull, @outlive + + struct zn_gl_shader *zn_gl_shader; // @nonnull, @outlive + + struct wl_listener zn_gl_shader_destroy_listener; +}; + +struct zn_client_gl_shader *zn_client_gl_shader_create(struct wl_client *client, + uint32_t id, struct zn_buffer *buffer, uint32_t type); + +/// @return value is nullable +struct zn_client_gl_shader *zn_client_gl_shader_get( + struct wl_resource *resource); diff --git a/zen/src/backend/immersive/gl/client-gl-texture.c b/zen/src/backend/immersive/gl/client-gl-texture.c new file mode 100644 index 00000000..e292a9f9 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-texture.c @@ -0,0 +1,154 @@ +#include "client-gl-texture.h" + +#include + +#include "backend/immersive/shm-buffer.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/backend.h" +#include "zen/gl-texture.h" +#include "zen/server.h" +#include "zen/xr-dispatcher.h" +#include "zen/xr-system.h" + +static void zn_client_gl_texture_destroy(struct zn_client_gl_texture *self); + +static void +zn_client_gl_texture_handle_destroy(struct wl_resource *resource) +{ + struct zn_client_gl_texture *self = zn_client_gl_texture_get(resource); + + zn_client_gl_texture_destroy(self); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_texture_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_texture_protocol_image_2d(struct wl_client *client UNUSED, + struct wl_resource *resource, uint32_t target, int32_t level, + int32_t internal_format, uint32_t width, uint32_t height, int32_t border, + uint32_t format, uint32_t type, struct wl_resource *data) +{ + struct zn_client_gl_texture *self = zn_client_gl_texture_get(resource); + struct zn_shm_buffer *shm_buffer = zn_shm_buffer_get(data); + + if (self == NULL || shm_buffer == NULL) { + return; + } + + zn_gl_texture_image_2d(self->zn_gl_texture, target, level, internal_format, + width, height, border, format, type, shm_buffer->zn_buffer); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_texture_protocol_generate_mipmap(struct wl_client *client UNUSED, + struct wl_resource *resource, uint32_t target) +{ + struct zn_client_gl_texture *self = zn_client_gl_texture_get(resource); + + if (self == NULL) { + return; + } + + zn_gl_texture_generate_mipmap(self->zn_gl_texture, target); +} + +static const struct zwn_gl_texture_interface implementation = { + .destroy = zn_client_gl_texture_protocol_destroy, + .image_2d = zn_client_gl_texture_protocol_image_2d, + .generate_mipmap = zn_client_gl_texture_protocol_generate_mipmap, +}; + +struct zn_client_gl_texture * +zn_client_gl_texture_get(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +static void +zn_client_gl_texture_handle_zn_gl_texture_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_client_gl_texture *self = + zn_container_of(listener, self, zn_gl_texture_destroy_listener); + + zn_client_gl_texture_destroy(self); +} + +struct zn_client_gl_texture * +zn_client_gl_texture_create(struct wl_client *client, uint32_t id) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + if (xr_system == NULL || !zn_xr_system_is_connected(xr_system)) { + zn_error("Failed to get xr_system"); + goto err; + } + + struct zn_client_gl_texture *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + wl_client_post_no_memory(client); + goto err; + } + + self->zn_gl_texture = + zn_xr_dispatcher_get_new_gl_texture(xr_system->default_dispatcher); + if (self->zn_gl_texture == NULL) { + zn_error("Failed to get new zn_gl_texture"); + goto err_free; + } + + self->resource = wl_resource_create(client, &zwn_gl_texture_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_gl_texture; + } + + wl_resource_set_implementation(self->resource, &implementation, self, + zn_client_gl_texture_handle_destroy); + + self->zn_gl_texture_destroy_listener.notify = + zn_client_gl_texture_handle_zn_gl_texture_destroy; + wl_signal_add(&self->zn_gl_texture->events.destroy, + &self->zn_gl_texture_destroy_listener); + + return self; + +err_gl_texture: + zn_xr_dispatcher_destroy_gl_texture( + xr_system->default_dispatcher, self->zn_gl_texture); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_client_gl_texture_destroy(struct zn_client_gl_texture *self) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + wl_resource_set_implementation(self->resource, &implementation, NULL, NULL); + wl_list_remove(&self->zn_gl_texture_destroy_listener.link); + + if (xr_system != NULL && zn_xr_system_is_connected(xr_system)) { + zn_xr_dispatcher_destroy_gl_texture( + xr_system->default_dispatcher, self->zn_gl_texture); + } + + free(self); +} diff --git a/zen/src/backend/immersive/gl/client-gl-texture.h b/zen/src/backend/immersive/gl/client-gl-texture.h new file mode 100644 index 00000000..e6ef9c5b --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-texture.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +struct zn_gl_texture; + +struct zn_client_gl_texture { + struct wl_resource *resource; // @nonnull, @outlive + + struct zn_gl_texture *zn_gl_texture; // @nonnull, @outlive + + struct wl_listener zn_gl_texture_destroy_listener; +}; + +struct zn_client_gl_texture *zn_client_gl_texture_create( + struct wl_client *client, uint32_t id); + +/// @return value is nullable +struct zn_client_gl_texture *zn_client_gl_texture_get( + struct wl_resource *resource); diff --git a/zen/src/backend/immersive/gl/client-gl-vertex-array.c b/zen/src/backend/immersive/gl/client-gl-vertex-array.c new file mode 100644 index 00000000..9651f939 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-vertex-array.c @@ -0,0 +1,195 @@ +#include "client-gl-vertex-array.h" + +#include +#include + +#include "client-gl-buffer.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen-common/wl-array.h" +#include "zen/backend.h" +#include "zen/gl-vertex-array.h" +#include "zen/server.h" +#include "zen/xr-dispatcher.h" +#include "zen/xr-system.h" + +static void zn_client_gl_vertex_array_destroy( + struct zn_client_gl_vertex_array *self); + +static void +zn_client_gl_vertex_array_handle_destroy(struct wl_resource *resource) +{ + struct zn_client_gl_vertex_array *self = + zn_client_gl_vertex_array_get(resource); + + zn_client_gl_vertex_array_destroy(self); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_vertex_array_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_vertex_array_protocol_enable_vertex_attrib_array( + struct wl_client *client UNUSED, struct wl_resource *resource, + uint32_t index) +{ + struct zn_client_gl_vertex_array *self = + zn_client_gl_vertex_array_get(resource); + + if (self == NULL) { + return; + } + + zn_gl_vertex_array_enable_vertex_attrib_array( + self->zn_gl_vertex_array, index); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_vertex_array_protocol_disable_vertex_attrib_array( + struct wl_client *client UNUSED, struct wl_resource *resource, + uint32_t index) +{ + struct zn_client_gl_vertex_array *self = + zn_client_gl_vertex_array_get(resource); + + if (self == NULL) { + return; + } + + zn_gl_vertex_array_disable_vertex_attrib_array( + self->zn_gl_vertex_array, index); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_client_gl_vertex_array_protocol_vertex_attrib_pointer( + struct wl_client *client UNUSED, struct wl_resource *resource, + uint32_t index, int32_t size, uint32_t type, uint32_t normalized, + int32_t stride, struct wl_array *offset_array, + struct wl_resource *gl_buffer_resource) +{ + struct zn_client_gl_vertex_array *self = + zn_client_gl_vertex_array_get(resource); + struct zn_client_gl_buffer *gl_buffer = + zn_client_gl_buffer_get(gl_buffer_resource); + + if (self == NULL || gl_buffer == NULL) { + return; + } + + uint64_t offset = 0; + + if (!zn_wl_array_to_uint64_t(offset_array, &offset)) { + wl_resource_post_error(resource, ZWN_COMPOSITOR_ERROR_INVALID_WL_ARRAY_SIZE, + "invalid wl_array size"); + return; + } + + zn_gl_vertex_array_vertex_attrib_pointer(self->zn_gl_vertex_array, index, + size, type, normalized, stride, offset, gl_buffer->zn_gl_buffer); +} + +static const struct zwn_gl_vertex_array_interface implementation = { + .destroy = zn_client_gl_vertex_array_protocol_destroy, + .enable_vertex_attrib_array = + zn_client_gl_vertex_array_protocol_enable_vertex_attrib_array, + .disable_vertex_attrib_array = + zn_client_gl_vertex_array_protocol_disable_vertex_attrib_array, + .vertex_attrib_pointer = + zn_client_gl_vertex_array_protocol_vertex_attrib_pointer, +}; + +struct zn_client_gl_vertex_array * +zn_client_gl_vertex_array_get(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +static void +zn_client_gl_vertex_array_handle_zn_gl_vertex_array_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_client_gl_vertex_array *self = + zn_container_of(listener, self, zn_gl_vertex_array_destroy_listener); + + zn_client_gl_vertex_array_destroy(self); +} + +struct zn_client_gl_vertex_array * +zn_client_gl_vertex_array_create(struct wl_client *client, uint32_t id) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + if (xr_system == NULL || !zn_xr_system_is_connected(xr_system)) { + zn_error("Failed to get xr_system"); + goto err; + } + + struct zn_client_gl_vertex_array *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + wl_client_post_no_memory(client); + goto err; + } + + self->zn_gl_vertex_array = + zn_xr_dispatcher_get_new_gl_vertex_array(xr_system->default_dispatcher); + if (self->zn_gl_vertex_array == NULL) { + zn_error("Failed to get new gl_vertex_array"); + wl_client_post_no_memory(client); + goto err_free; + } + + self->resource = + wl_resource_create(client, &zwn_gl_vertex_array_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_gl_vertex_array; + } + + wl_resource_set_implementation(self->resource, &implementation, self, + zn_client_gl_vertex_array_handle_destroy); + + self->zn_gl_vertex_array_destroy_listener.notify = + zn_client_gl_vertex_array_handle_zn_gl_vertex_array_destroy; + wl_signal_add(&self->zn_gl_vertex_array->events.destroy, + &self->zn_gl_vertex_array_destroy_listener); + + return self; + +err_gl_vertex_array: + zn_xr_dispatcher_destroy_gl_vertex_array( + xr_system->default_dispatcher, self->zn_gl_vertex_array); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_client_gl_vertex_array_destroy(struct zn_client_gl_vertex_array *self) +{ + struct zn_server *server = zn_server_get_singleton(); + struct zn_xr_system *xr_system = zn_backend_get_xr_system(server->backend); + + wl_resource_set_implementation(self->resource, &implementation, NULL, NULL); + wl_list_remove(&self->zn_gl_vertex_array_destroy_listener.link); + + if (xr_system != NULL && zn_xr_system_is_connected(xr_system)) { + zn_xr_dispatcher_destroy_gl_vertex_array( + xr_system->default_dispatcher, self->zn_gl_vertex_array); + } + + free(self); +} diff --git a/zen/src/backend/immersive/gl/client-gl-vertex-array.h b/zen/src/backend/immersive/gl/client-gl-vertex-array.h new file mode 100644 index 00000000..e039b9a4 --- /dev/null +++ b/zen/src/backend/immersive/gl/client-gl-vertex-array.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +struct zn_gl_vertex_array; + +struct zn_client_gl_vertex_array { + struct wl_resource *resource; // @nonnull, @outlive + + struct zn_gl_vertex_array *zn_gl_vertex_array; // @nonnull, @outlive + + struct wl_listener zn_gl_vertex_array_destroy_listener; +}; + +struct zn_client_gl_vertex_array *zn_client_gl_vertex_array_create( + struct wl_client *client, uint32_t id); + +/// @return value is nullable +struct zn_client_gl_vertex_array *zn_client_gl_vertex_array_get( + struct wl_resource *resource); diff --git a/zen/src/backend/immersive/gl/gl-context.c b/zen/src/backend/immersive/gl/gl-context.c new file mode 100644 index 00000000..255faf30 --- /dev/null +++ b/zen/src/backend/immersive/gl/gl-context.c @@ -0,0 +1,241 @@ +#include "backend/immersive/gl-context.h" + +#include + +#include "backend/immersive/client-virtual-object.h" +#include "backend/immersive/shm-buffer.h" +#include "client-gl-base-technique.h" +#include "client-gl-buffer.h" +#include "client-gl-program.h" +#include "client-gl-rendering-unit.h" +#include "client-gl-shader.h" +#include "client-gl-texture.h" +#include "client-gl-vertex-array.h" +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" +#include "zen/xr-system.h" + +static struct zn_cl_context * +zn_gl_context_get(struct wl_resource *resource) +{ + return wl_resource_get_user_data(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_gl_context_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_gl_context_protocol_create_gl_rendering_unit(struct wl_client *client UNUSED, + struct wl_resource *resource, uint32_t id UNUSED, + struct wl_resource *virtual_object_resource UNUSED) +{ + if (zn_gl_context_get(resource) == NULL) { + return; + } + + struct zn_client_virtual_object *client_virtual_object = + zn_client_virtual_object_get(virtual_object_resource); + + zn_client_gl_rendering_unit_create(client, id, client_virtual_object); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_gl_context_protocol_create_gl_buffer( + struct wl_client *client, struct wl_resource *resource UNUSED, uint32_t id) +{ + if (zn_gl_context_get(resource) == NULL) { + return; + } + + zn_client_gl_buffer_create(client, id); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_gl_context_protocol_create_gl_shader(struct wl_client *client, + struct wl_resource *resource UNUSED, uint32_t id, + struct wl_resource *buffer_resource, uint32_t type) +{ + if (zn_gl_context_get(resource) == NULL) { + return; + } + + struct zn_shm_buffer *shm_buffer = zn_shm_buffer_get(buffer_resource); + + zn_client_gl_shader_create(client, id, shm_buffer->zn_buffer, type); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_gl_context_protocol_create_gl_program( + struct wl_client *client, struct wl_resource *resource UNUSED, uint32_t id) +{ + if (zn_gl_context_get(resource) == NULL) { + return; + } + + zn_client_gl_program_create(client, id); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_gl_context_protocol_create_gl_texture( + struct wl_client *client, struct wl_resource *resource UNUSED, uint32_t id) +{ + if (zn_gl_context_get(resource) == NULL) { + return; + } + + zn_client_gl_texture_create(client, id); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_gl_context_protocol_create_gl_sampler(struct wl_client *client UNUSED, + struct wl_resource *resource UNUSED, uint32_t id UNUSED) +{ + if (zn_gl_context_get(resource) == NULL) { + return; + } +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_gl_context_protocol_create_gl_vertex_array( + struct wl_client *client, struct wl_resource *resource UNUSED, uint32_t id) +{ + if (zn_gl_context_get(resource) == NULL) { + return; + } + + zn_client_gl_vertex_array_create(client, id); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_gl_context_protocol_create_gl_base_technique(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *rendering_unit_resource) +{ + if (zn_gl_context_get(resource) == NULL) { + return; + } + + struct zn_client_gl_rendering_unit *rendering_unit = + zn_client_gl_rendering_unit_get(rendering_unit_resource); + + zn_client_gl_base_technique_create(client, id, rendering_unit); +} + +static const struct zwn_gl_context_interface implementation = { + .destroy = zn_gl_context_protocol_destroy, + .create_gl_rendering_unit = zn_gl_context_protocol_create_gl_rendering_unit, + .create_gl_buffer = zn_gl_context_protocol_create_gl_buffer, + .create_gl_shader = zn_gl_context_protocol_create_gl_shader, + .create_gl_program = zn_gl_context_protocol_create_gl_program, + .create_gl_texture = zn_gl_context_protocol_create_gl_texture, + .create_gl_sampler = zn_gl_context_protocol_create_gl_sampler, + .create_gl_vertex_array = zn_gl_context_protocol_create_gl_vertex_array, + .create_gl_base_technique = zn_gl_context_protocol_create_gl_base_technique, +}; + +static void +zn_gl_context_handle_xr_system_session_state_changed( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_gl_context *self = + zn_container_of(listener, self, xr_system_session_state_changed_listener); + + if (!zn_xr_system_is_connected(self->xr_system)) { + zn_gl_context_destroy(self); + } +} + +static void +zn_gl_context_bind( + struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct zn_gl_context *self = data; + + struct wl_resource *resource = + wl_resource_create(client, &zwn_gl_context_interface, (int)version, id); + if (resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &implementation, self, NULL); + + wl_list_insert(&self->resource_list, wl_resource_get_link(resource)); +} + +struct zn_gl_context * +zn_gl_context_create(struct wl_display *display, struct zn_xr_system *xr_system) +{ + if (!zn_assert(zn_xr_system_is_connected(xr_system), + "xr_system must be connected")) { + goto err; + } + + struct zn_gl_context *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + wl_list_init(&self->resource_list); + self->xr_system = xr_system; + wl_signal_init(&self->events.destroy); + self->destroying = false; + + self->global = wl_global_create( + display, &zwn_gl_context_interface, 1, self, zn_gl_context_bind); + if (self->global == NULL) { + zn_error("Failed to create zwn_gl_context global"); + goto err_free; + } + + self->xr_system_session_state_changed_listener.notify = + zn_gl_context_handle_xr_system_session_state_changed; + wl_signal_add(&xr_system->events.session_state_changed, + &self->xr_system_session_state_changed_listener); + + return self; + +err_free: + free(self); + +err: + return NULL; +} + +void +zn_gl_context_destroy(struct zn_gl_context *self UNUSED) +{ + if (self->destroying) { + return; + } + + self->destroying = true; + + zn_signal_emit_mutable(&self->events.destroy, NULL); + + struct wl_resource *resource = NULL; + wl_resource_for_each (resource, &self->resource_list) { + wl_resource_set_implementation(resource, &implementation, NULL, NULL); + } + + wl_global_destroy(self->global); + wl_list_remove(&self->xr_system_session_state_changed_listener.link); + wl_list_remove(&self->events.destroy.listener_list); + free(self); +} diff --git a/zen/src/backend/immersive/gl/meson.build b/zen/src/backend/immersive/gl/meson.build new file mode 100644 index 00000000..20d8437d --- /dev/null +++ b/zen/src/backend/immersive/gl/meson.build @@ -0,0 +1,29 @@ +_zen_gl_immersive_backend_srcs = [ + 'client-gl-base-technique.c', + 'client-gl-buffer.c', + 'client-gl-program.c', + 'client-gl-rendering-unit.c', + 'client-gl-shader.c', + 'client-gl-texture.c', + 'client-gl-vertex-array.c', + 'gl-context.c', + + protocols_code['zwin-gl'], + protocols_server_header['zwin-gl'], +] + +_zen_gl_immersive_backend_deps = [ + _zen_deps, +] + +_zen_gl_immersive_backend_lib = static_library( + 'zen-gl-immersive-backend', + _zen_gl_immersive_backend_srcs, + install: false, + include_directories: zen_private_inc, + dependencies: _zen_gl_immersive_backend_deps, +) + +zen_gl_immersive_backend_dep = declare_dependency( + link_with: _zen_gl_immersive_backend_lib, +) diff --git a/zen/src/backend/immersive/remote/gl-base-technique.cc b/zen/src/backend/immersive/remote/gl-base-technique.cc new file mode 100644 index 00000000..5ee584ae --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-base-technique.cc @@ -0,0 +1,148 @@ +#include "gl-base-technique.hh" + +#include + +#include "gl-buffer.hh" +#include "gl-program.hh" +#include "gl-rendering-unit.hh" +#include "gl-vertex-array.hh" +#include "zen-common/log.h" + +namespace zen::backend::immersive::remote { + +const zn_gl_base_technique_interface GlBaseTechnique::c_implementation_ = { + GlBaseTechnique::HandleBindProgram, + GlBaseTechnique::HandleBindVertexArray, + GlBaseTechnique::HandleUniformVector, + GlBaseTechnique::HandleUniformMatrix, + GlBaseTechnique::HandleDrawArrays, + GlBaseTechnique::HandleDrawElements, +}; + +GlBaseTechnique::~GlBaseTechnique() +{ + if (c_obj_ != nullptr) { + zn_gl_base_technique_destroy(c_obj_); + } +} + +std::unique_ptr +GlBaseTechnique::New(std::shared_ptr channel, + std::unique_ptr &rendering_unit) +{ + auto self = std::unique_ptr(new GlBaseTechnique()); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init(std::move(channel), rendering_unit)) { + zn_error("Failed to initialize a remote GlBaseTechnique"); + return nullptr; + } + + return self; +} + +bool +GlBaseTechnique::Init(std::shared_ptr channel, + std::unique_ptr &rendering_unit) +{ + c_obj_ = zn_gl_base_technique_create(this, &c_implementation_); + if (c_obj_ == nullptr) { + zn_error("Failed to create a zn_gl_base_technique"); + return false; + } + + remote_obj_ = zen::remote::server::CreateGlBaseTechnique( + std::move(channel), rendering_unit->remote_obj()->id()); + if (!remote_obj_) { + zn_error("Failed to create a remote GlBaseTechnique"); + zn_gl_base_technique_destroy(c_obj_); + c_obj_ = nullptr; + return false; + } + + return true; +} + +void +GlBaseTechnique::HandleBindProgram( + struct zn_gl_base_technique *c_obj, struct zn_gl_program *gl_program_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto *gl_program = static_cast(gl_program_c_obj->impl_data); + + self->remote_obj_->BindProgram(gl_program->remote_obj()->id()); +} + +void +GlBaseTechnique::HandleBindVertexArray(struct zn_gl_base_technique *c_obj, + struct zn_gl_vertex_array *gl_vertex_array_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto *gl_vertex_array = + static_cast(gl_vertex_array_c_obj->impl_data); + + self->remote_obj_->BindVertexArray(gl_vertex_array->remote_obj()->id()); +} + +void +GlBaseTechnique::HandleUniformVector(struct zn_gl_base_technique *c_obj, + uint32_t location, const char *name, uint32_t type, uint32_t size, + uint32_t count, void *value) +{ + auto *self = static_cast(c_obj->impl_data); + + switch (static_cast(type)) { + case ZWN_GL_BASE_TECHNIQUE_UNIFORM_VARIABLE_TYPE_FLOAT: + self->remote_obj_->GlUniformVector( + location, name, size, count, static_cast(value)); + break; + case ZWN_GL_BASE_TECHNIQUE_UNIFORM_VARIABLE_TYPE_UINT: + self->remote_obj_->GlUniformVector( + location, name, size, count, static_cast(value)); + break; + case ZWN_GL_BASE_TECHNIQUE_UNIFORM_VARIABLE_TYPE_INT: + self->remote_obj_->GlUniformVector( + location, name, size, count, static_cast(value)); + break; + } +} + +void +GlBaseTechnique::HandleUniformMatrix(struct zn_gl_base_technique *c_obj, + uint32_t location, const char *name, uint32_t col, uint32_t row, + uint32_t count, bool transpose, void *value) +{ + auto *self = static_cast(c_obj->impl_data); + + self->remote_obj_->GlUniformMatrix( + location, name, col, row, count, transpose, static_cast(value)); +} + +void +GlBaseTechnique::HandleDrawArrays(struct zn_gl_base_technique *c_obj, + uint32_t mode, int32_t first, uint32_t count) +{ + auto *self = static_cast(c_obj->impl_data); + + self->remote_obj_->GlDrawArrays(mode, first, count); +} + +void +GlBaseTechnique::HandleDrawElements(struct zn_gl_base_technique *c_obj, + uint32_t mode, uint32_t count, uint32_t type, uint64_t offset, + struct zn_gl_buffer *gl_buffer_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto *gl_buffer = static_cast(gl_buffer_c_obj->impl_data); + + self->remote_obj_->GlDrawElements( + mode, count, type, offset, gl_buffer->remote_obj()->id()); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-base-technique.hh b/zen/src/backend/immersive/remote/gl-base-technique.hh new file mode 100644 index 00000000..557ee1e6 --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-base-technique.hh @@ -0,0 +1,69 @@ +#pragma once + +#include "gl-base-technique.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class GlRenderingUnit; + +class GlBaseTechnique +{ + public: + DISABLE_MOVE_AND_COPY(GlBaseTechnique); + ~GlBaseTechnique(); + + static std::unique_ptr New( + std::shared_ptr channel, + std::unique_ptr &rendering_unit); + + inline std::unique_ptr &remote_obj(); + inline zn_gl_base_technique *c_obj(); + + private: + GlBaseTechnique() = default; + + bool Init(std::shared_ptr channel, + std::unique_ptr &rendering_unit); + + static void HandleBindProgram(struct zn_gl_base_technique *c_obj, + struct zn_gl_program *gl_program_c_obj); + + static void HandleBindVertexArray(struct zn_gl_base_technique *c_obj, + struct zn_gl_vertex_array *gl_vertex_array_c_obj); + + static void HandleUniformVector(struct zn_gl_base_technique *c_obj, + uint32_t location, const char *name, uint32_t type, uint32_t size, + uint32_t count, void *value); + + static void HandleUniformMatrix(struct zn_gl_base_technique *c_obj, + uint32_t location, const char *name, uint32_t col, uint32_t row, + uint32_t count, bool transpose, void *value); + + static void HandleDrawArrays(struct zn_gl_base_technique *c_obj, + uint32_t mode, int32_t first, uint32_t count); + + static void HandleDrawElements(struct zn_gl_base_technique *c_obj, + uint32_t mode, uint32_t count, uint32_t type, uint64_t offset, + struct zn_gl_buffer *gl_buffer_c_obj); + + std::unique_ptr remote_obj_; + + static const zn_gl_base_technique_interface c_implementation_; + + zn_gl_base_technique *c_obj_ = nullptr; // @nonnull after Init(), @owning +}; + +inline zn_gl_base_technique * +GlBaseTechnique::c_obj() +{ + return c_obj_; +} + +inline std::unique_ptr & +GlBaseTechnique::remote_obj() +{ + return remote_obj_; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-buffer.cc b/zen/src/backend/immersive/remote/gl-buffer.cc new file mode 100644 index 00000000..30ea1797 --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-buffer.cc @@ -0,0 +1,86 @@ +#include "gl-buffer.hh" + +#include "loop.hh" +#include "zen-common/log.h" +#include "zen/buffer.h" + +namespace zen::backend::immersive::remote { + +const zn_gl_buffer_interface GlBuffer::c_implementation_ = { + GlBuffer::HandleData, +}; + +GlBuffer::~GlBuffer() +{ + if (c_obj_ != nullptr) { + zn_gl_buffer_destroy(c_obj_); + } +} + +GlBuffer::GlBuffer(wl_display *display) : display_(display) {} + +std::unique_ptr +GlBuffer::New( + std::shared_ptr channel, wl_display *display) +{ + auto self = std::unique_ptr(new GlBuffer(display)); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init(std::move(channel))) { + zn_error("Failed to initialize a remote GlBuffer"); + return nullptr; + } + + return self; +} + +bool +GlBuffer::Init(std::shared_ptr channel) +{ + c_obj_ = zn_gl_buffer_create(this, &c_implementation_); + if (c_obj_ == nullptr) { + zn_error("Failed to create a zn_gl_buffer"); + return false; + } + + remote_obj_ = zen::remote::server::CreateGlBuffer(std::move(channel)); + if (!remote_obj_) { + zn_error("Failed to create a remote GlBuffer"); + zn_gl_buffer_destroy(c_obj_); + c_obj_ = nullptr; + return false; + } + + return true; +} + +void +GlBuffer::HandleData(struct zn_gl_buffer *c_obj, uint32_t target, + struct zn_buffer *buffer, uint32_t usage) +{ + auto *self = static_cast(c_obj->impl_data); + + const ssize_t size = zn_buffer_get_size(buffer); + + auto loop = std::make_unique(wl_display_get_event_loop(self->display_)); + + auto remote_buffer = zen::remote::server::CreateBuffer( + [buffer]() { return zn_buffer_begin_access(buffer); }, + [buffer]() { return zn_buffer_end_access(buffer); }, + [buffer]() { zn_buffer_unref(buffer); }, std::move(loop)); + + if (!remote_buffer) { + zn_abort("Failed to create remote buffer"); + return; + } + + zn_buffer_ref(buffer); + + self->remote_obj_->GlBufferData( + std::move(remote_buffer), target, size, usage); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-buffer.hh b/zen/src/backend/immersive/remote/gl-buffer.hh new file mode 100644 index 00000000..45b4acc3 --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-buffer.hh @@ -0,0 +1,50 @@ +#pragma once + +#include "gl-buffer.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class GlBuffer +{ + public: + DISABLE_MOVE_AND_COPY(GlBuffer); + ~GlBuffer(); + + static std::unique_ptr New( + std::shared_ptr channel, + wl_display *display); + + inline std::unique_ptr &remote_obj(); + inline zn_gl_buffer *c_obj() const; + + private: + explicit GlBuffer(wl_display *display); + + bool Init(std::shared_ptr channel); + + static void HandleData(struct zn_gl_buffer *c_obj, uint32_t target, + struct zn_buffer *buffer, uint32_t usage); + + wl_display *display_; // @nonnull, @outlive + + std::unique_ptr remote_obj_; + + static const zn_gl_buffer_interface c_implementation_; + + zn_gl_buffer *c_obj_ = nullptr; // @nonnull after Init(), @owning +}; + +inline std::unique_ptr & +GlBuffer::remote_obj() +{ + return remote_obj_; +} + +inline zn_gl_buffer * +GlBuffer::c_obj() const +{ + return c_obj_; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-program.cc b/zen/src/backend/immersive/remote/gl-program.cc new file mode 100644 index 00000000..f4d6d336 --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-program.cc @@ -0,0 +1,76 @@ +#include "gl-program.hh" + +#include "gl-shader.hh" +#include "zen-common/log.h" + +namespace zen::backend::immersive::remote { + +const zn_gl_program_interface GlProgram::c_implementation_ = { + GlProgram::HandleAttachShader, + GlProgram::HandleLink, +}; + +GlProgram::~GlProgram() +{ + if (c_obj_ != nullptr) { + zn_gl_program_destroy(c_obj_); + } +} + +std::unique_ptr +GlProgram::New(std::shared_ptr channel) +{ + auto self = std::unique_ptr(new GlProgram()); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init(std::move(channel))) { + zn_error("Failed to initialize a remote GlProgram"); + return nullptr; + } + + return self; +} + +bool +GlProgram::Init(std::shared_ptr channel) +{ + c_obj_ = zn_gl_program_create(this, &c_implementation_); + if (c_obj_ == nullptr) { + zn_error("Failed to create a zn_gl_program"); + return false; + } + + remote_obj_ = zen::remote::server::CreateGlProgram(std::move(channel)); + if (!remote_obj_) { + zn_error("Failed to create a remote GlProgram"); + zn_gl_program_destroy(c_obj_); + c_obj_ = nullptr; + return false; + } + + return true; +} + +void +GlProgram::HandleAttachShader( + struct zn_gl_program *c_obj, struct zn_gl_shader *gl_shader_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto *gl_shader = static_cast(gl_shader_c_obj->impl_data); + + self->remote_obj_->GlAttachShader(gl_shader->remote_obj()->id()); +} + +void +GlProgram::HandleLink(struct zn_gl_program *c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + self->remote_obj_->GlLinkProgram(); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-program.hh b/zen/src/backend/immersive/remote/gl-program.hh new file mode 100644 index 00000000..19c1ee5e --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-program.hh @@ -0,0 +1,49 @@ +#pragma once + +#include "gl-program.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class GlProgram +{ + public: + DISABLE_MOVE_AND_COPY(GlProgram); + ~GlProgram(); + + static std::unique_ptr New( + std::shared_ptr channel); + + inline std::unique_ptr &remote_obj(); + inline zn_gl_program *c_obj() const; + + private: + GlProgram() = default; + + bool Init(std::shared_ptr channel); + + static void HandleAttachShader( + struct zn_gl_program *c_obj, struct zn_gl_shader *gl_shader_c_obj); + + static void HandleLink(struct zn_gl_program *c_obj); + + std::unique_ptr remote_obj_; + + static const zn_gl_program_interface c_implementation_; + + zn_gl_program *c_obj_ = nullptr; // @nonnull after Init(), @owning +}; + +inline std::unique_ptr & +GlProgram::remote_obj() +{ + return remote_obj_; +} + +inline zn_gl_program * +GlProgram::c_obj() const +{ + return c_obj_; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-rendering-unit.cc b/zen/src/backend/immersive/remote/gl-rendering-unit.cc new file mode 100644 index 00000000..c3eed47a --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-rendering-unit.cc @@ -0,0 +1,68 @@ +#include "gl-rendering-unit.hh" + +#include "gl-virtual-object.hh" +#include "zen-common/log.h" + +namespace zen::backend::immersive::remote { + +const zn_gl_rendering_unit_interface GlRenderingUnit::c_implementation_ = { + GlRenderingUnit::HandleChangeVisibility, +}; + +GlRenderingUnit::~GlRenderingUnit() +{ + if (c_obj_ != nullptr) { + zn_gl_rendering_unit_destroy(c_obj_); + } +} + +std::unique_ptr +GlRenderingUnit::New(std::shared_ptr channel, + std::unique_ptr &virtual_object) +{ + auto self = std::unique_ptr(new GlRenderingUnit()); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init(std::move(channel), virtual_object)) { + zn_error("Failed to initialize a remote GlRenderingUnit"); + return nullptr; + } + + return self; +} + +bool +GlRenderingUnit::Init(std::shared_ptr channel, + std::unique_ptr &virtual_object) +{ + c_obj_ = zn_gl_rendering_unit_create(this, &c_implementation_); + if (c_obj_ == nullptr) { + zn_error("Failed to create a zn_gl_rendering_unit"); + return false; + } + + remote_obj_ = zen::remote::server::CreateRenderingUnit( + std::move(channel), virtual_object->remote_obj()->id()); + if (!remote_obj_) { + zn_error("Failed to create a remote RenderingUnit"); + zn_gl_rendering_unit_destroy(c_obj_); + c_obj_ = nullptr; + return false; + } + + return true; +} + +void +GlRenderingUnit::HandleChangeVisibility( + zn_gl_rendering_unit *c_obj, bool visible) +{ + auto *self = static_cast(c_obj->impl_data); + + self->remote_obj_->ChangeVisibility(visible); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-rendering-unit.hh b/zen/src/backend/immersive/remote/gl-rendering-unit.hh new file mode 100644 index 00000000..e34d9dd4 --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-rendering-unit.hh @@ -0,0 +1,50 @@ +#pragma once + +#include "gl-rendering-unit.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class GlVirtualObject; + +class GlRenderingUnit +{ + public: + DISABLE_MOVE_AND_COPY(GlRenderingUnit); + ~GlRenderingUnit(); + + static std::unique_ptr New( + std::shared_ptr channel, + std::unique_ptr &virtual_object); + + inline std::unique_ptr &remote_obj(); + inline zn_gl_rendering_unit *c_obj(); + + private: + GlRenderingUnit() = default; + + bool Init(std::shared_ptr channel, + std::unique_ptr &virtual_object); + + static void HandleChangeVisibility(zn_gl_rendering_unit *c_obj, bool visible); + + std::unique_ptr remote_obj_; + + static const zn_gl_rendering_unit_interface c_implementation_; + + zn_gl_rendering_unit *c_obj_ = nullptr; // @nonnull after Init(), @owning +}; + +inline zn_gl_rendering_unit * +GlRenderingUnit::c_obj() +{ + return c_obj_; +} + +inline std::unique_ptr & +GlRenderingUnit::remote_obj() +{ + return remote_obj_; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-shader.cc b/zen/src/backend/immersive/remote/gl-shader.cc new file mode 100644 index 00000000..436a7a1a --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-shader.cc @@ -0,0 +1,55 @@ +#include "gl-shader.hh" + +#include "zen-common/log.h" + +namespace zen::backend::immersive::remote { + +GlShader::~GlShader() +{ + if (c_obj_ != nullptr) { + zn_gl_shader_destroy(c_obj_); + } +} + +std::unique_ptr +GlShader::New(std::shared_ptr channel, + std::string source, uint32_t type) + +{ + auto self = std::unique_ptr(new GlShader()); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init(std::move(channel), std::move(source), type)) { + zn_error("Failed to initialize a remote GlShader"); + return nullptr; + } + + return self; +} + +bool +GlShader::Init(std::shared_ptr channel, + std::string source, uint32_t type) +{ + c_obj_ = zn_gl_shader_create(this); + if (c_obj_ == nullptr) { + zn_error("Failed to create a zn_gl_shader"); + return false; + } + + remote_obj_ = zen::remote::server::CreateGlShader( + std::move(channel), std::move(source), type); + if (!remote_obj_) { + zn_error("Failed to create a remote GlShader"); + zn_gl_shader_destroy(c_obj_); + c_obj_ = nullptr; + return false; + } + + return true; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-shader.hh b/zen/src/backend/immersive/remote/gl-shader.hh new file mode 100644 index 00000000..ad17bf5a --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-shader.hh @@ -0,0 +1,44 @@ +#pragma once + +#include "gl-shader.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class GlShader +{ + public: + DISABLE_MOVE_AND_COPY(GlShader); + ~GlShader(); + + static std::unique_ptr New( + std::shared_ptr channel, + std::string source, uint32_t type); + + inline std::unique_ptr &remote_obj(); + inline zn_gl_shader *c_obj() const; + + private: + GlShader() = default; + + bool Init(std::shared_ptr channel, + std::string source, uint32_t type); + + std::unique_ptr remote_obj_; + + zn_gl_shader *c_obj_ = nullptr; // @nonnull after Init(), @owning +}; + +inline std::unique_ptr & +GlShader::remote_obj() +{ + return remote_obj_; +} + +inline zn_gl_shader * +GlShader::c_obj() const +{ + return c_obj_; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-texture.cc b/zen/src/backend/immersive/remote/gl-texture.cc new file mode 100644 index 00000000..c1fd2997 --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-texture.cc @@ -0,0 +1,94 @@ +#include "gl-texture.hh" + +#include "loop.hh" +#include "zen-common/log.h" +#include "zen/buffer.h" + +namespace zen::backend::immersive::remote { + +const zn_gl_texture_interface GlTexture::c_implementation_ = { + GlTexture::HandleImage2D, + GlTexture::HandleGenerateMipmap, +}; + +GlTexture::~GlTexture() +{ + if (c_obj_ != nullptr) { + zn_gl_texture_destroy(c_obj_); + } +} + +GlTexture::GlTexture(wl_display *display) : display_(display) {} + +std::unique_ptr +GlTexture::New( + std::shared_ptr channel, wl_display *display) +{ + auto self = std::unique_ptr(new GlTexture(display)); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init(std::move(channel))) { + zn_error("Failed to initialize a remote GlTexture"); + return nullptr; + } + + return self; +} + +bool +GlTexture::Init(std::shared_ptr channel) +{ + c_obj_ = zn_gl_texture_create(this, &c_implementation_); + if (c_obj_ == nullptr) { + zn_error("Failed to create a zn_gl_texture"); + return false; + } + + remote_obj_ = zen::remote::server::CreateGlTexture(std::move(channel)); + if (!remote_obj_) { + zn_error("Failed to create a remote GlTexture"); + zn_gl_texture_destroy(c_obj_); + c_obj_ = nullptr; + return false; + } + + return true; +} + +void +GlTexture::HandleImage2D(struct zn_gl_texture *c_obj, uint32_t target, + int32_t level, int32_t internal_format, uint32_t width, uint32_t height, + int32_t border, uint32_t format, uint32_t type, struct zn_buffer *data) +{ + auto *self = static_cast(c_obj->impl_data); + + auto loop = std::make_unique(wl_display_get_event_loop(self->display_)); + + auto remote_buffer = zen::remote::server::CreateBuffer( + [data]() { return zn_buffer_begin_access(data); }, + [data]() { return zn_buffer_end_access(data); }, + [data]() { zn_buffer_unref(data); }, std::move(loop)); + + if (!remote_buffer) { + zn_abort("Failed to create remote buffer"); + return; + } + + zn_buffer_ref(data); + + self->remote_obj_->GlTexImage2D(target, level, internal_format, width, height, + border, format, type, std::move(remote_buffer)); +} + +void +GlTexture::HandleGenerateMipmap(struct zn_gl_texture *c_obj, uint32_t target) +{ + auto *self = static_cast(c_obj->impl_data); + + self->remote_obj_->GlGenerateMipmap(target); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-texture.hh b/zen/src/backend/immersive/remote/gl-texture.hh new file mode 100644 index 00000000..d1fca51a --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-texture.hh @@ -0,0 +1,54 @@ +#pragma once + +#include "gl-texture.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class GlTexture +{ + public: + DISABLE_MOVE_AND_COPY(GlTexture); + ~GlTexture(); + + static std::unique_ptr New( + std::shared_ptr channel, + wl_display *display); + + inline std::unique_ptr &remote_obj(); + inline zn_gl_texture *c_obj() const; + + private: + explicit GlTexture(wl_display *display); + + bool Init(std::shared_ptr channel); + + static void HandleImage2D(struct zn_gl_texture *c_obj, uint32_t target, + int32_t level, int32_t internal_format, uint32_t width, uint32_t height, + int32_t border, uint32_t format, uint32_t type, struct zn_buffer *data); + + static void HandleGenerateMipmap( + struct zn_gl_texture *c_obj, uint32_t target); + + wl_display *display_; // @nonnull, @outlive + + std::unique_ptr remote_obj_; + + static const zn_gl_texture_interface c_implementation_; + + zn_gl_texture *c_obj_ = nullptr; // @nonnull after Init(), @owning +}; + +inline std::unique_ptr & +GlTexture::remote_obj() +{ + return remote_obj_; +} + +inline zn_gl_texture * +GlTexture::c_obj() const +{ + return c_obj_; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-vertex-array.cc b/zen/src/backend/immersive/remote/gl-vertex-array.cc new file mode 100644 index 00000000..07245027 --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-vertex-array.cc @@ -0,0 +1,89 @@ +#include "gl-vertex-array.hh" + +#include "gl-buffer.hh" +#include "zen-common/log.h" + +namespace zen::backend::immersive::remote { + +const zn_gl_vertex_array_interface GlVertexArray::c_implementation_ = { + GlVertexArray::HandleEnableVertexAttribArray, + GlVertexArray::HandleDisableVertexAttribArray, + GlVertexArray::HandleVertexAttribPointer, +}; + +GlVertexArray::~GlVertexArray() +{ + if (c_obj_ != nullptr) { + zn_gl_vertex_array_destroy(c_obj_); + } +} + +std::unique_ptr +GlVertexArray::New(std::shared_ptr channel) +{ + auto self = std::unique_ptr(new GlVertexArray()); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init(std::move(channel))) { + zn_error("Failed to initialize a remote GlVertexArray"); + return nullptr; + } + + return self; +} + +bool +GlVertexArray::Init(std::shared_ptr channel) +{ + c_obj_ = zn_gl_vertex_array_create(this, &c_implementation_); + if (c_obj_ == nullptr) { + zn_error("Failed to create a zn_gl_vertex_array"); + return false; + } + + remote_obj_ = zen::remote::server::CreateGlVertexArray(std::move(channel)); + if (!remote_obj_) { + zn_error("Failed to create a remote GlVertexArray"); + zn_gl_vertex_array_destroy(c_obj_); + c_obj_ = nullptr; + return false; + } + + return true; +} + +void +GlVertexArray::HandleEnableVertexAttribArray( + struct zn_gl_vertex_array *c_obj, uint32_t index) +{ + auto *self = static_cast(c_obj->impl_data); + + self->remote_obj_->GlEnableVertexAttribArray(index); +} + +void +GlVertexArray::HandleDisableVertexAttribArray( + struct zn_gl_vertex_array *c_obj, uint32_t index) +{ + auto *self = static_cast(c_obj->impl_data); + + self->remote_obj_->GlDisableVertexAttribArray(index); +} + +void +GlVertexArray::HandleVertexAttribPointer(struct zn_gl_vertex_array *c_obj, + uint32_t index, int32_t size, uint32_t type, bool normalized, + int32_t stride, uint64_t offset, zn_gl_buffer *gl_buffer_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto *gl_buffer = static_cast(gl_buffer_c_obj->impl_data); + + self->remote_obj_->GlVertexAttribPointer(index, size, type, normalized, + stride, offset, gl_buffer->remote_obj()->id()); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-vertex-array.hh b/zen/src/backend/immersive/remote/gl-vertex-array.hh new file mode 100644 index 00000000..206157e1 --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-vertex-array.hh @@ -0,0 +1,54 @@ +#pragma once + +#include "gl-vertex-array.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class GlVertexArray +{ + public: + DISABLE_MOVE_AND_COPY(GlVertexArray); + ~GlVertexArray(); + + static std::unique_ptr New( + std::shared_ptr channel); + + inline std::unique_ptr &remote_obj(); + inline zn_gl_vertex_array *c_obj() const; + + private: + GlVertexArray() = default; + + bool Init(std::shared_ptr channel); + + static void HandleEnableVertexAttribArray( + struct zn_gl_vertex_array *c_obj, uint32_t index); + + static void HandleDisableVertexAttribArray( + struct zn_gl_vertex_array *c_obj, uint32_t index); + + static void HandleVertexAttribPointer(struct zn_gl_vertex_array *c_obj, + uint32_t index, int32_t size, uint32_t type, bool normalized, + int32_t stride, uint64_t offset, zn_gl_buffer *gl_buffer_c_obj); + + std::unique_ptr remote_obj_; + + static const zn_gl_vertex_array_interface c_implementation_; + + zn_gl_vertex_array *c_obj_ = nullptr; // @nonnull after Init(), @owning +}; + +inline std::unique_ptr & +GlVertexArray::remote_obj() +{ + return remote_obj_; +} + +inline zn_gl_vertex_array * +GlVertexArray::c_obj() const +{ + return c_obj_; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-virtual-object.cc b/zen/src/backend/immersive/remote/gl-virtual-object.cc new file mode 100644 index 00000000..d313a568 --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-virtual-object.cc @@ -0,0 +1,76 @@ +#include "gl-virtual-object.hh" + +#include "zen-common/log.h" + +namespace zen::backend::immersive::remote { + +const zn_gl_virtual_object_interface GlVirtualObject::c_implementation_ = { + GlVirtualObject::HandleCommitted, + GlVirtualObject::HandleChangeVisibility, +}; + +GlVirtualObject::~GlVirtualObject() +{ + if (c_obj_ != nullptr) { + zn_gl_virtual_object_destroy(c_obj_); + } +} + +std::unique_ptr +GlVirtualObject::New(std::shared_ptr channel, + struct zn_xr_dispatcher *xr_dispatcher_c_obj) +{ + auto self = std::unique_ptr(new GlVirtualObject()); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init(std::move(channel), xr_dispatcher_c_obj)) { + zn_error("Failed to initialize a remote VirtualObject"); + return nullptr; + } + + return self; +} + +bool +GlVirtualObject::Init(std::shared_ptr channel, + struct zn_xr_dispatcher *xr_dispatcher_c_obj) +{ + c_obj_ = zn_gl_virtual_object_create( + this, &c_implementation_, xr_dispatcher_c_obj); + if (c_obj_ == nullptr) { + zn_error("Failed to create a zn_virtual_object"); + return false; + } + + remote_obj_ = zen::remote::server::CreateVirtualObject(std::move(channel)); + if (!remote_obj_) { + zn_error("Failed to create a remote Virtual Object"); + zn_gl_virtual_object_destroy(c_obj_); + c_obj_ = nullptr; + return false; + } + + return true; +} + +void +GlVirtualObject::HandleCommitted(zn_gl_virtual_object *c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + self->remote_obj_->Commit(); +} + +void +GlVirtualObject::HandleChangeVisibility( + zn_gl_virtual_object *c_obj, bool visible) +{ + auto *self = static_cast(c_obj->impl_data); + + self->remote_obj_->ChangeVisibility(visible); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/gl-virtual-object.hh b/zen/src/backend/immersive/remote/gl-virtual-object.hh new file mode 100644 index 00000000..d81dd01b --- /dev/null +++ b/zen/src/backend/immersive/remote/gl-virtual-object.hh @@ -0,0 +1,50 @@ +#pragma once + +#include "gl-virtual-object.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class GlVirtualObject +{ + public: + DISABLE_MOVE_AND_COPY(GlVirtualObject); + ~GlVirtualObject(); + + static std::unique_ptr New( + std::shared_ptr channel, + struct zn_xr_dispatcher *xr_dispatcher_c_obj); + + inline std::unique_ptr &remote_obj(); + inline zn_gl_virtual_object *c_obj(); + + private: + GlVirtualObject() = default; + + bool Init(std::shared_ptr channel, + struct zn_xr_dispatcher *xr_dispatcher_c_obj); + + static void HandleCommitted(zn_gl_virtual_object *c_obj); + + static void HandleChangeVisibility(zn_gl_virtual_object *c_obj, bool visible); + + std::unique_ptr remote_obj_; + + static const zn_gl_virtual_object_interface c_implementation_; + + zn_gl_virtual_object *c_obj_ = nullptr; // @nonnull after Init(), @owning +}; + +inline zn_gl_virtual_object * +GlVirtualObject::c_obj() +{ + return c_obj_; +} + +inline std::unique_ptr & +GlVirtualObject::remote_obj() +{ + return remote_obj_; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/log.hh b/zen/src/backend/immersive/remote/log.hh new file mode 100644 index 00000000..4185cab2 --- /dev/null +++ b/zen/src/backend/immersive/remote/log.hh @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include + +#include "zen-common/log.h" + +namespace zen::backend::immersive::remote { + +class LogSink : public zen::remote::ILogSink +{ + void Sink(zen::remote::Severity severity, const char * /*pretty_function*/, + const char *file, int line, const char *format, va_list vp) override + { + zn_log_importance_t importance = ZEN_SILENT; + switch (severity) { + case zen::remote::Severity::DEBUG: + importance = ZEN_DEBUG; + break; + case zen::remote::Severity::INFO: + importance = ZEN_INFO; + break; + case zen::remote::Severity::WARN: + importance = ZEN_WARN; + break; + case zen::remote::Severity::ERROR: + importance = ZEN_ERROR; + break; + case zen::remote::Severity::FATAL: + importance = ZEN_ERROR; + break; + default: + break; + } + + // NOLINTNEXTLINE(cert-err33-c) + std::snprintf( + buffer_.data(), buffer_.size(), "[znr] [%s:%d] %s", file, line, format); + + zn_vlog_(importance, buffer_.data(), vp); + } + + std::array buffer_; +}; + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/loop.cc b/zen/src/backend/immersive/remote/loop.cc new file mode 100644 index 00000000..88eea0f8 --- /dev/null +++ b/zen/src/backend/immersive/remote/loop.cc @@ -0,0 +1,77 @@ +#include "loop.hh" + +#include "zen-common/terminate.h" + +namespace zen::backend::immersive::remote { + +namespace { + +int +loop_callback(int fd, uint32_t mask, void *data) +{ + auto *source = static_cast(data); + + uint32_t remote_mask = 0; + + if ((mask & WL_EVENT_READABLE) == WL_EVENT_READABLE) { + remote_mask |= zen::remote::FdSource::kReadable; + } + + if ((mask & WL_EVENT_WRITABLE) == WL_EVENT_WRITABLE) { + remote_mask |= zen::remote::FdSource::kWritable; + } + + if ((mask & WL_EVENT_HANGUP) == WL_EVENT_HANGUP) { + remote_mask |= zen::remote::FdSource::kHangup; + } + + if ((mask & WL_EVENT_ERROR) == WL_EVENT_ERROR) { + remote_mask |= zen::remote::FdSource::kError; + } + + source->callback(fd, remote_mask); + + return 1; +} + +} // namespace + +Loop::Loop(wl_event_loop *wl_loop) : wl_loop_(wl_loop) {} + +void +Loop::AddFd(zen::remote::FdSource *source) +{ + using FdSource = zen::remote::FdSource; + + uint32_t mask = 0; + + if ((source->mask & FdSource::kReadable) == FdSource::kReadable) { + mask |= WL_EVENT_READABLE; + } + if ((source->mask & FdSource::kWritable) == FdSource::kWritable) { + mask |= WL_EVENT_WRITABLE; + } + if ((source->mask & FdSource::kHangup) == FdSource::kHangup) { + mask |= WL_EVENT_HANGUP; + } + if ((source->mask & FdSource::kError) == FdSource::kError) { + mask |= WL_EVENT_ERROR; + } + + source->data = + wl_event_loop_add_fd(wl_loop_, source->fd, mask, loop_callback, source); +} + +void +Loop::RemoveFd(zen::remote::FdSource *source) +{ + wl_event_source_remove(static_cast(source->data)); +} + +void +Loop::Terminate() +{ + zn_terminate(EXIT_FAILURE); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/loop.hh b/zen/src/backend/immersive/remote/loop.hh new file mode 100644 index 00000000..481ba302 --- /dev/null +++ b/zen/src/backend/immersive/remote/loop.hh @@ -0,0 +1,22 @@ +#pragma once + +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class Loop : public zen::remote::ILoop +{ + public: + DISABLE_MOVE_AND_COPY(Loop); + explicit Loop(wl_event_loop *wl_loop); + ~Loop() override = default; + + void AddFd(zen::remote::FdSource *source) override; + void RemoveFd(zen::remote::FdSource *source) override; + void Terminate() override; + + private: + wl_event_loop *wl_loop_; // @nonnull, @outlive +}; + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/meson.build b/zen/src/backend/immersive/remote/meson.build new file mode 100644 index 00000000..d90a4044 --- /dev/null +++ b/zen/src/backend/immersive/remote/meson.build @@ -0,0 +1,33 @@ +_zen_remote_immersive_backend_srcs = [ + 'gl-base-technique.cc', + 'gl-buffer.cc', + 'gl-program.cc', + 'gl-rendering-unit.cc', + 'gl-shader.cc', + 'gl-texture.cc', + 'gl-vertex-array.cc', + 'gl-virtual-object.cc', + 'loop.cc', + 'xr-dispatcher.cc', + 'xr-system.cc', + 'xr-system-manager.cc', +] + +_zen_remote_immersive_backend_deps = [ + _zen_deps, + zen_remote_server_dep, +] + +_zen_remote_immersive_backend_lib = static_library( + 'zen-remote-immersive-backend', + _zen_remote_immersive_backend_srcs, + install: false, + cpp_args: [ '-include', 'pch/pch.h' ], + include_directories: zen_private_inc, + dependencies: _zen_remote_immersive_backend_deps, + cpp_pch: 'pch/pch.h', +) + +zen_remote_immersive_backend_dep = declare_dependency( + link_with: _zen_remote_immersive_backend_lib, +) diff --git a/zen/src/backend/immersive/remote/pch/pch.h b/zen/src/backend/immersive/remote/pch/pch.h new file mode 100644 index 00000000..097f018e --- /dev/null +++ b/zen/src/backend/immersive/remote/pch/pch.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include diff --git a/zen/src/backend/immersive/remote/xr-dispatcher.cc b/zen/src/backend/immersive/remote/xr-dispatcher.cc new file mode 100644 index 00000000..45a66954 --- /dev/null +++ b/zen/src/backend/immersive/remote/xr-dispatcher.cc @@ -0,0 +1,373 @@ +#include "xr-dispatcher.hh" + +#include + +#include "gl-base-technique.hh" +#include "gl-buffer.hh" +#include "gl-program.hh" +#include "gl-rendering-unit.hh" +#include "gl-shader.hh" +#include "gl-texture.hh" +#include "gl-vertex-array.hh" +#include "gl-virtual-object.hh" +#include "zen-common/log.h" +#include "zen/buffer.h" + +namespace zen::backend::immersive::remote { + +const zn_xr_dispatcher_interface XrDispatcher::c_implementation_ = { + XrDispatcher::HandleGetNewGlVirtualObject, + XrDispatcher::HandleDestroyGlVirtualObject, + XrDispatcher::HandleGetNewGlRenderingUnit, + XrDispatcher::HandleDestroyGlRenderingUnit, + XrDispatcher::HandleGetNewGlBaseTechnique, + XrDispatcher::HandleDestroyGlBaseTechnique, + XrDispatcher::HandleGetNewGlBuffer, + XrDispatcher::HandleDestroyGlBuffer, + XrDispatcher::HandleGetNewGlShader, + XrDispatcher::HandleDestroyGlShader, + XrDispatcher::HandleGetNewGlProgram, + XrDispatcher::HandleDestroyGlProgram, + XrDispatcher::HandleGetNewGlTexture, + XrDispatcher::HandleDestroyGlTexture, + XrDispatcher::HandleGetNewGlVertexArray, + XrDispatcher::HandleDestroyGlVertexArray, +}; + +XrDispatcher::XrDispatcher(wl_display *display) : display_(display) {} + +XrDispatcher::~XrDispatcher() +{ + if (c_obj_ != nullptr) { + zn_xr_dispatcher_destroy(c_obj_); + } +} + +std::unique_ptr +XrDispatcher::New( + std::shared_ptr session, wl_display *display) +{ + auto self = std::unique_ptr(new XrDispatcher(display)); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init(std::move(session))) { + zn_error("Failed to initialize remote XrDispatcher"); + return nullptr; + } + + return self; +} + +bool +XrDispatcher::Init(std::shared_ptr session) +{ + c_obj_ = zn_xr_dispatcher_create(this, &c_implementation_); + if (c_obj_ == nullptr) { + zn_error("Failed to create zn_xr_dispatcher"); + return false; + } + + channel_ = zen::remote::server::CreateChannel(session); + if (!channel_) { + zn_error("Failed to create a remote channel"); + zn_xr_dispatcher_destroy(c_obj_); + c_obj_ = nullptr; + return false; + } + + return true; +} + +zn_gl_virtual_object * +XrDispatcher::HandleGetNewGlVirtualObject(zn_xr_dispatcher *c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto virtual_object = GlVirtualObject::New(self->channel_, c_obj); + if (!virtual_object) { + zn_error("Failed to create a remote virtual object"); + return nullptr; + } + + auto *virtual_object_c_obj = virtual_object->c_obj(); + + self->virtual_objects_.push_back(std::move(virtual_object)); + + return virtual_object_c_obj; +} + +void +XrDispatcher::HandleDestroyGlVirtualObject( + zn_xr_dispatcher *c_obj, zn_gl_virtual_object *gl_virtual_object_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto result = std::remove_if(self->virtual_objects_.begin(), + self->virtual_objects_.end(), + [gl_virtual_object_c_obj]( + std::unique_ptr &gl_virtual_object) { + return gl_virtual_object->c_obj() == gl_virtual_object_c_obj; + }); + + self->virtual_objects_.erase(result, self->virtual_objects_.end()); +} + +zn_gl_rendering_unit * +XrDispatcher::HandleGetNewGlRenderingUnit( + zn_xr_dispatcher *c_obj, zn_gl_virtual_object *gl_virtual_object_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + for (auto &virtual_object : self->virtual_objects_) { + if (virtual_object->c_obj() != gl_virtual_object_c_obj) { + continue; + } + + auto gl_rendering_unit = + GlRenderingUnit::New(self->channel_, virtual_object); + + auto *gl_rendering_unit_c_obj = gl_rendering_unit->c_obj(); + + self->gl_rendering_units_.push_back(std::move(gl_rendering_unit)); + + return gl_rendering_unit_c_obj; + } + + return nullptr; +} + +void +XrDispatcher::HandleDestroyGlRenderingUnit( + zn_xr_dispatcher *c_obj, zn_gl_rendering_unit *gl_rendering_unit_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto result = std::remove_if(self->gl_rendering_units_.begin(), + self->gl_rendering_units_.end(), + [gl_rendering_unit_c_obj]( + std::unique_ptr &gl_rendering_unit) { + return gl_rendering_unit->c_obj() == gl_rendering_unit_c_obj; + }); + + self->gl_rendering_units_.erase(result, self->gl_rendering_units_.end()); +} + +zn_gl_base_technique * +XrDispatcher::HandleGetNewGlBaseTechnique( + zn_xr_dispatcher *c_obj, zn_gl_rendering_unit *gl_rendering_unit_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + for (auto &rendering_unit : self->gl_rendering_units_) { + if (rendering_unit->c_obj() != gl_rendering_unit_c_obj) { + continue; + } + + auto gl_base_technique = + GlBaseTechnique::New(self->channel_, rendering_unit); + + auto *gl_base_technique_c_obj = gl_base_technique->c_obj(); + + self->gl_base_techniques_.push_back(std::move(gl_base_technique)); + + return gl_base_technique_c_obj; + } + + return nullptr; +} + +void +XrDispatcher::HandleDestroyGlBaseTechnique( + zn_xr_dispatcher *c_obj, zn_gl_base_technique *gl_base_technique_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto result = std::remove_if(self->gl_base_techniques_.begin(), + self->gl_base_techniques_.end(), + [gl_base_technique_c_obj]( + std::unique_ptr &gl_base_technique) { + return gl_base_technique->c_obj() == gl_base_technique_c_obj; + }); + + self->gl_base_techniques_.erase(result, self->gl_base_techniques_.end()); +} + +zn_gl_buffer * +XrDispatcher::HandleGetNewGlBuffer(zn_xr_dispatcher *c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto gl_buffer = GlBuffer::New(self->channel_, self->display_); + if (!gl_buffer) { + zn_error("Failed to create a remote gl_buffer"); + return nullptr; + } + + auto *gl_buffer_c_obj = gl_buffer->c_obj(); + + self->gl_buffers_.push_back(std::move(gl_buffer)); + + return gl_buffer_c_obj; +} + +void +XrDispatcher::HandleDestroyGlBuffer( + zn_xr_dispatcher *c_obj, struct zn_gl_buffer *gl_buffer_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto result = + std::remove_if(self->gl_buffers_.begin(), self->gl_buffers_.end(), + [gl_buffer_c_obj](std::unique_ptr &gl_buffer) { + return gl_buffer->c_obj() == gl_buffer_c_obj; + }); + + self->gl_buffers_.erase(result, self->gl_buffers_.end()); +} + +zn_gl_shader * +XrDispatcher::HandleGetNewGlShader( + struct zn_xr_dispatcher *c_obj, struct zn_buffer *buffer, uint32_t type) +{ + auto *self = static_cast(c_obj->impl_data); + + std::string source; + + { + const ssize_t length = zn_buffer_get_size(buffer); + auto *data = zn_buffer_begin_access(buffer); + source = std::string(static_cast(data), length); + zn_buffer_end_access(buffer); + } + + auto gl_shader = GlShader::New(self->channel_, std::move(source), type); + if (!gl_shader) { + zn_error("Failed to create a remote gl_shader"); + return nullptr; + } + + auto *gl_shader_c_obj = gl_shader->c_obj(); + + self->gl_shaders_.push_back(std::move(gl_shader)); + + return gl_shader_c_obj; +} + +void +XrDispatcher::HandleDestroyGlShader( + struct zn_xr_dispatcher *c_obj, struct zn_gl_shader *gl_shader_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto result = + std::remove_if(self->gl_shaders_.begin(), self->gl_shaders_.end(), + [gl_shader_c_obj](std::unique_ptr &gl_shader) { + return gl_shader->c_obj() == gl_shader_c_obj; + }); + + self->gl_shaders_.erase(result, self->gl_shaders_.end()); +} + +zn_gl_program * +XrDispatcher::HandleGetNewGlProgram(struct zn_xr_dispatcher *c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto gl_program = GlProgram::New(self->channel_); + if (!gl_program) { + zn_error("Failed to create a remote gl_program"); + return nullptr; + } + + auto *gl_program_c_obj = gl_program->c_obj(); + + self->gl_programs_.push_back(std::move(gl_program)); + + return gl_program_c_obj; +} + +void +XrDispatcher::HandleDestroyGlProgram( + struct zn_xr_dispatcher *c_obj, struct zn_gl_program *gl_program_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto result = + std::remove_if(self->gl_programs_.begin(), self->gl_programs_.end(), + [gl_program_c_obj](std::unique_ptr &gl_program) { + return gl_program->c_obj() == gl_program_c_obj; + }); + + self->gl_programs_.erase(result, self->gl_programs_.end()); +} + +zn_gl_texture * +XrDispatcher::HandleGetNewGlTexture(struct zn_xr_dispatcher *c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto gl_texture = GlTexture::New(self->channel_, self->display_); + if (!gl_texture) { + zn_error("Failed to create a remote gl_texture"); + return nullptr; + } + + auto *gl_texture_c_obj = gl_texture->c_obj(); + + self->gl_textures_.push_back(std::move(gl_texture)); + + return gl_texture_c_obj; +} + +void +XrDispatcher::HandleDestroyGlTexture( + struct zn_xr_dispatcher *c_obj, struct zn_gl_texture *gl_texture_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto result = + std::remove_if(self->gl_textures_.begin(), self->gl_textures_.end(), + [gl_texture_c_obj](std::unique_ptr &gl_texture) { + return gl_texture->c_obj() == gl_texture_c_obj; + }); + + self->gl_textures_.erase(result, self->gl_textures_.end()); +} + +zn_gl_vertex_array * +XrDispatcher::HandleGetNewGlVertexArray(struct zn_xr_dispatcher *c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto gl_vertex_array = GlVertexArray::New(self->channel_); + if (!gl_vertex_array) { + zn_error("Failed to create a remote gl_vertex_array"); + return nullptr; + } + + auto *gl_vertex_array_c_obj = gl_vertex_array->c_obj(); + + self->gl_vertex_arrays_.push_back(std::move(gl_vertex_array)); + + return gl_vertex_array_c_obj; +} + +void +XrDispatcher::HandleDestroyGlVertexArray(struct zn_xr_dispatcher *c_obj, + struct zn_gl_vertex_array *gl_vertex_array_c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + auto result = std::remove_if(self->gl_vertex_arrays_.begin(), + self->gl_vertex_arrays_.end(), + [gl_vertex_array_c_obj](std::unique_ptr &gl_vertex_array) { + return gl_vertex_array->c_obj() == gl_vertex_array_c_obj; + }); + + self->gl_vertex_arrays_.erase(result, self->gl_vertex_arrays_.end()); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/xr-dispatcher.hh b/zen/src/backend/immersive/remote/xr-dispatcher.hh new file mode 100644 index 00000000..4062e51e --- /dev/null +++ b/zen/src/backend/immersive/remote/xr-dispatcher.hh @@ -0,0 +1,110 @@ +#pragma once + +#include "xr-dispatcher.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class GlBaseTechnique; +class GlBuffer; +class GlProgram; +class GlRenderingUnit; +class GlShader; +class GlTexture; +class GlVertexArray; +class GlVirtualObject; + +class XrDispatcher +{ + public: + DISABLE_MOVE_AND_COPY(XrDispatcher); + ~XrDispatcher(); + + static std::unique_ptr New( + std::shared_ptr session, + wl_display *display); + + inline zn_xr_dispatcher *c_obj(); + + private: + explicit XrDispatcher(wl_display *display); + + bool Init(std::shared_ptr session); + + static zn_gl_virtual_object *HandleGetNewGlVirtualObject( + zn_xr_dispatcher *c_obj); + + static void HandleDestroyGlVirtualObject( + zn_xr_dispatcher *c_obj, zn_gl_virtual_object *gl_virtual_object_c_obj); + + static zn_gl_rendering_unit *HandleGetNewGlRenderingUnit( + zn_xr_dispatcher *c_obj, zn_gl_virtual_object *gl_virtual_object_c_obj); + + static void HandleDestroyGlRenderingUnit( + zn_xr_dispatcher *c_obj, zn_gl_rendering_unit *gl_rendering_unit_c_obj); + + static zn_gl_base_technique *HandleGetNewGlBaseTechnique( + zn_xr_dispatcher *c_obj, zn_gl_rendering_unit *gl_rendering_unit_c_obj); + + static void HandleDestroyGlBaseTechnique( + zn_xr_dispatcher *c_obj, zn_gl_base_technique *gl_base_technique_c_obj); + + static zn_gl_buffer *HandleGetNewGlBuffer(zn_xr_dispatcher *c_obj); + + static void HandleDestroyGlBuffer( + zn_xr_dispatcher *c_obj, struct zn_gl_buffer *gl_buffer_c_obj); + + static zn_gl_shader *HandleGetNewGlShader( + struct zn_xr_dispatcher *c_obj, struct zn_buffer *buffer, uint32_t type); + + static void HandleDestroyGlShader( + struct zn_xr_dispatcher *c_obj, struct zn_gl_shader *gl_shader_c_obj); + + static zn_gl_program *HandleGetNewGlProgram(struct zn_xr_dispatcher *c_obj); + + static void HandleDestroyGlProgram( + struct zn_xr_dispatcher *c_obj, struct zn_gl_program *gl_program_c_obj); + + static zn_gl_texture *HandleGetNewGlTexture(struct zn_xr_dispatcher *c_obj); + + static void HandleDestroyGlTexture( + struct zn_xr_dispatcher *c_obj, struct zn_gl_texture *gl_texture_c_obj); + + static zn_gl_vertex_array *HandleGetNewGlVertexArray( + struct zn_xr_dispatcher *c_obj); + + static void HandleDestroyGlVertexArray(struct zn_xr_dispatcher *c_obj, + struct zn_gl_vertex_array *gl_vertex_array_c_obj); + + wl_display *display_; // @nonnull, @outlive + + std::shared_ptr channel_; + + std::vector> virtual_objects_; + + std::vector> gl_rendering_units_; + + std::vector> gl_base_techniques_; + + std::vector> gl_buffers_; + + std::vector> gl_shaders_; + + std::vector> gl_programs_; + + std::vector> gl_textures_; + + std::vector> gl_vertex_arrays_; + + static const zn_xr_dispatcher_interface c_implementation_; + + zn_xr_dispatcher *c_obj_ = nullptr; // @nonnull after Init(), @owning +}; + +inline zn_xr_dispatcher * +XrDispatcher::c_obj() +{ + return c_obj_; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/xr-system-manager.cc b/zen/src/backend/immersive/remote/xr-system-manager.cc new file mode 100644 index 00000000..d7fb20f6 --- /dev/null +++ b/zen/src/backend/immersive/remote/xr-system-manager.cc @@ -0,0 +1,129 @@ +#include "xr-system-manager.hh" + +#include "log.hh" +#include "loop.hh" +#include "xr-system.hh" +#include "zen-common/log.h" + +static void zn_xr_system_manager_destroy_remote( + struct zn_xr_system_manager *c_obj); + +namespace zen::backend::immersive::remote { + +XrSystemManager::XrSystemManager(wl_display *display) : display_(display) {} + +const zn_xr_system_manager_interface XrSystemManager::c_implementation_ = { + XrSystemManager::HandleDestroy, +}; + +bool +XrSystemManager::Init() +{ + zen::remote::InitializeLogger(std::make_unique()); + + auto loop = std::make_unique(wl_display_get_event_loop(display_)); + + peer_manager_ = zen::remote::server::CreatePeerManager(std::move(loop)); + if (!peer_manager_) { + zn_error("Failed to create a peer manager"); + return false; + } + + c_obj_.impl_data = this; + c_obj_.impl = &c_implementation_; + wl_signal_init(&c_obj_.events.new_system); + + peer_manager_->on_peer_discover.Connect( + [this](uint64_t id) { HandleRemotePeerDiscover(id); }); + + peer_manager_->on_peer_lost.Connect( + [this](uint64_t id) { HandleRemotePeerLost(id); }); + + return true; +} + +void +XrSystemManager::HandleDestroy(zn_xr_system_manager *c_obj) +{ + zn_xr_system_manager_destroy_remote(c_obj); +} + +void +XrSystemManager::RemoveDeadXrSystem() +{ + auto result = std::remove_if(xr_systems_.begin(), xr_systems_.end(), + [](std::unique_ptr &xr_system) { + return !xr_system->is_alive(); + }); + + xr_systems_.erase(result, xr_systems_.end()); +} + +void +XrSystemManager::HandleRemotePeerDiscover(uint64_t peer_id) +{ + std::for_each(xr_systems_.begin(), xr_systems_.end(), + [peer_id](std::unique_ptr &xr_system) { + if (xr_system->peer_id() == peer_id) { + xr_system->Kill(); + } + }); + RemoveDeadXrSystem(); + + auto peer = peer_manager_->Get(peer_id); + + zn_debug( + "Discover a new remote peer %ld (%s)", peer_id, peer->host().c_str()); + + auto xr_system = XrSystem::New(peer, display_, this); + + auto *xr_system_c_obj = xr_system->c_obj(); + + xr_systems_.push_back(std::move(xr_system)); + + wl_signal_emit(&c_obj_.events.new_system, xr_system_c_obj); +} + +void +XrSystemManager::HandleRemotePeerLost(uint64_t peer_id) +{ + std::for_each(xr_systems_.begin(), xr_systems_.end(), + [peer_id](std::unique_ptr &xr_system) { + if (xr_system->peer_id() == peer_id && !xr_system->is_connected()) { + xr_system->Kill(); + } + }); + RemoveDeadXrSystem(); + + zn_debug("Lost a remote peer %ld", peer_id); +} + +} // namespace zen::backend::immersive::remote + +struct zn_xr_system_manager * +zn_xr_system_manager_create_remote(struct wl_display *display) +{ + using XrSystemManager = zen::backend::immersive::remote::XrSystemManager; + + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + auto *self = new XrSystemManager(display); + + if (!self->Init()) { + zn_error("Failed to initialize remote XrSystemManager"); + delete self; // NOLINT(cppcoreguidelines-owning-memory) + return nullptr; + } + + return &self->c_obj_; +} + +static void +zn_xr_system_manager_destroy_remote(struct zn_xr_system_manager *c_obj) +{ + using XrSystemManager = zen::backend::immersive::remote::XrSystemManager; + + auto *self = static_cast(c_obj->impl_data); + + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + delete self; +} diff --git a/zen/src/backend/immersive/remote/xr-system-manager.hh b/zen/src/backend/immersive/remote/xr-system-manager.hh new file mode 100644 index 00000000..eac54356 --- /dev/null +++ b/zen/src/backend/immersive/remote/xr-system-manager.hh @@ -0,0 +1,43 @@ +#pragma once + +#include "backend/immersive/xr-system-manager.h" +#include "zen-common/cpp-util.h" + +namespace zen::backend::immersive::remote { + +class XrSystem; + +class XrSystemManager +{ + public: + DISABLE_MOVE_AND_COPY(XrSystemManager); + ~XrSystemManager() = default; + + void RemoveDeadXrSystem(); + + private: + friend struct zn_xr_system_manager * ::zn_xr_system_manager_create_remote( + struct wl_display *display); + + explicit XrSystemManager(wl_display *display); + + bool Init(); + + void HandleRemotePeerDiscover(uint64_t peer_id); + + void HandleRemotePeerLost(uint64_t peer_id); + + static void HandleDestroy(zn_xr_system_manager *c_obj); + + static const zn_xr_system_manager_interface c_implementation_; + + zn_xr_system_manager c_obj_{}; + + wl_display *display_; // @nonnull, @outlive + + std::unique_ptr peer_manager_; // @nonnull + + std::vector> xr_systems_; +}; + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/xr-system.cc b/zen/src/backend/immersive/remote/xr-system.cc new file mode 100644 index 00000000..807bf2b7 --- /dev/null +++ b/zen/src/backend/immersive/remote/xr-system.cc @@ -0,0 +1,158 @@ +#include "xr-system.hh" + +#include "gl-virtual-object.hh" +#include "loop.hh" +#include "xr-dispatcher.hh" +#include "xr-system-manager.hh" +#include "zen-common/log.h" +#include "zen-common/signal.h" + +namespace zen::backend::immersive::remote { + +XrSystem::XrSystem(std::shared_ptr peer, + wl_display *display, XrSystemManager *xr_system_manager) + : peer_(std::move(peer)), + display_(display), + xr_system_manager_(xr_system_manager) +{} + +XrSystem::~XrSystem() +{ + zn_signal_emit_mutable(&c_obj_.events.destroy, nullptr); +} + +std::unique_ptr +XrSystem::New(std::shared_ptr peer, + wl_display *display, XrSystemManager *xr_system_manager) +{ + auto self = std::unique_ptr( + new XrSystem(std::move(peer), display, xr_system_manager)); + if (!self) { + zn_error("Failed to allocate memory"); + return nullptr; + } + + if (!self->Init()) { + zn_error("Failed to initialize remote XrSystem"); + return nullptr; + } + + return self; +} + +bool +XrSystem::Init() +{ + c_obj_.impl_data = this; + c_obj_.impl = &c_implementation_; + c_obj_.state = ZN_XR_SYSTEM_SESSION_STATE_AVAILABLE; + wl_signal_init(&c_obj_.events.session_state_changed); + wl_signal_init(&c_obj_.events.destroy); + + return true; +} + +const zn_xr_system_interface XrSystem::c_implementation_ = { + XrSystem::HandleConnect, +}; + +void +XrSystem::HandleConnect(zn_xr_system *c_obj) +{ + auto *self = static_cast(c_obj->impl_data); + + self->Connect(); +} + +void +XrSystem::HandleDisconnect() +{ + zn_info("Disconnected from peer %ld", peer_->id()); + + Kill(); + + xr_system_manager_->RemoveDeadXrSystem(); // Will destroy this XrSystem +} + +void +XrSystem::Kill() +{ + if (c_obj_.state == ZN_XR_SYSTEM_SESSION_STATE_AVAILABLE) { + c_obj_.state = ZN_XR_SYSTEM_SESSION_STATE_DEAD; + zn_signal_emit_mutable(&c_obj_.events.session_state_changed, nullptr); + return; + } + + if (c_obj_.state == ZN_XR_SYSTEM_SESSION_STATE_FOCUS) { + c_obj_.state = ZN_XR_SYSTEM_SESSION_STATE_VISIBLE; + zn_signal_emit_mutable(&c_obj_.events.session_state_changed, nullptr); + } + + if (c_obj_.state == ZN_XR_SYSTEM_SESSION_STATE_VISIBLE) { + c_obj_.state = ZN_XR_SYSTEM_SESSION_STATE_SYNCHRONIZED; + zn_signal_emit_mutable(&c_obj_.events.session_state_changed, nullptr); + } + + if (c_obj_.state == ZN_XR_SYSTEM_SESSION_STATE_SYNCHRONIZED) { + c_obj_.state = ZN_XR_SYSTEM_SESSION_STATE_DEAD; + + session_.reset(); + high_priority_dispatcher_.reset(); + default_dispatcher_.reset(); + c_obj_.high_priority_dispatcher = nullptr; + c_obj_.default_dispatcher = nullptr; + + zn_signal_emit_mutable(&c_obj_.events.session_state_changed, nullptr); + } +} + +void +XrSystem::Connect() +{ + if (c_obj_.state != ZN_XR_SYSTEM_SESSION_STATE_AVAILABLE) { + zn_warn("Tried to Connect to %s but the current state(%d) is invalid.", + peer_->host().c_str(), c_obj_.state); + return; + } + + auto loop = std::make_unique(wl_display_get_event_loop(display_)); + + auto new_session = zen::remote::server::CreateSession(std::move(loop)); + + if (!new_session->Connect(peer_)) { + zn_warn("Failed to start new session"); + return; + } + + auto high_priority_dispatcher = XrDispatcher::New(new_session, display_); + if (!high_priority_dispatcher) { + zn_error("Failed to create a remote XrDispatcher"); + } + + auto default_dispatcher = XrDispatcher::New(new_session, display_); + if (!default_dispatcher) { + zn_error("Failed to create a remote XrDispatcher"); + } + + zn_info("Connected to peer %ld", peer_->id()); + + new_session->on_disconnect.Connect([this]() { HandleDisconnect(); }); + + session_ = new_session; + + c_obj_.state = ZN_XR_SYSTEM_SESSION_STATE_SYNCHRONIZED; + high_priority_dispatcher_ = std::move(high_priority_dispatcher); + default_dispatcher_ = std::move(default_dispatcher); + c_obj_.high_priority_dispatcher = high_priority_dispatcher_->c_obj(); + c_obj_.default_dispatcher = default_dispatcher_->c_obj(); + + zn_signal_emit_mutable(&c_obj_.events.session_state_changed, nullptr); + + c_obj_.state = ZN_XR_SYSTEM_SESSION_STATE_VISIBLE; + zn_signal_emit_mutable(&c_obj_.events.session_state_changed, nullptr); + + c_obj_.state = ZN_XR_SYSTEM_SESSION_STATE_FOCUS; + zn_signal_emit_mutable(&c_obj_.events.session_state_changed, nullptr); +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/xr-system.hh b/zen/src/backend/immersive/remote/xr-system.hh new file mode 100644 index 00000000..bf548179 --- /dev/null +++ b/zen/src/backend/immersive/remote/xr-system.hh @@ -0,0 +1,87 @@ +#pragma once + +#include "zen-common/cpp-util.h" +#include "zen/xr-system.h" + +namespace zen::backend::immersive::remote { + +class XrSystemManager; +class XrDispatcher; + +class XrSystem +{ + public: + DISABLE_MOVE_AND_COPY(XrSystem); + ~XrSystem(); + + static std::unique_ptr New( + std::shared_ptr peer, wl_display *display, + XrSystemManager *xr_system_manager); + + void Kill(); + + inline uint64_t peer_id() const; + + inline zn_xr_system *c_obj(); + + inline bool is_alive() const; + + inline bool is_connected() const; + + private: + XrSystem(std::shared_ptr peer, + wl_display *display, XrSystemManager *xr_system_manager); + + bool Init(); + + void Connect(); + + static void HandleConnect(zn_xr_system *c_obj); + + void HandleDisconnect(); + + static const zn_xr_system_interface c_implementation_; + + std::shared_ptr peer_; // @nonnull + + wl_display *display_; // @nonnull, @outlive + + XrSystemManager *xr_system_manager_; // @nonnull, @outlive + + // These objects are null when state is AVAILABLE or DEAD, not null otherwise. + std::shared_ptr session_; + std::unique_ptr high_priority_dispatcher_; + std::unique_ptr default_dispatcher_; + + zn_xr_system c_obj_{}; +}; + +inline uint64_t +XrSystem::peer_id() const +{ + return peer_->id(); +} + +inline zn_xr_system * +XrSystem::c_obj() +{ + return &c_obj_; +} + +inline bool +XrSystem::is_alive() const +{ + return c_obj_.state != ZN_XR_SYSTEM_SESSION_STATE_DEAD; +} + +inline bool +XrSystem::is_connected() const +{ + const uint32_t connected = ZN_XR_SYSTEM_SESSION_STATE_SYNCHRONIZED | + ZN_XR_SYSTEM_SESSION_STATE_VISIBLE | + ZN_XR_SYSTEM_SESSION_STATE_FOCUS; + + return (c_obj_.state & connected) != 0; +} + +} // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/shell/bounded-configure.c b/zen/src/backend/immersive/shell/bounded-configure.c new file mode 100644 index 00000000..e7d5a038 --- /dev/null +++ b/zen/src/backend/immersive/shell/bounded-configure.c @@ -0,0 +1,32 @@ +#include "bounded-configure.h" + +#include + +#include "zen-common/log.h" +#include "zen-common/util.h" + +struct zn_bounded_configure * +zn_bounded_configure_create(uint32_t serial, vec3 half_size) +{ + struct zn_bounded_configure *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->serial = serial; + glm_vec3_copy(half_size, self->half_size); + wl_list_init(&self->link); + + return self; + +err: + return NULL; +} + +void +zn_bounded_configure_destroy(struct zn_bounded_configure *self) +{ + wl_list_remove(&self->link); + free(self); +} diff --git a/zen/src/backend/immersive/shell/bounded-configure.h b/zen/src/backend/immersive/shell/bounded-configure.h new file mode 100644 index 00000000..19346047 --- /dev/null +++ b/zen/src/backend/immersive/shell/bounded-configure.h @@ -0,0 +1,6 @@ +#include "zen/bounded-configure.h" + +struct zn_bounded_configure *zn_bounded_configure_create( + uint32_t serial, vec3 half_size); + +void zn_bounded_configure_destroy(struct zn_bounded_configure *self); diff --git a/zen/src/backend/immersive/shell/bounded.c b/zen/src/backend/immersive/shell/bounded.c new file mode 100644 index 00000000..85c979d8 --- /dev/null +++ b/zen/src/backend/immersive/shell/bounded.c @@ -0,0 +1,270 @@ +#include "bounded.h" + +#include +#include +#include + +#include "backend/default-backend.h" +#include "bounded-configure.h" +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" +#include "zen-common/wl-array.h" +#include "zen/server.h" +#include "zen/virtual-object.h" + +static void zn_bounded_destroy(struct zn_bounded *self); + +static void +zn_bounded_send_configure(void *user_data) +{ + struct zn_bounded *self = user_data; + + self->scheduled_config.idle = NULL; + + struct wl_array half_size_array; + wl_array_init(&half_size_array); + + if (!zn_wl_array_from_vec3( + &half_size_array, self->scheduled_config.half_size)) { + zn_error("Failed to convert from vec3 to wl_array"); + goto out; + } + + struct zn_bounded_configure *configure = zn_bounded_configure_create( + self->scheduled_config.serial, self->scheduled_config.half_size); + if (configure == NULL) { + wl_client_post_no_memory(wl_resource_get_client(self->resource)); + zn_error("Failed to create a zn_bounded_configure"); + goto out; + } + + zwn_bounded_send_configure( + self->resource, &half_size_array, self->scheduled_config.serial); + + wl_list_insert(self->configure_list.prev, &configure->link); + +out: + wl_array_release(&half_size_array); +} + +uint32_t +zn_bounded_schedule_configure(struct zn_bounded *self) +{ + struct wl_client *client = wl_resource_get_client(self->resource); + struct wl_display *display = wl_client_get_display(client); + struct wl_event_loop *loop = wl_display_get_event_loop(display); + + if (self->scheduled_config.idle == NULL) { + self->scheduled_config.serial = wl_display_get_serial(display); + self->scheduled_config.idle = + wl_event_loop_add_idle(loop, zn_bounded_send_configure, self); + if (self->scheduled_config.idle == NULL) { + wl_client_post_no_memory(client); + } + } + + return self->scheduled_config.serial; +} + +static void +zn_bounded_handle_destroy(struct wl_resource *resource) +{ + struct zn_bounded *self = wl_resource_get_user_data(resource); + + zn_bounded_destroy(self); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_bounded_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_bounded_protocol_ack_configure(struct wl_client *client UNUSED, + struct wl_resource *resource, struct wl_array *half_size_array, + uint32_t serial) +{ + struct zn_bounded *self = wl_resource_get_user_data(resource); + if (self == NULL) { + return; + } + + vec3 half_size; + + if (!zn_wl_array_to_vec3(half_size_array, half_size)) { + wl_resource_post_error(resource, ZWN_COMPOSITOR_ERROR_INVALID_WL_ARRAY_SIZE, + "invalid wl_array size"); + return; + } + + bool found = false; + struct zn_bounded_configure *configure = NULL; + + wl_list_for_each (configure, &self->configure_list, link) { + if (configure->serial == serial) { + found = true; + break; + } + } + + if (!found) { + wl_resource_post_error(resource, ZWN_BOUNDED_ERROR_INVALID_SERIAL, + "wrong configure serial: %u", serial); + return; + } + + struct zn_bounded_configure *configure_tmp = NULL; + + wl_list_for_each_safe ( + configure, configure_tmp, &self->configure_list, link) { + if (configure->serial == serial) { + break; + } + + zn_bounded_configure_destroy(configure); + } + + self->configured = true; + + glm_vec3_copy(half_size, self->pending.half_size); + + zn_bounded_configure_destroy(configure); +} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_bounded_protocol_set_title(struct wl_client *client UNUSED, + struct wl_resource *resource UNUSED, const char *title UNUSED) +{} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_bounded_protocol_set_input_region(struct wl_client *client UNUSED, + struct wl_resource *resource UNUSED, struct wl_resource *region UNUSED) +{} + +/// @param resource can be inert (resource->user_data == NULL) +static void +zn_bounded_protocol_move(struct wl_client *client UNUSED, + struct wl_resource *resource UNUSED, struct wl_resource *seat UNUSED, + uint32_t serial UNUSED) +{} + +static const struct zwn_bounded_interface implementation = { + .destroy = zn_bounded_protocol_destroy, + .ack_configure = zn_bounded_protocol_ack_configure, + .set_title = zn_bounded_protocol_set_title, + .set_input_region = zn_bounded_protocol_set_input_region, + .move = zn_bounded_protocol_move, +}; + +static void +zn_bounded_handle_virtual_object_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_bounded *self = + zn_container_of(listener, self, virtual_object_destroy_listener); + + zn_bounded_destroy(self); +} + +static void +zn_bounded_handle_commit(struct wl_listener *listener, void *data UNUSED) +{ + struct zn_bounded *self = zn_container_of(listener, self, commit_listener); + struct zn_server *server = zn_server_get_singleton(); + struct zn_default_backend *backend = zn_default_backend_get(server->backend); + + glm_vec3_copy(self->pending.half_size, self->current.half_size); + + if (!self->added) { + // on the initial commit + self->added = true; + glm_vec3_zero(self->scheduled_config.half_size); + zn_bounded_schedule_configure(self); + } + + if (self->configured && !self->mapped) { + self->mapped = true; + zn_default_backend_notify_bounded_mapped(backend, self); + } +} + +struct zn_bounded * +zn_bounded_create(struct wl_client *client, uint32_t id, + struct zn_virtual_object *virtual_object) +{ + struct zn_bounded *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + wl_client_post_no_memory(client); + goto err; + } + + self->virtual_object = virtual_object; + wl_list_init(&self->configure_list); + glm_vec3_zero(self->scheduled_config.half_size); + self->scheduled_config.idle = NULL; + self->added = false; + self->configured = false; + self->mapped = false; + glm_vec3_zero(self->pending.half_size); + glm_vec3_zero(self->current.half_size); + wl_signal_init(&self->events.destroy); + + self->resource = wl_resource_create(client, &zwn_bounded_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_free; + } + + wl_resource_set_implementation( + self->resource, &implementation, self, zn_bounded_handle_destroy); + + self->virtual_object_destroy_listener.notify = + zn_bounded_handle_virtual_object_destroy; + wl_signal_add(&self->virtual_object->events.destroy, + &self->virtual_object_destroy_listener); + + self->commit_listener.notify = zn_bounded_handle_commit; + wl_signal_add(&self->virtual_object->events.commit, &self->commit_listener); + + return self; + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_bounded_destroy(struct zn_bounded *self) +{ + struct zn_bounded_configure *configure = NULL; + struct zn_bounded_configure *configure_tmp = NULL; + + zn_signal_emit_mutable(&self->events.destroy, NULL); + + if (self->scheduled_config.idle != NULL) { + wl_event_source_remove(self->scheduled_config.idle); + } + + wl_list_for_each_safe ( + configure, configure_tmp, &self->configure_list, link) { + zn_bounded_configure_destroy(configure); + } + + wl_list_remove(&self->configure_list); + wl_list_remove(&self->events.destroy.listener_list); + wl_list_remove(&self->virtual_object_destroy_listener.link); + wl_list_remove(&self->commit_listener.link); + wl_resource_set_implementation(self->resource, &implementation, NULL, NULL); + free(self); +} diff --git a/zen/src/backend/immersive/shell/bounded.h b/zen/src/backend/immersive/shell/bounded.h new file mode 100644 index 00000000..14c5a660 --- /dev/null +++ b/zen/src/backend/immersive/shell/bounded.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +#include "zen/bounded.h" + +struct zn_bounded *zn_bounded_create(struct wl_client *client, uint32_t id, + struct zn_virtual_object *virtual_object); diff --git a/zen/src/backend/immersive/shell/meson.build b/zen/src/backend/immersive/shell/meson.build new file mode 100644 index 00000000..1d1c6cd7 --- /dev/null +++ b/zen/src/backend/immersive/shell/meson.build @@ -0,0 +1,24 @@ +_zen_shell_immersive_backend_srcs = [ + 'xr-shell.c', + 'bounded.c', + 'bounded-configure.c', + + protocols_code['zwin-shell'], + protocols_server_header['zwin-shell'], +] + +_zen_shell_immersive_backend_deps = [ + _zen_deps, +] + +_zen_shell_immersive_backend_lib = static_library( + 'zen-shell-immersive-backend', + _zen_shell_immersive_backend_srcs, + install: false, + include_directories: zen_private_inc, + dependencies: _zen_shell_immersive_backend_deps, +) + +zen_shell_immersive_backend_dep = declare_dependency( + link_with: _zen_shell_immersive_backend_lib, +) diff --git a/zen/src/backend/immersive/shell/xr-shell.c b/zen/src/backend/immersive/shell/xr-shell.c new file mode 100644 index 00000000..af00ba1c --- /dev/null +++ b/zen/src/backend/immersive/shell/xr-shell.c @@ -0,0 +1,107 @@ +#include "backend/immersive/xr-shell.h" + +#include +#include + +#include "backend/immersive/client-virtual-object.h" +#include "bounded.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/virtual-object.h" + +static void +zn_xr_shell_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +zn_xr_shell_protocol_get_bounded(struct wl_client *client, + struct wl_resource *resource UNUSED, uint32_t id, + struct wl_resource *virtual_object_resource) +{ + struct zn_client_virtual_object *client_virtual_object = + zn_client_virtual_object_get(virtual_object_resource); + + if (client_virtual_object == NULL) { + return; + } + + struct zn_bounded *bounded = + zn_bounded_create(client, id, client_virtual_object->zn_virtual_object); + if (bounded == NULL) { + zn_error("Failed to create a zn_bounded"); + return; + } + + if (!zn_virtual_object_set_role(client_virtual_object->zn_virtual_object, + ZN_VIRTUAL_OBJECT_ROLE_BOUNDED, bounded)) { + wl_resource_post_error(client_virtual_object->resource, + ZWN_VIRTUAL_OBJECT_ERROR_ROLE, + "Failed assign bounded role to the virtual object (%d)", + wl_resource_get_id(client_virtual_object->resource)); + wl_resource_destroy(bounded->resource); + } +} + +static void +zn_xr_shell_protocol_get_expansive(struct wl_client *client UNUSED, + struct wl_resource *resource UNUSED, uint32_t id UNUSED, + struct wl_resource *virtual_object_resource UNUSED) +{} + +static const struct zwn_shell_interface implementation = { + .destroy = zn_xr_shell_protocol_destroy, + .get_bounded = zn_xr_shell_protocol_get_bounded, + .get_expansive = zn_xr_shell_protocol_get_expansive, +}; + +static void +zn_xr_shell_bind( + struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct zn_xr_shell *self = data; + + struct wl_resource *resource = + wl_resource_create(client, &zwn_shell_interface, (int)version, id); + if (resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &implementation, self, NULL); +} + +struct zn_xr_shell * +zn_xr_shell_create(struct wl_display *display) +{ + struct zn_xr_shell *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->global = wl_global_create( + display, &zwn_shell_interface, 1, self, zn_xr_shell_bind); + if (self->global == NULL) { + zn_error("Failed to create zwn_shell global"); + goto err_free; + } + + return self; + +err_free: + free(self); + +err: + return NULL; +} + +void +zn_xr_shell_destroy(struct zn_xr_shell *self) +{ + wl_global_destroy(self->global); + free(self); +} diff --git a/zen/src/backend/immersive/shm-buffer.c b/zen/src/backend/immersive/shm-buffer.c new file mode 100644 index 00000000..26ddc18c --- /dev/null +++ b/zen/src/backend/immersive/shm-buffer.c @@ -0,0 +1,302 @@ +#include "shm-buffer.h" + +#include +#include +#include +#include + +#include "shm-pool.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/buffer.h" + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static pthread_once_t zn_shm_sigbus_once = PTHREAD_ONCE_INIT; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static pthread_key_t zn_shm_sigbus_data_key; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +static struct sigaction zn_shm_old_sigbus_action; + +struct zn_shm_sigbus_data { + struct zn_shm_pool *current_pool; + int access_count; + int fallback_mapping_used; +}; + +static void zn_shm_buffer_destroy(struct zn_shm_buffer *self); + +static void +reraise_sigbus(void) +{ + sigaction(SIGBUS, &zn_shm_old_sigbus_action, NULL); + raise(SIGBUS); // NOLINT(cert-err33-c) +} + +static void +sigbus_handler(int signum UNUSED, siginfo_t *info, void *context UNUSED) +{ + struct zn_shm_sigbus_data *sigbus_data = + pthread_getspecific(zn_shm_sigbus_data_key); + struct zn_shm_pool *pool = NULL; + + if (sigbus_data == NULL) { + reraise_sigbus(); + return; + } + + pool = sigbus_data->current_pool; + + if (pool == NULL || (char *)info->si_addr < pool->data || + (char *)info->si_addr >= pool->data + pool->size) { + reraise_sigbus(); + return; + } + + sigbus_data->fallback_mapping_used = 1; + + if (mmap(pool->data, pool->size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, 0, + 0) == MAP_FAILED) { // NOLINT(performance-no-int-to-ptr) + reraise_sigbus(); + return; + } +} + +static void +destroy_sigbus_data(void *data) +{ + struct zn_shm_sigbus_data *sigbus_data = data; + + free(sigbus_data); +} + +static void +init_sigbus_data_key(void) +{ + struct sigaction new_action = { + .sa_sigaction = sigbus_handler, + .sa_flags = SA_SIGINFO | SA_NODEFER, + }; + + sigemptyset(&new_action.sa_mask); + + sigaction(SIGBUS, &new_action, &zn_shm_old_sigbus_action); + + pthread_key_create(&zn_shm_sigbus_data_key, destroy_sigbus_data); +} + +static void +zn_shm_buffer_ref(struct zn_buffer *zn_buffer) +{ + struct zn_shm_buffer *self = (struct zn_shm_buffer *)(zn_buffer->impl_data); + + zn_assert(self->refcount > 0, "invalid refcount status"); + + self->refcount++; +} + +static void +zn_shm_buffer_unref(struct zn_buffer *zn_buffer) +{ + struct zn_shm_buffer *self = (struct zn_shm_buffer *)(zn_buffer->impl_data); + + self->refcount--; + zn_assert(self->refcount >= 0, "invalid refcount status"); + + if (self->refcount == 0) { + zn_shm_buffer_destroy(self); + } +} + +static void * +zn_shm_buffer_begin_access(struct zn_buffer *zn_buffer) +{ + struct zn_shm_buffer *self = (struct zn_shm_buffer *)(zn_buffer->impl_data); + struct zn_shm_pool *pool = self->pool; + + if (self->pool->external_refcount && + self->pool->size != self->pool->new_size) { + zn_warn( + "Buffer address requested when its parent pool has an external " + "reference and a deferred resize pending."); + } + + void *data = self->pool->data + self->offset; + + if (pool->sigbuf_is_impossible) { + return data; + } + + pthread_once(&zn_shm_sigbus_once, init_sigbus_data_key); + + struct zn_shm_sigbus_data *sigbus_data = + pthread_getspecific(zn_shm_sigbus_data_key); + if (sigbus_data == NULL) { + sigbus_data = zalloc(sizeof *sigbus_data); + if (sigbus_data == NULL) { + return NULL; + } + + pthread_setspecific(zn_shm_sigbus_data_key, sigbus_data); + } + + zn_assert( + sigbus_data->current_pool == NULL || sigbus_data->current_pool == pool, + "Accessing a pool data while accessing another pool data is prohibited"); + + sigbus_data->current_pool = pool; + sigbus_data->access_count++; + + return data; +} + +static bool +zn_shm_buffer_end_access(struct zn_buffer *zn_buffer) +{ + struct zn_shm_buffer *self = (struct zn_shm_buffer *)(zn_buffer->impl_data); + struct zn_shm_pool *pool = self->pool; + + if (pool->sigbuf_is_impossible) { + return true; + } + + struct zn_shm_sigbus_data *sigbus_data = + pthread_getspecific(zn_shm_sigbus_data_key); + if (!zn_assert(sigbus_data && sigbus_data->access_count >= 1, + "More zn_shm_buffer_end_access was called than the previously called " + "zn_shm_buffer_begin_access.")) { + return false; + } + + bool status = true; + + if (--sigbus_data->access_count == 0) { + if (sigbus_data->fallback_mapping_used) { + status = false; + if (self->resource != NULL) { + wl_resource_post_error(self->resource, ZWN_SHM_ERROR_INVALID_FD, + "error accessing SHM buffer"); + } + sigbus_data->fallback_mapping_used = 0; + } + + sigbus_data->current_pool = NULL; + } + + return status; +} + +static ssize_t +zn_shm_buffer_get_size(struct zn_buffer *zn_buffer) +{ + struct zn_shm_buffer *self = (struct zn_shm_buffer *)(zn_buffer->impl_data); + + return self->size; +} + +static const struct zn_buffer_interface buffer_implementation = { + .ref = zn_shm_buffer_ref, + .unref = zn_shm_buffer_unref, + .begin_access = zn_shm_buffer_begin_access, + .end_access = zn_shm_buffer_end_access, + .get_size = zn_shm_buffer_get_size, +}; + +static void +zn_shm_buffer_handle_destroy(struct wl_resource *resource) +{ + struct zn_shm_buffer *self = wl_resource_get_user_data(resource); + + self->resource = NULL; + + zn_shm_buffer_unref(self->zn_buffer); +} + +static void +zn_shm_buffer_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zwn_buffer_interface implementation = { + .destroy = zn_shm_buffer_protocol_destroy, +}; + +struct zn_shm_buffer * +zn_shm_buffer_get(struct wl_resource *resource) +{ + if (resource == NULL) { + return NULL; + } + + if (!zn_assert(wl_resource_instance_of( + resource, &zwn_buffer_interface, &implementation), + "resource is not zn_shm_buffer")) { + return NULL; + } + + return wl_resource_get_user_data(resource); +} + +struct zn_shm_buffer * +zn_shm_buffer_create(struct wl_client *client, uint32_t id, int64_t offset, + int64_t size, struct wl_resource *pool_resource) +{ + struct zn_shm_pool *pool = wl_resource_get_user_data(pool_resource); + + if (offset < 0 || size <= 0 || offset > pool->size - size) { + wl_resource_post_error( + pool_resource, ZWN_SHM_ERROR_INVALID_SIZE, "invalid size (%ld)", size); + goto err; + } + + struct zn_shm_buffer *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + wl_client_post_no_memory(client); + goto err; + } + + self->refcount = 1; + self->size = size; + self->offset = offset; + self->pool = pool; + + self->zn_buffer = zn_buffer_create(self, &buffer_implementation); + if (self->zn_buffer == NULL) { + zn_error("Failed to create zn_buffer"); + goto err_free; + } + + pool->internal_refcount++; + + self->resource = wl_resource_create(client, &zwn_buffer_interface, 1, id); + if (self->resource == NULL) { + wl_client_post_no_memory(client); + goto err_ref; + } + + wl_resource_set_implementation( + self->resource, &implementation, self, zn_shm_buffer_handle_destroy); + + return self; + +err_ref: + zn_shm_pool_unref(pool, false); + zn_buffer_destroy(self->zn_buffer); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_shm_buffer_destroy(struct zn_shm_buffer *self) +{ + zn_shm_pool_unref(self->pool, false); + zn_buffer_destroy(self->zn_buffer); + free(self); +} diff --git a/zen/src/backend/immersive/shm-buffer.h b/zen/src/backend/immersive/shm-buffer.h new file mode 100644 index 00000000..7917b781 --- /dev/null +++ b/zen/src/backend/immersive/shm-buffer.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +struct zn_buffer; + +struct zn_shm_buffer { + struct wl_resource *resource; // @nullable, @ref + int refcount; + ssize_t size; + ssize_t offset; + struct zn_shm_pool *pool; // @nonnull, @owning(shared) + struct zn_buffer *zn_buffer; // @nonnull, @owning +}; + +struct zn_shm_buffer *zn_shm_buffer_get(struct wl_resource *resource); + +struct zn_shm_buffer *zn_shm_buffer_create(struct wl_client *client, + uint32_t id, int64_t offset, int64_t size, + struct wl_resource *pool_resource); diff --git a/zen/src/backend/immersive/shm-pool.c b/zen/src/backend/immersive/shm-pool.c new file mode 100644 index 00000000..a90fa7ff --- /dev/null +++ b/zen/src/backend/immersive/shm-pool.c @@ -0,0 +1,206 @@ +#include "shm-pool.h" + +#include +#include +#include +#include + +#include "shm-buffer.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen-common/wl-array.h" + +static void zn_shm_pool_destroy(struct zn_shm_pool *self); + +static void * +zn_shm_pool_grow_mapping(struct zn_shm_pool *self) +{ + void *data = NULL; + +#ifdef MREMAP_MAYMOVE + data = mremap(self->data, self->size, self->new_size, MREMAP_MAYMOVE); +#else +#error Operating system must implement mremap(MREMAP_MAYMOVE) +// TODO: fallback +#endif + + return data; +} + +static void +zn_shm_pool_finish_resize(struct zn_shm_pool *self) +{ + if (self->size == self->new_size) { + return; + } + + void *data = zn_shm_pool_grow_mapping(self); + if (data == MAP_FAILED) { // NOLINT(performance-no-int-to-ptr) + if (self->resource) { + wl_resource_post_error( + self->resource, ZWN_SHM_ERROR_INVALID_FD, "failed mremap"); + } + return; + } + + self->data = data; + self->size = self->new_size; +} + +static void +zn_shm_pool_handle_destroy(struct wl_resource *resource) +{ + struct zn_shm_pool *self = wl_resource_get_user_data(resource); + + self->resource = NULL; + + zn_shm_pool_unref(self, false); +} + +static void +zn_shm_pool_protocol_destroy( + struct wl_client *client UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +zn_shm_pool_protocol_create_buffer(struct wl_client *client, + struct wl_resource *resource, uint32_t id, struct wl_array *offset_array, + struct wl_array *size_array) +{ + int64_t offset = 0; + int64_t size = 0; + + if (!zn_wl_array_to_int64_t(offset_array, &offset) || + !zn_wl_array_to_int64_t(size_array, &size)) { + wl_resource_post_error(resource, ZWN_COMPOSITOR_ERROR_INVALID_WL_ARRAY_SIZE, + "invalid wl_array size"); + return; + } + + zn_shm_buffer_create(client, id, offset, size, resource); +} + +static void +zn_shm_pool_protocol_resize( + struct wl_client *client UNUSED, struct wl_resource *resource, int32_t size) +{ + struct zn_shm_pool *self = wl_resource_get_user_data(resource); + + if (size < self->size) { + wl_resource_post_error( + resource, ZWN_SHM_ERROR_INVALID_SIZE, "shrinking pool invalid"); + return; + } + + self->new_size = size; + + if (self->external_refcount == 0) { + zn_shm_pool_finish_resize(self); + } +} + +static const struct zwn_shm_pool_interface implementation = { + .destroy = zn_shm_pool_protocol_destroy, + .create_buffer = zn_shm_pool_protocol_create_buffer, + .resize = zn_shm_pool_protocol_resize, +}; + +void +zn_shm_pool_ref(struct zn_shm_pool *self) +{ + zn_assert(self->internal_refcount + self->external_refcount > 0, + "invalid refcount status"); + + self->external_refcount++; +} + +void +zn_shm_pool_unref(struct zn_shm_pool *self, bool external) +{ + if (external) { + self->external_refcount--; + zn_assert(self->external_refcount >= 0, "invalid external refcount status"); + if (self->external_refcount == 0) { + zn_shm_pool_finish_resize(self); + } + } else { + self->internal_refcount--; + zn_assert(self->external_refcount >= 0, "invalid internal refcount status"); + } + + if (self->internal_refcount + self->external_refcount > 0) { + return; + } + + zn_shm_pool_destroy(self); +} + +struct zn_shm_pool * +zn_shm_pool_create(struct wl_client *client, uint32_t id, int fd, int64_t size, + struct wl_resource *error_resource) +{ + if (size <= 0) { + wl_resource_post_error( + error_resource, ZWN_SHM_ERROR_INVALID_SIZE, "invalid size"); + } + + struct zn_shm_pool *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + int seals = fcntl(fd, F_GET_SEALS); + if (seals == -1) { + seals = 0; + } + + struct stat stat_buf; + if ((seals & F_SEAL_SHRINK) && fstat(fd, &stat_buf) >= 0) { + self->sigbuf_is_impossible = stat_buf.st_size >= size; + } else { + self->sigbuf_is_impossible = false; + } + + self->internal_refcount = 1; + self->external_refcount = 0; + self->size = size; + self->new_size = size; + + self->data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (self->data == MAP_FAILED) { // NOLINT(performance-no-int-to-ptr) + wl_resource_post_error(error_resource, ZWN_SHM_ERROR_INVALID_FD, + "failed mmap fd %d: %s", fd, strerror(errno)); + goto err_free; + } + + self->resource = wl_resource_create(client, &zwn_shm_pool_interface, 1, id); + if (self->resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + goto err_mmap; + } + + wl_resource_set_implementation( + self->resource, &implementation, self, zn_shm_pool_handle_destroy); + + return self; + +err_mmap: + munmap(self->data, self->size); + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_shm_pool_destroy(struct zn_shm_pool *self) +{ + munmap(self->data, self->size); + free(self); +} diff --git a/zen/src/backend/immersive/shm-pool.h b/zen/src/backend/immersive/shm-pool.h new file mode 100644 index 00000000..51754a8d --- /dev/null +++ b/zen/src/backend/immersive/shm-pool.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +struct zn_shm_pool { + struct wl_resource *resource; // @nullable, @ref + int internal_refcount; + int external_refcount; + char *data; // @nonnull, mapped + ssize_t size; + ssize_t new_size; + bool sigbuf_is_impossible; +}; + +void zn_shm_pool_ref(struct zn_shm_pool *self); + +void zn_shm_pool_unref(struct zn_shm_pool *self, bool external); + +struct zn_shm_pool *zn_shm_pool_create(struct wl_client *client, uint32_t id, + int fd, int64_t size, struct wl_resource *error_resource); diff --git a/zen/src/backend/immersive/shm.c b/zen/src/backend/immersive/shm.c new file mode 100644 index 00000000..09b2631f --- /dev/null +++ b/zen/src/backend/immersive/shm.c @@ -0,0 +1,56 @@ +#include "shm.h" + +#include +#include + +#include "shm-pool.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen-common/wl-array.h" + +static void +zn_shm_protocol_create_pool(struct wl_client *client, + struct wl_resource *resource, uint32_t id, int fd, + struct wl_array *size_array) +{ + int64_t size = 0; + + if (!zn_wl_array_to_int64_t(size_array, &size)) { + wl_resource_post_error(resource, ZWN_COMPOSITOR_ERROR_INVALID_WL_ARRAY_SIZE, + "invalid wl_array size"); + return; + } + + zn_shm_pool_create(client, id, fd, size, resource); + + close(fd); +} + +static const struct zwn_shm_interface implementation = { + .create_pool = zn_shm_protocol_create_pool, +}; + +static void +zn_shm_bind( + struct wl_client *client, void *data UNUSED, uint32_t version, uint32_t id) +{ + struct wl_resource *resource = + wl_resource_create(client, &zwn_shm_interface, (int)version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + zn_error("Failed to create a wl_resource"); + return; + } + + wl_resource_set_implementation(resource, &implementation, NULL, NULL); +} + +bool +zn_shm_init(struct wl_display *display) +{ + if (!wl_global_create(display, &zwn_shm_interface, 1, NULL, zn_shm_bind)) { + return false; + } + + return true; +} diff --git a/zen/src/backend/immersive/shm.h b/zen/src/backend/immersive/shm.h new file mode 100644 index 00000000..635b9690 --- /dev/null +++ b/zen/src/backend/immersive/shm.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +/// @return true if successful, false otherwise +bool zn_shm_init(struct wl_display *display); diff --git a/zen/src/backend/immersive/xr-compositor.c b/zen/src/backend/immersive/xr-compositor.c new file mode 100644 index 00000000..a6884315 --- /dev/null +++ b/zen/src/backend/immersive/xr-compositor.c @@ -0,0 +1,80 @@ +#include "xr-compositor.h" + +#include + +#include "client-virtual-object.h" +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" +#include "zen/xr-system.h" + +static void +zn_xr_compositor_protocol_create_virtual_object( + struct wl_client *client, struct wl_resource *resource UNUSED, uint32_t id) +{ + zn_client_virtual_object_create(client, id); +} + +static void +zn_xr_compositor_protocol_create_region(struct wl_client *client UNUSED, + struct wl_resource *resource UNUSED, uint32_t id UNUSED) +{} + +static const struct zwn_compositor_interface implementation = { + .create_virtual_object = zn_xr_compositor_protocol_create_virtual_object, + .create_region = zn_xr_compositor_protocol_create_region, +}; + +static void +zn_xr_compositor_bind( + struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct zn_xr_compositor *self = data; + + struct wl_resource *resource = + wl_resource_create(client, &zwn_compositor_interface, (int)version, id); + if (resource == NULL) { + zn_error("Failed to create a wl_resource"); + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &implementation, self, NULL); +} + +struct zn_xr_compositor * +zn_xr_compositor_create(struct wl_display *display UNUSED) +{ + struct zn_xr_compositor *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + wl_signal_init(&self->events.destroy); + + self->global = wl_global_create( + display, &zwn_compositor_interface, 1, self, zn_xr_compositor_bind); + if (self->global == NULL) { + zn_error("Failed to create zwn_compositor global"); + goto err_free; + } + + return self; + +err_free: + free(self); + +err: + return NULL; +} + +void +zn_xr_compositor_destroy(struct zn_xr_compositor *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_global_destroy(self->global); + wl_list_remove(&self->events.destroy.listener_list); + free(self); +} diff --git a/zen/src/backend/immersive/xr-compositor.h b/zen/src/backend/immersive/xr-compositor.h new file mode 100644 index 00000000..342a4388 --- /dev/null +++ b/zen/src/backend/immersive/xr-compositor.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +struct zn_xr_compositor { + struct wl_global *global; // @nonnull, @owning + + struct { + struct wl_signal destroy; + } events; +}; + +struct zn_xr_compositor *zn_xr_compositor_create(struct wl_display *display); + +void zn_xr_compositor_destroy(struct zn_xr_compositor *self); diff --git a/zen/src/backend/immersive/xr-shell.h b/zen/src/backend/immersive/xr-shell.h new file mode 100644 index 00000000..5f4679c4 --- /dev/null +++ b/zen/src/backend/immersive/xr-shell.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +struct zn_xr_shell { + struct wl_global *global; // @nonnull, @outlive +}; + +struct zn_xr_shell *zn_xr_shell_create(struct wl_display *display); + +void zn_xr_shell_destroy(struct zn_xr_shell *self); diff --git a/zen/src/backend/immersive/xr-system-manager.h b/zen/src/backend/immersive/xr-system-manager.h new file mode 100644 index 00000000..600318c4 --- /dev/null +++ b/zen/src/backend/immersive/xr-system-manager.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_xr_system_manager; + +struct zn_xr_system_manager_interface { + void (*destroy)(struct zn_xr_system_manager *self); +}; + +struct zn_xr_system_manager { + void *impl_data; // @nullable, @outlive if exists + const struct zn_xr_system_manager_interface *impl; // @nonnull, @outlive + + struct { + struct wl_signal new_system; // (struct zn_xr_system *) + } events; +}; + +struct zn_xr_system_manager *zn_xr_system_manager_create_remote( + struct wl_display *display); + +UNUSED static inline void +zn_xr_system_manager_destroy(struct zn_xr_system_manager *self) +{ + self->impl->destroy(self); +} + +#ifdef __cplusplus +} +#endif diff --git a/zen/src/backend/immersive/xr.c b/zen/src/backend/immersive/xr.c new file mode 100644 index 00000000..809f690b --- /dev/null +++ b/zen/src/backend/immersive/xr.c @@ -0,0 +1,58 @@ +#include "xr.h" + +#include "xr-compositor.h" +#include "xr-shell.h" +#include "xr-system-manager.h" +#include "zen-common/log.h" +#include "zen-common/util.h" + +struct zn_xr * +zn_xr_create(struct wl_display *display) +{ + struct zn_xr *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->xr_system_manager = zn_xr_system_manager_create_remote(display); + if (self->xr_system_manager == NULL) { + zn_error("Failed to create zn_xr_system_manager"); + goto err_free; + } + + self->xr_compositor = zn_xr_compositor_create(display); + if (self->xr_compositor == NULL) { + zn_error("Failed to create zn_xr_compositor"); + goto err_free_system_manager; + } + + self->xr_shell = zn_xr_shell_create(display); + if (self->xr_shell == NULL) { + zn_error("Failed to create zn_xr_shell"); + goto err_xr_compositor; + } + + return self; + +err_xr_compositor: + zn_xr_compositor_destroy(self->xr_compositor); + +err_free_system_manager: + zn_xr_system_manager_destroy(self->xr_system_manager); + +err_free: + free(self); + +err: + return NULL; +} + +void +zn_xr_destroy(struct zn_xr *self) +{ + zn_xr_shell_destroy(self->xr_shell); + zn_xr_compositor_destroy(self->xr_compositor); + zn_xr_system_manager_destroy(self->xr_system_manager); + free(self); +} diff --git a/zen/src/backend/immersive/xr.h b/zen/src/backend/immersive/xr.h new file mode 100644 index 00000000..0057cc5a --- /dev/null +++ b/zen/src/backend/immersive/xr.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +struct zn_xr_system_manager; +struct zn_xr_compositor; +struct zn_xr_shell; + +struct zn_xr { + struct zn_xr_system_manager *xr_system_manager; // @nonnull, @owning + + struct zn_xr_compositor *xr_compositor; // @nonnull, @owning + struct zn_xr_shell *xr_shell; // @nonnull, @owning +}; + +struct zn_xr *zn_xr_create(struct wl_display *display); + +void zn_xr_destroy(struct zn_xr *self); diff --git a/zen/src/backend/meson.build b/zen/src/backend/meson.build index c8746b0a..517903f4 100644 --- a/zen/src/backend/meson.build +++ b/zen/src/backend/meson.build @@ -1,13 +1,37 @@ +subdir('immersive/gl') +subdir('immersive/remote') +subdir('immersive/shell') + _zen_backend_srcs = [ - 'backend.c', - 'compositor.c', - 'keyboard.c', - 'layer-surface.c', - 'output.c', - 'pointer.c', - 'screen-backend.c', - 'surface-snode.c', - 'xwayland-surface.c', + 'default-backend.c', + + 'immersive/client-virtual-object.c', + 'immersive/shm.c', + 'immersive/shm-buffer.c', + 'immersive/shm-pool.c', + 'immersive/xr.c', + 'immersive/xr-compositor.c', + + 'screen/compositor.c', + 'screen/keyboard.c', + 'screen/layer-shell.c', + 'screen/layer-surface.c', + 'screen/output.c', + 'screen/pointer.c', + 'screen/surface-snode.c', + 'screen/xdg-decoration.c', + 'screen/xdg-toplevel.c', + 'screen/xwayland-surface.c', + + protocols_code['zwin'], + protocols_server_header['zwin'], +] + +_zen_backend_deps = [ + _zen_deps, + zen_gl_immersive_backend_dep, + zen_remote_immersive_backend_dep, + zen_shell_immersive_backend_dep, ] _zen_backend_lib = static_library( @@ -15,7 +39,7 @@ _zen_backend_lib = static_library( _zen_backend_srcs, install: false, include_directories: zen_private_inc, - dependencies: _zen_deps, + dependencies: _zen_backend_deps, ) zen_backend_dep = declare_dependency( diff --git a/zen/src/backend/compositor.c b/zen/src/backend/screen/compositor.c similarity index 74% rename from zen/src/backend/compositor.c rename to zen/src/backend/screen/compositor.c index aceeb2b2..a14739b9 100644 --- a/zen/src/backend/compositor.c +++ b/zen/src/backend/screen/compositor.c @@ -1,10 +1,12 @@ -#include "backend/compositor.h" +#include "compositor.h" #include +#include "layer-shell.h" #include "layer-surface.h" #include "output.h" -#include "screen-backend.h" +#include "xdg-decoration.h" +#include "xdg-toplevel.h" #include "xwayland-surface.h" #include "zen-common/log.h" #include "zen-common/util.h" @@ -26,6 +28,17 @@ zn_compositor_handle_new_xwayland_surface( zn_xwayland_surface_create(wlr_xsurface); } +static void +zn_compositor_handle_new_xdg_surface( + struct wl_listener *listener UNUSED, void *data) +{ + struct wlr_xdg_surface *xdg_surface = data; + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + zn_xdg_toplevel_create(xdg_surface->toplevel); + } +} + static void zn_compositor_handle_new_layer_surface( struct wl_listener *listener UNUSED, void *data) @@ -58,8 +71,28 @@ zn_compositor_handle_new_layer_surface( return; } - zn_screen_backend_add_layer_surface( - output->screen_backend, layer_surface, wlr_layer_surface->current.layer); + zn_layer_shell_add_layer_surface( + output->layer_shell, layer_surface, wlr_layer_surface->current.layer); +} + +static void +zn_compositor_handle_new_xdg_decoration( + struct wl_listener *listener UNUSED, void *data) +{ + struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration = data; + + struct zn_xdg_decoration *decoration = + zn_xdg_decoration_create(wlr_decoration); + if (decoration == NULL) { + zn_error("Failed to create zn_xdg_decoration"); + return; + } + + struct zn_xdg_toplevel *toplevel = + zn_xdg_toplevel_from_wlr_xdg_surface(wlr_decoration->surface); + if (toplevel) { + zn_xdg_toplevel_set_decoration(toplevel, decoration); + } } static void @@ -125,6 +158,12 @@ zn_compositor_create(struct wl_display *display, struct wlr_renderer *renderer) goto err_output_layout; } + self->xdg_decoration_manager = wlr_xdg_decoration_manager_v1_create(display); + if (self->xdg_decoration_manager == NULL) { + zn_error("Failed to create a wlr_xdg_decoration_manager_v1"); + goto err_output_layout; + } + for (int i = 0; i <= 32; i++) { sprintf(socket_name_candidate, "wayland-%d", i); // NOLINT(cert-err33-c) if (wl_display_add_socket(display, socket_name_candidate) >= 0) { @@ -157,11 +196,20 @@ zn_compositor_create(struct wl_display *display, struct wlr_renderer *renderer) wl_signal_add(&self->xwayland->events.new_surface, &self->new_xwayland_surface_listener); + self->new_xdg_surface_listener.notify = zn_compositor_handle_new_xdg_surface; + wl_signal_add( + &self->xdg_shell->events.new_surface, &self->new_xdg_surface_listener); + self->new_layer_surface_listener.notify = zn_compositor_handle_new_layer_surface; wl_signal_add(&self->wlr_layer_shell->events.new_surface, &self->new_layer_surface_listener); + self->new_xdg_decoration_listener.notify = + zn_compositor_handle_new_xdg_decoration; + wl_signal_add(&self->xdg_decoration_manager->events.new_toplevel_decoration, + &self->new_xdg_decoration_listener); + self->server_end_listener.notify = zn_compositor_handle_server_end; wl_signal_add(&server->events.end, &self->server_end_listener); @@ -181,7 +229,9 @@ void zn_compositor_destroy(struct zn_compositor *self) { wl_list_remove(&self->server_end_listener.link); + wl_list_remove(&self->new_xdg_decoration_listener.link); wl_list_remove(&self->new_layer_surface_listener.link); + wl_list_remove(&self->new_xdg_surface_listener.link); wl_list_remove(&self->new_xwayland_surface_listener.link); if (self->xwayland) { wlr_xwayland_destroy(self->xwayland); diff --git a/zen/src/backend/compositor.h b/zen/src/backend/screen/compositor.h similarity index 84% rename from zen/src/backend/compositor.h rename to zen/src/backend/screen/compositor.h index 4f65a24a..615c768c 100644 --- a/zen/src/backend/compositor.h +++ b/zen/src/backend/screen/compositor.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,8 @@ struct zn_compositor { struct wlr_layer_shell_v1 *wlr_layer_shell; // @nonnull, @outlive struct wlr_data_device_manager *data_device_manager; // @nonnull, @outlive struct wlr_xdg_output_manager_v1 *xdg_output_manager; // @nonnull, @outlive + struct wlr_xdg_decoration_manager_v1 + *xdg_decoration_manager; // @nonnull, @outlive // TODO(@Aki-7): Use this appropriately #388 struct wlr_output_layout *output_layout; // @nonnull, @owning @@ -22,7 +25,9 @@ struct zn_compositor { struct wlr_xwayland *xwayland; // @nullable, @owning struct wl_listener new_xwayland_surface_listener; + struct wl_listener new_xdg_surface_listener; struct wl_listener new_layer_surface_listener; + struct wl_listener new_xdg_decoration_listener; struct wl_listener server_end_listener; }; diff --git a/zen/src/backend/input-device.h b/zen/src/backend/screen/input-device.h similarity index 100% rename from zen/src/backend/input-device.h rename to zen/src/backend/screen/input-device.h diff --git a/zen/src/backend/keyboard.c b/zen/src/backend/screen/keyboard.c similarity index 81% rename from zen/src/backend/keyboard.c rename to zen/src/backend/screen/keyboard.c index fafeb026..bce4f9fd 100644 --- a/zen/src/backend/keyboard.c +++ b/zen/src/backend/screen/keyboard.c @@ -1,9 +1,14 @@ #include "keyboard.h" +#include +#include +#include #include -#include "backend.h" +#include "binding.h" +#include "default-backend.h" #include "zen-common/log.h" +#include "zen-common/terminate.h" #include "zen-common/util.h" #include "zen/seat.h" #include "zen/server.h" @@ -28,11 +33,25 @@ zn_keyboard_handle_keyboard_key(struct wl_listener *listener, void *user_data) struct wlr_event_keyboard_key *event = user_data; struct zn_server *server = zn_server_get_singleton(); - // TODO(@Aki-7): Handle keyboard bindings + // TODO(@Aki-7): For the development convenience, fix this later + if (wlr_keyboard_get_modifiers(self->base.wlr_input_device->keyboard) == + WLR_MODIFIER_ALT && + event->keycode == KEY_Q) { + zn_terminate(EXIT_SUCCESS); + return; + } wlr_seat_set_keyboard(server->seat->wlr_seat, self->base.wlr_input_device); - wlr_seat_keyboard_send_key( - server->seat->wlr_seat, event->time_msec, event->keycode, event->state); + + uint32_t modifiers = wlr_keyboard_get_modifiers( + server->seat->wlr_seat->keyboard_state.keyboard); + bool handled = + zn_binding_handle_key(server->binding, event->keycode, modifiers); + + if (!handled) { + wlr_seat_keyboard_send_key( + server->seat->wlr_seat, event->time_msec, event->keycode, event->state); + } } static void diff --git a/zen/src/backend/keyboard.h b/zen/src/backend/screen/keyboard.h similarity index 100% rename from zen/src/backend/keyboard.h rename to zen/src/backend/screen/keyboard.h diff --git a/zen/src/backend/screen-backend.c b/zen/src/backend/screen/layer-shell.c similarity index 91% rename from zen/src/backend/screen-backend.c rename to zen/src/backend/screen/layer-shell.c index 631c53aa..402f46e9 100644 --- a/zen/src/backend/screen-backend.c +++ b/zen/src/backend/screen/layer-shell.c @@ -1,4 +1,4 @@ -#include "screen-backend.h" +#include "layer-shell.h" #include @@ -147,7 +147,7 @@ calculate_surface_box( } static void -zn_screen_backend_arrange_layer(struct zn_screen_backend *self, +zn_layer_shell_arrange_layer(struct zn_layer_shell *self, enum zwlr_layer_shell_v1_layer layer, struct wlr_box *usable_area, bool exclusive) { @@ -198,7 +198,7 @@ zn_screen_backend_arrange_layer(struct zn_screen_backend *self, } static void -zn_screen_backend_arrange_layers(struct zn_screen_backend *self) +zn_layer_shell_arrange_layers(struct zn_layer_shell *self) { struct wlr_box usable_area = {0}; wlr_output_effective_resolution( @@ -206,41 +206,41 @@ zn_screen_backend_arrange_layers(struct zn_screen_backend *self) // Arrange exclusive surfaces from top to bottom for (int i = 3; i >= 0; i--) { - zn_screen_backend_arrange_layer(self, i, &usable_area, true); + zn_layer_shell_arrange_layer(self, i, &usable_area, true); } // TODO(@Aki-7): Notify screen the usable area change // Arrange non-exclusive surfaces from top to bottom for (int i = 3; i >= 0; i--) { - zn_screen_backend_arrange_layer(self, i, &usable_area, false); + zn_layer_shell_arrange_layer(self, i, &usable_area, false); } // TODO(@Aki-7): Configure keyboard interactivity } void -zn_screen_backend_add_layer_surface(struct zn_screen_backend *self, +zn_layer_shell_add_layer_surface(struct zn_layer_shell *self, struct zn_layer_surface *layer_surface, enum zwlr_layer_shell_v1_layer layer) { zn_snode_set_position( layer_surface->snode, self->layers[layer], GLM_VEC2_ZERO); - zn_screen_backend_arrange_layers(self); + zn_layer_shell_arrange_layers(self); } struct zn_snode * -zn_screen_backend_get_layer( - struct zn_screen_backend *self, enum zwlr_layer_shell_v1_layer layer) +zn_layer_shell_get_layer( + struct zn_layer_shell *self, enum zwlr_layer_shell_v1_layer layer) { return self->layers[layer]; } -struct zn_screen_backend * -zn_screen_backend_create(struct zn_output *output) +struct zn_layer_shell * +zn_layer_shell_create(struct zn_output *output) { - struct zn_screen_backend *self = zalloc(sizeof *self); + struct zn_layer_shell *self = zalloc(sizeof *self); if (self == NULL) { zn_error("Failed to allocate memory"); goto err; @@ -272,7 +272,7 @@ zn_screen_backend_create(struct zn_output *output) } void -zn_screen_backend_destroy(struct zn_screen_backend *self) +zn_layer_shell_destroy(struct zn_layer_shell *self) { for (int i = 0; i < 4; i++) { zn_snode_destroy(self->layers[i]); diff --git a/zen/src/backend/screen-backend.h b/zen/src/backend/screen/layer-shell.h similarity index 57% rename from zen/src/backend/screen-backend.h rename to zen/src/backend/screen/layer-shell.h index 94245b21..804e829f 100644 --- a/zen/src/backend/screen-backend.h +++ b/zen/src/backend/screen/layer-shell.h @@ -5,7 +5,7 @@ struct zn_snode; struct zn_layer_surface; -struct zn_screen_backend { +struct zn_layer_shell { struct zn_output *output; // @nonnull, @outlive // Every child snode in each layer snode must be that of zn_layer_surface @@ -14,13 +14,13 @@ struct zn_screen_backend { struct zn_snode *layers[4]; // each layer is @nonnull, @owning }; -void zn_screen_backend_add_layer_surface(struct zn_screen_backend *self, +void zn_layer_shell_add_layer_surface(struct zn_layer_shell *self, struct zn_layer_surface *layer_surface, enum zwlr_layer_shell_v1_layer layer); -struct zn_snode *zn_screen_backend_get_layer( - struct zn_screen_backend *self, enum zwlr_layer_shell_v1_layer layer); +struct zn_snode *zn_layer_shell_get_layer( + struct zn_layer_shell *self, enum zwlr_layer_shell_v1_layer layer); -struct zn_screen_backend *zn_screen_backend_create(struct zn_output *output); +struct zn_layer_shell *zn_layer_shell_create(struct zn_output *output); -void zn_screen_backend_destroy(struct zn_screen_backend *self); +void zn_layer_shell_destroy(struct zn_layer_shell *self); diff --git a/zen/src/backend/layer-surface.c b/zen/src/backend/screen/layer-surface.c similarity index 97% rename from zen/src/backend/layer-surface.c rename to zen/src/backend/screen/layer-surface.c index 431f3eed..a3c55f20 100644 --- a/zen/src/backend/layer-surface.c +++ b/zen/src/backend/screen/layer-surface.c @@ -3,8 +3,8 @@ #include #include +#include "layer-shell.h" #include "output.h" -#include "screen-backend.h" #include "surface-snode.h" #include "zen-common/log.h" #include "zen-common/util.h" @@ -58,7 +58,7 @@ zn_layer_surface_handle_surface_commit( bool layer_changed = self->layer != self->wlr_layer_surface->current.layer; if (layer_changed) { - zn_screen_backend_add_layer_surface(self->output->screen_backend, self, + zn_layer_shell_add_layer_surface(self->output->layer_shell, self, self->wlr_layer_surface->current.layer); zn_surface_snode_damage_whole(self->surface_snode); } else { diff --git a/zen/src/backend/layer-surface.h b/zen/src/backend/screen/layer-surface.h similarity index 100% rename from zen/src/backend/layer-surface.h rename to zen/src/backend/screen/layer-surface.h diff --git a/zen/src/backend/output.c b/zen/src/backend/screen/output.c similarity index 95% rename from zen/src/backend/output.c rename to zen/src/backend/screen/output.c index 88a75ebe..39dd19f5 100644 --- a/zen/src/backend/output.c +++ b/zen/src/backend/screen/output.c @@ -11,14 +11,15 @@ #include #include -#include "backend.h" #include "compositor.h" -#include "screen-backend.h" +#include "default-backend.h" +#include "layer-shell.h" +#include "screen.h" #include "zen-common/log.h" #include "zen-common/signal.h" #include "zen-common/util.h" -#include "zen/screen.h" #include "zen/server.h" +#include "zen/snode-root.h" #include "zen/snode.h" static void zn_output_destroy(struct zn_output *self); @@ -145,7 +146,7 @@ zn_output_render(struct zn_output *self, pixman_region32_t *damage) wlr_renderer_clear(self->wlr_output->renderer, screen_background_color); } - zn_output_render_snode(self, self->screen->snode_root, &screen_damage); + zn_output_render_snode(self, self->screen->snode_root->node, &screen_damage); screen_damage_finish: pixman_region32_fini(&screen_damage); @@ -208,7 +209,7 @@ static struct zn_snode * zn_output_get_layer(void *impl_data, enum zwlr_layer_shell_v1_layer layer) { struct zn_output *self = impl_data; - return zn_screen_backend_get_layer(self->screen_backend, layer); + return zn_layer_shell_get_layer(self->layer_shell, layer); } const struct zn_screen_interface screen_implementation = { @@ -279,16 +280,16 @@ zn_output_create(struct wlr_output *wlr_output) self->wlr_output = wlr_output; wlr_output->data = self; - self->screen_backend = zn_screen_backend_create(self); - if (self->screen_backend == NULL) { - zn_error("Failed to create a zn_screen_backend"); + self->layer_shell = zn_layer_shell_create(self); + if (self->layer_shell == NULL) { + zn_error("Failed to create a zn_layer_shell"); goto err_free; } self->screen = zn_screen_create(self, &screen_implementation); if (self->screen == NULL) { zn_error("Failed to create a zn_screen"); - goto err_screen_backend; + goto err_layer_shell; } self->damage = wlr_output_damage_create(wlr_output); @@ -345,8 +346,8 @@ zn_output_create(struct wlr_output *wlr_output) err_screen: zn_screen_destroy(self->screen); -err_screen_backend: - zn_screen_backend_destroy(self->screen_backend); +err_layer_shell: + zn_layer_shell_destroy(self->layer_shell); err_free: free(self); @@ -372,6 +373,6 @@ zn_output_destroy(struct zn_output *self) */ // wl_list_remove(&self->damage_frame_listener.link); - zn_screen_backend_destroy(self->screen_backend); + zn_layer_shell_destroy(self->layer_shell); free(self); } diff --git a/zen/src/backend/output.h b/zen/src/backend/screen/output.h similarity index 83% rename from zen/src/backend/output.h rename to zen/src/backend/screen/output.h index 176dcfe6..fe23a3d5 100644 --- a/zen/src/backend/output.h +++ b/zen/src/backend/screen/output.h @@ -5,11 +5,11 @@ #include struct zn_screen; -struct zn_screen_backend; +struct zn_layer_shell; struct zn_output { - struct zn_screen *screen; // @nonnull, @owning - struct zn_screen_backend *screen_backend; // @nonnull, @owning + struct zn_screen *screen; // @nonnull, @owning + struct zn_layer_shell *layer_shell; // @nonnull, @owning struct wlr_output *wlr_output; // @nonnull, @outlive diff --git a/zen/src/backend/pointer.c b/zen/src/backend/screen/pointer.c similarity index 99% rename from zen/src/backend/pointer.c rename to zen/src/backend/screen/pointer.c index 59c0fcc2..2d8efa0a 100644 --- a/zen/src/backend/pointer.c +++ b/zen/src/backend/screen/pointer.c @@ -4,8 +4,8 @@ #include #include -#include "backend.h" #include "cursor.h" +#include "default-backend.h" #include "seat.h" #include "zen-common/log.h" #include "zen-common/util.h" diff --git a/zen/src/backend/pointer.h b/zen/src/backend/screen/pointer.h similarity index 100% rename from zen/src/backend/pointer.h rename to zen/src/backend/screen/pointer.h diff --git a/zen/src/backend/surface-snode.c b/zen/src/backend/screen/surface-snode.c similarity index 50% rename from zen/src/backend/surface-snode.c rename to zen/src/backend/screen/surface-snode.c index 192b8097..8ffbe983 100644 --- a/zen/src/backend/surface-snode.c +++ b/zen/src/backend/screen/surface-snode.c @@ -1,5 +1,8 @@ #include "surface-snode.h" +#include +#include + #include "zen-common/log.h" #include "zen-common/signal.h" #include "zen-common/util.h" @@ -94,6 +97,84 @@ static const struct zn_snode_interface implementation = { .on_focus = zn_snode_noop_on_focus, }; +typedef void (*zn_surface_snode_iter_children_func_t)( + struct zn_surface_snode *self, struct zn_surface_snode *child, void *data); + +static void +zn_surface_snode_iter_children(struct zn_surface_snode *self, + zn_surface_snode_iter_children_func_t func, void *data) +{ + struct zn_snode *snode = NULL; + + wl_list_for_each ( + snode, &self->subsurfaces_below_node->child_node_list, link) { + struct zn_surface_snode *surface_snode = snode->user_data; + func(self, surface_snode, data); + } + + wl_list_for_each ( + snode, &self->subsurfaces_above_node->child_node_list, link) { + struct zn_surface_snode *surface_snode = snode->user_data; + func(self, surface_snode, data); + } +} + +static void +zn_surface_snode_update_child_subsurface_position( + struct zn_surface_snode *self UNUSED, struct zn_surface_snode *child, + void *data UNUSED) +{ + if (!wlr_surface_is_subsurface(child->surface)) { + return; + } + + struct wlr_subsurface *subsurface = + wlr_subsurface_from_wlr_surface(child->surface); + + vec2 position = {(float)subsurface->current.x, (float)subsurface->current.y}; + + zn_snode_change_position(child->surface_node, position); +} + +static void +zn_surface_snode_update_child_subsurfaces_position( + struct zn_surface_snode *self) +{ + zn_surface_snode_iter_children( + self, zn_surface_snode_update_child_subsurface_position, NULL); +} + +static void +zn_surface_snode_handle_new_subsurface( + struct wl_listener *listener UNUSED, void *user_data) +{ + struct wlr_subsurface *subsurface = user_data; + + zn_surface_snode_create(subsurface->surface); +} + +static void +zn_surface_snode_handle_commit( + struct wl_listener *listener, void *user_data UNUSED) +{ + struct zn_surface_snode *self = + zn_container_of(listener, self, surface_commit_listener); + + zn_surface_snode_commit_damage(self); + + zn_surface_snode_update_child_subsurfaces_position(self); +} + +static void +zn_surface_snode_handle_surface_destroy( + struct wl_listener *listener, void *user_data UNUSED) +{ + struct zn_surface_snode *self = + zn_container_of(listener, self, surface_destroy_listener); + + zn_surface_snode_destroy(self); +} + void zn_surface_snode_commit_damage(struct zn_surface_snode *self) { @@ -116,7 +197,7 @@ zn_surface_snode_commit_damage(struct zn_surface_snode *self) .height = rects[i].y2 - rects[i].y1, }; - zn_snode_damage(self->snode, &damage_fbox); + zn_snode_damage(self->surface_node, &damage_fbox); } pixman_region32_fini(&damage); @@ -125,17 +206,7 @@ zn_surface_snode_commit_damage(struct zn_surface_snode *self) void zn_surface_snode_damage_whole(struct zn_surface_snode *self) { - zn_snode_damage_whole(self->snode); -} - -static void -zn_surface_snode_handle_surface_destroy( - struct wl_listener *listener, void *user_data UNUSED) -{ - struct zn_surface_snode *self = - zn_container_of(listener, self, surface_destroy_listener); - - zn_surface_snode_destroy(self); + zn_snode_damage_whole(self->surface_node); } struct zn_surface_snode * @@ -149,19 +220,81 @@ zn_surface_snode_create(struct wlr_surface *surface) self->surface = surface; wl_signal_init(&self->events.destroy); + surface->data = self; - self->snode = zn_snode_create(self, &implementation); + self->snode = zn_snode_create(self, &zn_snode_noop_implementation); if (self->snode == NULL) { zn_error("Failed to create a snode"); goto err_free; } + self->surface_node = zn_snode_create(self, &implementation); + if (self->surface_node == NULL) { + zn_error("Failed to create a surface snode"); + goto err_snode; + } + + self->subsurfaces_above_node = + zn_snode_create(self, &zn_snode_noop_implementation); + if (self->subsurfaces_above_node == NULL) { + zn_error("Failed to create an above subsurfaces node"); + goto err_surface_node; + } + + self->subsurfaces_below_node = + zn_snode_create(self, &zn_snode_noop_implementation); + if (self->subsurfaces_below_node == NULL) { + zn_error("Failed to create an below subsurface node"); + goto err_subsurface_above_node; + } + self->surface_destroy_listener.notify = zn_surface_snode_handle_surface_destroy; wl_signal_add(&surface->events.destroy, &self->surface_destroy_listener); + self->new_subsurface_listener.notify = zn_surface_snode_handle_new_subsurface; + wl_signal_add( + &surface->events.new_subsurface, &self->new_subsurface_listener); + + self->surface_commit_listener.notify = zn_surface_snode_handle_commit; + wl_signal_add(&self->surface->events.commit, &self->surface_commit_listener); + + zn_snode_set_position( + self->subsurfaces_below_node, self->snode, GLM_VEC2_ZERO); + zn_snode_set_position(self->surface_node, self->snode, GLM_VEC2_ZERO); + zn_snode_set_position( + self->subsurfaces_above_node, self->snode, GLM_VEC2_ZERO); + + struct wlr_subsurface *subsurface = NULL; + wl_list_for_each ( + subsurface, &self->surface->current.subsurfaces_below, current.link) { + struct zn_surface_snode *surface_snode = + zn_surface_snode_create(subsurface->surface); + zn_snode_set_position( + surface_snode->snode, self->subsurfaces_below_node, GLM_VEC2_ZERO); + } + + wl_list_for_each ( + subsurface, &self->surface->current.subsurfaces_above, current.link) { + struct zn_surface_snode *surface_snode = + zn_surface_snode_create(subsurface->surface); + zn_snode_set_position( + surface_snode->snode, self->subsurfaces_above_node, GLM_VEC2_ZERO); + } + + zn_surface_snode_update_child_subsurfaces_position(self); + return self; +err_subsurface_above_node: + zn_snode_destroy(self->subsurfaces_above_node); + +err_surface_node: + zn_snode_destroy(self->surface_node); + +err_snode: + zn_snode_destroy(self->snode); + err_free: free(self); @@ -174,7 +307,12 @@ zn_surface_snode_destroy(struct zn_surface_snode *self) { zn_signal_emit_mutable(&self->events.destroy, NULL); + zn_snode_destroy(self->subsurfaces_below_node); + zn_snode_destroy(self->subsurfaces_above_node); + zn_snode_destroy(self->surface_node); zn_snode_destroy(self->snode); + wl_list_remove(&self->new_subsurface_listener.link); + wl_list_remove(&self->surface_commit_listener.link); wl_list_remove(&self->surface_destroy_listener.link); wl_list_remove(&self->events.destroy.listener_list); diff --git a/zen/src/backend/surface-snode.h b/zen/src/backend/screen/surface-snode.h similarity index 60% rename from zen/src/backend/surface-snode.h rename to zen/src/backend/screen/surface-snode.h index 5bf1c378..62feb120 100644 --- a/zen/src/backend/surface-snode.h +++ b/zen/src/backend/screen/surface-snode.h @@ -6,10 +6,15 @@ struct zn_snode; // Lifetime is identical with given wlr_surface struct zn_surface_snode { - struct zn_snode *snode; // @nonnull, @owning + struct zn_snode *snode; // @nonnull, @owning + struct zn_snode *surface_node; // @nonnull, @owning + struct zn_snode *subsurfaces_below_node; // @nonnull, @owning + struct zn_snode *subsurfaces_above_node; // @nonnull, @owning struct wlr_surface *surface; // @nonnull, @outlive + struct wl_listener new_subsurface_listener; + struct wl_listener surface_commit_listener; struct wl_listener surface_destroy_listener; struct { diff --git a/zen/src/backend/screen/xdg-decoration.c b/zen/src/backend/screen/xdg-decoration.c new file mode 100644 index 00000000..4e9503af --- /dev/null +++ b/zen/src/backend/screen/xdg-decoration.c @@ -0,0 +1,82 @@ +#include "xdg-decoration.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +static void zn_xdg_decoration_destroy(struct zn_xdg_decoration *self); + +static void +zn_xdg_decoration_handle_wlr_decoration_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_xdg_decoration *self = + zn_container_of(listener, self, wlr_decoration_destroy_listener); + + zn_xdg_decoration_destroy(self); +} + +static void +zn_xdg_decoration_handle_request_mode( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_xdg_decoration *self = + zn_container_of(listener, self, wlr_decoration_request_mode_listener); + + // TODO(@Aki-7): make this configurable from the desktop component + enum wlr_xdg_toplevel_decoration_v1_mode mode = + WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; + + enum wlr_xdg_toplevel_decoration_v1_mode client_mode = + self->wlr_decoration->requested_mode; + + if (client_mode) { + mode = client_mode; + } + + wlr_xdg_toplevel_decoration_v1_set_mode(self->wlr_decoration, mode); +} + +struct zn_xdg_decoration * +zn_xdg_decoration_create(struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration) +{ + struct zn_xdg_decoration *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->wlr_decoration = wlr_decoration; + wl_signal_init(&self->events.destroy); + + self->wlr_decoration_destroy_listener.notify = + zn_xdg_decoration_handle_wlr_decoration_destroy; + wl_signal_add(&self->wlr_decoration->events.destroy, + &self->wlr_decoration_destroy_listener); + + self->wlr_decoration_request_mode_listener.notify = + zn_xdg_decoration_handle_request_mode; + wl_signal_add(&self->wlr_decoration->events.request_mode, + &self->wlr_decoration_request_mode_listener); + + /// For some clients, the set_mode request has already been processed at this + /// point. + zn_xdg_decoration_handle_request_mode( + &self->wlr_decoration_request_mode_listener, wlr_decoration); + + return self; + +err: + return NULL; +} + +static void +zn_xdg_decoration_destroy(struct zn_xdg_decoration *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + wl_list_remove(&self->wlr_decoration_request_mode_listener.link); + wl_list_remove(&self->wlr_decoration_destroy_listener.link); + free(self); +} diff --git a/zen/src/backend/screen/xdg-decoration.h b/zen/src/backend/screen/xdg-decoration.h new file mode 100644 index 00000000..967c71a9 --- /dev/null +++ b/zen/src/backend/screen/xdg-decoration.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +struct zn_xdg_decoration { + struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration; // @nonnull, @outlive + + struct wl_listener wlr_decoration_destroy_listener; + struct wl_listener wlr_decoration_request_mode_listener; + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +struct zn_xdg_decoration *zn_xdg_decoration_create( + struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration); diff --git a/zen/src/backend/screen/xdg-toplevel.c b/zen/src/backend/screen/xdg-toplevel.c new file mode 100644 index 00000000..6e1b1437 --- /dev/null +++ b/zen/src/backend/screen/xdg-toplevel.c @@ -0,0 +1,335 @@ +#include "xdg-toplevel.h" + +#include + +#include "default-backend.h" +#include "surface-snode.h" +#include "view.h" +#include "xdg-decoration.h" +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/seat.h" +#include "zen/server.h" +#include "zen/snode.h" + +static void zn_xdg_toplevel_destroy(struct zn_xdg_toplevel *self); + +static void +zn_xdg_toplevel_set_focus(void *impl_data, bool focused) +{ + struct zn_xdg_toplevel *self = impl_data; + struct zn_server *server = zn_server_get_singleton(); + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(server->seat->wlr_seat); + + if (keyboard) { + wlr_seat_keyboard_enter(server->seat->wlr_seat, + self->wlr_xdg_toplevel->base->surface, keyboard->keycodes, + keyboard->num_keycodes, &keyboard->modifiers); + } else { + wlr_seat_keyboard_enter(server->seat->wlr_seat, + self->wlr_xdg_toplevel->base->surface, NULL, 0, NULL); + } + + wlr_xdg_toplevel_set_activated(self->wlr_xdg_toplevel->base, focused); +} + +static void +zn_xdg_toplevel_configure_size(void *impl_data, vec2 size) +{ + struct zn_xdg_toplevel *self = impl_data; + + wlr_xdg_toplevel_set_size( + self->wlr_xdg_toplevel->base, (uint32_t)size[0], (uint32_t)size[1]); +} + +static void +zn_xdg_toplevel_close(void *impl_data) +{ + struct zn_xdg_toplevel *self = impl_data; + + wlr_xdg_toplevel_send_close(self->wlr_xdg_toplevel->base); +} + +static const struct zn_view_interface view_implementation = { + .set_focus = zn_xdg_toplevel_set_focus, + .configure_size = zn_xdg_toplevel_configure_size, + .close = zn_xdg_toplevel_close, +}; + +static void +zn_xdg_toplevel_update_view_size(struct zn_xdg_toplevel *self) +{ + struct wlr_box geometry; + wlr_xdg_surface_get_geometry(self->wlr_xdg_toplevel->base, &geometry); + vec2 new_size = {(float)geometry.width, (float)geometry.height}; + + if (!glm_vec2_eqv(self->view->size, new_size)) { + zn_view_notify_resized(self->view, new_size); + } +} + +static void +zn_xdg_toplevel_update_surface_position(struct zn_xdg_toplevel *self) +{ + struct wlr_box geometry; + wlr_xdg_surface_get_geometry(self->wlr_xdg_toplevel->base, &geometry); + + if (self->surface_snode == NULL) { + return; + } + + struct zn_snode *snode = self->surface_snode->snode; + vec2 position = {(float)-geometry.x, (float)-geometry.y}; + + zn_snode_change_position(snode, position); +} + +static void +zn_xdg_toplevel_update_view_decoration( + struct zn_xdg_toplevel *self, bool use_pending_state) +{ + enum wlr_xdg_toplevel_decoration_v1_mode wlr_mode = + WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_NONE; + + if (self->decoration) { + wlr_mode = use_pending_state + ? self->decoration->wlr_decoration->pending.mode + : self->decoration->wlr_decoration->current.mode; + } + + enum zn_view_decoration_mode mode = ZN_VIEW_DECORATION_MODE_CLIENT_SIDE; + + switch (wlr_mode) { + case WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: + mode = ZN_VIEW_DECORATION_MODE_CLIENT_SIDE; + break; + case WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: + mode = ZN_VIEW_DECORATION_MODE_SERVER_SIDE; + break; + case WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_NONE: + mode = ZN_VIEW_DECORATION_MODE_CLIENT_SIDE; + break; + } + + if (self->view->decoration_mode != mode) { + zn_view_notify_decoration(self->view, mode); + } +} + +static void +zn_xdg_toplevel_handle_surface_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_xdg_toplevel *self = + zn_container_of(listener, self, surface_destroy_listener); + + zn_xdg_toplevel_destroy(self); +} + +static void +zn_xdg_toplevel_handle_surface_map( + struct wl_listener *listener UNUSED, void *data UNUSED) +{ + struct zn_xdg_toplevel *self = + zn_container_of(listener, self, surface_map_listener); + struct zn_server *server = zn_server_get_singleton(); + struct zn_default_backend *backend = zn_default_backend_get(server->backend); + + wl_signal_add(&self->wlr_xdg_toplevel->base->surface->events.commit, + &self->surface_commit_listener); + + self->surface_snode = + zn_surface_snode_create(self->wlr_xdg_toplevel->base->surface); + if (self->surface_snode == NULL) { + zn_error("Failed to create a surface snode"); + wlr_xdg_toplevel_send_close(self->wlr_xdg_toplevel->base); + return; + } + + // TODO(@Aki-7): Respect window geometry + zn_snode_set_position( + self->surface_snode->snode, self->view->snode, GLM_VEC2_ZERO); + + zn_xdg_toplevel_update_surface_position(self); + + zn_xdg_toplevel_update_view_size(self); + + zn_xdg_toplevel_update_view_decoration(self, false); + + zn_default_backend_notify_view_mapped(backend, self->view); +} + +static void +zn_xdg_toplevel_handle_surface_unmap( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_xdg_toplevel *self = + zn_container_of(listener, self, surface_unmap_listener); + + wl_list_remove(&self->surface_commit_listener.link); + wl_list_init(&self->surface_commit_listener.link); + + zn_snode_set_position(self->surface_snode->snode, NULL, GLM_VEC2_ZERO); + self->surface_snode = NULL; + + zn_view_notify_unmap(self->view); +} + +static void +zn_xdg_toplevel_handle_surface_commit( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_xdg_toplevel *self = + zn_container_of(listener, self, surface_commit_listener); + + if (!zn_assert(self->surface_snode, + "Commit signal must be handled only when the view is mapped")) { + return; + } + + zn_surface_snode_commit_damage(self->surface_snode); + + zn_xdg_toplevel_update_surface_position(self); + + zn_xdg_toplevel_update_view_size(self); + + // Use the pending state because the commit signal handler of the decoration + // object can be called after this commit signal handler. + zn_xdg_toplevel_update_view_decoration(self, true); +} + +static void +zn_xdg_toplevel_handle_surface_move_request( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_xdg_toplevel *self = + zn_container_of(listener, self, surface_move_request_listener); + + zn_view_notify_move_request(self->view); +} + +static void +zn_xdg_toplevel_handle_surface_resize_request( + struct wl_listener *listener, void *data) +{ + struct zn_xdg_toplevel *self = + zn_container_of(listener, self, surface_resize_request_listener); + + struct wlr_xdg_toplevel_resize_event *wlr_event = data; + struct zn_view_resize_event event; + + event.edges = wlr_event->edges; + + zn_view_notify_resize_request(self->view, &event); +} + +static void +zn_xdg_toplevel_handle_decoration_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_xdg_toplevel *self = + zn_container_of(listener, self, decoration_destroy_listener); + + zn_xdg_toplevel_set_decoration(self, NULL); +} + +void +zn_xdg_toplevel_set_decoration( + struct zn_xdg_toplevel *self, struct zn_xdg_decoration *decoration) +{ + if (self->decoration) { + wl_list_remove(&self->decoration_destroy_listener.link); + wl_list_init(&self->decoration_destroy_listener.link); + } + + self->decoration = decoration; + + if (decoration) { + wl_signal_add( + &decoration->events.destroy, &self->decoration_destroy_listener); + } +} + +struct zn_xdg_toplevel * +zn_xdg_toplevel_from_wlr_xdg_surface(struct wlr_xdg_surface *surface) +{ + if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + return surface->data; + } + + return NULL; +} + +struct zn_xdg_toplevel * +zn_xdg_toplevel_create(struct wlr_xdg_toplevel *wlr_xdg_toplevel) +{ + struct zn_xdg_toplevel *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->wlr_xdg_toplevel = wlr_xdg_toplevel; + wlr_xdg_toplevel->base->data = self; + + self->view = zn_view_create(self, &view_implementation); + if (self->view == NULL) { + zn_error("Failed to create a zn_view"); + goto err_free; + } + + self->decoration = NULL; + self->surface_snode = NULL; + + self->surface_destroy_listener.notify = + zn_xdg_toplevel_handle_surface_destroy; + wl_signal_add(&self->wlr_xdg_toplevel->base->events.destroy, + &self->surface_destroy_listener); + + self->surface_map_listener.notify = zn_xdg_toplevel_handle_surface_map; + wl_signal_add( + &self->wlr_xdg_toplevel->base->events.map, &self->surface_map_listener); + + self->surface_unmap_listener.notify = zn_xdg_toplevel_handle_surface_unmap; + wl_signal_add(&self->wlr_xdg_toplevel->base->events.unmap, + &self->surface_unmap_listener); + + self->surface_commit_listener.notify = zn_xdg_toplevel_handle_surface_commit; + wl_list_init(&self->surface_commit_listener.link); + + self->surface_move_request_listener.notify = + zn_xdg_toplevel_handle_surface_move_request; + wl_signal_add(&self->wlr_xdg_toplevel->events.request_move, + &self->surface_move_request_listener); + + self->surface_resize_request_listener.notify = + zn_xdg_toplevel_handle_surface_resize_request; + wl_signal_add(&self->wlr_xdg_toplevel->events.request_resize, + &self->surface_resize_request_listener); + + self->decoration_destroy_listener.notify = + zn_xdg_toplevel_handle_decoration_destroy; + wl_list_init(&self->decoration_destroy_listener.link); + + return self; + +err_free: + free(self); + +err: + return NULL; +} + +static void +zn_xdg_toplevel_destroy(struct zn_xdg_toplevel *self) +{ + wl_list_remove(&self->decoration_destroy_listener.link); + wl_list_remove(&self->surface_resize_request_listener.link); + wl_list_remove(&self->surface_move_request_listener.link); + wl_list_remove(&self->surface_commit_listener.link); + wl_list_remove(&self->surface_unmap_listener.link); + wl_list_remove(&self->surface_map_listener.link); + wl_list_remove(&self->surface_destroy_listener.link); + zn_view_destroy(self->view); + free(self); +} diff --git a/zen/src/backend/screen/xdg-toplevel.h b/zen/src/backend/screen/xdg-toplevel.h new file mode 100644 index 00000000..c362513d --- /dev/null +++ b/zen/src/backend/screen/xdg-toplevel.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +struct zn_view; +struct zn_surface_snode; +struct zn_xdg_decoration; + +struct zn_xdg_toplevel { + struct wlr_xdg_toplevel *wlr_xdg_toplevel; // @nonnull, @outlive + + struct zn_view *view; // @nonnull, @owning + + struct zn_xdg_decoration *decoration; // @nullable, @ref + + // @nonnull while mapped + // Automatically destroyed when given wlr_surface is destroyed; + struct zn_surface_snode *surface_snode; + + // Developer note: Reading through the wlroots source, the unmap signal is + // always emitted before the destroy signal + struct wl_listener surface_destroy_listener; + struct wl_listener surface_map_listener; + struct wl_listener surface_unmap_listener; + struct wl_listener surface_commit_listener; // listen only when mapped + struct wl_listener surface_move_request_listener; + struct wl_listener surface_resize_request_listener; + struct wl_listener decoration_destroy_listener; +}; + +/// @param decoration may NULL +void zn_xdg_toplevel_set_decoration( + struct zn_xdg_toplevel *self, struct zn_xdg_decoration *decoration); + +/// @return value may NULL +struct zn_xdg_toplevel *zn_xdg_toplevel_from_wlr_xdg_surface( + struct wlr_xdg_surface *surface); + +struct zn_xdg_toplevel *zn_xdg_toplevel_create( + struct wlr_xdg_toplevel *wlr_xdg_toplevel); diff --git a/zen/src/backend/xwayland-surface.c b/zen/src/backend/screen/xwayland-surface.c similarity index 97% rename from zen/src/backend/xwayland-surface.c rename to zen/src/backend/screen/xwayland-surface.c index adb1186e..53689b93 100644 --- a/zen/src/backend/xwayland-surface.c +++ b/zen/src/backend/screen/xwayland-surface.c @@ -2,14 +2,14 @@ #include -#include "backend.h" +#include "default-backend.h" #include "surface-snode.h" +#include "view.h" #include "zen-common/log.h" #include "zen-common/util.h" #include "zen/seat.h" #include "zen/server.h" #include "zen/snode.h" -#include "zen/view.h" static void zn_xwayland_surface_destroy(struct zn_xwayland_surface *self); @@ -38,8 +38,7 @@ zn_xwayland_surface_configure_size(void *impl_data, vec2 size) { struct zn_xwayland_surface *self = impl_data; - if (self->surface_snode == NULL || - self->surface_snode->snode->screen == NULL) { + if (self->surface_snode == NULL || self->surface_snode->snode->root == NULL) { return; } @@ -96,8 +95,7 @@ zn_xwayland_surface_handle_snode_position_changed( struct zn_xwayland_surface *self = zn_container_of(listener, self, snode_position_changed_listener); - if (self->surface_snode == NULL || - self->surface_snode->snode->screen == NULL) { + if (self->surface_snode == NULL || self->surface_snode->snode->root == NULL) { return; } @@ -230,6 +228,7 @@ zn_xwayland_surface_handle_surface_unmap( wl_list_remove(&self->surface_commit_listener.link); wl_list_init(&self->surface_commit_listener.link); + zn_snode_set_position(self->surface_snode->snode, NULL, GLM_VEC2_ZERO); self->surface_snode = NULL; zn_view_notify_unmap(self->view); diff --git a/zen/src/backend/xwayland-surface.h b/zen/src/backend/screen/xwayland-surface.h similarity index 93% rename from zen/src/backend/xwayland-surface.h rename to zen/src/backend/screen/xwayland-surface.h index 72f4f2ac..4d083807 100644 --- a/zen/src/backend/xwayland-surface.h +++ b/zen/src/backend/screen/xwayland-surface.h @@ -11,7 +11,7 @@ struct zn_xwayland_surface { struct zn_view *view; // @nonnull, @owning // @nonnull while mapped - // Automatically destroyed when give wlr_surface is destroyed. + // Automatically destroyed when given wlr_surface is destroyed. struct zn_surface_snode *surface_snode; // Developer note: Reading through the wlroots source, the unmap signal is diff --git a/zen/src/binding.c b/zen/src/binding.c new file mode 100644 index 00000000..c6d103db --- /dev/null +++ b/zen/src/binding.c @@ -0,0 +1,209 @@ +#include "binding.h" + +#include +#include + +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/server.h" + +#define CONFIG_SECTION_KEY "binding" + +const char *const TOKEN_SHIFT = "shift"; +const char *const TOKEN_CTRL = "ctrl"; +const char *const TOKEN_ALT = "alt"; +const char *const TOKEN_LOGO = "logo"; +const char *const TOKEN_RIGHT = "right"; +const char *const TOKEN_LEFT = "left"; +const char *const TOKEN_UP = "up"; +const char *const TOKEN_DOWN = "down"; +const char TOKEN_SEPARATOR = '+'; + +static const uint32_t ascii_to_keycode[] = { + ['a'] = KEY_A, + ['b'] = KEY_B, + ['c'] = KEY_C, + ['d'] = KEY_D, + ['e'] = KEY_E, + ['f'] = KEY_F, + ['g'] = KEY_G, + ['h'] = KEY_H, + ['i'] = KEY_I, + ['j'] = KEY_J, + ['k'] = KEY_K, + ['l'] = KEY_L, + ['m'] = KEY_M, + ['n'] = KEY_N, + ['o'] = KEY_O, + ['p'] = KEY_P, + ['q'] = KEY_Q, + ['r'] = KEY_R, + ['s'] = KEY_S, + ['t'] = KEY_T, + ['u'] = KEY_U, + ['v'] = KEY_V, + ['w'] = KEY_W, + ['x'] = KEY_X, + ['y'] = KEY_Y, + ['z'] = KEY_Z, + ['0'] = KEY_0, + ['1'] = KEY_1, + ['2'] = KEY_2, + ['3'] = KEY_3, + ['4'] = KEY_4, + ['5'] = KEY_5, + ['6'] = KEY_6, + ['7'] = KEY_7, + ['8'] = KEY_8, + ['9'] = KEY_9, +}; + +void +zn_binding_add(struct zn_binding *self, const char *name, + zn_binding_handler_t handler, void *user_data) +{ + struct zn_binding_item *item = NULL; + + wl_array_for_each (item, &self->items) { + if (strcmp(name, item->name) == 0) { + zn_warn("Binding name is duplicated (%s). Skipping.", name); + return; + } + } + + item = wl_array_add(&self->items, sizeof *item); + item->name = name; + item->handler = handler; + item->key = 0; + item->modifiers = 0; + item->user_data = user_data; +} + +bool +zn_binding_handle_key(struct zn_binding *self, uint32_t key, uint32_t modifiers) +{ + struct zn_binding_item *item = NULL; + + wl_array_for_each (item, &self->items) { + bool matches = item->key == key && item->modifiers == modifiers; + + if (!matches) { + continue; + } + + if (item->handler(item->name, key, modifiers, item->user_data)) { + return true; + } + } + + return false; +} + +static int +find_next_separator(const char *string, char separator) +{ + for (int i = 0;; i++) { + if (string[i] == separator || string[i] == '\0') { + return i; + } + } + + assert(false && "Unreachable"); +} + +static void +zn_binding_item_reconfigure(struct zn_binding_item *item, char *description) +{ + char *current = description; + while (true) { + int length = find_next_separator(current, TOKEN_SEPARATOR); + if (length <= 0) { + break; + } + + if (strncmp(current, TOKEN_SHIFT, length) == 0) { + item->modifiers |= WLR_MODIFIER_SHIFT; + } else if (strncmp(current, TOKEN_CTRL, length) == 0) { + item->modifiers |= WLR_MODIFIER_CTRL; + } else if (strncmp(current, TOKEN_ALT, length) == 0) { + item->modifiers |= WLR_MODIFIER_ALT; + } else if (strncmp(current, TOKEN_LOGO, length) == 0) { + item->modifiers |= WLR_MODIFIER_LOGO; + } else if (strncmp(current, TOKEN_RIGHT, length) == 0) { + item->key = KEY_RIGHT; + } else if (strncmp(current, TOKEN_LEFT, length) == 0) { + item->key = KEY_LEFT; + } else if (strncmp(current, TOKEN_UP, length) == 0) { + item->key = KEY_UP; + } else if (strncmp(current, TOKEN_DOWN, length) == 0) { + item->key = KEY_DOWN; + } else if (length == 1) { + char key = current[0]; + if (('0' <= key && key <= '9') || ('a' <= key && key <= 'z')) { + item->key = ascii_to_keycode[(int)key]; + } + } + + if (current[length] == '\0') { + break; + } + + current += length + 1; + } +} + +void +zn_binding_remap(struct zn_binding *self) +{ + struct zn_server *server = zn_server_get_singleton(); + + toml_table_t *table = NULL; + if (server->config->root_table) { + table = toml_table_in(server->config->root_table, CONFIG_SECTION_KEY); + } + + if (table == NULL) { + return; + } + + struct zn_binding_item *item = NULL; + + // TODO(@Aki-7): Refactor implementing lexer for more flexibility + // TODO(@Aki-7): Enable to add multiple key bindings to one command + wl_array_for_each (item, &self->items) { + toml_datum_t data = toml_string_in(table, item->name); + if (!data.ok) { + continue; + } + + zn_binding_item_reconfigure(item, data.u.s); + } +} + +struct zn_binding * +zn_binding_create(void) +{ + struct zn_server *server = zn_server_get_singleton(); + + struct zn_binding *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + wl_array_init(&self->items); + + zn_config_reserve_section(server->config, CONFIG_SECTION_KEY); + + return self; + +err: + return NULL; +} + +void +zn_binding_destroy(struct zn_binding *self) +{ + wl_array_release(&self->items); + free(self); +} diff --git a/zen/src/binding.h b/zen/src/binding.h new file mode 100644 index 00000000..de2d21e2 --- /dev/null +++ b/zen/src/binding.h @@ -0,0 +1,26 @@ +#pragma once + +#include "zen/binding.h" + +struct zn_binding_item { + const char *name; // @nonnull, @outlive + uint32_t key; + uint32_t modifiers; + zn_binding_handler_t handler; // nonnull + void *user_data; +}; + +struct zn_binding { + struct wl_array items; // [zn_binding_item] +}; + +/// @return true if the key is used and should not be used any more +/// @param modifiers is a bitfield of enum wlr_keyboard_modifier +bool zn_binding_handle_key( + struct zn_binding *self, uint32_t key, uint32_t modifiers); + +void zn_binding_remap(struct zn_binding *self); + +struct zn_binding *zn_binding_create(void); + +void zn_binding_destroy(struct zn_binding *self); diff --git a/zen/src/buffer.c b/zen/src/buffer.c new file mode 100644 index 00000000..d56c2488 --- /dev/null +++ b/zen/src/buffer.c @@ -0,0 +1,29 @@ +#include "zen/buffer.h" + +#include "zen-common/log.h" +#include "zen-common/util.h" + +struct zn_buffer * +zn_buffer_create( + void *impl_data, const struct zn_buffer_interface *implementation) +{ + struct zn_buffer *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + + return self; + +err: + return NULL; +} + +void +zn_buffer_destroy(struct zn_buffer *self) +{ + free(self); +} diff --git a/zen/src/config.c b/zen/src/config.c index 9fc223c7..00d7a4bc 100644 --- a/zen/src/config.c +++ b/zen/src/config.c @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -87,59 +86,24 @@ get_config_toml_table(void) } bool -zn_config_add_section( - struct zn_config *self, const char *name, struct zn_config_section *section) +zn_config_reserve_section(struct zn_config *self, const char *section_name) { - struct zn_config_section_info *info = NULL; - wl_array_for_each (info, &self->sections) { - if (strcmp(info->name, name) == 0) { - zn_error("Config section %s is used by two or more modules", name); + const char **name = NULL; + + wl_array_for_each (name, &self->reserved_sections) { + if (strcmp(*name, section_name) == 0) { + zn_warn( + "Section name '%s' has been reserved by another component", *name); return false; } } - toml_table_t *table = NULL; - if (self->root_table) { - table = toml_table_in(self->root_table, name); - } - - if (!section->load(section, table)) { - zn_error("Failed to load '%s' config section", name); - return false; - } - - char *name_copy = strdup(name); - if (name_copy == NULL) { - zn_error("Failed to strdup"); - return false; - } - - info = wl_array_add(&self->sections, sizeof(*info)); - if (info == NULL) { - zn_error("Failed to allocate memory"); - free(name_copy); - return false; - } - - info->name = name_copy; - info->section = section; + name = wl_array_add(&self->reserved_sections, sizeof *name); + *name = section_name; return true; } -struct zn_config_section * -zn_config_get_section(struct zn_config *self, const char *name) -{ - struct zn_config_section_info *info = NULL; - wl_array_for_each (info, &self->sections) { - if (strcmp(info->name, name) == 0) { - return info->section; - } - } - - return NULL; -} - struct zn_config * zn_config_create(void) { @@ -150,7 +114,7 @@ zn_config_create(void) } self->root_table = get_config_toml_table(); - wl_array_init(&self->sections); + wl_array_init(&self->reserved_sections); return self; @@ -165,12 +129,6 @@ zn_config_destroy(struct zn_config *self) toml_free(self->root_table); } - struct zn_config_section_info *info = NULL; - wl_array_for_each (info, &self->sections) { - free(info->name); - info->section->destroy(info->section); - } - - wl_array_release(&self->sections); + wl_array_release(&self->reserved_sections); free(self); } diff --git a/zen/src/gl-base-technique.c b/zen/src/gl-base-technique.c new file mode 100644 index 00000000..d7ab9ee7 --- /dev/null +++ b/zen/src/gl-base-technique.c @@ -0,0 +1,35 @@ +#include "gl-base-technique.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +struct zn_gl_base_technique * +zn_gl_base_technique_create(void *impl_data, + const struct zn_gl_base_technique_interface *implementation) +{ + struct zn_gl_base_technique *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + wl_signal_init(&self->events.destroy); + + return self; + +err: + return NULL; +} + +void +zn_gl_base_technique_destroy(struct zn_gl_base_technique *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + + free(self); +} diff --git a/zen/src/gl-base-technique.h b/zen/src/gl-base-technique.h new file mode 100644 index 00000000..9ba47153 --- /dev/null +++ b/zen/src/gl-base-technique.h @@ -0,0 +1,18 @@ +#pragma once + +#include "zen/gl-base-technique.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Called by impl object +struct zn_gl_base_technique *zn_gl_base_technique_create(void *impl_data, + const struct zn_gl_base_technique_interface *implementation); + +/// Called by impl object +void zn_gl_base_technique_destroy(struct zn_gl_base_technique *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/src/gl-buffer.c b/zen/src/gl-buffer.c new file mode 100644 index 00000000..56243346 --- /dev/null +++ b/zen/src/gl-buffer.c @@ -0,0 +1,34 @@ +#include "gl-buffer.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +struct zn_gl_buffer * +zn_gl_buffer_create( + void *impl_data, const struct zn_gl_buffer_interface *implementation) +{ + struct zn_gl_buffer *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + wl_signal_init(&self->events.destroy); + + return self; + +err: + return NULL; +} + +void +zn_gl_buffer_destroy(struct zn_gl_buffer *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + free(self); +} diff --git a/zen/src/gl-buffer.h b/zen/src/gl-buffer.h new file mode 100644 index 00000000..4b70d3f7 --- /dev/null +++ b/zen/src/gl-buffer.h @@ -0,0 +1,18 @@ +#pragma once + +#include "zen/gl-buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Called by impl object +struct zn_gl_buffer *zn_gl_buffer_create( + void *impl_data, const struct zn_gl_buffer_interface *implementation); + +/// Called by impl object +void zn_gl_buffer_destroy(struct zn_gl_buffer *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/src/gl-program.c b/zen/src/gl-program.c new file mode 100644 index 00000000..3e95bcf5 --- /dev/null +++ b/zen/src/gl-program.c @@ -0,0 +1,34 @@ +#include "gl-program.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +struct zn_gl_program * +zn_gl_program_create( + void *impl_data, const struct zn_gl_program_interface *implementation) +{ + struct zn_gl_program *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + wl_signal_init(&self->events.destroy); + + return self; + +err: + return NULL; +} + +void +zn_gl_program_destroy(struct zn_gl_program *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + free(self); +} diff --git a/zen/src/gl-program.h b/zen/src/gl-program.h new file mode 100644 index 00000000..a39b8973 --- /dev/null +++ b/zen/src/gl-program.h @@ -0,0 +1,18 @@ +#pragma once + +#include "zen/gl-program.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Called by impl object +struct zn_gl_program *zn_gl_program_create( + void *impl_data, const struct zn_gl_program_interface *implementation); + +/// Called by impl object +void zn_gl_program_destroy(struct zn_gl_program *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/src/gl-rendering-unit.c b/zen/src/gl-rendering-unit.c new file mode 100644 index 00000000..011327d7 --- /dev/null +++ b/zen/src/gl-rendering-unit.c @@ -0,0 +1,35 @@ +#include "gl-rendering-unit.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +struct zn_gl_rendering_unit * +zn_gl_rendering_unit_create(void *impl_data, + const struct zn_gl_rendering_unit_interface *implementation) +{ + struct zn_gl_rendering_unit *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + wl_signal_init(&self->events.destroy); + + return self; + +err: + return NULL; +} + +void +zn_gl_rendering_unit_destroy(struct zn_gl_rendering_unit *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + + free(self); +} diff --git a/zen/src/gl-rendering-unit.h b/zen/src/gl-rendering-unit.h new file mode 100644 index 00000000..298f53a8 --- /dev/null +++ b/zen/src/gl-rendering-unit.h @@ -0,0 +1,18 @@ +#pragma once + +#include "zen/gl-rendering-unit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Called by impl object +struct zn_gl_rendering_unit *zn_gl_rendering_unit_create(void *impl_data, + const struct zn_gl_rendering_unit_interface *implementation); + +/// Called by impl object +void zn_gl_rendering_unit_destroy(struct zn_gl_rendering_unit *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/src/gl-shader.c b/zen/src/gl-shader.c new file mode 100644 index 00000000..1dc587d8 --- /dev/null +++ b/zen/src/gl-shader.c @@ -0,0 +1,32 @@ +#include "gl-shader.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +struct zn_gl_shader * +zn_gl_shader_create(void *impl_data) +{ + struct zn_gl_shader *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + wl_signal_init(&self->events.destroy); + + return self; + +err: + return NULL; +} + +void +zn_gl_shader_destroy(struct zn_gl_shader *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + free(self); +} diff --git a/zen/src/gl-shader.h b/zen/src/gl-shader.h new file mode 100644 index 00000000..f9b4ae3d --- /dev/null +++ b/zen/src/gl-shader.h @@ -0,0 +1,17 @@ +#pragma once + +#include "zen/gl-shader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Called by impl object +struct zn_gl_shader *zn_gl_shader_create(void *impl_data); + +/// Called by impl object +void zn_gl_shader_destroy(struct zn_gl_shader *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/src/gl-texture.c b/zen/src/gl-texture.c new file mode 100644 index 00000000..f8cfeed5 --- /dev/null +++ b/zen/src/gl-texture.c @@ -0,0 +1,34 @@ +#include "gl-texture.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +struct zn_gl_texture * +zn_gl_texture_create( + void *impl_data, const struct zn_gl_texture_interface *implementation) +{ + struct zn_gl_texture *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + wl_signal_init(&self->events.destroy); + + return self; + +err: + return NULL; +} + +void +zn_gl_texture_destroy(struct zn_gl_texture *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + free(self); +} diff --git a/zen/src/gl-texture.h b/zen/src/gl-texture.h new file mode 100644 index 00000000..4290c8f6 --- /dev/null +++ b/zen/src/gl-texture.h @@ -0,0 +1,18 @@ +#pragma once + +#include "zen/gl-texture.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Called by impl object +struct zn_gl_texture *zn_gl_texture_create( + void *impl_data, const struct zn_gl_texture_interface *implementation); + +/// Called by impl object +void zn_gl_texture_destroy(struct zn_gl_texture *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/src/gl-vertex-array.c b/zen/src/gl-vertex-array.c new file mode 100644 index 00000000..71539bc2 --- /dev/null +++ b/zen/src/gl-vertex-array.c @@ -0,0 +1,34 @@ +#include "gl-vertex-array.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +struct zn_gl_vertex_array * +zn_gl_vertex_array_create( + void *impl_data, const struct zn_gl_vertex_array_interface *implementation) +{ + struct zn_gl_vertex_array *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + wl_signal_init(&self->events.destroy); + + return self; + +err: + return NULL; +} + +void +zn_gl_vertex_array_destroy(struct zn_gl_vertex_array *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + free(self); +} diff --git a/zen/src/gl-vertex-array.h b/zen/src/gl-vertex-array.h new file mode 100644 index 00000000..dcf06389 --- /dev/null +++ b/zen/src/gl-vertex-array.h @@ -0,0 +1,18 @@ +#pragma once + +#include "zen/gl-vertex-array.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Called by impl object +struct zn_gl_vertex_array *zn_gl_vertex_array_create( + void *impl_data, const struct zn_gl_vertex_array_interface *implementation); + +/// Called by impl object +void zn_gl_vertex_array_destroy(struct zn_gl_vertex_array *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/src/gl-virtual-object.c b/zen/src/gl-virtual-object.c new file mode 100644 index 00000000..25917362 --- /dev/null +++ b/zen/src/gl-virtual-object.c @@ -0,0 +1,36 @@ +#include "gl-virtual-object.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +struct zn_gl_virtual_object * +zn_gl_virtual_object_create(void *impl_data, + const struct zn_gl_virtual_object_interface *implementation, + struct zn_xr_dispatcher *dispatcher) +{ + struct zn_gl_virtual_object *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + self->dispatcher = dispatcher; + wl_signal_init(&self->events.destroy); + + return self; + +err: + return NULL; +} + +void +zn_gl_virtual_object_destroy(struct zn_gl_virtual_object *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + free(self); +} diff --git a/zen/src/gl-virtual-object.h b/zen/src/gl-virtual-object.h new file mode 100644 index 00000000..dd4b0588 --- /dev/null +++ b/zen/src/gl-virtual-object.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +#include "zen-common/util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_gl_virtual_object; + +struct zn_gl_virtual_object_interface { + void (*commited)(struct zn_gl_virtual_object *self); + void (*change_visibility)(struct zn_gl_virtual_object *self, bool visible); +}; + +struct zn_gl_virtual_object { + void *impl_data; // @nullable, @outlive if exists + const struct zn_gl_virtual_object_interface *impl; // @nonnull, @outlive + + struct zn_xr_dispatcher *dispatcher; // @nonnull, @outlive + + struct { + struct wl_signal destroy; // (NULL) + } events; +}; + +UNUSED static inline void +zn_gl_virtual_object_change_visibility( + struct zn_gl_virtual_object *self, bool visible) +{ + self->impl->change_visibility(self, visible); +} + +UNUSED static inline void +zn_gl_virtual_object_committed(struct zn_gl_virtual_object *self) +{ + self->impl->commited(self); +} + +/// Called by impl object +struct zn_gl_virtual_object *zn_gl_virtual_object_create(void *impl_data, + const struct zn_gl_virtual_object_interface *implementation, + struct zn_xr_dispatcher *dispatcher); + +/// Called by impl object +void zn_gl_virtual_object_destroy(struct zn_gl_virtual_object *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/src/inode.c b/zen/src/inode.c new file mode 100644 index 00000000..65024a89 --- /dev/null +++ b/zen/src/inode.c @@ -0,0 +1,197 @@ +#include "inode.h" + +#include +#include + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +void +zn_inode_noop_mapped(void *impl_data UNUSED) +{} + +void +zn_inode_noop_unmapped(void *impl_data UNUSED) +{} + +void +zn_inode_noop_activated(void *impl_data UNUSED) +{} + +void +zn_inode_noop_deactivated(void *impl_data UNUSED) +{} + +void +zn_inode_noop_moved(void *impl_data UNUSED) +{} + +const struct zn_inode_interface zn_inode_noop_implementation = { + .mapped = zn_inode_noop_mapped, + .unmapped = zn_inode_noop_unmapped, + .activated = zn_inode_noop_activated, + .deactivated = zn_inode_noop_deactivated, + .moved = zn_inode_noop_moved, +}; + +static void +zn_inode_map_internal(struct zn_inode *self, bool mapped) +{ + if (self->mapped == mapped) { + return; + } + + struct zn_inode *child = NULL; + + self->mapped = mapped; + + wl_list_for_each (child, &self->child_list, link) { + zn_inode_map_internal(child, mapped); + } + + if (self->mapped) { + self->impl->mapped(self->impl_data); + } else { + self->impl->unmapped(self->impl_data); + } +} + +void +zn_inode_map(struct zn_inode *self) +{ + zn_assert(self->parent == NULL, "self must be root"); + + zn_inode_map_internal(self, true); +} + +void +zn_inode_unmap(struct zn_inode *self) +{ + zn_assert(self->parent == NULL, "self must be root"); + + zn_inode_map_internal(self, false); +} + +static void +zn_inode_set_xr_system_internal( + struct zn_inode *self, struct zn_xr_system *xr_system) +{ + if (self->xr_system == xr_system) { + return; + } + + bool was_active = zn_inode_is_active(self); + + self->xr_system = xr_system; + + struct zn_inode *child = NULL; + + wl_list_for_each (child, &self->child_list, link) { + zn_inode_set_xr_system_internal(child, xr_system); + } + + bool is_active = zn_inode_is_active(self); + + if (was_active != is_active) { + if (is_active) { + self->impl->activated(self->impl_data); + } else { + self->impl->deactivated(self->impl_data); + } + } +} + +void +zn_inode_set_xr_system(struct zn_inode *self, struct zn_xr_system *xr_system) +{ + zn_assert(self->parent == NULL, "self must be root"); + + zn_inode_set_xr_system_internal(self, xr_system); +} + +static void +zn_inode_update_position_recursive(struct zn_inode *self, mat4 parent_transform) +{ + mat4 transform; + glm_quat_mat4(self->quaternion, transform); + transform[3][0] = self->position[0]; + transform[3][1] = self->position[1]; + transform[3][2] = self->position[2]; + + glm_mul(parent_transform, transform, self->transform_abs); + + struct zn_inode *child = NULL; + + wl_list_for_each (child, &self->child_list, link) { + zn_inode_update_position_recursive(child, self->transform_abs); + } + + self->impl->moved(self->impl_data); +} + +void +zn_inode_move(struct zn_inode *self, struct zn_inode *parent, vec3 position, + versor quaternion) +{ + glm_vec3_copy(position, self->position); + glm_quat_copy(quaternion, self->quaternion); + + if (self->parent != parent) { + if (self->parent) { + wl_list_remove(&self->link); + wl_list_init(&self->link); + } + + self->parent = parent; + + if (self->parent) { + wl_list_insert(&self->parent->child_list, &self->link); + } + + zn_inode_set_xr_system_internal(self, parent ? parent->xr_system : NULL); + + zn_inode_map_internal(self, parent ? parent->mapped : false); + } + + zn_inode_update_position_recursive( + self, self->parent ? self->parent->transform_abs : GLM_MAT4_IDENTITY); +} + +struct zn_inode * +zn_inode_create( + void *impl_data, const struct zn_inode_interface *implementation) +{ + struct zn_inode *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + self->xr_system = NULL; + self->mapped = false; + glm_vec3_zero(self->position); + glm_quat_identity(self->quaternion); + glm_mat4_identity(self->transform_abs); + self->parent = NULL; + wl_list_init(&self->link); + wl_list_init(&self->child_list); + + return self; + +err: + return NULL; +} + +void +zn_inode_destroy(struct zn_inode *self) +{ + zn_assert(wl_list_empty(&self->child_list), + "Destroy child inodes before the parent inode"); + + wl_list_remove(&self->child_list); + wl_list_remove(&self->link); + free(self); +} diff --git a/zen/src/inode.h b/zen/src/inode.h new file mode 100644 index 00000000..1679d24b --- /dev/null +++ b/zen/src/inode.h @@ -0,0 +1,10 @@ +#pragma once + +#include "zen/inode.h" + +/// @param self must be root +/// @param xr_system is nullable +/// If this is called with nonnull xr_system, the caller must call this with +/// NULL when the xr_system is disconnected or destroyed +void zn_inode_set_xr_system( + struct zn_inode *self, struct zn_xr_system *xr_system); diff --git a/zen/src/screen.c b/zen/src/screen.c index e811ca55..837515e5 100644 --- a/zen/src/screen.c +++ b/zen/src/screen.c @@ -1,4 +1,4 @@ -#include "zen/screen.h" +#include "screen.h" #include #include @@ -6,8 +6,29 @@ #include "zen-common/log.h" #include "zen-common/signal.h" #include "zen-common/util.h" +#include "zen/snode-root.h" #include "zen/snode.h" +static void +zn_screen_snode_root_damage(void *user_data, struct wlr_fbox *fbox) +{ + struct zn_screen *self = user_data; + zn_screen_damage(self, fbox); +} + +static void +zn_screen_snode_root_layout_position(void *user_data, vec2 position) +{ + struct zn_screen *self = user_data; + glm_vec2_copy(self->layout_position, position); +} + +static const struct zn_snode_root_interface + zn_screen_snode_root_implementation = { + .damage = zn_screen_snode_root_damage, + .layout_position = zn_screen_snode_root_layout_position, +}; + void zn_screen_set_layout_position(struct zn_screen *self, vec2 layout_position) { @@ -32,7 +53,18 @@ zn_screen_notify_resize(struct zn_screen *self, vec2 size) void zn_screen_notify_frame(struct zn_screen *self, struct timespec *when) { - zn_snode_notify_frame(self->snode_root, when); + zn_snode_notify_frame(self->snode_root->node, when); +} + +struct zn_screen * +zn_screen_from_snode_root(struct zn_snode_root *snode_root) +{ + if (snode_root == NULL || + snode_root->impl != &zn_screen_snode_root_implementation) { + return NULL; + } + + return snode_root->user_data; } struct zn_screen * @@ -53,7 +85,8 @@ zn_screen_create( wl_signal_init(&self->events.destroy); wl_signal_init(&self->events.layout_position_changed); - self->snode_root = zn_snode_create_root(self); + self->snode_root = + zn_snode_root_create(self, &zn_screen_snode_root_implementation); if (self->snode_root == NULL) { zn_error("Failed to create a zn_snode"); goto err_free; @@ -70,7 +103,7 @@ zn_screen_create( return self; err_snode_root: - zn_snode_destroy(self->snode_root); + zn_snode_root_destroy(self->snode_root); err_free: free(self); @@ -84,7 +117,7 @@ zn_screen_destroy(struct zn_screen *self) { zn_signal_emit_mutable(&self->events.destroy, NULL); - zn_snode_destroy(self->snode_root); + zn_snode_root_destroy(self->snode_root); wl_list_remove(&self->events.layout_position_changed.listener_list); wl_list_remove(&self->events.destroy.listener_list); wl_list_remove(&self->events.resized.listener_list); diff --git a/zen/src/screen.h b/zen/src/screen.h new file mode 100644 index 00000000..3e770ba6 --- /dev/null +++ b/zen/src/screen.h @@ -0,0 +1,17 @@ +#pragma once + +#include "zen/screen.h" + +/// Called by the impl object +/// @param size : effective coords +void zn_screen_notify_resize(struct zn_screen *self, vec2 size); + +/// Called by the impl object +void zn_screen_notify_frame(struct zn_screen *self, struct timespec *when); + +/// Called by the impl object +struct zn_screen *zn_screen_create( + void *impl_data, const struct zn_screen_interface *implementation); + +/// Called by the impl object +void zn_screen_destroy(struct zn_screen *self); diff --git a/zen/src/seat.c b/zen/src/seat.c index 35250b9c..e276bab2 100644 --- a/zen/src/seat.c +++ b/zen/src/seat.c @@ -5,6 +5,7 @@ #include "cursor.h" #include "zen-common/log.h" #include "zen-common/util.h" +#include "zen/screen.h" #include "zen/snode.h" #define DEFAULT_SEAT "seat0" @@ -54,14 +55,16 @@ zn_seat_get_focused_screen(struct zn_seat *self) { struct zn_screen *focus_screen = NULL; - // TODO(@Aki-7): Respect keyboard focus + if (self->focus) { + focus_screen = zn_screen_from_snode_root(self->focus->root); + } - if (self->pointer_state.focus) { - focus_screen = self->pointer_state.focus->screen; + if (!focus_screen && self->pointer_state.focus) { + focus_screen = zn_screen_from_snode_root(self->pointer_state.focus->root); } if (!focus_screen) { - focus_screen = self->cursor->snode->screen; + focus_screen = zn_screen_from_snode_root(self->cursor->snode->root); } return focus_screen; diff --git a/zen/src/server.c b/zen/src/server.c index 0a40835d..dba4eda3 100644 --- a/zen/src/server.c +++ b/zen/src/server.c @@ -6,10 +6,13 @@ #include #include "backend.h" +#include "binding.h" #include "seat.h" #include "zen-common/log.h" #include "zen-common/util.h" #include "zen/backend.h" +#include "zen/config.h" +#include "zen/inode.h" // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static struct zn_server *server_singleton = NULL; @@ -37,6 +40,8 @@ zn_server_run(struct zn_server *self) return self->exit_status; } + zn_binding_remap(self->binding); + wl_display_run(self->display); if (!zn_assert(!self->running, "Not terminated with zn_server_terminate")) { @@ -91,7 +96,8 @@ handle_wlr_log( } struct zn_server * -zn_server_create(struct wl_display *display, struct zn_backend *backend) +zn_server_create(struct wl_display *display, struct zn_backend *backend, + struct zn_config *config) { wlr_log_init(WLR_DEBUG, handle_wlr_log); @@ -111,11 +117,31 @@ zn_server_create(struct wl_display *display, struct zn_backend *backend) self->display = display; self->running = false; self->exit_status = EXIT_FAILURE; + self->config = config; + + self->binding = zn_binding_create(); + if (self->binding == NULL) { + zn_error("Failed to create a zn_binding"); + goto err_free; + } self->seat = zn_seat_create(display); if (self->seat == NULL) { zn_error("Failed to create a zn_seat"); - goto err_free; + goto err_binding; + } + + self->inode_root = zn_inode_create(self, &zn_inode_noop_implementation); + if (self->inode_root == NULL) { + zn_error("Failed to create a root inode"); + goto err_seat; + } + + self->inode_invisible_root = + zn_inode_create(self, &zn_inode_noop_implementation); + if (self->inode_invisible_root == NULL) { + zn_error("Failed to create a root inode"); + goto err_inode_root; } if (backend) { @@ -125,14 +151,23 @@ zn_server_create(struct wl_display *display, struct zn_backend *backend) } if (self->backend == NULL) { zn_error("Failed to create a zn_backend"); - goto err_seat; + goto err_inode_invisible_root; } return self; +err_inode_invisible_root: + zn_inode_destroy(self->inode_invisible_root); + +err_inode_root: + zn_inode_destroy(self->inode_root); + err_seat: zn_seat_destroy(self->seat); +err_binding: + zn_binding_destroy(self->binding); + err_free: free(self); @@ -145,6 +180,9 @@ zn_server_destroy(struct zn_server *self) { zn_backend_destroy(self->backend); zn_seat_destroy(self->seat); + zn_inode_destroy(self->inode_root); + zn_inode_destroy(self->inode_invisible_root); + zn_binding_destroy(self->binding); server_singleton = NULL; wl_list_remove(&self->events.start.listener_list); wl_list_remove(&self->events.end.listener_list); diff --git a/zen/src/snode-root.c b/zen/src/snode-root.c new file mode 100644 index 00000000..9ef2e4e8 --- /dev/null +++ b/zen/src/snode-root.c @@ -0,0 +1,42 @@ +#include "zen/snode-root.h" + +#include "zen-common/log.h" +#include "zen-common/util.h" +#include "zen/snode.h" + +struct zn_snode_root * +zn_snode_root_create( + void *user_data, const struct zn_snode_root_interface *implementation) +{ + struct zn_snode_root *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl = implementation; + self->user_data = user_data; + + self->node = zn_snode_create(self, &zn_snode_noop_implementation); + if (self->node == NULL) { + zn_error("Failed to create root snode"); + goto err_free; + } + + self->node->root = self; + + return self; + +err_free: + free(self); + +err: + return NULL; +} + +void +zn_snode_root_destroy(struct zn_snode_root *self) +{ + zn_snode_destroy(self->node); + free(self); +} diff --git a/zen/src/snode.c b/zen/src/snode.c index 72e580a4..557ba019 100644 --- a/zen/src/snode.c +++ b/zen/src/snode.c @@ -6,6 +6,7 @@ #include "zen-common/signal.h" #include "zen-common/util.h" #include "zen/screen.h" +#include "zen/snode-root.h" bool zn_snode_is_focusable(struct zn_snode *self) @@ -113,12 +114,12 @@ void zn_snode_damage_whole(struct zn_snode *self) { struct wlr_fbox box; - if (!self->screen) { + if (!self->root) { return; } zn_snode_get_fbox(self, &box); - zn_screen_damage(self->screen, &box); + zn_snode_root_damage(self->root, &box); } static void @@ -158,7 +159,7 @@ zn_snode_get_snode_at(struct zn_snode *self, vec2 point, vec2 local_point) void zn_snode_damage(struct zn_snode *self, struct wlr_fbox *damage) { - if (!self->screen) { + if (!self->root) { return; } @@ -169,7 +170,7 @@ zn_snode_damage(struct zn_snode *self, struct wlr_fbox *damage) .height = damage->height, }; - zn_screen_damage(self->screen, &fbox); + zn_snode_root_damage(self->root, &fbox); } void @@ -179,7 +180,10 @@ zn_snode_move_front(struct zn_snode *self) return; } - zn_snode_set_position(self, self->parent, self->position); + struct zn_snode *front = + zn_container_of(self->parent->child_node_list.prev, front, link); + + zn_snode_place_above(self, front); } void @@ -196,6 +200,10 @@ void zn_snode_set_position( struct zn_snode *self, struct zn_snode *parent, vec2 position) { + if (self->parent == parent && glm_vec2_eqv(self->position, position)) { + return; + } + zn_snode_damage_whole(self); if (self->parent) { @@ -215,7 +223,29 @@ zn_snode_set_position( } self->parent = parent; - self->screen = parent ? parent->screen : NULL; + self->root = parent ? parent->root : NULL; + glm_vec2_copy(position, self->position); + + zn_snode_update_absolute_position(self); + + zn_snode_damage_whole(self); + + wl_signal_emit(&self->events.position_changed, NULL); +} + +void +zn_snode_change_position(struct zn_snode *self, vec2 position) +{ + if (self->parent == NULL) { + return; + } + + if (glm_vec2_eqv(self->position, position)) { + return; + } + + zn_snode_damage_whole(self); + glm_vec2_copy(position, self->position); zn_snode_update_absolute_position(self); @@ -225,6 +255,49 @@ zn_snode_set_position( wl_signal_emit(&self->events.position_changed, NULL); } +static void +zn_snode_place_next_to( + struct zn_snode *self, struct zn_snode *sibling, bool above) +{ + if (!zn_assert(self->parent, "snode should have a parent")) { + return; + } + + if (!zn_assert(self->parent == sibling->parent, + "self and sibling should have the same parent")) { + return; + } + + if (self == sibling) { + return; + } + + struct wl_list *link = above ? &sibling->link : sibling->link.prev; + + if (&self->link == link->next) { + return; + } + + wl_list_remove(&self->link); + wl_list_insert(link, &self->link); + + zn_snode_damage_whole(self); + + wl_signal_emit(&self->events.position_changed, NULL); +} + +void +zn_snode_place_above(struct zn_snode *self, struct zn_snode *sibling) +{ + zn_snode_place_next_to(self, sibling, true); +} + +void +zn_snode_place_below(struct zn_snode *self, struct zn_snode *sibling) +{ + zn_snode_place_next_to(self, sibling, false); +} + struct wlr_texture * zn_snode_get_texture(struct zn_snode *self) { @@ -251,9 +324,12 @@ zn_snode_get_layout_fbox(struct zn_snode *self, struct wlr_fbox *fbox) { zn_snode_get_fbox(self, fbox); - if (self->screen) { - fbox->x += self->screen->layout_position[0]; - fbox->y += self->screen->layout_position[1]; + if (self->root) { + vec2 root_layout_position; + zn_snode_root_layout_position(self->root, root_layout_position); + + fbox->x += root_layout_position[0]; + fbox->y += root_layout_position[1]; } } @@ -267,7 +343,7 @@ zn_snode_handle_parent_position_changed( zn_snode_damage_whole(self); zn_snode_update_absolute_position(self); - self->screen = self->parent->screen; + self->root = self->parent->root; zn_snode_damage_whole(self); @@ -301,7 +377,7 @@ zn_snode_create( glm_vec2_zero(self->position); glm_vec2_zero(self->absolute_position); - self->screen = NULL; + self->root = NULL; wl_list_init(&self->child_node_list); wl_list_init(&self->link); @@ -335,14 +411,6 @@ zn_snode_create_focusable( return self; } -struct zn_snode * -zn_snode_create_root(struct zn_screen *screen) -{ - struct zn_snode *self = zn_snode_create(NULL, &zn_snode_noop_implementation); - self->screen = screen; - return self; -} - void zn_snode_destroy(struct zn_snode *self) { diff --git a/zen/src/view.c b/zen/src/view.c index 22c7db16..a25d0e42 100644 --- a/zen/src/view.c +++ b/zen/src/view.c @@ -86,7 +86,7 @@ zn_view_create(void *impl_data, const struct zn_view_interface *implementation) self->impl_data = impl_data; self->impl = implementation; - self->decoration_mode = ZN_VIEW_DECORATION_MODE_SERVER_SIDE; + self->decoration_mode = ZN_VIEW_DECORATION_MODE_CLIENT_SIDE; wl_signal_init(&self->events.resized); wl_signal_init(&self->events.unmap); diff --git a/zen/src/view.h b/zen/src/view.h new file mode 100644 index 00000000..78f7e0aa --- /dev/null +++ b/zen/src/view.h @@ -0,0 +1,27 @@ +#pragma once + +#include "zen/view.h" + +/// Called by the impl object +void zn_view_notify_resized(struct zn_view *self, vec2 size); + +/// Called by the impl object +void zn_view_notify_decoration( + struct zn_view *self, enum zn_view_decoration_mode mode); + +/// Called by the impl object +void zn_view_notify_move_request(struct zn_view *self); + +/// Called by the impl object +void zn_view_notify_resize_request( + struct zn_view *self, struct zn_view_resize_event *event); + +/// Called by the impl object +void zn_view_notify_unmap(struct zn_view *self); + +/// Called by the impl object +struct zn_view *zn_view_create( + void *impl_data, const struct zn_view_interface *implementation); + +/// Called by the impl object +void zn_view_destroy(struct zn_view *self); diff --git a/zen/src/virtual-object.c b/zen/src/virtual-object.c new file mode 100644 index 00000000..b8cc64e6 --- /dev/null +++ b/zen/src/virtual-object.c @@ -0,0 +1,176 @@ +#include "zen/virtual-object.h" + +#include +#include + +#include "gl-virtual-object.h" +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" +#include "zen/bounded.h" +#include "zen/inode.h" +#include "zen/server.h" +#include "zen/xr-dispatcher.h" +#include "zen/xr-system.h" + +bool +zn_virtual_object_set_role(struct zn_virtual_object *self, + enum zn_virtual_object_role role, void *role_object) +{ + zn_assert(role != ZN_VIRTUAL_OBJECT_ROLE_NONE, + "Do not pass NONE role to zn_virtual_object_set_role"); + + bool valid = self->role == ZN_VIRTUAL_OBJECT_ROLE_NONE || + (self->role == role && self->role_object == NULL); + + if (!valid) { + return false; + } + + zn_assert(self->role_object == NULL, "Invalid role_object state"); + + self->role = role; + self->role_object = role_object; + + if (self->role_object) { + switch (self->role) { + case ZN_VIRTUAL_OBJECT_ROLE_NONE: + zn_assert(false, "unreachable"); + break; + case ZN_VIRTUAL_OBJECT_ROLE_BOUNDED: + wl_signal_add(&self->bounded->events.destroy, + &self->role_object_destroy_listener); + break; + } + } + + return true; +} + +static void +zn_virtual_object_handle_role_object_destroy( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_virtual_object *self = + zn_container_of(listener, self, role_object_destroy_listener); + + self->role_object = NULL; + wl_list_remove(&self->role_object_destroy_listener.link); + wl_list_init(&self->role_object_destroy_listener.link); +} + +void +zn_virtual_object_change_visibility( + struct zn_virtual_object *self, bool visible) +{ + if (self->gl_virtual_object) { + zn_gl_virtual_object_change_visibility(self->gl_virtual_object, visible); + } +} + +void +zn_virtual_object_commit(struct zn_virtual_object *self) +{ + zn_signal_emit_mutable(&self->events.commit, NULL); + + if (self->gl_virtual_object) { + zn_gl_virtual_object_committed(self->gl_virtual_object); + } +} + +static void +zn_virtual_object_inode_mapped(void *impl_data UNUSED) +{} + +static void +zn_virtual_object_inode_unmapped(void *impl_data UNUSED) +{} + +static void +zn_virtual_object_inode_activated(void *impl_data) +{ + struct zn_virtual_object *self = impl_data; + struct zn_xr_system *xr_system = self->inode->xr_system; + + zn_assert(xr_system != NULL, "active inode must have xr_system"); + zn_assert(self->gl_virtual_object == NULL, + "gl_virtual_object must be NULL before being activated"); + + self->gl_virtual_object = + zn_xr_dispatcher_get_new_gl_virtual_object(xr_system->default_dispatcher); +} + +static void +zn_virtual_object_inode_deactivated(void *impl_data) +{ + struct zn_virtual_object *self = impl_data; + self->gl_virtual_object = NULL; +} + +static void +zn_virtual_object_inode_moved(void *impl_data UNUSED) +{} + +static const struct zn_inode_interface inode_implementation = { + .mapped = zn_virtual_object_inode_mapped, + .unmapped = zn_virtual_object_inode_unmapped, + .activated = zn_virtual_object_inode_activated, + .deactivated = zn_virtual_object_inode_deactivated, + .moved = zn_virtual_object_inode_moved, +}; + +struct zn_virtual_object * +zn_virtual_object_create(void) +{ + struct zn_server *server = zn_server_get_singleton(); + + struct zn_virtual_object *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->inode = zn_inode_create(self, &inode_implementation); + if (self->inode == NULL) { + zn_error("Failed to create a inode"); + goto err_free; + } + + self->gl_virtual_object = NULL; + self->role = ZN_VIRTUAL_OBJECT_ROLE_NONE; + + wl_signal_init(&self->events.destroy); + wl_signal_init(&self->events.commit); + + self->role_object_destroy_listener.notify = + zn_virtual_object_handle_role_object_destroy; + wl_list_init(&self->role_object_destroy_listener.link); + + zn_inode_move(self->inode, server->inode_invisible_root, GLM_VEC3_ZERO, + GLM_QUAT_IDENTITY); + + return self; + +err_free: + free(self); + +err: + return NULL; +} + +void +zn_virtual_object_destroy(struct zn_virtual_object *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + if (self->gl_virtual_object) { + zn_xr_dispatcher_destroy_gl_virtual_object( + self->gl_virtual_object->dispatcher, self->gl_virtual_object); + } + + zn_inode_destroy(self->inode); + wl_list_remove(&self->role_object_destroy_listener.link); + wl_list_remove(&self->events.destroy.listener_list); + wl_list_remove(&self->events.commit.listener_list); + free(self); +} diff --git a/zen/src/xr-dispatcher.c b/zen/src/xr-dispatcher.c new file mode 100644 index 00000000..a8ba71fb --- /dev/null +++ b/zen/src/xr-dispatcher.c @@ -0,0 +1,35 @@ +#include "xr-dispatcher.h" + +#include "zen-common/log.h" +#include "zen-common/signal.h" +#include "zen-common/util.h" + +struct zn_xr_dispatcher * +zn_xr_dispatcher_create( + void *impl_data, const struct zn_xr_dispatcher_interface *implementation) +{ + struct zn_xr_dispatcher *self = zalloc(sizeof *self); + if (self == NULL) { + zn_error("Failed to allocate memory"); + goto err; + } + + self->impl_data = impl_data; + self->impl = implementation; + + wl_signal_init(&self->events.destroy); + + return self; + +err: + return NULL; +} + +void +zn_xr_dispatcher_destroy(struct zn_xr_dispatcher *self) +{ + zn_signal_emit_mutable(&self->events.destroy, NULL); + + wl_list_remove(&self->events.destroy.listener_list); + free(self); +} diff --git a/zen/src/xr-dispatcher.h b/zen/src/xr-dispatcher.h new file mode 100644 index 00000000..796654ce --- /dev/null +++ b/zen/src/xr-dispatcher.h @@ -0,0 +1,16 @@ +#pragma once + +#include "zen/xr-dispatcher.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct zn_xr_dispatcher *zn_xr_dispatcher_create( + void *impl_data, const struct zn_xr_dispatcher_interface *implementation); + +void zn_xr_dispatcher_destroy(struct zn_xr_dispatcher *self); + +#ifdef __cplusplus +} +#endif diff --git a/zen/tests/binding.c b/zen/tests/binding.c new file mode 100644 index 00000000..438ff3c8 --- /dev/null +++ b/zen/tests/binding.c @@ -0,0 +1,89 @@ +#include "binding.h" + +#include + +#include "mock/backend.h" +#include "test-harness.h" +#include "zen/config.h" +#include "zen/server.h" + +static struct zn_server * +setup(void) +{ + struct zn_mock_backend *backend = zn_mock_backend_create(); + struct wl_display *display = wl_display_create(); + struct zn_config *config = zn_config_create(); + struct zn_server *server = zn_server_create(display, &backend->base, config); + return server; +} + +static void +teardown(struct zn_server *server) +{ + struct zn_config *config = server->config; + zn_server_destroy(server); + zn_config_destroy(config); +} + +static bool +handle_test1(const char *name UNUSED, uint32_t key UNUSED, + uint32_t modifiers UNUSED, void *user_data) +{ + bool *test1 = (bool *)user_data; + *test1 = true; + + return true; +} + +static bool +handle_test2(const char *name UNUSED, uint32_t key UNUSED, + uint32_t modifiers UNUSED, void *user_data) +{ + bool *test2 = (bool *)user_data; + *test2 = true; + + return true; +} + +TEST(binding) +{ + struct zn_server *server = setup(); + + bool test1 = false; + bool test2 = false; + + zn_binding_add(server->binding, "test1", handle_test1, &test1); + zn_binding_add(server->binding, "test2", handle_test2, &test2); + + zn_binding_remap(server->binding); + + zn_binding_handle_key(server->binding, KEY_4, WLR_MODIFIER_ALT); + ASSERT_EQUAL_BOOL(true, test1); + ASSERT_EQUAL_BOOL(false, test2); + + zn_binding_handle_key( + server->binding, KEY_4, WLR_MODIFIER_ALT | WLR_MODIFIER_LOGO); + ASSERT_EQUAL_BOOL(true, test1); + ASSERT_EQUAL_BOOL(true, test2); + + teardown(server); +} + +TEST(duplicated) +{ + struct zn_server *server = setup(); + + bool test1 = false; + bool test2 = false; + + zn_binding_add(server->binding, "test1", handle_test1, &test1); + zn_binding_add(server->binding, "test1", handle_test1, &test1); + zn_binding_add(server->binding, "test2", handle_test2, &test2); + + ulong item_count = + server->binding->items.size / sizeof(struct zn_binding_item); + + ASSERT_EQUAL_INT(2, item_count); + + teardown(server); +} diff --git a/zen/tests/config/config-section.c b/zen/tests/config/config-section.c index df79156a..6359958d 100644 --- a/zen/tests/config/config-section.c +++ b/zen/tests/config/config-section.c @@ -1,148 +1,33 @@ -#include #include -#include "config.c" // NOLINT(bugprone-suspicious-include) #include "test-harness.h" -#include "zen-common/util.h" #include "zen/config.h" -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) -static bool test_config_section_destroyed = false; - -static void test_config_section_destroy(struct zn_config_section *base); - -struct test_config_section { - struct zn_config_section base; - bool loaded; - int64_t number; -}; - -static bool -test_config_section_load(struct zn_config_section *base, toml_table_t *table) -{ - struct test_config_section *self = zn_container_of(base, self, base); - - self->loaded = true; - if (table) { - toml_datum_t result = toml_int_in(table, "number"); - if (result.ok) { - self->number = result.u.i; - } - } - - return self->number >= 0; -} - -static struct test_config_section * -test_config_section_create(void) -{ - struct test_config_section *section = zalloc(sizeof *section); - - section->base.load = test_config_section_load; - section->base.destroy = test_config_section_destroy; - section->loaded = false; - section->number = 0; - - return section; -} - -static void -test_config_section_destroy(struct zn_config_section *base) +TEST(reserve) { - struct test_config_section *self = zn_container_of(base, self, base); - free(self); - - test_config_section_destroyed = true; -} - -/// valid section -TEST(config_section_valid) -{ - struct test_config_section *section = test_config_section_create(); - struct zn_config *config = zn_config_create(); - zn_config_add_section(config, "test", §ion->base); - - ASSERT_EQUAL_POINTER(section, zn_config_get_section(config, "test")); + ASSERT_EQUAL_BOOL(true, zn_config_reserve_section(config, "section1")); - ASSERT_EQUAL_BOOL(true, section->loaded); - - ASSERT_EQUAL_INT(4, section->number); + ASSERT_EQUAL_BOOL(false, zn_config_reserve_section(config, "section1")); zn_config_destroy(config); - - ASSERT_EQUAL_BOOL(true, test_config_section_destroyed); } -/// missing section -TEST(config_section_invalid) +TEST(get_section) { - struct test_config_section *section = test_config_section_create(); - struct zn_config *config = zn_config_create(); - zn_config_add_section(config, "missing_section", §ion->base); + toml_table_t *table = toml_table_in(config->root_table, "test"); + toml_datum_t result = toml_int_in(table, "number"); - ASSERT_EQUAL_POINTER( - section, zn_config_get_section(config, "missing_section")); + ASSERT_NOT_EQUAL_POINTER(NULL, table); + ASSERT_EQUAL_INT(1, result.ok); + ASSERT_EQUAL_INT(4, result.u.i); - ASSERT_EQUAL_BOOL(true, section->loaded); + table = toml_table_in(config->root_table, "test2"); - ASSERT_EQUAL_INT(0, section->number); + ASSERT_EQUAL_POINTER(NULL, table); zn_config_destroy(config); - - ASSERT_EQUAL_BOOL(true, test_config_section_destroyed); -} - -/// duplicated section -TEST(config_section_duplicate) -{ - struct test_config_section *section1 = test_config_section_create(); - struct test_config_section *section2 = test_config_section_create(); - - struct zn_config *config = zn_config_create(); - - ASSERT_EQUAL_BOOL( - true, zn_config_add_section(config, "test", §ion1->base)); - ASSERT_EQUAL_BOOL( - false, zn_config_add_section(config, "test", §ion2->base)); -} - -/// load error -TEST(config_section_error) -{ - struct test_config_section *section = test_config_section_create(); - - struct zn_config *config = zn_config_create(); - - ASSERT_EQUAL_BOOL( - false, zn_config_add_section(config, "error", §ion->base)); -} - -/// missing config file -TEST(config_section_no_file) -{ - setenv("XDG_CONFIG_HOME", "/tmp/path", 1); - - struct test_config_section *section = test_config_section_create(); - - struct zn_config *config = zn_config_create(); - - zn_config_add_section(config, "test", §ion->base); - - ASSERT_EQUAL_POINTER(section, zn_config_get_section(config, "test")); - - ASSERT_EQUAL_BOOL(true, section->loaded); - - ASSERT_EQUAL_INT(0, section->number); -} - -/// unknown section -TEST(config_section_unknown) -{ - struct zn_config *config = zn_config_create(); - - ASSERT_EQUAL_POINTER(NULL, zn_config_get_section(config, "unkown")); } diff --git a/zen/tests/inode/inode-map.c b/zen/tests/inode/inode-map.c new file mode 100644 index 00000000..592d8008 --- /dev/null +++ b/zen/tests/inode/inode-map.c @@ -0,0 +1,162 @@ +#include +#include + +#include "inode.h" +#include "test-harness.h" + +TEST(map) +{ + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + + struct zn_inode *n1 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n2 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n3 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n11 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n12 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n21 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n211 = zn_inode_create(NULL, &zn_inode_noop_implementation); + + zn_inode_move(n1, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n2, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n3, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n11, n1, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n12, n1, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n21, n2, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n211, n21, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + zn_inode_map(root); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(root)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n1)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n2)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n3)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n11)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n12)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n21)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n211)); + + zn_inode_unmap(root); + + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(root)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n1)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n2)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n3)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n11)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n12)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n21)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n211)); + + zn_inode_map(root); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(root)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n1)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n2)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n3)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n11)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n12)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n21)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n211)); + + zn_inode_move(n1, NULL, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n2, NULL, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(root)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n1)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n2)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n3)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n11)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n12)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n21)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n211)); + + zn_inode_unmap(root); + zn_inode_move(n1, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(root)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n1)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n2)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n3)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n11)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n12)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n21)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n211)); + + zn_inode_map(root); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(root)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n1)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n2)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n3)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n11)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n12)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n21)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(n211)); + + zn_inode_move(n2, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(root)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n1)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n2)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n3)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n11)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n12)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n21)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(n211)); +} + +FAIL_TEST(non_root_map) +{ + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(NULL, &zn_inode_noop_implementation); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + zn_inode_map(node); +} + +FAIL_TEST(non_root_unmap) +{ + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(NULL, &zn_inode_noop_implementation); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + zn_inode_unmap(node); +} + +TEST(already_mapped) +{ + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(NULL, &zn_inode_noop_implementation); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + zn_inode_map(root); + zn_inode_map(root); // no error +} + +TEST(destroy) +{ + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(NULL, &zn_inode_noop_implementation); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + zn_inode_map(root); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(node)); +} + +FAIL_TEST(destroy_parent) +{ + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *parent = + zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(NULL, &zn_inode_noop_implementation); + + // NOLINTNEXTLINE(readability-suspicious-call-argument) + zn_inode_move(parent, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(node, parent, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + zn_inode_destroy(parent); +} diff --git a/zen/tests/inode/inode-move.c b/zen/tests/inode/inode-move.c new file mode 100644 index 00000000..16b742be --- /dev/null +++ b/zen/tests/inode/inode-move.c @@ -0,0 +1,161 @@ +#include +#include +#include + +#include "inode.h" +#include "test-harness.h" + +TEST(general) +{ + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + + struct zn_inode *n1 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n2 = zn_inode_create(NULL, &zn_inode_noop_implementation); + + versor x90; + versor y90; + versor z90; + + glm_quat(x90, GLM_PI_2f, 1, 0, 0); + glm_quat(y90, GLM_PI_2f, 0, 1, 0); + glm_quat(z90, GLM_PI_2f, 0, 0, 1); + + zn_inode_move(root, NULL, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n1, root, (vec3){20, 30, 40}, y90); + zn_inode_move(n2, n1, (vec3){30, 40, 50}, z90); + + vec3 local = {1, 0, 0}; + vec3 global; + + glm_mat4_mulv3(n1->transform_abs, local, 1, global); + + ASSERT_EQUAL_VEC3(((vec3){20, 30, 39}), global); + + zn_inode_move(root, NULL, (vec3){10, 20, 30}, x90); + + glm_mat4_mulv3(n1->transform_abs, local, 1, global); + + ASSERT_EQUAL_VEC3(((vec3){30, -19, 60}), global); + + glm_mat4_mulv3(n2->transform_abs, local, 1, global); + + ASSERT_EQUAL_VEC3(((vec3){80, 10, 101}), global); +} + +struct test_data { + bool mapped; + bool unmapped; + bool activated; + bool deactivated; + bool moved; +}; + +static void +mapped(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->mapped = true; +} + +static void +unmapped(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->unmapped = true; +} + +static void +activated(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->activated = true; +} + +static void +deactivated(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->deactivated = true; +} + +static void +moved(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->moved = true; +} + +TEST(signal) +{ + static const struct zn_inode_interface implementation = { + .mapped = mapped, + .unmapped = unmapped, + .activated = activated, + .deactivated = deactivated, + .moved = moved, + }; + + struct test_data data = {0}; + + struct zn_xr_system *xr_system = (void *)1; + + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *root2 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(&data, &implementation); + + zn_inode_map(root); + zn_inode_set_xr_system(root, xr_system); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(true, data.mapped); + ASSERT_EQUAL_BOOL(true, data.activated); + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(node)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(node)); + + zn_inode_move(node, root2, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(true, data.unmapped); + ASSERT_EQUAL_BOOL(true, data.deactivated); + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(node)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(node)); +} + +TEST(map) +{ + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *root2 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(NULL, &zn_inode_noop_implementation); + + zn_inode_map(root); + + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(node)); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_mapped(node)); + + zn_inode_move(node, root2, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(false, zn_inode_is_mapped(node)); +} + +TEST(active) +{ + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *root2 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_xr_system *xr_system = (void *)1; + + zn_inode_set_xr_system(root, xr_system); + + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(node)); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(node)); + + zn_inode_move(node, root2, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(node)); +} diff --git a/zen/tests/inode/inode-set-xr-system.c b/zen/tests/inode/inode-set-xr-system.c new file mode 100644 index 00000000..77f22391 --- /dev/null +++ b/zen/tests/inode/inode-set-xr-system.c @@ -0,0 +1,197 @@ +#include +#include +#include + +#include "inode.h" +#include "test-harness.h" + +TEST(map) +{ + struct zn_xr_system *xr_system = (void *)1; + + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + + struct zn_inode *n1 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n2 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n3 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n11 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n12 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n21 = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *n211 = zn_inode_create(NULL, &zn_inode_noop_implementation); + + zn_inode_move(n1, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n2, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n3, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n11, n1, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n12, n1, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n21, n2, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n211, n21, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + zn_inode_set_xr_system(root, xr_system); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(root)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n1)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n2)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n3)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n11)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n12)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n21)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n211)); + + zn_inode_set_xr_system(root, NULL); + + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(root)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n1)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n2)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n3)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n11)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n12)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n21)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n211)); + + zn_inode_set_xr_system(root, xr_system); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(root)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n1)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n2)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n3)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n11)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n12)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n21)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n211)); + + zn_inode_move(n1, NULL, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + zn_inode_move(n2, NULL, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(root)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n1)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n2)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n3)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n11)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n12)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n21)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n211)); + + zn_inode_set_xr_system(root, NULL); + zn_inode_move(n1, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(root)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n1)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n2)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n3)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n11)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n12)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n21)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n211)); + + zn_inode_set_xr_system(root, xr_system); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(root)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n1)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n2)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n3)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n11)); + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(n12)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n21)); + ASSERT_EQUAL_BOOL(false, zn_inode_is_active(n211)); +} + +struct test_data { + bool mapped; + bool unmapped; + bool activated; + bool deactivated; + bool moved; +}; + +static void +mapped(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->mapped = true; +} + +static void +unmapped(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->unmapped = true; +} + +static void +activated(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->activated = true; +} + +static void +deactivated(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->deactivated = true; +} + +static void +moved(void *impl_data UNUSED) +{ + struct test_data *data = impl_data; + data->moved = true; +} + +TEST(signal) +{ + static const struct zn_inode_interface implementation = { + .mapped = mapped, + .unmapped = unmapped, + .activated = activated, + .deactivated = deactivated, + .moved = moved, + }; + + struct test_data data = {0}; + + struct zn_xr_system *xr_system = (void *)1; + + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(&data, &implementation); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + ASSERT_EQUAL_BOOL(true, data.moved); + + ASSERT_EQUAL_BOOL(false, data.activated); + zn_inode_set_xr_system(root, xr_system); + ASSERT_EQUAL_BOOL(true, data.activated); + + ASSERT_EQUAL_BOOL(false, data.deactivated); + zn_inode_set_xr_system(root, NULL); + ASSERT_EQUAL_BOOL(true, data.deactivated); +} + +FAIL_TEST(non_root_map) +{ + struct zn_xr_system *xr_system = (void *)1; + + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(NULL, &zn_inode_noop_implementation); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + zn_inode_set_xr_system(node, xr_system); +} + +TEST(destroy) +{ + struct zn_xr_system *xr_system = (void *)1; + + struct zn_inode *root = zn_inode_create(NULL, &zn_inode_noop_implementation); + struct zn_inode *node = zn_inode_create(NULL, &zn_inode_noop_implementation); + + zn_inode_move(node, root, GLM_VEC3_ZERO, GLM_QUAT_IDENTITY); + + zn_inode_map(root); + zn_inode_set_xr_system(root, xr_system); + + ASSERT_EQUAL_BOOL(true, zn_inode_is_active(node)); +} diff --git a/zen/tests/meson.build b/zen/tests/meson.build index e23a6bdb..359a0bd5 100644 --- a/zen/tests/meson.build +++ b/zen/tests/meson.build @@ -8,12 +8,20 @@ _zen_unit_tests_deps = [ ] _zen_unit_tests = { + 'binding.c': { + 'env': { + 'XDG_CONFIG_HOME': '@0@/testdata'.format(meson.current_source_dir()), + }, + }, 'config/config-section.c': { 'env': { 'XDG_CONFIG_HOME': '@0@/testdata'.format(meson.current_source_dir()), }, }, 'config/config-path.c': {}, + 'inode/inode-map.c': {}, + 'inode/inode-move.c': {}, + 'inode/inode-set-xr-system.c': {}, 'server/construct-destroy.c': {}, 'snode/snode-damage.c': {}, 'snode/snode-focus.c': {}, diff --git a/zen/tests/mock/src/backend.c b/zen/tests/mock/src/backend.c index a343b57b..d0dff0ef 100644 --- a/zen/tests/mock/src/backend.c +++ b/zen/tests/mock/src/backend.c @@ -19,6 +19,17 @@ zn_mock_backend_create_wlr_texture_from_pixels(struct zn_backend *base UNUSED, return texture; } +static void +zn_mock_backend_set_xr_system( + struct zn_backend *base UNUSED, struct zn_xr_system *xr_system UNUSED) +{} + +static struct zn_xr_system * +zn_mock_backend_get_xr_system(struct zn_backend *base UNUSED) +{ + return NULL; +} + static bool zn_mock_backend_start(struct zn_backend *base UNUSED) { @@ -39,6 +50,8 @@ zn_mock_backend_handle_destroy(struct zn_backend *base) static const struct zn_backend_interface implementation = { .create_wlr_texture_from_pixels = zn_mock_backend_create_wlr_texture_from_pixels, + .set_xr_system = zn_mock_backend_set_xr_system, + .get_xr_system = zn_mock_backend_get_xr_system, .start = zn_mock_backend_start, .stop = zn_mock_backend_stop, .destroy = zn_mock_backend_handle_destroy, @@ -54,6 +67,9 @@ zn_mock_backend_create(void) wl_signal_init(&self->base.events.new_screen); wl_signal_init(&self->base.events.view_mapped); wl_signal_init(&self->base.events.destroy); + wl_signal_init(&self->base.events.new_xr_system); + wl_signal_init(&self->base.events.bounded_mapped); + wl_signal_init(&self->base.events.xr_system_changed); return self; } @@ -63,6 +79,8 @@ zn_mock_backend_destroy(struct zn_mock_backend *self) { zn_signal_emit_mutable(&self->base.events.destroy, NULL); + wl_list_remove(&self->base.events.xr_system_changed.listener_list); + wl_list_remove(&self->base.events.new_xr_system.listener_list); wl_list_remove(&self->base.events.destroy.listener_list); wl_list_remove(&self->base.events.view_mapped.listener_list); wl_list_remove(&self->base.events.new_screen.listener_list); diff --git a/zen/tests/mock/src/output.c b/zen/tests/mock/src/output.c index a4978478..652f20e2 100644 --- a/zen/tests/mock/src/output.c +++ b/zen/tests/mock/src/output.c @@ -3,8 +3,8 @@ #include #include +#include "screen.h" #include "zen-common/util.h" -#include "zen/screen.h" #include "zen/snode.h" static void diff --git a/zen/tests/server/construct-destroy.c b/zen/tests/server/construct-destroy.c index 5466dd3d..f1ccb0c9 100644 --- a/zen/tests/server/construct-destroy.c +++ b/zen/tests/server/construct-destroy.c @@ -2,6 +2,7 @@ #include "mock/backend.h" #include "test-harness.h" +#include "zen/config.h" #include "zen/cursor.h" #include "zen/seat.h" #include "zen/server.h" @@ -10,9 +11,11 @@ TEST(construct_destroy) { struct zn_mock_backend *backend = zn_mock_backend_create(); struct wl_display *display = wl_display_create(); - struct zn_server *server = zn_server_create(display, &backend->base); + struct zn_config *config = zn_config_create(); + struct zn_server *server = zn_server_create(display, &backend->base, config); ASSERT_NOT_EQUAL_POINTER(NULL, server); zn_server_destroy(server); + zn_config_destroy(config); } diff --git a/zen/tests/snode/snode-damage.c b/zen/tests/snode/snode-damage.c index 86ea1607..dd11ecfe 100644 --- a/zen/tests/snode/snode-damage.c +++ b/zen/tests/snode/snode-damage.c @@ -3,6 +3,7 @@ #include "mock/output.h" #include "test-harness.h" #include "zen-common/util.h" +#include "zen/snode-root.h" #include "zen/snode.h" static struct wlr_texture * @@ -36,7 +37,7 @@ TEST(set_position) texture2.width = 20; texture2.height = 20; - struct zn_snode *root = zn_snode_create_root(output->screen); + struct zn_snode *root = output->screen->snode_root->node; struct zn_snode *node1 = zn_snode_create(&texture1, &test_impl); struct zn_snode *node2 = zn_snode_create(&texture2, &test_impl); @@ -77,8 +78,8 @@ TEST(set_position_rebase_parent) texture.width = 10; texture.height = 10; - struct zn_snode *root = zn_snode_create_root(output->screen); - struct zn_snode *root2 = zn_snode_create_root(output2->screen); + struct zn_snode *root = output->screen->snode_root->node; + struct zn_snode *root2 = output2->screen->snode_root->node; struct zn_snode *node1 = zn_snode_create(&texture, &test_impl); struct zn_snode *node2 = zn_snode_create(&texture, &test_impl); @@ -145,7 +146,7 @@ TEST(damage) texture.width = 50; texture.height = 50; - struct zn_snode *root = zn_snode_create_root(output->screen); + struct zn_snode *root = output->screen->snode_root->node; struct zn_snode *node1 = zn_snode_create(&texture, &test_impl); struct zn_snode *node2 = zn_snode_create(&texture, &test_impl); @@ -171,3 +172,111 @@ TEST(damage) zn_snode_damage(node4, &(struct wlr_fbox){10, 30, 10, 10}); ASSERT_EQUAL_BOOL(false, pixman_region32_not_empty(&output->damage)); } + +TEST(change_position) +{ + struct zn_mock_output *output = zn_mock_output_create(0, 0); + + struct wlr_texture texture; + texture.width = 50; + texture.height = 50; + + struct zn_snode *root = output->screen->snode_root->node; + + struct zn_snode *node1 = zn_snode_create(&texture, &test_impl); + struct zn_snode *node2 = zn_snode_create(&texture, &test_impl); + struct zn_snode *node3 = zn_snode_create(&texture, &test_impl); + + zn_snode_set_position(node3, node2, (vec2){200, 100}); + zn_snode_set_position(node2, node1, (vec2){100, 200}); + zn_snode_set_position(node1, root, (vec2){100, 100}); + + zn_mock_output_damage_clear(output); + + zn_snode_change_position(node1, (vec2){-100, -100}); + + ASSERT_EQUAL_BOOL(true, zn_mock_output_damage_contains(output, + 100 + 100 + 200 + 25, 100 + 200 + 100 + 25)); + ASSERT_EQUAL_BOOL(true, + zn_mock_output_damage_contains(output, 100 + 100 + 25, 100 + 200 + 25)); + ASSERT_EQUAL_BOOL( + true, zn_mock_output_damage_contains(output, 100 + 25, 100 + 25)); + ASSERT_EQUAL_BOOL( + false, zn_mock_output_damage_contains(output, 100 - 25, 100 - 25)); + + ASSERT_EQUAL_BOOL(true, zn_mock_output_damage_contains(output, + -100 + 100 + 200 + 25, -100 + 200 + 100 + 25)); + ASSERT_EQUAL_BOOL(true, + zn_mock_output_damage_contains(output, -100 + 100 + 25, -100 + 200 + 25)); + ASSERT_EQUAL_BOOL( + true, zn_mock_output_damage_contains(output, -100 + 25, -100 + 25)); + ASSERT_EQUAL_BOOL( + false, zn_mock_output_damage_contains(output, -100 - 25, -100 - 25)); +} + +TEST(set_next_to) +{ + struct zn_mock_output *output = zn_mock_output_create(0, 0); + + struct wlr_texture texture; + texture.width = 50; + texture.height = 50; + + struct zn_snode *root = output->screen->snode_root->node; + + struct zn_snode *node1 = zn_snode_create(&texture, &test_impl); + struct zn_snode *node11 = zn_snode_create(&texture, &test_impl); + struct zn_snode *node12 = zn_snode_create(&texture, &test_impl); + + zn_snode_set_position(node1, root, (vec2){100, 100}); + zn_snode_set_position(node12, node1, (vec2){100, 200}); + zn_snode_set_position(node11, node1, (vec2){200, 100}); + + zn_mock_output_damage_clear(output); + + zn_snode_place_below(node11, node12); + + ASSERT_EQUAL_BOOL(true, + zn_mock_output_damage_contains(output, 100 + 200 + 25, 100 + 100 + 25)); + ASSERT_EQUAL_BOOL(false, + zn_mock_output_damage_contains(output, 100 + 100 + 25, 100 + 200 + 25)); +} + +TEST(no_damage) +{ + struct zn_mock_output *output = zn_mock_output_create(0, 0); + + struct wlr_texture texture; + texture.width = 50; + texture.height = 50; + + struct zn_snode *root = output->screen->snode_root->node; + + struct zn_snode *node1 = zn_snode_create(&texture, &test_impl); + struct zn_snode *node11 = zn_snode_create(&texture, &test_impl); + struct zn_snode *node12 = zn_snode_create(&texture, &test_impl); + + zn_snode_set_position(node1, root, (vec2){100, 100}); + zn_snode_set_position(node12, node1, (vec2){100, 200}); + zn_snode_set_position(node11, node1, (vec2){200, 100}); + + zn_mock_output_damage_clear(output); + + ASSERT_EQUAL_BOOL(false, pixman_region32_not_empty(&output->damage)); + + zn_snode_set_position(node1, root, (vec2){100, 100}); + + ASSERT_EQUAL_BOOL(false, pixman_region32_not_empty(&output->damage)); + + zn_snode_change_position(node11, (vec2){200, 100}); + + ASSERT_EQUAL_BOOL(false, pixman_region32_not_empty(&output->damage)); + + zn_snode_place_above(node11, node12); + + ASSERT_EQUAL_BOOL(false, pixman_region32_not_empty(&output->damage)); + + zn_snode_place_above(node12, node11); + + ASSERT_EQUAL_BOOL(true, pixman_region32_not_empty(&output->damage)); +} diff --git a/zen/tests/snode/snode-frame.c b/zen/tests/snode/snode-frame.c index 5ffc11e0..8ed24d01 100644 --- a/zen/tests/snode/snode-frame.c +++ b/zen/tests/snode/snode-frame.c @@ -2,8 +2,10 @@ #include #include "mock/output.h" +#include "screen.h" #include "test-harness.h" #include "zen-common/util.h" +#include "zen/snode-root.h" #include "zen/snode.h" static void @@ -38,7 +40,7 @@ TEST(frame) struct zn_snode *node3 = zn_snode_create(&counter, &test_impl); struct zn_snode *node4 = zn_snode_create(&counter, &test_impl); - zn_snode_set_position(node1, output->screen->snode_root, (vec2){0, 0}); + zn_snode_set_position(node1, output->screen->snode_root->node, (vec2){0, 0}); zn_snode_set_position(node2, node1, (vec2){0, 0}); zn_snode_set_position(node3, node1, (vec2){0, 0}); zn_snode_set_position(node4, node2, (vec2){0, 0}); diff --git a/zen/tests/snode/snode-get-at.c b/zen/tests/snode/snode-get-at.c index 0e678ff7..7ac04380 100644 --- a/zen/tests/snode/snode-get-at.c +++ b/zen/tests/snode/snode-get-at.c @@ -5,6 +5,7 @@ #include "mock/output.h" #include "test-harness.h" #include "zen-common/wlr/box.h" +#include "zen/snode-root.h" #include "zen/snode.h" bool @@ -53,7 +54,7 @@ TEST(normal) { struct zn_mock_output *output = zn_mock_output_create(0, 0); - struct zn_snode *root = zn_snode_create_root(output->screen); + struct zn_snode *root = output->screen->snode_root->node; struct zn_snode *node1 = create_snode_with_input_region(0, 0, 900, 800); struct zn_snode *node2 = create_snode_with_input_region(0, 0, 300, 300); struct zn_snode *node3 = create_snode_with_input_region(400, -100, 200, 300); diff --git a/zen/tests/snode/snode-position.c b/zen/tests/snode/snode-position.c index 086ccedf..1e1e87e9 100644 --- a/zen/tests/snode/snode-position.c +++ b/zen/tests/snode/snode-position.c @@ -78,3 +78,86 @@ TEST(parent_change) ASSERT_EQUAL_DOUBLE(3 + 4, box4.x); ASSERT_EQUAL_DOUBLE(0.3F + 0.4F, box4.y); } + +TEST(change_position) +{ + struct zn_snode *root = zn_snode_create(NULL, &zn_snode_noop_implementation); + + struct zn_snode *node1 = zn_snode_create(NULL, &zn_snode_noop_implementation); + struct zn_snode *node2 = zn_snode_create(NULL, &zn_snode_noop_implementation); + struct zn_snode *node3 = zn_snode_create(NULL, &zn_snode_noop_implementation); + struct zn_snode *node4 = zn_snode_create(NULL, &zn_snode_noop_implementation); + + zn_snode_set_position(node4, node3, (vec2){4, 0.4F}); + zn_snode_set_position(node3, node2, (vec2){3, 0.3F}); + zn_snode_set_position(node2, node1, (vec2){2, 0.2F}); + zn_snode_set_position(node1, root, (vec2){1, 0.1F}); + + struct wlr_fbox box4; + zn_snode_get_fbox(node4, &box4); + + ASSERT_EQUAL_DOUBLE(1 + 2 + 3 + 4, box4.x); + ASSERT_EQUAL_DOUBLE(0.1F + 0.2F + 0.3F + 0.4F, box4.y); + + zn_snode_change_position(node2, (vec2){12, 1.2F}); + zn_snode_get_fbox(node4, &box4); + + ASSERT_EQUAL_DOUBLE(1 + 12 + 3 + 4, box4.x); + ASSERT_EQUAL_DOUBLE(0.1F + 1.2F + 0.3F + 0.4F, box4.y); +} + +TEST(set_next_to) +{ + struct zn_snode *root = zn_snode_create(NULL, &zn_snode_noop_implementation); + + struct zn_snode *node1 = zn_snode_create(NULL, &zn_snode_noop_implementation); + struct zn_snode *node11 = + zn_snode_create(NULL, &zn_snode_noop_implementation); + struct zn_snode *node12 = + zn_snode_create(NULL, &zn_snode_noop_implementation); + struct zn_snode *node13 = + zn_snode_create(NULL, &zn_snode_noop_implementation); + + zn_snode_set_position(node1, root, (vec2){1, 0.1F}); + zn_snode_set_position(node13, node1, (vec2){4, 0.4F}); + zn_snode_set_position(node12, node1, (vec2){3, 0.3F}); + zn_snode_set_position(node11, node1, (vec2){2, 0.2F}); + + struct wlr_fbox box11; + zn_snode_get_fbox(node11, &box11); + + ASSERT_EQUAL_DOUBLE(1 + 2, box11.x); + ASSERT_EQUAL_DOUBLE(0.1F + 0.2F, box11.y); + ASSERT_EQUAL_POINTER(&node11->link, node12->link.next); + + zn_snode_place_above(node11, node13); + zn_snode_get_fbox(node11, &box11); + + ASSERT_EQUAL_DOUBLE(1 + 2, box11.x); + ASSERT_EQUAL_DOUBLE(0.1F + 0.2F, box11.y); + ASSERT_EQUAL_POINTER(&node11->link, node13->link.next); + + zn_snode_place_below(node11, node13); + zn_snode_get_fbox(node11, &box11); + + ASSERT_EQUAL_DOUBLE(1 + 2, box11.x); + ASSERT_EQUAL_DOUBLE(0.1F + 0.2F, box11.y); + ASSERT_EQUAL_POINTER(&node11->link, node13->link.prev); +} + +FAIL_TEST(set_next_to_not_sibling) +{ + struct zn_snode *root = zn_snode_create(NULL, &zn_snode_noop_implementation); + + struct zn_snode *node1 = zn_snode_create(NULL, &zn_snode_noop_implementation); + struct zn_snode *node2 = zn_snode_create(NULL, &zn_snode_noop_implementation); + struct zn_snode *node3 = zn_snode_create(NULL, &zn_snode_noop_implementation); + struct zn_snode *node4 = zn_snode_create(NULL, &zn_snode_noop_implementation); + + zn_snode_set_position(node4, node3, (vec2){4, 0.4F}); + zn_snode_set_position(node3, node2, (vec2){3, 0.3F}); + zn_snode_set_position(node2, node1, (vec2){2, 0.2F}); + zn_snode_set_position(node1, root, (vec2){1, 0.1F}); + + zn_snode_place_above(node1, node3); +} diff --git a/zen/tests/testdata/zen-desktop/config.toml b/zen/tests/testdata/zen-desktop/config.toml index 3a506e7c..ad23e78e 100644 --- a/zen/tests/testdata/zen-desktop/config.toml +++ b/zen/tests/testdata/zen-desktop/config.toml @@ -5,3 +5,7 @@ number = 4 [error] number = -1 + +[binding] +test1 = "alt+4" +test2 = "logo+alt+4"