diff --git a/src/server.cpp b/src/server.cpp index f2cc77529..dad4ae69a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -68,6 +68,48 @@ void Server::focus_view(std::shared_ptr&& view) { view->set_activated(true); } +void Server::try_focus_next_exclusive_layer() { + std::shared_ptr topmost_exclusive_layer = nullptr; + + // find the topmost layer in exclusive focus mode. if there are multiple within a single scene layer, the spec defines that + // focus order within that scene layer is implementation defined, so it doesn't matter which is chosen + for (const auto& output : outputs) { + for (const auto& layer : output->layers) { + if (layer->wlr.current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && + (topmost_exclusive_layer == nullptr || layer->scene_layer > topmost_exclusive_layer->scene_layer)) { + topmost_exclusive_layer = layer; + } + } + } + + focus_layer(topmost_exclusive_layer); +} + +void Server::focus_layer(std::shared_ptr layer) { + if (layer == nullptr) { + focused_layer.reset(); + if (focused_view.lock() != nullptr) { + focused_view.lock()->set_activated(true); + } + } + + // if there's already an exclusive focused shell layer with an equal or higher scene layer, just return + auto focused_layer_locked = focused_layer.lock(); + if (focused_layer_locked != nullptr && + focused_layer_locked->wlr.current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && + focused_layer_locked->scene_layer >= layer->scene_layer) { + return; + } + + focused_layer = layer; + + const auto* keyboard = wlr_seat_get_keyboard(seat->wlr); + if (keyboard != nullptr) { + wlr_seat_keyboard_notify_enter( + seat->wlr, layer->get_wlr_surface(), keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); + } +} + std::weak_ptr Server::surface_at(const double lx, const double ly, wlr_surface** wlr, double* sx, double* sy) const { /* This returns the topmost node in the scene at the given layout coords. * we only care about surface nodes as we are specifically looking for a diff --git a/src/server.hpp b/src/server.hpp index 08eb1d828..1cff4d4c7 100644 --- a/src/server.hpp +++ b/src/server.hpp @@ -109,6 +109,8 @@ class Server final : public std::enable_shared_from_this { std::weak_ptr surface_at(double lx, double ly, wlr_surface** wlr, double* sx, double* sy) const; void focus_view(std::shared_ptr&& view); + void focus_layer(std::shared_ptr layer); + void try_focus_next_exclusive_layer(); bool is_restricted(const wl_global* global) const; }; diff --git a/src/surface/layer.cpp b/src/surface/layer.cpp index 3df87f827..59c5322d3 100644 --- a/src/surface/layer.cpp +++ b/src/surface/layer.cpp @@ -79,7 +79,7 @@ static void wlr_layer_surface_v1_destroy_notify(wl_listener* listener, [[maybe_u static void wlr_layer_surface_v1_commit_notify(wl_listener* listener, [[maybe_unused]] void* data) { Layer& layer = magpie_container_of(listener, layer, commit); - const Server& server = layer.output.server; + Server& server = layer.output.server; const wlr_layer_surface_v1& surface = layer.wlr; const uint32_t committed = surface.current.committed; @@ -88,6 +88,18 @@ static void wlr_layer_surface_v1_commit_notify(wl_listener* listener, [[maybe_un wlr_scene_node_reparent(layer.scene_node, server.scene_layers[layer.scene_layer]); } + if ((committed & WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY) != 0) { + switch (layer.wlr.current.keyboard_interactive) { + case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE: + server.try_focus_next_exclusive_layer(); + break; + case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND: + case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE: + server.focus_layer(std::dynamic_pointer_cast(layer.shared_from_this())); + break; + } + } + if (committed != 0) { layer.output.update_layout(); }