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 8752e09d79575a20a1a6c413fff9f6e77f7109cf
parent 57e3d1ce4b0480a4414c200fb693a79018462e35
Author: deurzen <max@deurzen.net>
Date:   Thu, 26 May 2022 22:24:55 +0200

adds view {,un}mapping functionality

Diffstat:
Minclude/kranewl/cycle.hh | 15+++++++++++++++
Minclude/kranewl/decoration.hh | 5+++--
Minclude/kranewl/geometry.hh | 12++++++++++++
Minclude/kranewl/model.hh | 24+++++++++++++++++++++++-
Minclude/kranewl/server.hh | 8+++++---
Minclude/kranewl/tree/output.hh | 11+++++++----
Minclude/kranewl/tree/view.hh | 24++++++++++++++++--------
Minclude/kranewl/tree/xdg_view.hh | 7+++----
Minclude/kranewl/tree/xwayland_view.hh | 5+----
Msrc/kranewl/layout.cc | 213+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/kranewl/model.cc | 313++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/kranewl/server.cc | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/kranewl/tree/output.cc | 6+++++-
Msrc/kranewl/tree/view.cc | 161++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/kranewl/tree/xdg_view.cc | 74+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/kranewl/tree/xwayland_view.cc | 8+-------
Msrc/kranewl/workspace.cc | 4++++
17 files changed, 791 insertions(+), 193 deletions(-)

diff --git a/include/kranewl/cycle.hh b/include/kranewl/cycle.hh @@ -71,6 +71,21 @@ public: std::optional<T> element_at_front(T) const; std::optional<T> element_at_back(T) const; + template<typename UnaryPredicate> + std::optional<T> first_element_with_condition(UnaryPredicate predicate) + { + auto it = std::find_if( + m_elements.begin(), + m_elements.end(), + predicate + ); + + if (it != m_elements.end()) + return *it; + + return {}; + } + void activate_first(); void activate_last(); void activate_at_index(Index); diff --git a/include/kranewl/decoration.hh b/include/kranewl/decoration.hh @@ -58,12 +58,13 @@ struct Frame final { struct Decoration final { std::optional<Frame> frame; - const Extents extents() const + Extents const& extents() const { + static Extents NO_EXTENTS{0, 0, 0, 0}; if (frame) return frame->extents; - return Extents{0, 0, 0, 0}; + return NO_EXTENTS; } }; diff --git a/include/kranewl/geometry.hh b/include/kranewl/geometry.hh @@ -1,6 +1,7 @@ #pragma once #include <ostream> +#include <sstream> extern "C" { #include <wlr/util/box.h> @@ -214,6 +215,17 @@ operator<<(std::ostream& os, Region const& region) return os << "[" << region.pos << " " << region.dim << "]"; } +namespace std { + + inline std::string + to_string(Region const& region) { + std::ostringstream oss; + oss << region; + return oss.str(); + } + +} + struct DRegion final { DPos pos; DDim dim; diff --git a/include/kranewl/model.hh b/include/kranewl/model.hh @@ -4,6 +4,7 @@ #include <kranewl/common.hh> #include <kranewl/cycle.hh> #include <kranewl/geometry.hh> +#include <kranewl/placement.hh> #include <kranewl/tree/view.hh> #include <optional> @@ -35,7 +36,7 @@ public: void register_server(Server_ptr); void exit(); - Output_ptr create_output(struct wlr_output*, struct wlr_scene_output*); + Output_ptr create_output(struct wlr_output*, struct wlr_scene_output*, Region const&&); void register_output(Output_ptr); void unregister_output(Output_ptr); @@ -46,6 +47,27 @@ public: void register_view(View_ptr); void unregister_view(View_ptr); + void map_view(View_ptr); + void unmap_view(View_ptr); + void iconify_view(View_ptr); + void deiconify_view(View_ptr); + void disown_view(View_ptr); + void reclaim_view(View_ptr); + void focus_view(View_ptr); + void place_view(Placement&); + + void move_view_to_workspace(View_ptr, Index); + void move_view_to_workspace(View_ptr, Workspace_ptr); + void move_view_to_context(View_ptr, Index); + void move_view_to_context(View_ptr, Context_ptr); + void move_view_to_output(View_ptr, Index); + void move_view_to_output(View_ptr, Output_ptr); + + void move_view_to_focused_output(View_ptr); + + void apply_layout(Index); + void apply_layout(Workspace_ptr); + void spawn_external(std::string&&) const; private: diff --git a/include/kranewl/server.hh b/include/kranewl/server.hh @@ -13,8 +13,9 @@ extern "C" { #include <string> typedef class Model* Model_ptr; -typedef class Client* Client_ptr; +typedef class View* View_ptr; typedef class Server* Server_ptr; + typedef class Server final { enum class CursorMode { Passthrough, @@ -28,6 +29,8 @@ public: void run() noexcept; + void moveresize_view(View_ptr, Region const&, Extents const&, bool); + private: static void handle_new_output(struct wl_listener*, void*); static void handle_output_layout_change(struct wl_listener*, void*); @@ -47,7 +50,7 @@ private: static void handle_xdg_toplevel_destroy(struct wl_listener*, void*); static void handle_xdg_toplevel_request_move(struct wl_listener*, void*); static void handle_xdg_toplevel_request_resize(struct wl_listener*, void*); - static void handle_xdg_toplevel_handle_moveresize(Client_ptr, CursorMode, uint32_t); + static void handle_xdg_toplevel_handle_moveresize(View_ptr, CursorMode, uint32_t); #ifdef XWAYLAND static void handle_xwayland_ready(struct wl_listener*, void*); static void handle_new_xwayland_surface(struct wl_listener*, void*); @@ -74,7 +77,6 @@ public: struct wlr_xwayland* mp_xwayland; #endif - /* Root m_root; */ Seat m_seat; diff --git a/include/kranewl/tree/output.hh b/include/kranewl/tree/output.hh @@ -20,7 +20,8 @@ public: Server_ptr, Model_ptr, struct wlr_output*, - struct wlr_scene_output* + struct wlr_scene_output*, + Region const&& ); ~Output(); @@ -30,12 +31,12 @@ public: static void handle_mode(struct wl_listener*, void*); static void handle_destroy(struct wl_listener*, void*); - void set_context(Context_ptr context); + void set_context(Context_ptr); Context_ptr context() const; Region full_region() const; Region placeable_region() const; - bool contains(Pos pos) const; - bool contains(Region region) const; + bool contains(Pos) const; + bool contains(Region) const; private: Context_ptr mp_context; @@ -46,6 +47,8 @@ public: Server_ptr mp_server; Model_ptr mp_model; + bool m_dirty; + struct wlr_output* mp_wlr_output; struct wlr_scene_output* mp_wlr_scene_output; diff --git a/include/kranewl/tree/view.hh b/include/kranewl/tree/view.hh @@ -3,8 +3,6 @@ #include <kranewl/common.hh> #include <kranewl/decoration.hh> #include <kranewl/geometry.hh> -#include <kranewl/context.hh> -#include <kranewl/workspace.hh> #include <kranewl/model.hh> #include <kranewl/tree/surface.hh> @@ -45,9 +43,6 @@ typedef struct View { Server_ptr, Model_ptr, Seat_ptr, - Output_ptr, - Context_ptr, - Workspace_ptr, struct wlr_surface*, void(*)(wl_listener*, void*), void(*)(wl_listener*, void*), @@ -62,9 +57,6 @@ typedef struct View { Server_ptr, Model_ptr, Seat_ptr, - Output_ptr, - Context_ptr, - Workspace_ptr, struct wlr_surface*, void(*)(wl_listener*, void*), void(*)(wl_listener*, void*), @@ -77,6 +69,7 @@ typedef struct View { virtual ~View(); static void map_view(View_ptr, struct wlr_surface*, bool, struct wlr_output*, bool); + static void unmap_view(View_ptr); static bool is_free(View_ptr view) @@ -86,6 +79,15 @@ typedef struct View { || view->m_disowned; } + uint32_t free_decoration_to_wlr_edges() const; + uint32_t tile_decoration_to_wlr_edges() const; + + void set_free_region(Region const&); + void set_tile_region(Region const&); + + void set_free_decoration(Decoration const&); + void set_tile_decoration(Decoration const&); + Uid m_uid; Type m_type; @@ -104,6 +106,7 @@ typedef struct View { std::string m_title; std::string m_title_formatted; + std::string m_app_id; float m_alpha; uint32_t m_resize; @@ -114,6 +117,7 @@ typedef struct View { Decoration m_free_decoration; Decoration m_active_decoration; + Dim m_minimum_dim; Dim m_preferred_dim; Region m_free_region; Region m_tile_region; @@ -138,6 +142,10 @@ typedef struct View { std::chrono::time_point<std::chrono::steady_clock> m_last_focused; std::chrono::time_point<std::chrono::steady_clock> m_managed_since; +private: + void set_inner_region(Region const&); + void set_active_region(Region const&); + struct wlr_foreign_toplevel_handle_v1* foreign_toplevel; struct wl_listener ml_foreign_activate_request; diff --git a/include/kranewl/tree/xdg_view.hh b/include/kranewl/tree/xdg_view.hh @@ -3,6 +3,8 @@ #include <kranewl/tree/view.hh> #include <kranewl/util.hh> +#include <cstdint> + typedef class Server* Server_ptr; typedef class Model* Model_ptr; typedef class Seat* Seat_ptr; @@ -15,10 +17,7 @@ typedef struct XDGView final : public View { struct wlr_xdg_surface*, Server_ptr, Model_ptr, - Seat_ptr, - Output_ptr, - Context_ptr, - Workspace_ptr + Seat_ptr ); ~XDGView(); diff --git a/include/kranewl/tree/xwayland_view.hh b/include/kranewl/tree/xwayland_view.hh @@ -16,10 +16,7 @@ typedef struct XWaylandView final : public View { struct wlr_xwayland_surface*, Server_ptr, Model_ptr, - Seat_ptr, - Output_ptr, - Context_ptr, - Workspace_ptr + Seat_ptr ); ~XWaylandView(); diff --git a/src/kranewl/layout.cc b/src/kranewl/layout.cc @@ -1,3 +1,5 @@ +#include <trace.hh> + #include <kranewl/layout.hh> #include <kranewl/cycle.t.hh> @@ -72,6 +74,8 @@ LayoutHandler::arrange( view_iter end ) const { + TRACE(); + if (mp_layout->config.margin) { Layout::LayoutData_ptr data = *mp_layout->data.active_element(); @@ -83,91 +87,91 @@ LayoutHandler::arrange( } switch (m_kind) { - case LayoutKind::Float: - { - arrange_float(screen_region, placements, begin, end); - break; - } - case LayoutKind::FramelessFloat: - { - arrange_frameless_float(screen_region, placements, begin, end); - break; - } - case LayoutKind::SingleFloat: - { - arrange_single_float(screen_region, placements, begin, end); - break; - } - case LayoutKind::FramelessSingleFloat: - { - arrange_frameless_single_float(screen_region, placements, begin, end); - break; - } - case LayoutKind::Center: - { - arrange_center(screen_region, placements, begin, end); - break; - } - case LayoutKind::Monocle: - { - arrange_monocle(screen_region, placements, begin, end); - break; - } - case LayoutKind::MainDeck: - { - arrange_main_deck(screen_region, placements, begin, end); - break; - } - case LayoutKind::StackDeck: - { - arrange_stack_deck(screen_region, placements, begin, end); - break; - } - case LayoutKind::DoubleDeck: - { - arrange_double_deck(screen_region, placements, begin, end); - break; - } - case LayoutKind::Paper: - { - arrange_paper(screen_region, placements, begin, end); - break; - } - case LayoutKind::CompactPaper: - { - arrange_compact_paper(screen_region, placements, begin, end); - break; - } - case LayoutKind::DoubleStack: - { - arrange_double_stack(screen_region, placements, begin, end); - break; - } - case LayoutKind::CompactDoubleStack: - { - arrange_compact_double_stack(screen_region, placements, begin, end); - break; - } - case LayoutKind::HorizontalStack: - { - arrange_horizontal_stack(screen_region, placements, begin, end); - break; - } - case LayoutKind::CompactHorizontalStack: - { - arrange_compact_horizontal_stack(screen_region, placements, begin, end); - break; - } - case LayoutKind::VerticalStack: - { - arrange_vertical_stack(screen_region, placements, begin, end); - break; - } - case LayoutKind::CompactVerticalStack: - { - arrange_compact_vertical_stack(screen_region, placements, begin, end); - break; - } + case LayoutKind::Float: + { + arrange_float(screen_region, placements, begin, end); + break; + } + case LayoutKind::FramelessFloat: + { + arrange_frameless_float(screen_region, placements, begin, end); + break; + } + case LayoutKind::SingleFloat: + { + arrange_single_float(screen_region, placements, begin, end); + break; + } + case LayoutKind::FramelessSingleFloat: + { + arrange_frameless_single_float(screen_region, placements, begin, end); + break; + } + case LayoutKind::Center: + { + arrange_center(screen_region, placements, begin, end); + break; + } + case LayoutKind::Monocle: + { + arrange_monocle(screen_region, placements, begin, end); + break; + } + case LayoutKind::MainDeck: + { + arrange_main_deck(screen_region, placements, begin, end); + break; + } + case LayoutKind::StackDeck: + { + arrange_stack_deck(screen_region, placements, begin, end); + break; + } + case LayoutKind::DoubleDeck: + { + arrange_double_deck(screen_region, placements, begin, end); + break; + } + case LayoutKind::Paper: + { + arrange_paper(screen_region, placements, begin, end); + break; + } + case LayoutKind::CompactPaper: + { + arrange_compact_paper(screen_region, placements, begin, end); + break; + } + case LayoutKind::DoubleStack: + { + arrange_double_stack(screen_region, placements, begin, end); + break; + } + case LayoutKind::CompactDoubleStack: + { + arrange_compact_double_stack(screen_region, placements, begin, end); + break; + } + case LayoutKind::HorizontalStack: + { + arrange_horizontal_stack(screen_region, placements, begin, end); + break; + } + case LayoutKind::CompactHorizontalStack: + { + arrange_compact_horizontal_stack(screen_region, placements, begin, end); + break; + } + case LayoutKind::VerticalStack: + { + arrange_vertical_stack(screen_region, placements, begin, end); + break; + } + case LayoutKind::CompactVerticalStack: + { + arrange_compact_vertical_stack(screen_region, placements, begin, end); + break; + } } if (mp_layout->config.gap) { @@ -427,6 +431,8 @@ LayoutHandler::cycle_layout_data(Direction direction) void LayoutHandler::save_layout(int number) const { + TRACE(); + std::stringstream datadir_ss; std::string home_path = std::getenv("HOME"); @@ -458,6 +464,8 @@ LayoutHandler::save_layout(int number) const void LayoutHandler::load_layout(int number) { + TRACE(); + std::stringstream datadir_ss; std::string home_path = std::getenv("HOME"); @@ -503,6 +511,8 @@ LayoutHandler::arrange_float( view_iter end ) const { + TRACE(); + std::transform( begin, end, @@ -526,6 +536,8 @@ LayoutHandler::arrange_frameless_float( view_iter end ) const { + TRACE(); + arrange_float(screen_region, placements, begin, end); } @@ -537,6 +549,8 @@ LayoutHandler::arrange_single_float( view_iter end ) const { + TRACE(); + std::transform( begin, end, @@ -560,6 +574,8 @@ LayoutHandler::arrange_frameless_single_float( view_iter end ) const { + TRACE(); + arrange_single_float(screen_region, placements, begin, end); } @@ -571,6 +587,8 @@ LayoutHandler::arrange_center( view_iter end ) const { + TRACE(); + const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); int h_comp = Layout::LayoutData::MAX_MAIN_COUNT; @@ -614,6 +632,8 @@ LayoutHandler::arrange_monocle( view_iter end ) const { + TRACE(); + std::transform( begin, end, @@ -637,6 +657,8 @@ LayoutHandler::arrange_main_deck( view_iter end ) const { + TRACE(); + const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); int n = static_cast<int>(end - begin); @@ -728,6 +750,8 @@ LayoutHandler::arrange_stack_deck( view_iter end ) const { + TRACE(); + const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); int n = static_cast<int>(end - begin); @@ -817,6 +841,8 @@ LayoutHandler::arrange_double_deck( view_iter end ) const { + TRACE(); + const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); int n = static_cast<int>(end - begin); @@ -904,6 +930,8 @@ LayoutHandler::arrange_paper( view_iter end ) const { + TRACE(); + static const float MIN_W_RATIO = 0.5; const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); @@ -1007,6 +1035,8 @@ LayoutHandler::arrange_compact_paper( view_iter end ) const { + TRACE(); + arrange_paper(screen_region, placements, begin, end); } @@ -1018,6 +1048,8 @@ LayoutHandler::arrange_double_stack( view_iter end ) const { + TRACE(); + const Layout::LayoutData_ptr data = *mp_layout->data.active_element(); int n = static_cast<int>(end - begin); @@ -1107,6 +1139,8 @@ LayoutHandler::arrange_compact_double_stack( view_iter end ) const { + TRACE(); + arrange_double_stack(screen_region, placements, begin, end); } @@ -1118,6 +1152,8 @@ LayoutHandler::arrange_horizontal_stack( view_iter end ) const { + TRACE(); + int n = static_cast<int>(end - begin); if (n == 1) { @@ -1166,6 +1202,8 @@ LayoutHandler::arrange_compact_horizontal_stack( view_iter end ) const { + TRACE(); + arrange_horizontal_stack(screen_region, placements, begin, end); } @@ -1177,6 +1215,8 @@ LayoutHandler::arrange_vertical_stack( view_iter end ) const { + TRACE(); + int n = static_cast<int>(end - begin); if (n == 1) { @@ -1225,6 +1265,8 @@ LayoutHandler::arrange_compact_vertical_stack( view_iter end ) const { + TRACE(); + arrange_vertical_stack(screen_region, placements, begin, end); } @@ -1548,4 +1590,3 @@ LayoutHandler::Layout::kind_to_default_data(LayoutKind kind) return kind_to_default_data(LayoutKind::Float); } - diff --git a/src/kranewl/model.cc b/src/kranewl/model.cc @@ -5,7 +5,6 @@ #include <kranewl/common.hh> #include <kranewl/conf/config.hh> #include <kranewl/context.hh> -#include <kranewl/context.hh> #include <kranewl/cycle.t.hh> #include <kranewl/exec.hh> #include <kranewl/input/mouse.hh> @@ -19,6 +18,19 @@ #include <spdlog/spdlog.h> #include <iomanip> +#include <optional> + +// https://github.com/swaywm/wlroots/issues/682 +#include <pthread.h> +#define class class_ +#define namespace namespace_ +#define static +extern "C" { +#include <wlr/types/wlr_surface.h> +} +#undef static +#undef class +#undef namespace Model::Model( Config const& config, @@ -94,14 +106,14 @@ void Model::register_server(Server_ptr server) { TRACE(); - mp_server = server; } Output_ptr Model::create_output( struct wlr_output* wlr_output, - struct wlr_scene_output* wlr_scene_output + struct wlr_scene_output* wlr_scene_output, + Region const&& output_region ) { TRACE(); @@ -110,9 +122,30 @@ Model::create_output( mp_server, this, wlr_output, - wlr_scene_output + wlr_scene_output, + std::forward<Region const&&>(output_region) ); + std::optional<Context_ptr> context + = m_contexts.first_element_with_condition([](Context_ptr context) { + return !context->output(); + }); + + if (context) { + output->set_context(*context); + (*context)->set_output(output); + + spdlog::info("Assigned context {} to output {}", + (*context)->index(), + output->mp_wlr_output->name + ); + } else + // TODO: dynamically generate new context + spdlog::error("Depleted allocatable contexts," + " output {} will not house any workspaces", + output->mp_wlr_output->name + ); + register_output(output); return output; @@ -124,6 +157,9 @@ Model::register_output(Output_ptr output) TRACE(); m_outputs.insert_at_back(output); + mp_output = *m_outputs.active_element(); + mp_context = mp_output->context(); + mp_workspace = mp_context->workspace(); } void @@ -135,6 +171,265 @@ Model::unregister_output(Output_ptr output) delete output; } +void +Model::map_view(View_ptr view) +{ + TRACE(); +} + +void +Model::unmap_view(View_ptr view) +{ + TRACE(); +} + +void +Model::iconify_view(View_ptr) +{ + TRACE(); +} + +void +Model::deiconify_view(View_ptr) +{ + TRACE(); +} + +void +Model::disown_view(View_ptr) +{ + TRACE(); +} + +void +Model::reclaim_view(View_ptr) +{ + TRACE(); +} + +void +Model::focus_view(View_ptr) +{ + TRACE(); +} + +void +Model::place_view(Placement& placement) +{ + TRACE(); + + View_ptr view = placement.view; + + if (!placement.region) { + switch (placement.method) { + case Placement::PlacementMethod::Free: + { + view->set_free_decoration(placement.decoration); + break; + } + case Placement::PlacementMethod::Tile: + { + view->set_free_decoration(FREE_DECORATION); + view->set_tile_decoration(placement.decoration); + break; + } + } + + unmap_view(view); + return; + } + + switch (placement.method) { + case Placement::PlacementMethod::Free: + { + view->set_free_decoration(placement.decoration); + view->set_free_region(*placement.region); + break; + } + case Placement::PlacementMethod::Tile: + { + view->set_free_decoration(FREE_DECORATION); + view->set_tile_decoration(placement.decoration); + view->set_tile_region(*placement.region); + break; + } + } + + spdlog::info("Placing view {} at {}", view->m_uid, std::to_string(view->m_active_region)); + + map_view(view); + mp_server->moveresize_view( + view, + view->m_active_region, + view->m_active_decoration.extents(), + false + ); +} + +void +Model::move_view_to_workspace(View_ptr view, Index index) +{ + TRACE(); + + if (index < m_workspaces.size()) + move_view_to_workspace(view, m_workspaces[index]); +} + +void +Model::move_view_to_workspace(View_ptr view, Workspace_ptr workspace_to) +{ + TRACE(); + + Workspace_ptr workspace_from = view->mp_workspace; + + if (!workspace_to || workspace_to == workspace_from) + return; + + view->mp_workspace = workspace_to; + + Context_ptr context_from = workspace_from->context(); + Output_ptr output_from = context_from->output(); + + Context_ptr context_to = workspace_to->context(); + Output_ptr output_to = context_to->output(); + + view->mp_context = context_to; + if (output_to != output_from) + view->mp_output = output_to; + + workspace_to->add_view(view); + workspace_from->remove_view(view); + + apply_layout(workspace_to); + apply_layout(workspace_from); + + if (!output_to) + unmap_view(view); + else + map_view(view); +} + +void +Model::move_view_to_context(View_ptr view, Index index) +{ + TRACE(); + + if (index < m_workspaces.size()) + move_view_to_context(view, m_contexts[index]); +} + +void +Model::move_view_to_context(View_ptr view, Context_ptr context_to) +{ + TRACE(); + + Context_ptr context_from = view->mp_context; + + if (!context_to || context_to == context_from) + return; + + view->mp_context = context_to; + + Workspace_ptr workspace_from = view->mp_workspace; + Output_ptr output_from = context_from->output(); + + Workspace_ptr workspace_to = context_to->workspace(); + Output_ptr output_to = context_to->output(); + + view->mp_workspace = workspace_to; + view->mp_output = output_to; + + workspace_to->add_view(view); + workspace_from->remove_view(view); + + apply_layout(workspace_to); + apply_layout(workspace_from); + + if (!output_to) + unmap_view(view); + else + map_view(view); +} + +void +Model::move_view_to_output(View_ptr view, Index index) +{ + TRACE(); + + if (index < m_outputs.size()) + move_view_to_output(view, m_outputs[index]); +} + +void +Model::move_view_to_output(View_ptr view, Output_ptr output_to) +{ + TRACE(); + + Output_ptr output_from = view->mp_output; + + if (!output_to || output_to == output_from) + return; + + if (output_from) { + Workspace_ptr workspace_from = view->mp_workspace; + + if (workspace_from) { + workspace_from->remove_view(view); + apply_layout(workspace_from); + } + } + + Context_ptr context_to = output_to->context(); + Workspace_ptr workspace_to = context_to->workspace(); + + view->mp_output = output_to; + view->mp_context = context_to; + view->mp_workspace = workspace_to; + + workspace_to->add_view(view); + apply_layout(workspace_to); + + if (output_from) + wlr_surface_send_leave(view->mp_wlr_surface, output_from->mp_wlr_output); + + if (output_to) { + wlr_surface_send_enter(view->mp_wlr_surface, output_to->mp_wlr_output); + map_view(view); + } else + unmap_view(view); +} + +void +Model::move_view_to_focused_output(View_ptr view) +{ + TRACE(); + move_view_to_output(view, mp_output); +} + +void +Model::apply_layout(Index index) +{ + TRACE(); + + if (index < m_workspaces.size()) + apply_layout(m_workspaces[index]); +} + +void +Model::apply_layout(Workspace_ptr workspace) +{ + TRACE(); + + Context_ptr context = workspace->context(); + Output_ptr output = context->output(); + + if (!output) + return; + + for (Placement placement : workspace->arrange(output->placeable_region())) + place_view(placement); +} + XDGView_ptr Model::create_xdg_shell_view( struct wlr_xdg_surface* wlr_xdg_surface, @@ -147,10 +442,7 @@ Model::create_xdg_shell_view( wlr_xdg_surface, mp_server, this, - seat, - mp_output, - mp_context, - mp_workspace + seat ); register_view(view); @@ -170,10 +462,7 @@ Model::create_xwayland_view( wlr_xwayland_surface, mp_server, this, - seat, - mp_output, - mp_context, - mp_workspace + seat ); register_view(view); diff --git a/src/kranewl/server.cc b/src/kranewl/server.cc @@ -233,6 +233,23 @@ Server::run() noexcept } void +Server::moveresize_view(View_ptr view, Region const& region, Extents const& extents, bool interactive) +{ + TRACE(); + + wlr_scene_node_set_position(view->mp_scene, region.pos.x, region.pos.y); + wlr_scene_node_set_position(view->mp_scene_surface, extents.left, extents.top); + wlr_scene_rect_set_size(view->m_protrusions[0], region.dim.w, extents.top); + wlr_scene_rect_set_size(view->m_protrusions[1], region.dim.w, extents.bottom); + wlr_scene_rect_set_size(view->m_protrusions[2], extents.left, region.dim.h - extents.top - extents.bottom); + wlr_scene_rect_set_size(view->m_protrusions[3], extents.right, region.dim.h - extents.top - extents.bottom); + wlr_scene_node_set_position(&view->m_protrusions[0]->node, 0, 0); + wlr_scene_node_set_position(&view->m_protrusions[1]->node, 0, region.dim.h - extents.bottom); + wlr_scene_node_set_position(&view->m_protrusions[2]->node, 0, extents.top); + wlr_scene_node_set_position(&view->m_protrusions[3]->node, region.dim.w - extents.right, extents.top); +} + +void Server::handle_new_output(struct wl_listener* listener, void* data) { TRACE(); @@ -255,13 +272,26 @@ Server::handle_new_output(struct wl_listener* listener, void* data) return; } + wlr_output_layout_add_auto(server->mp_output_layout, wlr_output); + struct wlr_box output_box + = *wlr_output_layout_get_box(server->mp_output_layout, wlr_output); + Output_ptr output = server->mp_model->create_output( wlr_output, - wlr_scene_output_create(server->mp_scene, wlr_output) + wlr_scene_output_create(server->mp_scene, wlr_output), + Region{ + .pos = Pos{ + .x = output_box.x, + .y = output_box.y + }, + .dim = Dim{ + .w = output_box.width, + .h = output_box.height + } + } ); wlr_output->data = output; - wlr_output_layout_add_auto(server->mp_output_layout, wlr_output); } void @@ -293,11 +323,13 @@ view_from_popup(struct wlr_xdg_popup* popup) for (;;) switch (surface->role) { case WLR_XDG_SURFACE_ROLE_POPUP: + { if (!wlr_surface_is_xdg_surface(surface->popup->parent)) return nullptr; surface = wlr_xdg_surface_from_wlr_surface(surface->popup->parent); break; + } case WLR_XDG_SURFACE_ROLE_TOPLEVEL: return reinterpret_cast<View_ptr>(surface->data); case WLR_XDG_SURFACE_ROLE_NONE: return nullptr; } @@ -315,28 +347,29 @@ Server::handle_new_xdg_surface(struct wl_listener* listener, void* data) switch (xdg_surface->role) { case WLR_XDG_SURFACE_ROLE_POPUP: - { - struct wlr_box mappable_box; + { + struct wlr_box mappable_box; + + struct wlr_xdg_surface* parent + = wlr_xdg_surface_from_wlr_surface(xdg_surface->popup->parent); - struct wlr_xdg_surface* parent - = wlr_xdg_surface_from_wlr_surface(xdg_surface->popup->parent); + struct wlr_scene_node* parent_node + = reinterpret_cast<struct wlr_scene_node*>(parent->data); - struct wlr_scene_node* parent_node - = reinterpret_cast<struct wlr_scene_node*>(parent->data); + xdg_surface->data + = wlr_scene_xdg_surface_create(parent_node, xdg_surface); - xdg_surface->data - = wlr_scene_xdg_surface_create(parent_node, xdg_surface); + if (!(view = view_from_popup(xdg_surface->popup)) || !view->mp_output) + return; - if (!(view = view_from_popup(xdg_surface->popup)) || !view->mp_output) - return; + mappable_box = view->mp_output->placeable_region(); + mappable_box.x -= view->m_active_region.pos.x; + mappable_box.y -= view->m_active_region.pos.y; - mappable_box = view->mp_output->placeable_region(); - mappable_box.x -= view->m_active_region.pos.x; - mappable_box.y -= view->m_active_region.pos.y; + wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &mappable_box); - wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &mappable_box); - } return; + } case WLR_XDG_SURFACE_ROLE_NONE: return; default: break; } @@ -380,23 +413,26 @@ Server::handle_new_input(struct wl_listener* listener, void* data) switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD: - { - Keyboard_ptr keyboard = server->m_seat.create_keyboard(device); + { + Keyboard_ptr keyboard = server->m_seat.create_keyboard(device); - struct xkb_context* context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - struct xkb_keymap* keymap - = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); + struct xkb_context* context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap* keymap + = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); + + wlr_keyboard_set_keymap(device->keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + wlr_keyboard_set_repeat_info(device->keyboard, 200, 100); + wlr_seat_set_keyboard(server->m_seat.mp_seat, device); - wlr_keyboard_set_keymap(device->keyboard, keymap); - xkb_keymap_unref(keymap); - xkb_context_unref(context); - wlr_keyboard_set_repeat_info(device->keyboard, 200, 100); - wlr_seat_set_keyboard(server->m_seat.mp_seat, device); - } break; + } case WLR_INPUT_DEVICE_POINTER: + { wlr_cursor_attach_input_device(server->m_seat.mp_cursor, device); break; + } default: break; } @@ -485,7 +521,7 @@ Server::handle_xdg_toplevel_request_resize(struct wl_listener*, void*) } void -Server::handle_xdg_toplevel_handle_moveresize(Client_ptr, CursorMode, uint32_t) +Server::handle_xdg_toplevel_handle_moveresize(View_ptr, CursorMode, uint32_t) { TRACE(); diff --git a/src/kranewl/tree/output.cc b/src/kranewl/tree/output.cc @@ -22,12 +22,16 @@ Output::Output( Server_ptr server, Model_ptr model, struct wlr_output* wlr_output, - struct wlr_scene_output* wlr_scene_output + struct wlr_scene_output* wlr_scene_output, + Region const&& output_region ) : Node(this), mp_context(nullptr), + m_full_region(output_region), + m_placeable_region(output_region), mp_server(server), mp_model(model), + m_dirty(true), mp_wlr_output(wlr_output), mp_wlr_scene_output(wlr_scene_output), m_subpixel(wlr_output->subpixel), diff --git a/src/kranewl/tree/view.cc b/src/kranewl/tree/view.cc @@ -2,6 +2,7 @@ #include <kranewl/layers.hh> #include <kranewl/server.hh> +#include <kranewl/model.hh> #include <kranewl/tree/view.hh> #include <kranewl/tree/xdg_view.hh> @@ -14,6 +15,8 @@ extern "C" { #include <sys/types.h> #include <wlr/types/wlr_xdg_shell.h> #include <wlr/types/wlr_scene.h> +#include <wlr/util/box.h> +#include <wlr/util/edges.h> } #undef static #undef class @@ -25,9 +28,6 @@ View::View( Server_ptr server, Model_ptr model, Seat_ptr seat, - Output_ptr output, - Context_ptr context, - Workspace_ptr workspace, struct wlr_surface* wlr_surface, void(*handle_foreign_activate_request)(wl_listener*, void*), void(*handle_foreign_fullscreen_request)(wl_listener*, void*), @@ -40,14 +40,15 @@ View::View( mp_server(server), mp_model(model), mp_seat(seat), - mp_output(output), - mp_context(context), - mp_workspace(workspace), + mp_output(nullptr), + mp_context(nullptr), + mp_workspace(nullptr), mp_wlr_surface(wlr_surface), m_alpha(1.f), m_tile_decoration(FREE_DECORATION), m_free_decoration(FREE_DECORATION), m_active_decoration(FREE_DECORATION), + m_minimum_dim({}), m_preferred_dim({}), m_free_region({}), m_tile_region({}), @@ -85,9 +86,6 @@ View::View( Server_ptr server, Model_ptr model, Seat_ptr seat, - Output_ptr output, - Context_ptr context, - Workspace_ptr workspace, struct wlr_surface* wlr_surface, void(*handle_foreign_activate_request)(wl_listener*, void*), void(*handle_foreign_fullscreen_request)(wl_listener*, void*), @@ -100,9 +98,6 @@ View::View( mp_server(server), mp_model(model), mp_seat(seat), - mp_output(output), - mp_context(context), - mp_workspace(workspace), mp_wlr_surface(wlr_surface), ml_foreign_activate_request({ .notify = handle_foreign_activate_request }), ml_foreign_fullscreen_request({ .notify = handle_foreign_fullscreen_request }), @@ -120,23 +115,25 @@ set_view_pid(View_ptr view) { switch (view->m_type) { case View::Type::XDGShell: - { - pid_t pid; - struct wl_client* client - = wl_resource_get_client(view->mp_wlr_surface->resource); - - wl_client_get_credentials(client, &pid, NULL, NULL); - view->m_pid = pid; - } + { + pid_t pid; + struct wl_client* client + = wl_resource_get_client(view->mp_wlr_surface->resource); + + wl_client_get_credentials(client, &pid, NULL, NULL); + view->m_pid = pid; + break; + } #if HAVE_XWAYLAND case View::Type::XWayland: - { - struct wlr_xwayland_surface* wlr_xwayland_surface - = wlr_xwayland_surface_from_wlr_surface(view->mp_wlr_surface); - view->m_pid = wlr_xwayland_surface->pid; - } + { + struct wlr_xwayland_surface* wlr_xwayland_surface + = wlr_xwayland_surface_from_wlr_surface(view->mp_wlr_surface); + view->m_pid = wlr_xwayland_surface->pid; + break; + } #endif default: break; } @@ -153,6 +150,9 @@ View::map_view( { TRACE(); + Server_ptr server = view->mp_server; + Model_ptr model = view->mp_model; + view->mp_wlr_surface = wlr_surface; set_view_pid(view); @@ -165,9 +165,120 @@ View::map_view( : wlr_scene_subsurface_tree_create(view->mp_scene, view->mp_wlr_surface); view->mp_scene_surface->data = view; + wlr_scene_node_reparent( + view->mp_scene, + server->m_layers[view->m_floating ? Layer::Free : Layer::Tile] + ); + + // TODO: globalize + static const float border_rgba[4] = {0.5, 0.5, 0.5, 1.0}; + + for (std::size_t i = 0; i < 4; ++i) { + view->m_protrusions[i] = wlr_scene_rect_create(view->mp_scene, 0, 0, border_rgba); + view->m_protrusions[i]->node.data = view; + wlr_scene_rect_set_color(view->m_protrusions[i], border_rgba); + wlr_scene_node_lower_to_bottom(&view->m_protrusions[i]->node); + } + if (fullscreen_output && fullscreen_output->data) { Output_ptr output = reinterpret_cast<Output_ptr>(fullscreen_output->data); } + + model->move_view_to_focused_output(view); +} + + +void +View::unmap_view(View_ptr view) +{ + TRACE(); + + wlr_scene_node_destroy(view->mp_scene); +} + +static uint32_t +extents_to_wlr_edges(Extents const& extents) +{ + uint32_t wlr_edges = WLR_EDGE_NONE; + + if (extents.top) + wlr_edges |= WLR_EDGE_TOP; + + if (extents.bottom) + wlr_edges |= WLR_EDGE_BOTTOM; + + if (extents.left) + wlr_edges |= WLR_EDGE_LEFT; + + if (extents.right) + wlr_edges |= WLR_EDGE_RIGHT; + + return wlr_edges; +} + +uint32_t +View::free_decoration_to_wlr_edges() const +{ + return extents_to_wlr_edges(m_free_decoration.extents()); +} + +uint32_t +View::tile_decoration_to_wlr_edges() const +{ + return extents_to_wlr_edges(m_tile_decoration.extents()); +} + +void +View::set_free_region(Region const& region) +{ + m_free_region = region; + set_active_region(region); +} + +void +View::set_tile_region(Region const& region) +{ + m_tile_region = region; + set_active_region(region); +} + +void +View::set_free_decoration(Decoration const& decoration) +{ + m_free_decoration = decoration; + m_active_decoration = decoration; +} + +void +View::set_tile_decoration(Decoration const& decoration) +{ + m_tile_decoration = decoration; + m_active_decoration = decoration; +} + +void +View::set_active_region(Region const& region) +{ + m_previous_region = m_active_region; + set_inner_region(region); + m_active_region = region; +} + +void +View::set_inner_region(Region const& region) +{ + if (m_active_decoration.frame) { + Frame const& frame = *m_active_decoration.frame; + + m_inner_region.pos.x = frame.extents.left; + m_inner_region.pos.y = frame.extents.top; + m_inner_region.dim.w = region.dim.w - frame.extents.left - frame.extents.right; + m_inner_region.dim.h = region.dim.h - frame.extents.top - frame.extents.bottom; + } else { + m_inner_region.pos.x = 0; + m_inner_region.pos.y = 0; + m_inner_region.dim = region.dim; + } } ViewChild::ViewChild(SubsurfaceViewChild_ptr) diff --git a/src/kranewl/tree/xdg_view.cc b/src/kranewl/tree/xdg_view.cc @@ -1,20 +1,28 @@ #include <trace.hh> +#include <kranewl/layers.hh> +#include <kranewl/server.hh> #include <kranewl/tree/view.hh> #include <kranewl/tree/xdg_view.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_scene.h> #include <wlr/types/wlr_xdg_shell.h> } +#undef static +#undef class +#undef namespace XDGView::XDGView( struct wlr_xdg_surface* wlr_xdg_surface, Server_ptr server, Model_ptr model, - Seat_ptr seat, - Output_ptr output, - Context_ptr context, - Workspace_ptr workspace + Seat_ptr seat ) : View( this, @@ -22,9 +30,6 @@ XDGView::XDGView( server, model, seat, - output, - context, - workspace, wlr_xdg_surface->surface, XDGView::handle_foreign_activate_request, XDGView::handle_foreign_fullscreen_request, @@ -143,6 +148,7 @@ XDGView::handle_map(struct wl_listener* listener, void* data) TRACE(); XDGView_ptr view = wl_container_of(listener, view, ml_map); + struct wlr_xdg_surface* wlr_xdg_surface = view->mp_wlr_xdg_surface; struct wlr_xdg_toplevel* wlr_xdg_toplevel = view->mp_wlr_xdg_toplevel; view->m_mapped = true; @@ -156,6 +162,40 @@ XDGView::handle_map(struct wl_listener* listener, void* data) view->m_preferred_dim.h = wlr_xdg_toplevel->base->surface->current.height; } + Extents const& extents = view->m_free_decoration.extents(); + struct wlr_box geometry; + wlr_xdg_surface_get_geometry(wlr_xdg_surface, &geometry); + view->m_preferred_dim.w = extents.left + extents.right + geometry.width; + view->m_preferred_dim.h = extents.top + extents.bottom + geometry.height; + + view->set_free_region(Region{ + .pos = {0, 0}, + .dim = view->m_preferred_dim + }); + + view->set_tile_region(Region{ + .pos = {0, 0}, + .dim = view->m_preferred_dim + }); + + view->m_app_id = wlr_xdg_toplevel->app_id + ? std::string(wlr_xdg_toplevel->app_id) + : "N/a"; + view->m_title = wlr_xdg_toplevel->title + ? std::string(wlr_xdg_toplevel->title) + : "N/a"; + view->m_title_formatted = view->m_title; // TODO: format title + + struct wlr_xdg_toplevel_state state = wlr_xdg_toplevel->current; + view->m_floating = wlr_xdg_toplevel->parent + || (state.min_width != 0 && state.min_height != 0 + && (state.min_width == state.max_width || state.min_height == state.max_height)); + + view->m_minimum_dim = Dim{ + .w = state.min_width, + .h = state.min_height + }; + View::map_view( view, wlr_xdg_toplevel->base->surface, @@ -164,6 +204,12 @@ XDGView::handle_map(struct wl_listener* listener, void* data) false // TODO: determine if client has decorations ); + wlr_xdg_toplevel_set_tiled( + wlr_xdg_surface, + // TODO: determine from view decorations + view->free_decoration_to_wlr_edges() + ); + wl_signal_add(&wlr_xdg_toplevel->base->surface->events.commit, &view->ml_commit); wl_signal_add(&wlr_xdg_toplevel->base->events.new_popup, &view->ml_new_popup); wl_signal_add(&wlr_xdg_toplevel->events.request_fullscreen, &view->ml_request_fullscreen); @@ -178,6 +224,9 @@ XDGView::handle_unmap(struct wl_listener* listener, void* data) { TRACE(); + XDGView_ptr view = wl_container_of(listener, view, ml_unmap); + + View::unmap_view(view); } void @@ -185,4 +234,15 @@ XDGView::handle_destroy(struct wl_listener* listener, void* data) { TRACE(); + XDGView_ptr view = wl_container_of(listener, view, ml_destroy); + + view->mp_model->unregister_view(view); + + wl_list_remove(&view->ml_commit.link); + wl_list_remove(&view->ml_new_popup.link); + wl_list_remove(&view->ml_request_fullscreen.link); + wl_list_remove(&view->ml_request_move.link); + wl_list_remove(&view->ml_request_resize.link); + wl_list_remove(&view->ml_set_title.link); + wl_list_remove(&view->ml_set_app_id.link); } diff --git a/src/kranewl/tree/xwayland_view.cc b/src/kranewl/tree/xwayland_view.cc @@ -20,10 +20,7 @@ XWaylandView::XWaylandView( struct wlr_xwayland_surface* wlr_xwayland_surface, Server_ptr server, Model_ptr model, - Seat_ptr seat, - Output_ptr output, - Context_ptr context, - Workspace_ptr workspace + Seat_ptr seat ) : View( this, @@ -31,9 +28,6 @@ XWaylandView::XWaylandView( server, model, seat, - output, - context, - workspace, wlr_xwayland_surface->surface, XWaylandView::handle_foreign_activate_request, XWaylandView::handle_foreign_fullscreen_request, diff --git a/src/kranewl/workspace.cc b/src/kranewl/workspace.cc @@ -1,3 +1,5 @@ +#include <trace.hh> + #include <kranewl/workspace.hh> #include <kranewl/context.hh> @@ -464,6 +466,8 @@ Workspace::set_layout(LayoutHandler::LayoutKind layout) std::vector<Placement> Workspace::arrange(Region region) const { + TRACE(); + std::deque<View_ptr> views = m_views.as_deque(); std::vector<Placement> placements; placements.reserve(views.size());