diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index db9a0ce4..3a7d3f1b 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 + ln -s /usr/bin/clang-tidy /usr/bin/clang-tidy-15 + ln -s /usr/bin/clang-format /usr/bin/clang-format-15 - 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 diff --git a/desktop/include/zen-desktop/xr-system.h b/desktop/include/zen-desktop/xr-system.h index 01a2e737..211f470f 100644 --- a/desktop/include/zen-desktop/xr-system.h +++ b/desktop/include/zen-desktop/xr-system.h @@ -12,6 +12,7 @@ struct zn_desktop_xr_system { struct wl_list resource_list; // wl_resource::link of zen_xr_system struct wl_listener zn_xr_system_destroy_listener; + struct wl_listener session_status_changed_listener; }; struct zn_desktop_xr_system *zn_desktop_xr_system_create( diff --git a/desktop/src/xr-system.c b/desktop/src/xr-system.c index 6e5c4c58..40a9c5ff 100644 --- a/desktop/src/xr-system.c +++ b/desktop/src/xr-system.c @@ -16,8 +16,12 @@ zn_desktop_xr_system_handle_destroy(struct wl_resource *resource) static void zn_desktop_xr_system_protocol_connect( - struct wl_client *client UNUSED, struct wl_resource *resource UNUSED) -{} + 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, @@ -53,6 +57,31 @@ zn_desktop_xr_system_handle_xr_system_destroy( zn_desktop_xr_system_destroy(self); } +static void +zn_desktop_xr_system_handle_session_status_changed( + struct wl_listener *listener, void *data UNUSED) +{ + struct zn_desktop_xr_system *self = + zn_container_of(listener, self, session_status_changed_listener); + + struct wl_resource *resource = NULL; + + enum zen_xr_system_status status = ZEN_XR_SYSTEM_STATUS_CONNECTED; + + switch (self->zn_xr_system->status) { + case ZN_XR_SYSTEM_SESSION_STATUS_CONNECTED: + status = ZEN_XR_SYSTEM_STATUS_CONNECTED; + break; + case ZN_XR_SYSTEM_SESSION_STATUS_NOT_CONNECTED: + status = ZEN_XR_SYSTEM_STATUS_UNAVAILABLE; + break; + } + + 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) @@ -78,6 +107,11 @@ zn_desktop_xr_system_create( wl_signal_add( &zn_xr_system->events.destroy, &self->zn_xr_system_destroy_listener); + self->session_status_changed_listener.notify = + zn_desktop_xr_system_handle_session_status_changed; + wl_signal_add(&zn_xr_system->events.session_status_changed, + &self->session_status_changed_listener); + return self; err_free: @@ -90,6 +124,7 @@ zn_desktop_xr_system_create( static void zn_desktop_xr_system_destroy(struct zn_desktop_xr_system *self) { + wl_list_remove(&self->session_status_changed_listener.link); wl_list_remove(&self->zn_xr_system_destroy_listener.link); wl_global_destroy(self->global); wl_list_remove(&self->resource_list); diff --git a/meson.build b/meson.build index 8effedc9..6f341fba 100644 --- a/meson.build +++ b/meson.build @@ -121,7 +121,7 @@ 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.1.1' +zen_remote_server_req = '0.2.0' glew_proj = subproject( diff --git a/zen/include/zen/xr-system.h b/zen/include/zen/xr-system.h index 1545db50..a950df0b 100644 --- a/zen/include/zen/xr-system.h +++ b/zen/include/zen/xr-system.h @@ -6,12 +6,35 @@ extern "C" { #endif +struct zn_xr_system; + +struct zn_xr_system_interface { + void (*connect)(struct zn_xr_system *self); +}; + +enum zn_xr_system_session_status { + ZN_XR_SYSTEM_SESSION_STATUS_NOT_CONNECTED, // initial status + ZN_XR_SYSTEM_SESSION_STATUS_CONNECTED, +}; + struct zn_xr_system { + void *impl_data; // @outlive + const struct zn_xr_system_interface *impl; // @nonnull, @outlive + + enum zn_xr_system_session_status status; + struct { + struct wl_signal session_status_changed; // (NULL) struct wl_signal destroy; } events; }; +inline void +zn_xr_system_connect(struct zn_xr_system *self) +{ + self->impl->connect(self); +} + #ifdef __cplusplus } #endif diff --git a/zen/src/backend/immersive/remote/log.h b/zen/src/backend/immersive/remote/log.h new file mode 100644 index 00000000..f38a7f3f --- /dev/null +++ b/zen/src/backend/immersive/remote/log.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include + +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/pch/pch.h b/zen/src/backend/immersive/remote/pch/pch.h index 2c9952b9..9ee57188 100644 --- a/zen/src/backend/immersive/remote/pch/pch.h +++ b/zen/src/backend/immersive/remote/pch/pch.h @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include #include #include diff --git a/zen/src/backend/immersive/remote/xr-system-manager.cc b/zen/src/backend/immersive/remote/xr-system-manager.cc index 8173284b..141e2e8e 100644 --- a/zen/src/backend/immersive/remote/xr-system-manager.cc +++ b/zen/src/backend/immersive/remote/xr-system-manager.cc @@ -1,5 +1,6 @@ #include "xr-system-manager.h" +#include "log.h" #include "loop.h" #include "xr-system.h" #include "zen-common/log.h" @@ -11,13 +12,15 @@ namespace zen::backend::immersive::remote { XrSystemManager::XrSystemManager(wl_display *display) : display_(display) {} -static const struct zn_xr_system_manager_interface implementation = { +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)); @@ -27,7 +30,7 @@ XrSystemManager::Init() } c_obj_.impl_data = this; - c_obj_.impl = &implementation; + c_obj_.impl = &c_implementation_; wl_signal_init(&c_obj_.events.new_system); peer_manager_->on_peer_discover.Connect( @@ -46,11 +49,11 @@ XrSystemManager::HandleDestroy(zn_xr_system_manager *c_obj) } void -XrSystemManager::RemoveXrSystem(uint64_t peer_id) +XrSystemManager::RemoveDeadXrSystem() { auto result = std::remove_if(xr_systems_.begin(), xr_systems_.end(), - [peer_id](std::unique_ptr &xr_system) { - return xr_system->peer_id() == peer_id; + [](std::unique_ptr &xr_system) { + return !xr_system->is_alive(); }); xr_systems_.erase(result, xr_systems_.end()); @@ -59,9 +62,20 @@ XrSystemManager::RemoveXrSystem(uint64_t peer_id) void XrSystemManager::HandleRemotePeerDiscover(uint64_t peer_id) { - RemoveXrSystem(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->set_unavailable(); + } + }); + RemoveDeadXrSystem(); + + auto peer = peer_manager_->Get(peer_id); - auto xr_system = XrSystem::New(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(); @@ -73,7 +87,15 @@ XrSystemManager::HandleRemotePeerDiscover(uint64_t peer_id) void XrSystemManager::HandleRemotePeerLost(uint64_t peer_id) { - RemoveXrSystem(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->set_unavailable(); + } + }); + RemoveDeadXrSystem(); + + zn_debug("Lost a remote peer %ld", peer_id); } } // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/xr-system-manager.h b/zen/src/backend/immersive/remote/xr-system-manager.h index eef3bfb3..eac54356 100644 --- a/zen/src/backend/immersive/remote/xr-system-manager.h +++ b/zen/src/backend/immersive/remote/xr-system-manager.h @@ -13,7 +13,7 @@ class XrSystemManager DISABLE_MOVE_AND_COPY(XrSystemManager); ~XrSystemManager() = default; - static void HandleDestroy(zn_xr_system_manager *c_obj); + void RemoveDeadXrSystem(); private: friend struct zn_xr_system_manager * ::zn_xr_system_manager_create_remote( @@ -23,12 +23,14 @@ class XrSystemManager bool Init(); - void RemoveXrSystem(uint64_t peer_id); - 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 diff --git a/zen/src/backend/immersive/remote/xr-system.cc b/zen/src/backend/immersive/remote/xr-system.cc index 5137d861..ce5a1d03 100644 --- a/zen/src/backend/immersive/remote/xr-system.cc +++ b/zen/src/backend/immersive/remote/xr-system.cc @@ -1,17 +1,26 @@ #include "xr-system.h" +#include "loop.h" +#include "xr-system-manager.h" #include "zen-common/log.h" namespace zen::backend::immersive::remote { -XrSystem::XrSystem(uint64_t peer_id) : peer_id_(peer_id) {} +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() { wl_signal_emit(&c_obj_.events.destroy, nullptr); } std::unique_ptr -XrSystem::New(uint64_t peer_id) +XrSystem::New(std::shared_ptr peer, + wl_display *display, XrSystemManager *xr_system_manager) { - auto self = std::unique_ptr(new XrSystem(peer_id)); + auto self = std::unique_ptr( + new XrSystem(std::move(peer), display, xr_system_manager)); if (!self) { zn_error("Failed to allocate memory"); return nullptr; @@ -28,9 +37,66 @@ XrSystem::New(uint64_t peer_id) bool XrSystem::Init() { + c_obj_.impl_data = this; + c_obj_.impl = &c_implementation_; + c_obj_.status = ZN_XR_SYSTEM_SESSION_STATUS_NOT_CONNECTED; + wl_signal_init(&c_obj_.events.session_status_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()); + + c_obj_.status = ZN_XR_SYSTEM_SESSION_STATUS_NOT_CONNECTED; + + wl_signal_emit(&c_obj_.events.session_status_changed, nullptr); + + xr_system_manager_->RemoveDeadXrSystem(); // Will destroy this XrSystem +} + +void +XrSystem::Connect() +{ + if (session_) { + 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; + } + + zn_info("Connected to peer %ld", peer_->id()); + + set_unavailable(); + + new_session->on_disconnect.Connect([this]() { HandleDisconnect(); }); + + session_ = new_session; + + c_obj_.status = ZN_XR_SYSTEM_SESSION_STATUS_CONNECTED; + + wl_signal_emit(&c_obj_.events.session_status_changed, nullptr); +} + } // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/immersive/remote/xr-system.h b/zen/src/backend/immersive/remote/xr-system.h index 6782e20a..ea3551b4 100644 --- a/zen/src/backend/immersive/remote/xr-system.h +++ b/zen/src/backend/immersive/remote/xr-system.h @@ -5,24 +5,51 @@ namespace zen::backend::immersive::remote { +class XrSystemManager; + class XrSystem { public: DISABLE_MOVE_AND_COPY(XrSystem); ~XrSystem(); - static std::unique_ptr New(uint64_t peer_id); + static std::unique_ptr New( + std::shared_ptr peer, wl_display *display, + XrSystemManager *xr_system_manager); inline uint64_t peer_id() const; inline zn_xr_system *c_obj(); + inline void set_unavailable(); + + inline bool is_alive() const; + private: - explicit XrSystem(uint64_t peer_id); + XrSystem(std::shared_ptr peer, + wl_display *display, XrSystemManager *xr_system_manager); bool Init(); - const uint64_t peer_id_; + 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 + + // Null when status is NOT_CONNECTED. Not null otherwise. + std::shared_ptr session_; + + // True if available to create a new session. + bool is_available_ = true; zn_xr_system c_obj_{}; }; @@ -30,7 +57,7 @@ class XrSystem inline uint64_t XrSystem::peer_id() const { - return peer_id_; + return peer_->id(); } inline zn_xr_system * @@ -39,4 +66,17 @@ XrSystem::c_obj() return &c_obj_; } +inline void +XrSystem::set_unavailable() +{ + is_available_ = false; +} + +inline bool +XrSystem::is_alive() const +{ + return is_available_ || + c_obj_.status == ZN_XR_SYSTEM_SESSION_STATUS_CONNECTED; +} + } // namespace zen::backend::immersive::remote diff --git a/zen/src/backend/screen/keyboard.c b/zen/src/backend/screen/keyboard.c index c80c7fff..098b0367 100644 --- a/zen/src/backend/screen/keyboard.c +++ b/zen/src/backend/screen/keyboard.c @@ -1,9 +1,13 @@ #include "keyboard.h" +#include +#include +#include #include #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" @@ -30,6 +34,14 @@ zn_keyboard_handle_keyboard_key(struct wl_listener *listener, void *user_data) // 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);