kranewl

A wlroots-based dynamic Wayland compositor, written in C++, configurable with Lua
git clone git://git.deurzen.net/kranewl
Log | Files | Refs | LICENSE

commit 99c5c6058b30e11dbdfd313190a75835a9186e29
parent c8f1b325571eafb69ac3eb53bdd1bcad7f1c66f0
Author: deurzen <max@deurzen.net>
Date:   Mon, 30 May 2022 12:51:59 +0200

implements full layer shell compliance

Diffstat:
Minclude/kranewl/input/cursor.hh | 2++
Minclude/kranewl/model.hh | 5++++-
Minclude/kranewl/tree/layer.hh | 5+++--
Minclude/kranewl/tree/node.hh | 60+++++++++++++++++++++---------------------------------------
Minclude/kranewl/tree/output.hh | 3++-
Minclude/kranewl/tree/view.hh | 12++----------
Msrc/kranewl/input/cursor.cc | 42++++++++++++++++++++++--------------------
Msrc/kranewl/model.cc | 49++++++++++++++++++++++++++++++++++++-------------
Msrc/kranewl/server.cc | 5+++--
Msrc/kranewl/tree/layer.cc | 119++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Dsrc/kranewl/tree/node.cc | 49-------------------------------------------------
Msrc/kranewl/tree/output.cc | 31++++++++++++++++++++++++++++---
Msrc/kranewl/tree/view.cc | 10+++++-----
Msrc/kranewl/tree/xdg_view.cc | 2+-
Msrc/kranewl/tree/xwayland_view.cc | 2+-
15 files changed, 242 insertions(+), 154 deletions(-)

diff --git a/include/kranewl/input/cursor.hh b/include/kranewl/input/cursor.hh @@ -62,6 +62,8 @@ typedef struct Cursor { void initiate_cursor_interactive(Mode, View_ptr); void abort_cursor_interactive(); + void process_cursor_motion(uint32_t time); + static void handle_cursor_motion(struct wl_listener*, void*); static void handle_cursor_motion_absolute(struct wl_listener*, void*); static void handle_cursor_button(struct wl_listener*, void*); diff --git a/include/kranewl/model.hh b/include/kranewl/model.hh @@ -61,10 +61,13 @@ public: Layer_ptr create_layer(struct wlr_layer_surface_v1*, Output_ptr, SceneLayer); void register_layer(Layer_ptr); - void destroy_layer(Layer_ptr); void focus_view(View_ptr); + void refocus(); void place_view(Placement&); + + void focus_output(Output_ptr); + void cursor_interactive(Cursor::Mode, View_ptr); void abort_cursor_interactive(); diff --git a/include/kranewl/tree/layer.hh b/include/kranewl/tree/layer.hh @@ -3,6 +3,7 @@ #include <kranewl/common.hh> #include <kranewl/geometry.hh> #include <kranewl/scene-layer.hh> +#include <kranewl/tree/node.hh> #include <chrono> @@ -14,7 +15,7 @@ typedef class Server* Server_ptr; typedef class Model* Model_ptr; typedef class Output* Output_ptr; -typedef struct Layer { +typedef struct Layer : public Node { Layer( struct wlr_layer_surface_v1*, Server_ptr, @@ -57,7 +58,7 @@ typedef struct Layer { private: Region m_region; - bool m_mapped; + int m_mapped; std::chrono::time_point<std::chrono::steady_clock> m_managed_since; }* Layer_ptr; diff --git a/include/kranewl/tree/node.hh b/include/kranewl/tree/node.hh @@ -1,51 +1,33 @@ #pragma once -#include <kranewl/common.hh> - -extern "C" { -#include <wayland-server-core.h> -} - -typedef class Root* Root_ptr; -typedef class Output* Output_ptr; -typedef class Context* Context_ptr; -typedef class Workspace* Workspace_ptr; -typedef struct Container* Container_ptr; - typedef struct Node { enum class Type { - Root, - Output, - Context, - Workspace, - Container, + XDGShell, + LayerShell, +#ifdef XWAYLAND + XWaylandManaged, + XWaylandUnmanaged, +#endif }; + virtual bool is_focusable() const + { +#ifdef XWAYLAND + return m_type == Type::XDGShell + || m_type == Type::XWaylandManaged; +#else + return m_type == Type::XDGShell; +#endif + } + protected: - Node(Root_ptr); - Node(Output_ptr); - Node(Context_ptr); - Node(Workspace_ptr); - Node(Container_ptr); - ~Node(); + Node(Type type) + : m_type(type) + {} -public: - Uid m_uid; + ~Node() + {} Type m_type; - union { - Root_ptr m_root; - Output_ptr m_output; - Context_ptr m_context; - Workspace_ptr m_workspace; - Container_ptr m_container; - }; - - bool m_destroying; - bool m_dirty; - - struct { - struct wl_signal destroy; - } m_events; }* Node_ptr; diff --git a/include/kranewl/tree/output.hh b/include/kranewl/tree/output.hh @@ -19,7 +19,7 @@ typedef class Model* Model_ptr; typedef class Context* Context_ptr; typedef class Layer* Layer_ptr; -typedef class Output final : public Node { +typedef class Output final { public: Output( Server_ptr, @@ -51,6 +51,7 @@ public: void add_layer(Layer_ptr); void remove_layer(Layer_ptr); + void relayer_layer(Layer_ptr, SceneLayer, SceneLayer); void arrange_layers(); diff --git a/include/kranewl/tree/view.hh b/include/kranewl/tree/view.hh @@ -4,6 +4,7 @@ #include <kranewl/decoration.hh> #include <kranewl/geometry.hh> #include <kranewl/scene-layer.hh> +#include <kranewl/tree/node.hh> #include <vector> #include <chrono> @@ -25,7 +26,7 @@ typedef struct XWaylandView* XWaylandView_ptr; #endif typedef struct View* View_ptr; -typedef struct View { +typedef struct View : public Node { static constexpr Dim MIN_VIEW_DIM = Dim{25, 10}; static constexpr Dim PREFERRED_INIT_VIEW_DIM = Dim{480, 260}; @@ -39,13 +40,6 @@ typedef struct View { Urgent }; - enum class Type { - XDGShell, -#ifdef XWAYLAND - XWayland, -#endif - }; - View( XDGView_ptr, Uid, @@ -162,8 +156,6 @@ typedef struct View { Uid m_uid; std::string m_uid_formatted; - Type m_type; - Server_ptr mp_server; Model_ptr mp_model; Seat_ptr mp_seat; diff --git a/src/kranewl/input/cursor.cc b/src/kranewl/input/cursor.cc @@ -23,8 +23,8 @@ extern "C" { #include <wlr/types/wlr_xcursor_manager.h> } #undef static -#undef class #undef namespace +#undef class #include <algorithm> @@ -99,7 +99,10 @@ view_at( while (node && !node->data) node = node->parent; - return reinterpret_cast<View_ptr>(node->data); + if (node && node->data && static_cast<Node_ptr>(node->data)->is_focusable()) + return reinterpret_cast<View_ptr>(node->data); + + return nullptr; } } @@ -291,20 +294,20 @@ cursor_motion_to_client( prev_view = view; } -static inline void -process_cursor_motion(Cursor_ptr cursor, uint32_t time) +void +Cursor::process_cursor_motion(uint32_t time) { struct wlr_drag_icon* icon; - if (cursor->mp_seat->mp_wlr_seat->drag && (icon = cursor->mp_seat->mp_wlr_seat->drag->icon)) + if (mp_seat->mp_wlr_seat->drag && (icon = mp_seat->mp_wlr_seat->drag->icon)) wlr_scene_node_set_position( reinterpret_cast<struct wlr_scene_node*>(icon->data), - cursor->mp_wlr_cursor->x + icon->surface->sx, - cursor->mp_wlr_cursor->y + icon->surface->sy + mp_wlr_cursor->x + icon->surface->sx, + mp_wlr_cursor->y + icon->surface->sy ); - switch (cursor->m_cursor_mode) { - case Cursor::Mode::Move: process_cursor_move(cursor, time); return; - case Cursor::Mode::Resize: process_cursor_resize(cursor, time); return; + switch (m_cursor_mode) { + case Cursor::Mode::Move: process_cursor_move(this, time); return; + case Cursor::Mode::Resize: process_cursor_resize(this, time); return; case Cursor::Mode::Passthrough: // fallthrough default: break; } @@ -313,22 +316,21 @@ process_cursor_motion(Cursor_ptr cursor, uint32_t time) struct wlr_surface* surface = nullptr; View_ptr view = view_at( - cursor->mp_server, - cursor->mp_wlr_cursor->x, - cursor->mp_wlr_cursor->y, + mp_server, + mp_wlr_cursor->x, + mp_wlr_cursor->y, &surface, &sx, &sy ); - if (!view && time) { + if (!surface && time) wlr_xcursor_manager_set_cursor_image( - cursor->mp_cursor_manager, + mp_cursor_manager, "left_ptr", - cursor->mp_wlr_cursor + mp_wlr_cursor ); - } - cursor_motion_to_client(cursor, view, surface, sx, sy, time); + cursor_motion_to_client(this, view, surface, sx, sy, time); } void @@ -339,7 +341,7 @@ Cursor::handle_cursor_motion(struct wl_listener* listener, void* data) = reinterpret_cast<struct wlr_event_pointer_motion*>(data); wlr_cursor_move(cursor->mp_wlr_cursor, event->device, event->delta_x, event->delta_y); - process_cursor_motion(cursor, event->time_msec); + cursor->process_cursor_motion(event->time_msec); } void @@ -350,7 +352,7 @@ Cursor::handle_cursor_motion_absolute(struct wl_listener* listener, void* data) = reinterpret_cast<struct wlr_event_pointer_motion_absolute*>(data); wlr_cursor_warp_absolute(cursor->mp_wlr_cursor, event->device, event->x, event->y); - process_cursor_motion(cursor, event->time_msec); + cursor->process_cursor_motion(event->time_msec); } bool diff --git a/src/kranewl/model.cc b/src/kranewl/model.cc @@ -32,8 +32,8 @@ extern "C" { #include <wlr/types/wlr_surface.h> } #undef static -#undef class #undef namespace +#undef class Model::Model( Config const& config, @@ -241,12 +241,15 @@ Model::focus_view(View_ptr view) { TRACE(); + if (!view || !view->mp_context) + return; + Output_ptr output = view->mp_context->output(); if (!output || mp_focus == view) return; - if (!view->sticky()) { + if (!view->sticky() || view->mp_context != mp_context) { activate_workspace(view->mp_workspace); mp_workspace->activate_view(view); } @@ -263,6 +266,36 @@ Model::focus_view(View_ptr view) } void +Model::refocus() +{ + TRACE(); + + Output_ptr output; + if (!mp_focus || !(output = mp_focus->mp_context->output())) + return; + + if (!mp_focus->sticky() || mp_focus->mp_context != mp_context) { + activate_workspace(mp_focus->mp_workspace); + mp_workspace->activate_view(mp_focus); + } + + mp_focus->focus(Toggle::On); + mp_focus->set_urgent(false); + + if (mp_workspace->layout_is_persistent() || mp_workspace->layout_is_single()) + apply_layout(mp_workspace); +} + +void +Model::focus_output(Output_ptr output) +{ + TRACE(); + + if (output && output != mp_output) + focus_view(output->context()->workspace()->active()); +} + +void Model::place_view(Placement& placement) { TRACE(); @@ -1840,8 +1873,8 @@ Model::destroy_view(View_ptr view) TRACE(); m_view_map.erase(view->m_uid); - delete view; spdlog::info("Destroyed view {}", view->m_uid_formatted); + delete view; } Layer_ptr @@ -1873,16 +1906,6 @@ Model::register_layer(Layer_ptr layer) spdlog::info("Registered layer {}", layer->m_uid_formatted); } -void -Model::destroy_layer(Layer_ptr layer) -{ - TRACE(); - - layer->mp_output->remove_layer(layer); - delete layer; - spdlog::info("Destroyed layer {}", layer->m_uid_formatted); -} - bool Model::is_free(View_ptr view) const { diff --git a/src/kranewl/server.cc b/src/kranewl/server.cc @@ -67,8 +67,8 @@ extern "C" { #endif } #undef static -#undef class #undef namespace +#undef class extern "C" { #include <xkbcommon/xkbcommon.h> @@ -427,8 +427,9 @@ Server::handle_new_layer_shell_surface(struct wl_listener* listener, void* data) break; default: spdlog::error("No applicable scene layer found for layer surface"); + spdlog::warn("Ignoring layer surface"); scene_layer = SCENE_LAYER_NONE; - break; + return; } Output_ptr output = reinterpret_cast<Output_ptr>(layer_surface->output->data); diff --git a/src/kranewl/tree/layer.cc b/src/kranewl/tree/layer.cc @@ -1,13 +1,23 @@ #include <trace.hh> +#include <kranewl/model.hh> +#include <kranewl/server.hh> #include <kranewl/tree/layer.hh> +#include <kranewl/tree/output.hh> // https://github.com/swaywm/wlroots/issues/682 +#include <pthread.h> +#define class class_ #define namespace namespace_ +#define static extern "C" { #include <wlr/types/wlr_layer_shell_v1.h> +#include <wlr/types/wlr_scene.h> +#include <wlr/types/wlr_seat.h> } +#undef static #undef namespace +#undef class Layer::Layer( struct wlr_layer_surface_v1* layer_surface, @@ -16,7 +26,8 @@ Layer::Layer( Output_ptr output, SceneLayer scene_layer ) - : m_uid(reinterpret_cast<std::uintptr_t>(layer_surface)), + : Node(Type::LayerShell), + m_uid(reinterpret_cast<std::uintptr_t>(layer_surface)), m_uid_formatted([this]() { std::stringstream uid_ss; uid_ss << "0x" << std::hex << m_uid; @@ -31,12 +42,12 @@ Layer::Layer( ml_unmap({ .notify = Layer::handle_unmap }), ml_surface_commit({ .notify = Layer::handle_surface_commit }), ml_destroy({ .notify = Layer::handle_destroy }), - m_mapped(false), + m_mapped(0), m_managed_since(std::chrono::steady_clock::now()) { wl_signal_add(&mp_layer_surface->events.map, &ml_map); wl_signal_add(&mp_layer_surface->events.unmap, &ml_unmap); - wl_signal_add(&mp_layer_surface->surface->events.new_subsurface, &ml_destroy); + wl_signal_add(&mp_layer_surface->surface->events.commit, &ml_surface_commit); wl_signal_add(&mp_layer_surface->events.destroy, &ml_destroy); } @@ -56,25 +67,119 @@ Layer::set_region(Region const& region) } void -Layer::handle_map(struct wl_listener*, void*) +Layer::handle_map(struct wl_listener* listener, void*) { + TRACE(); + Layer_ptr layer = wl_container_of(listener, layer, ml_map); + + wlr_surface_send_enter( + layer->mp_layer_surface->surface, + layer->mp_layer_surface->output + ); + + if (layer->mp_server->m_seat.mp_cursor) + layer->mp_server->m_seat.mp_cursor->process_cursor_motion(0); +} + +static inline void +unmap_layer(Layer_ptr layer) +{ + TRACE(); + + layer->mp_layer_surface->mapped = 0; + struct wlr_seat* seat = layer->mp_server->m_seat.mp_wlr_seat; + + if (layer->mp_layer_surface->surface == seat->keyboard_state.focused_surface) + layer->mp_model->refocus(); + + if (layer->mp_server->m_seat.mp_cursor) + layer->mp_server->m_seat.mp_cursor->process_cursor_motion(0); } void -Layer::handle_unmap(struct wl_listener*, void*) +Layer::handle_unmap(struct wl_listener* listener, void*) { + TRACE(); + Layer_ptr layer = wl_container_of(listener, layer, ml_unmap); + unmap_layer(layer); } void -Layer::handle_surface_commit(struct wl_listener*, void*) +Layer::handle_surface_commit(struct wl_listener* listener, void*) { + TRACE(); + + Layer_ptr layer = wl_container_of(listener, layer, ml_surface_commit); + + struct wlr_layer_surface_v1* layer_surface = layer->mp_layer_surface; + struct wlr_output* wlr_output = layer_surface->output; + SceneLayer scene_layer; + switch (layer_surface->current.layer) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + scene_layer = SCENE_LAYER_BACKGROUND; + break; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + scene_layer = SCENE_LAYER_BOTTOM; + break; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + scene_layer = SCENE_LAYER_TOP; + break; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + scene_layer = SCENE_LAYER_OVERLAY; + break; + default: + spdlog::error("No applicable scene layer found for layer surface"); + spdlog::warn("Not committing surface"); + scene_layer = SCENE_LAYER_NONE; + return; + } + + wlr_scene_node_reparent( + layer->mp_scene, + layer->mp_server->m_scene_layers[scene_layer] + ); + + Output_ptr output; + if (!wlr_output || !(output = reinterpret_cast<Output_ptr>(wlr_output->data))) + return; + + if (layer_surface->current.committed == 0 && layer->m_mapped == layer_surface->mapped) + return; + + layer->m_mapped = layer_surface->mapped; + + if (layer->mp_server->m_scene_layers[scene_layer] != layer->mp_scene) + output->relayer_layer(layer, layer->m_scene_layer, scene_layer); + + output->arrange_layers(); } void -Layer::handle_destroy(struct wl_listener*, void*) +Layer::handle_destroy(struct wl_listener* listener, void*) { + TRACE(); + + Layer_ptr layer = wl_container_of(listener, layer, ml_destroy); + struct wlr_output* wlr_output = layer->mp_layer_surface->output; + + if (layer->mp_layer_surface->mapped) + unmap_layer(layer); + + wl_list_remove(&layer->ml_map.link); + wl_list_remove(&layer->ml_unmap.link); + wl_list_remove(&layer->ml_surface_commit.link); + wl_list_remove(&layer->ml_destroy.link); + + Output_ptr output; + if (wlr_output && (output = reinterpret_cast<Output_ptr>(wlr_output->data))) { + output->remove_layer(layer); + output->arrange_layers(); + layer->mp_layer_surface->output = nullptr; + } + spdlog::info("Destroyed layer {}", layer->m_uid_formatted); + delete layer; } diff --git a/src/kranewl/tree/node.cc b/src/kranewl/tree/node.cc @@ -1,49 +0,0 @@ -#include <kranewl/tree/node.hh> - -Node::Node(Root_ptr root) - : m_uid(reinterpret_cast<Uid>(root)), - m_type(Type::Root), - m_root(root), - m_destroying(false), - m_dirty(false), - m_events{ .destroy = {} } -{} - -Node::Node(Output_ptr output) - : m_uid(reinterpret_cast<Uid>(output)), - m_type(Type::Output), - m_output(output), - m_destroying(false), - m_dirty(false), - m_events{ .destroy = {} } -{} - -Node::Node(Context_ptr context) - : m_uid(reinterpret_cast<Uid>(context)), - m_type(Type::Context), - m_context(context), - m_destroying(false), - m_dirty(false), - m_events{ .destroy = {} } -{} - -Node::Node(Workspace_ptr workspace) - : m_uid(reinterpret_cast<Uid>(workspace)), - m_type(Type::Workspace), - m_workspace(workspace), - m_destroying(false), - m_dirty(false), - m_events{ .destroy = {} } -{} - -Node::Node(Container_ptr container) - : m_uid(reinterpret_cast<Uid>(container)), - m_type(Type::Container), - m_container(container), - m_destroying(false), - m_dirty(false), - m_events{ .destroy = {} } -{} - -Node::~Node() -{} diff --git a/src/kranewl/tree/output.cc b/src/kranewl/tree/output.cc @@ -19,8 +19,8 @@ extern "C" { #include <wlr/types/wlr_seat.h> } #undef static -#undef class #undef namespace +#undef class #include <vector> @@ -31,8 +31,7 @@ Output::Output( struct wlr_scene_output* wlr_scene_output, Region const&& output_region ) - : Node(this), - mp_context(nullptr), + : mp_context(nullptr), m_full_region(output_region), m_placeable_region(output_region), mp_server(server), @@ -121,6 +120,13 @@ Output::handle_destroy(struct wl_listener*, void* data) { TRACE(); + static const std::vector<SceneLayer> scene_layers = { + SCENE_LAYER_BACKGROUND, + SCENE_LAYER_BOTTOM, + SCENE_LAYER_TOP, + SCENE_LAYER_OVERLAY, + }; + struct wlr_output* wlr_output = reinterpret_cast<struct wlr_output*>(data); Output_ptr output = reinterpret_cast<Output_ptr>(wlr_output->data); @@ -133,6 +139,15 @@ Output::handle_destroy(struct wl_listener*, void* data) wlr_scene_output_destroy(output->mp_wlr_scene_output); wlr_output_layout_remove(output->mp_server->mp_output_layout, output->mp_wlr_output); + for (SceneLayer scene_layer : scene_layers) { + for (Layer_ptr layer : output->m_layer_map.at(scene_layer)) { + layer->mp_output = nullptr; + layer->mp_layer_surface->output = nullptr; + } + + output->m_layer_map.at(scene_layer).clear(); + } + output->mp_model->unregister_output(output); } @@ -207,6 +222,16 @@ Output::remove_layer(Layer_ptr layer) Util::erase_remove(m_layer_map.at(layer->m_scene_layer), layer); } +void +Output::relayer_layer(Layer_ptr layer, SceneLayer old_layer, SceneLayer new_layer) +{ + TRACE(); + + Util::erase_remove(m_layer_map.at(old_layer), layer); + m_layer_map[new_layer].push_back(layer); + layer->m_scene_layer = new_layer; +} + static inline void propagate_exclusivity( Region& placeable_region, diff --git a/src/kranewl/tree/view.cc b/src/kranewl/tree/view.cc @@ -19,8 +19,8 @@ extern "C" { #include <wlr/util/edges.h> } #undef static -#undef class #undef namespace +#undef class View::View( XDGView_ptr, @@ -30,13 +30,13 @@ View::View( Seat_ptr seat, struct wlr_surface* wlr_surface ) - : m_uid(uid), + : Node(Type::XDGShell), + m_uid(uid), m_uid_formatted([uid]() { std::stringstream uid_ss; uid_ss << "0x" << std::hex << uid; return uid_ss.str(); }()), - m_type(Type::XDGShell), mp_server(server), mp_model(model), mp_seat(seat), @@ -88,8 +88,8 @@ View::View( Seat_ptr seat, struct wlr_surface* wlr_surface ) - : m_uid(uid), - m_type(Type::XWayland), + : Node(Type::XWaylandManaged), + m_uid(uid), mp_server(server), mp_model(model), mp_seat(seat), diff --git a/src/kranewl/tree/xdg_view.cc b/src/kranewl/tree/xdg_view.cc @@ -22,8 +22,8 @@ extern "C" { #include <wlr/types/wlr_xdg_shell.h> } #undef static -#undef class #undef namespace +#undef class XDGView::XDGView( struct wlr_xdg_surface* wlr_xdg_surface, diff --git a/src/kranewl/tree/xwayland_view.cc b/src/kranewl/tree/xwayland_view.cc @@ -13,8 +13,8 @@ extern "C" { #include <wlr/xwayland.h> } #undef static -#undef class #undef namespace +#undef class XWaylandView::XWaylandView( struct wlr_xwayland_surface* wlr_xwayland_surface,