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 b610179a4d714cfefb7af519dc8be3349f80954e
parent 9884692d54433f5fca0c3dcb77bc4df01116c608
Author: deurzen <max@deurzen.net>
Date:   Tue, 31 May 2022 10:29:32 +0200

adds xwayland shell {,un}mapping functionality

Diffstat:
Minclude/kranewl/input/cursor.hh | 4++++
Minclude/kranewl/model.hh | 15+++++++++++++--
Minclude/kranewl/tree/layer.hh | 12++++++++----
Minclude/kranewl/tree/node.hh | 36+++++++++++++++++++++++++++++++++---
Minclude/kranewl/tree/output.hh | 2++
Minclude/kranewl/tree/view.hh | 14++++----------
Minclude/kranewl/tree/xdg-view.hh | 4----
Minclude/kranewl/tree/xwayland-view.hh | 88++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Minclude/kranewl/xwayland.hh | 12+++++++++++-
Msrc/kranewl/input/cursor.cc | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/kranewl/model.cc | 42+++++++++++++++++++++++++++++++++---------
Msrc/kranewl/server.cc | 9+--------
Msrc/kranewl/tree/layer.cc | 53+++++++++++++++++++++++++++++++++++++++++------------
Msrc/kranewl/tree/output.cc | 10++++++----
Msrc/kranewl/tree/view.cc | 19+++++++------------
Msrc/kranewl/tree/xdg-view.cc | 47++++++++++-------------------------------------
Msrc/kranewl/tree/xwayland-view.cc | 559++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/kranewl/xwayland.cc | 22++++++++++++++++++++--
18 files changed, 842 insertions(+), 205 deletions(-)

diff --git a/include/kranewl/input/cursor.hh b/include/kranewl/input/cursor.hh @@ -5,6 +5,7 @@ extern "C" { #include <linux/input-event-codes.h> #include <wlr/backend.h> +#include <wlr/util/edges.h> #include <xkbcommon/xkbcommon.h> } @@ -39,6 +40,7 @@ struct CursorInput { typedef class Server* Server_ptr; typedef class Seat* Seat_ptr; typedef struct View* View_ptr; +typedef struct Node* Node_ptr; typedef struct Cursor { enum class Mode { @@ -58,7 +60,9 @@ typedef struct Cursor { ~Cursor(); View_ptr view_under_cursor() const; + Node_ptr node_under_cursor() const; + void initiate_cursor_interactive(Mode, View_ptr, uint32_t); void initiate_cursor_interactive(Mode, View_ptr); void abort_cursor_interactive(); diff --git a/include/kranewl/model.hh b/include/kranewl/model.hh @@ -27,6 +27,7 @@ typedef struct XWayland* XWayland_ptr; typedef struct XDGView* XDGView_ptr; #ifdef XWAYLAND typedef struct XWaylandView* XWaylandView_ptr; +typedef struct XWaylandUnmanaged* XWaylandUnmanaged_ptr; #endif class Config; @@ -54,7 +55,17 @@ public: XDGView_ptr create_xdg_shell_view(struct wlr_xdg_surface*, Seat_ptr); #ifdef XWAYLAND - XWaylandView_ptr create_xwayland_view(struct wlr_xwayland_surface*, Seat_ptr, XWayland_ptr); + XWaylandView_ptr create_xwayland_view( + struct wlr_xwayland_surface*, + Seat_ptr, + XWayland_ptr + ); + + XWaylandUnmanaged_ptr create_xwayland_unmanaged( + struct wlr_xwayland_surface*, + Seat_ptr, + XWayland_ptr + ); #endif void register_view(View_ptr, Workspace_ptr); void unregister_view(View_ptr); @@ -191,11 +202,11 @@ private: Workspace_ptr mp_prev_workspace; std::unordered_map<Uid, View_ptr> m_view_map; + std::unordered_map<Uid, Node_ptr> m_unmanaged_map; std::unordered_map<pid_t, View_ptr> m_pid_map; std::unordered_map<View_ptr, Region> m_fullscreen_map; std::vector<View_ptr> m_sticky_views; - std::vector<View_ptr> m_unmanaged_views; View_ptr mp_focus; diff --git a/include/kranewl/tree/layer.hh b/include/kranewl/tree/layer.hh @@ -13,19 +13,25 @@ extern "C" { typedef class Server* Server_ptr; typedef class Model* Model_ptr; +typedef class Seat* Seat_ptr; typedef class Output* Output_ptr; -typedef struct Layer : public Node { +typedef struct Layer final : public Node { Layer( struct wlr_layer_surface_v1*, Server_ptr, Model_ptr, + Seat_ptr, Output_ptr, SceneLayer ); ~Layer(); + void format_uid() override; + + pid_t pid(); + static void handle_map(struct wl_listener*, void*); static void handle_unmap(struct wl_listener*, void*); static void handle_surface_commit(struct wl_listener*, void*); @@ -37,11 +43,9 @@ typedef struct Layer : public Node { Region const& region() { return m_region; } void set_region(Region const&); - Uid m_uid; - std::string m_uid_formatted; - Server_ptr mp_server; Model_ptr mp_model; + Seat_ptr mp_seat; Output_ptr mp_output; diff --git a/include/kranewl/tree/node.hh b/include/kranewl/tree/node.hh @@ -1,5 +1,10 @@ #pragma once +#include <kranewl/common.hh> + +#include <string> +#include <sstream> + typedef struct Node { enum class Type { XDGShell, @@ -10,7 +15,8 @@ typedef struct Node { #endif }; - virtual bool is_focusable() const + virtual bool + is_focusable() const { #ifdef XWAYLAND return m_type == Type::XDGShell @@ -20,14 +26,38 @@ typedef struct Node { #endif } + virtual bool + is_view() const + { +#ifdef XWAYLAND + return m_type != Type::LayerShell + && m_type != Type::XWaylandUnmanaged; +#else + return m_type != Type::LayerShell; +#endif + } + + virtual Uid uid() const { return m_uid; } + virtual std::string const& uid_formatted() const { return m_uid_formatted; } + + virtual void format_uid() = 0; + protected: - Node(Type type) - : m_type(type) + Node(Type type, Uid uid) + : m_type(type), + m_uid(uid), + m_uid_formatted([uid]() { + std::stringstream uid_ss; + uid_ss << "0x" << std::hex << uid; + return uid_ss.str(); + }()) {} ~Node() {} Type m_type; + Uid m_uid; + std::string m_uid_formatted; }* Node_ptr; diff --git a/include/kranewl/tree/output.hh b/include/kranewl/tree/output.hh @@ -24,6 +24,7 @@ public: Output( Server_ptr, Model_ptr, + Seat_ptr, struct wlr_output*, struct wlr_scene_output*, Region const&& @@ -67,6 +68,7 @@ private: public: Server_ptr mp_server; Model_ptr mp_model; + Seat_ptr mp_seat; bool m_dirty; diff --git a/include/kranewl/tree/view.hh b/include/kranewl/tree/view.hh @@ -61,21 +61,19 @@ typedef struct View : public Node { virtual ~View(); + void format_uid() override; + virtual Region constraints() = 0; virtual pid_t pid() = 0; virtual bool prefers_floating() = 0; - virtual View_ptr is_transient_for() = 0; virtual void focus(Toggle) = 0; virtual void activate(Toggle) = 0; - virtual void set_tiled(Toggle) = 0; virtual void set_fullscreen(Toggle) = 0; - virtual void set_resizing(Toggle) = 0; virtual void configure(Region const&, Extents const&, bool) = 0; virtual void close() = 0; virtual void close_popups() = 0; - virtual void destroy() = 0; void map(); void unmap(); @@ -125,6 +123,7 @@ typedef struct View : public Node { Pos const& free_pos() const { return m_free_region.pos; } Region const& tile_region() const { return m_tile_region; } Region const& active_region() const { return m_active_region; } + Region const& inner_region() const { return m_inner_region; } Region const& prev_region() const { return m_prev_region; } void set_free_region(Region const&); void set_free_pos(Pos const&); @@ -141,21 +140,16 @@ typedef struct View : public Node { void set_tile_decoration(Decoration const&); void touch() { m_last_touched = std::chrono::steady_clock::now(); } - void format_uid(); static bool is_free(View_ptr view) { return (view->m_floating && (!view->m_fullscreen || view->m_contained)) - || view->m_disowned - || !view->m_managed; + || view->m_disowned; } OutsideState outside_state() const; - Uid m_uid; - std::string m_uid_formatted; - Server_ptr mp_server; Model_ptr mp_model; Seat_ptr mp_seat; diff --git a/include/kranewl/tree/xdg-view.hh b/include/kranewl/tree/xdg-view.hh @@ -25,18 +25,14 @@ typedef struct XDGView final : public View { Region constraints() override; pid_t pid() override; bool prefers_floating() override; - View_ptr is_transient_for() override; void focus(Toggle) override; void activate(Toggle) override; - void set_tiled(Toggle) override; void set_fullscreen(Toggle) override; - void set_resizing(Toggle) override; void configure(Region const&, Extents const&, bool) override; void close() override; void close_popups() override; - void destroy() override; static void handle_commit(struct wl_listener*, void*); static void handle_request_move(struct wl_listener*, void*); diff --git a/include/kranewl/tree/xwayland-view.hh b/include/kranewl/tree/xwayland-view.hh @@ -23,85 +23,105 @@ typedef struct XWaylandView final : public View { ~XWaylandView(); + void format_uid() override; + Region constraints() override; pid_t pid() override; bool prefers_floating() override; - View_ptr is_transient_for() override; void focus(Toggle) override; void activate(Toggle) override; - void set_tiled(Toggle) override; void set_fullscreen(Toggle) override; - void set_resizing(Toggle) override; void configure(Region const&, Extents const&, bool) override; void close() override; void close_popups() override; - void destroy() override; + static void handle_map(struct wl_listener*, void*); + static void handle_unmap(struct wl_listener*, void*); static void handle_commit(struct wl_listener*, void*); - static void handle_request_move(struct wl_listener*, void*); - static void handle_request_resize(struct wl_listener*, void*); - static void handle_request_maximize(struct wl_listener*, void*); - static void handle_request_minimize(struct wl_listener*, void*); + static void handle_request_activate(struct wl_listener*, void*); static void handle_request_configure(struct wl_listener*, void*); static void handle_request_fullscreen(struct wl_listener*, void*); - static void handle_request_activate(struct wl_listener*, void*); + static void handle_request_minimize(struct wl_listener*, void*); + static void handle_request_maximize(struct wl_listener*, void*); + static void handle_request_move(struct wl_listener*, void*); + static void handle_request_resize(struct wl_listener*, void*); + static void handle_set_override_redirect(struct wl_listener*, void*); static void handle_set_title(struct wl_listener*, void*); static void handle_set_class(struct wl_listener*, void*); - static void handle_set_role(struct wl_listener*, void*); - static void handle_set_window_type(struct wl_listener*, void*); static void handle_set_hints(struct wl_listener*, void*); - static void handle_set_decorations(struct wl_listener*, void*); - static void handle_map(struct wl_listener*, void*); - static void handle_unmap(struct wl_listener*, void*); static void handle_destroy(struct wl_listener*, void*); - static void handle_override_redirect(struct wl_listener*, void*); XWayland_ptr mp_xwayland; + std::string m_class; + std::string m_instance; + struct wlr_xwayland_surface* mp_wlr_xwayland_surface; + struct wl_listener ml_map; + struct wl_listener ml_unmap; struct wl_listener ml_commit; - struct wl_listener ml_request_move; - struct wl_listener ml_request_resize; - struct wl_listener ml_request_maximize; - struct wl_listener ml_request_minimize; + struct wl_listener ml_request_activate; struct wl_listener ml_request_configure; struct wl_listener ml_request_fullscreen; - struct wl_listener ml_request_activate; + struct wl_listener ml_request_minimize; + struct wl_listener ml_request_maximize; + struct wl_listener ml_request_move; + struct wl_listener ml_request_resize; + struct wl_listener ml_set_override_redirect; struct wl_listener ml_set_title; struct wl_listener ml_set_class; - struct wl_listener ml_set_role; - struct wl_listener ml_set_window_type; struct wl_listener ml_set_hints; - struct wl_listener ml_set_decorations; - struct wl_listener ml_map; - struct wl_listener ml_unmap; struct wl_listener ml_destroy; - struct wl_listener ml_override_redirect; }* XWaylandView_ptr; -typedef struct XWaylandUnmanaged final { - XWaylandUnmanaged(struct wlr_xwayland_surface*, XWayland_ptr); +typedef struct XWaylandUnmanaged final : public Node { + XWaylandUnmanaged( + struct wlr_xwayland_surface*, + Server_ptr, + Model_ptr, + Seat_ptr, + XWayland_ptr + ); + ~XWaylandUnmanaged(); + void format_uid() override; + + static void handle_map(struct wl_listener*, void*); + static void handle_unmap(struct wl_listener*, void*); + static void handle_commit(struct wl_listener*, void*); + static void handle_set_override_redirect(struct wl_listener*, void*); + static void handle_set_geometry(struct wl_listener*, void*); + static void handle_request_activate(struct wl_listener*, void*); + static void handle_request_configure(struct wl_listener*, void*); + static void handle_request_fullscreen(struct wl_listener*, void*); + static void handle_destroy(struct wl_listener*, void*); + + Server_ptr mp_server; + Model_ptr mp_model; + Seat_ptr mp_seat; + + Output_ptr mp_output; + XWayland_ptr mp_xwayland; - Pos m_pos; + Region m_region; struct wlr_xwayland_surface* mp_wlr_xwayland_surface; + struct wl_listener ml_map; + struct wl_listener ml_unmap; + struct wl_listener ml_commit; + struct wl_listener ml_set_override_redirect; + struct wl_listener ml_set_geometry; struct wl_listener ml_request_activate; struct wl_listener ml_request_configure; struct wl_listener ml_request_fullscreen; - struct wl_listener ml_commit; - struct wl_listener ml_set_geometry; - struct wl_listener ml_map; - struct wl_listener ml_unmap; struct wl_listener ml_destroy; - struct wl_listener ml_override_redirect; }* XWaylandUnmanaged_ptr; #endif diff --git a/include/kranewl/xwayland.hh b/include/kranewl/xwayland.hh @@ -11,6 +11,8 @@ extern "C" { #include <array> typedef class Server* Server_ptr; +typedef class Model* Model_ptr; +typedef class Seat* Seat_ptr; typedef struct XWayland final { enum XAtom { @@ -28,7 +30,13 @@ typedef struct XWayland final { XATOM_LAST, }; - XWayland(struct wlr_xwayland*, Server_ptr); + XWayland( + struct wlr_xwayland*, + Server_ptr, + Model_ptr, + Seat_ptr + ); + ~XWayland(); static void handle_ready(struct wl_listener*, void*); @@ -40,6 +48,8 @@ typedef struct XWayland final { std::array<xcb_atom_t, XATOM_LAST> m_atoms; Server_ptr mp_server; + Model_ptr mp_model; + Seat_ptr mp_seat; struct wl_listener ml_ready; struct wl_listener ml_new_surface; diff --git a/src/kranewl/input/cursor.cc b/src/kranewl/input/cursor.cc @@ -72,8 +72,8 @@ Cursor::~Cursor() } -static inline View_ptr -view_at( +static inline Node_ptr +node_at( Server_ptr server, double lx, double ly, struct wlr_surface** surface, @@ -99,8 +99,8 @@ view_at( while (node && !node->data) node = node->parent; - if (node && node->data && static_cast<Node_ptr>(node->data)->is_focusable()) - return reinterpret_cast<View_ptr>(node->data); + if (node && node->data) + return reinterpret_cast<Node_ptr>(node->data); return nullptr; } @@ -109,6 +109,39 @@ view_at( return nullptr; } +static inline View_ptr +view_at( + Server_ptr server, + double lx, double ly, + struct wlr_surface** surface, + double* sx, double* sy +) +{ + Node_ptr node = node_at(server, lx, ly, surface, sx, sy); + + if (node && node->is_view()) + return reinterpret_cast<View_ptr>(node); + + return nullptr; +} + +Node_ptr +Cursor::node_under_cursor() const +{ + double sx, sy; + struct wlr_surface* surface = nullptr; + + Node_ptr node = node_at( + mp_server, + mp_wlr_cursor->x, + mp_wlr_cursor->y, + &surface, + &sx, &sy + ); + + return node; +} + View_ptr Cursor::view_under_cursor() const { @@ -127,6 +160,62 @@ Cursor::view_under_cursor() const } void +Cursor::initiate_cursor_interactive( + Mode mode, + View_ptr view, + uint32_t edges +) +{ + TRACE(); + + m_grab_state = { + .view = view, + .x = mp_wlr_cursor->x, + .y = mp_wlr_cursor->y, + .region = view->free_region(), + .edges = edges + }; + + switch (mode) { + case Mode::Move: + { + wlr_xcursor_manager_set_cursor_image( + mp_cursor_manager, + "grab", + mp_wlr_cursor + ); + break; + } + case Mode::Resize: + { + char const* cursor; + if ((edges & WLR_EDGE_LEFT)) { + if ((edges & WLR_EDGE_TOP)) + cursor = "top_left_corner"; + else + cursor = "bottom_left_corner"; + } else { + if ((edges & WLR_EDGE_TOP)) + cursor = "top_right_corner"; + else + cursor = "bottom_right_corner"; + } + + wlr_xcursor_manager_set_cursor_image( + mp_cursor_manager, + cursor, + mp_wlr_cursor + ); + + break; + } + default: break; + } + + m_cursor_mode = mode; +} + +void Cursor::initiate_cursor_interactive(Mode mode, View_ptr view) { TRACE(); @@ -144,7 +233,7 @@ Cursor::initiate_cursor_interactive(Mode mode, View_ptr view) { wlr_xcursor_manager_set_cursor_image( mp_cursor_manager, - "fleur", + "grab", mp_wlr_cursor ); break; diff --git a/src/kranewl/model.cc b/src/kranewl/model.cc @@ -51,10 +51,10 @@ Model::Model( mp_prev_context{nullptr}, mp_prev_workspace{nullptr}, m_view_map{}, + m_unmanaged_map{}, m_pid_map{}, m_fullscreen_map{}, m_sticky_views{}, - m_unmanaged_views{}, mp_focus(nullptr), m_key_bindings(Bindings::key_bindings), m_cursor_bindings(Bindings::cursor_bindings) @@ -177,6 +177,7 @@ Model::create_output( Output_ptr output = new Output( mp_server, this, + &mp_server->m_seat, wlr_output, wlr_scene_output, std::forward<Region const&&>(output_region) @@ -339,7 +340,7 @@ Model::place_view(Placement& placement) spdlog::info( "Placing view {} at {}", - view->m_uid_formatted, + view->uid_formatted(), std::to_string(view->active_region()) ); @@ -1814,7 +1815,7 @@ Model::create_xdg_shell_view( seat ); - m_view_map[view->m_uid] = view; + m_view_map[view->uid()] = view; return view; } @@ -1837,10 +1838,32 @@ Model::create_xwayland_view( xwayland ); - m_view_map[view->m_uid] = view; + m_view_map[view->uid()] = view; return view; } + +XWaylandUnmanaged_ptr +Model::create_xwayland_unmanaged( + struct wlr_xwayland_surface* wlr_xwayland_surface, + Seat_ptr seat, + XWayland_ptr xwayland +) +{ + TRACE(); + + XWaylandUnmanaged_ptr node = new XWaylandUnmanaged( + wlr_xwayland_surface, + mp_server, + this, + seat, + xwayland + ); + + m_unmanaged_map[node->uid()] = node; + + return node; +} #endif void @@ -1850,7 +1873,7 @@ Model::register_view(View_ptr view, Workspace_ptr workspace) view->format_uid(); move_view_to_workspace(view, workspace); - spdlog::info("Registered view {}", view->m_uid_formatted); + spdlog::info("Registered view {}", view->uid_formatted()); sync_focus(); } @@ -1864,7 +1887,7 @@ Model::unregister_view(View_ptr view) apply_layout(view->mp_workspace); } - spdlog::info("Unregistered view {}", view->m_uid_formatted); + spdlog::info("Unregistered view {}", view->uid_formatted()); mp_output->focus_at_cursor(); sync_focus(); } @@ -1874,8 +1897,8 @@ Model::destroy_view(View_ptr view) { TRACE(); - m_view_map.erase(view->m_uid); - spdlog::info("Destroyed view {}", view->m_uid_formatted); + m_view_map.erase(view->uid()); + spdlog::info("Destroyed view {}", view->uid_formatted()); delete view; } @@ -1892,6 +1915,7 @@ Model::create_layer( layer_surface, mp_server, this, + &mp_server->m_seat, output, scene_layer ); @@ -1905,7 +1929,7 @@ Model::register_layer(Layer_ptr layer) TRACE(); layer->mp_output->add_layer(layer); - spdlog::info("Registered layer {}", layer->m_uid_formatted); + spdlog::info("Registered layer {}", layer->uid_formatted()); } bool diff --git a/src/kranewl/server.cc b/src/kranewl/server.cc @@ -132,7 +132,7 @@ Server::Server(Model_ptr model) }()), #ifdef XWAYLAND mp_wlr_xwayland(wlr_xwayland_create(mp_display, mp_compositor, true)), - m_xwayland({mp_wlr_xwayland, this}), + m_xwayland({mp_wlr_xwayland, this, model, &m_seat}), #endif mp_layer_shell(wlr_layer_shell_v1_create(mp_display)), mp_xdg_shell(wlr_xdg_shell_create(mp_display)), @@ -433,13 +433,6 @@ Server::handle_new_layer_shell_surface(struct wl_listener* listener, void* data) layer_surface->surface ); layer->mp_scene->data = layer; - - server->mp_model->register_layer(layer); - - struct wlr_layer_surface_v1_state initial_state = layer_surface->current; - layer_surface->current = layer_surface->pending; - output->arrange_layers(); - layer_surface->current = initial_state; } void diff --git a/src/kranewl/tree/layer.cc b/src/kranewl/tree/layer.cc @@ -23,18 +23,14 @@ Layer::Layer( struct wlr_layer_surface_v1* layer_surface, Server_ptr server, Model_ptr model, + Seat_ptr seat, Output_ptr output, SceneLayer scene_layer ) - : 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; - return uid_ss.str(); - }()), + : Node(Type::LayerShell, reinterpret_cast<std::uintptr_t>(layer_surface)), mp_server(server), mp_model(model), + mp_seat(seat), mp_output(output), m_scene_layer(scene_layer), mp_layer_surface(layer_surface), @@ -55,6 +51,29 @@ Layer::~Layer() {} void +Layer::format_uid() +{ + std::stringstream uid_ss; + uid_ss << "0x" << std::hex << uid() << std::dec; + uid_ss << " [" << m_pid << "]"; + uid_ss << " (L)"; + m_uid_formatted = uid_ss.str(); +} + +pid_t +Layer::pid() +{ + TRACE(); + + pid_t pid; + struct wl_client* client + = wl_resource_get_client(mp_layer_surface->surface->resource); + + wl_client_get_credentials(client, &pid, nullptr, nullptr); + return pid; +} + +void Layer::set_mapped(bool mapped) { m_mapped = mapped; @@ -73,13 +92,23 @@ Layer::handle_map(struct wl_listener* listener, void*) Layer_ptr layer = wl_container_of(listener, layer, ml_map); + layer->m_pid = layer->pid(); + layer->format_uid(); + + layer->mp_model->register_layer(layer); + + struct wlr_layer_surface_v1_state initial_state = layer->mp_layer_surface->current; + layer->mp_layer_surface->current = layer->mp_layer_surface->pending; + layer->mp_output->arrange_layers(); + layer->mp_layer_surface->current = initial_state; + 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); + if (layer->mp_seat->mp_cursor) + layer->mp_seat->mp_cursor->process_cursor_motion(0); } static inline void @@ -88,13 +117,13 @@ unmap_layer(Layer_ptr layer) TRACE(); layer->mp_layer_surface->mapped = 0; - struct wlr_seat* seat = layer->mp_server->m_seat.mp_wlr_seat; + struct wlr_seat* seat = layer->mp_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); + if (layer->mp_seat->mp_cursor) + layer->mp_seat->mp_cursor->process_cursor_motion(0); } void diff --git a/src/kranewl/tree/output.cc b/src/kranewl/tree/output.cc @@ -27,6 +27,7 @@ extern "C" { Output::Output( Server_ptr server, Model_ptr model, + Seat_ptr seat, struct wlr_output* wlr_output, struct wlr_scene_output* wlr_scene_output, Region const&& output_region @@ -36,6 +37,7 @@ Output::Output( m_placeable_region(output_region), mp_server(server), mp_model(model), + mp_seat(seat), m_dirty(true), m_cursor_focus_on_present(false), m_layer_map{ @@ -98,7 +100,7 @@ Output::handle_present(struct wl_listener* listener, void*) if (output->m_cursor_focus_on_present && output == output->mp_model->mp_output) { if (true /* TODO: focus_follows_mouse */) { View_ptr view_under_cursor - = output->mp_server->m_seat.mp_cursor->view_under_cursor(); + = output->mp_seat->mp_cursor->view_under_cursor(); if (view_under_cursor && view_under_cursor->managed()) output->mp_model->focus_view(view_under_cursor); @@ -427,7 +429,7 @@ Output::arrange_layers() Region placeable_region = m_full_region; struct wlr_keyboard* keyboard - = wlr_seat_get_keyboard(mp_server->m_seat.mp_wlr_seat); + = wlr_seat_get_keyboard(mp_seat->mp_wlr_seat); // exclusive surfaces for (SceneLayer scene_layer : scene_layers_top_bottom) @@ -452,7 +454,7 @@ Output::arrange_layers() if (keyboard) wlr_seat_keyboard_notify_enter( - mp_server->m_seat.mp_wlr_seat, + mp_seat->mp_wlr_seat, layer->mp_layer_surface->surface, keyboard->keycodes, keyboard->num_keycodes, @@ -460,7 +462,7 @@ Output::arrange_layers() ); else wlr_seat_keyboard_notify_enter( - mp_server->m_seat.mp_wlr_seat, + mp_seat->mp_wlr_seat, layer->mp_layer_surface->surface, nullptr, 0, diff --git a/src/kranewl/tree/view.cc b/src/kranewl/tree/view.cc @@ -30,13 +30,7 @@ View::View( Seat_ptr seat, struct wlr_surface* wlr_surface ) - : Node(Type::XDGShell), - m_uid(uid), - m_uid_formatted([uid]() { - std::stringstream uid_ss; - uid_ss << "0x" << std::hex << uid; - return uid_ss.str(); - }()), + : Node(Type::XDGShell, uid), mp_server(server), mp_model(model), mp_seat(seat), @@ -88,8 +82,7 @@ View::View( Seat_ptr seat, struct wlr_surface* wlr_surface ) - : Node(Type::XWaylandManaged), - m_uid(uid), + : Node(Type::XWaylandManaged, uid), mp_server(server), mp_model(model), mp_seat(seat), @@ -109,6 +102,7 @@ View::View( m_active_region({}), m_prev_region({}), m_inner_region({}), + m_activated(false), m_focused(false), m_mapped(false), m_managed(true), @@ -125,7 +119,8 @@ View::View( m_scene_layer(SCENE_LAYER_NONE), m_last_focused(std::chrono::steady_clock::now()), m_last_touched(std::chrono::steady_clock::now()), - m_managed_since(std::chrono::steady_clock::now()) + m_managed_since(std::chrono::steady_clock::now()), + m_outside_state(OutsideState::Unfocused) { wl_signal_init(&m_events.unmap); } @@ -512,10 +507,10 @@ void View::format_uid() { std::stringstream uid_ss; - uid_ss << "0x" << std::hex << m_uid << std::dec; + uid_ss << "0x" << std::hex << uid() << std::dec; uid_ss << " [" << m_title; uid_ss << ", " << m_pid << "]"; - uid_ss << " (" << (m_type == Type::XDGShell ? "W" : "X") << ")"; + uid_ss << " (W)"; m_uid_formatted = uid_ss.str(); } diff --git a/src/kranewl/tree/xdg-view.cc b/src/kranewl/tree/xdg-view.cc @@ -90,13 +90,6 @@ XDGView::prefers_floating() && (state->min_width == state->max_width || state->min_height == state->max_height)); } -View_ptr -XDGView::is_transient_for() -{ - TRACE(); - -} - void XDGView::focus(Toggle toggle) { @@ -111,6 +104,7 @@ XDGView::focus(Toggle toggle) set_focused(true); activate(toggle); render_decoration(); + raise(); break; } case Toggle::Off: @@ -193,24 +187,10 @@ XDGView::activate(Toggle toggle) } void -XDGView::set_tiled(Toggle) -{ - TRACE(); - -} - -void XDGView::set_fullscreen(Toggle) { TRACE(); - -} - -void -XDGView::set_resizing(Toggle) -{ - TRACE(); - + // TODO } void @@ -251,13 +231,6 @@ XDGView::close_popups() } void -XDGView::destroy() -{ - TRACE(); - -} - -void XDGView::handle_commit(struct wl_listener* listener, void* data) { TRACE(); @@ -295,7 +268,9 @@ XDGView::handle_set_title(struct wl_listener* listener, void* data) TRACE(); XDGView_ptr view = wl_container_of(listener, view, ml_set_title); - view->m_title = view->mp_wlr_xdg_toplevel->title; + view->m_title = view->mp_wlr_xdg_toplevel->title + ? view->mp_wlr_xdg_toplevel->title + : "N/a"; view->m_title_formatted = view->m_title; view->format_uid(); } @@ -327,6 +302,8 @@ XDGView::handle_map(struct wl_listener* listener, void* data) Model_ptr model = view->mp_model; view->m_pid = view->pid(); + view->format_uid(); + view->set_floating(view->prefers_floating()); struct wlr_xdg_surface* wlr_xdg_surface = view->mp_wlr_xdg_surface; @@ -357,17 +334,13 @@ XDGView::handle_map(struct wl_listener* listener, void* data) .pos = Pos{0, 0}, .dim = preferred_dim }); - - view->set_tile_region(Region{ - .pos = Pos{0, 0}, - .dim = preferred_dim - }); + view->set_tile_region(view->free_region()); view->m_app_id = wlr_xdg_toplevel->app_id - ? std::string(wlr_xdg_toplevel->app_id) + ? wlr_xdg_toplevel->app_id : "N/a"; view->m_title = wlr_xdg_toplevel->title - ? std::string(wlr_xdg_toplevel->title) + ? wlr_xdg_toplevel->title : "N/a"; view->m_title_formatted = view->m_title; // TODO: format title diff --git a/src/kranewl/tree/xwayland-view.cc b/src/kranewl/tree/xwayland-view.cc @@ -1,8 +1,14 @@ #ifdef XWAYLAND #include <trace.hh> +#include <kranewl/context.hh> +#include <kranewl/model.hh> +#include <kranewl/scene-layer.hh> +#include <kranewl/server.hh> +#include <kranewl/tree/output.hh> #include <kranewl/tree/view.hh> #include <kranewl/tree/xwayland-view.hh> +#include <kranewl/workspace.hh> // https://github.com/swaywm/wlroots/issues/682 #include <pthread.h> @@ -10,6 +16,10 @@ #define namespace namespace_ #define static extern "C" { +#include <wlr/types/wlr_idle.h> +#include <wlr/types/wlr_idle_inhibit_v1.h> +#include <wlr/types/wlr_layer_shell_v1.h> +#include <wlr/types/wlr_scene.h> #include <wlr/xwayland.h> } #undef static @@ -33,29 +43,54 @@ XWaylandView::XWaylandView( ), mp_xwayland(xwayland), mp_wlr_xwayland_surface(wlr_xwayland_surface), + ml_map({ .notify = XWaylandView::handle_map }), + ml_unmap({ .notify = XWaylandView::handle_unmap }), ml_commit({ .notify = XWaylandView::handle_commit }), - ml_request_move({ .notify = XWaylandView::handle_request_move }), - ml_request_resize({ .notify = XWaylandView::handle_request_resize }), - ml_request_maximize({ .notify = XWaylandView::handle_request_maximize }), - ml_request_minimize({ .notify = XWaylandView::handle_request_minimize }), + ml_request_activate({ .notify = XWaylandView::handle_request_activate }), ml_request_configure({ .notify = XWaylandView::handle_request_configure }), ml_request_fullscreen({ .notify = XWaylandView::handle_request_fullscreen }), - ml_request_activate({ .notify = XWaylandView::handle_request_activate }), + ml_request_minimize({ .notify = XWaylandView::handle_request_minimize }), + ml_request_maximize({ .notify = XWaylandView::handle_request_maximize }), + ml_request_move({ .notify = XWaylandView::handle_request_move }), + ml_request_resize({ .notify = XWaylandView::handle_request_resize }), + ml_set_override_redirect({ .notify = XWaylandView::handle_set_override_redirect }), ml_set_title({ .notify = XWaylandView::handle_set_title }), ml_set_class({ .notify = XWaylandView::handle_set_class }), - ml_set_role({ .notify = XWaylandView::handle_set_role }), - ml_set_window_type({ .notify = XWaylandView::handle_set_window_type }), ml_set_hints({ .notify = XWaylandView::handle_set_hints }), - ml_set_decorations({ .notify = XWaylandView::handle_set_decorations }), - ml_map({ .notify = XWaylandView::handle_map }), - ml_unmap({ .notify = XWaylandView::handle_unmap }), - ml_destroy({ .notify = XWaylandView::handle_destroy }), - ml_override_redirect({ .notify = XWaylandView::handle_override_redirect }) -{} + ml_destroy({ .notify = XWaylandView::handle_destroy }) +{ + wl_signal_add(&mp_wlr_xwayland_surface->events.map, &ml_map); + wl_signal_add(&mp_wlr_xwayland_surface->events.unmap, &ml_unmap); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_activate, &ml_request_activate); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_configure, &ml_request_configure); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_fullscreen, &ml_request_fullscreen); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_minimize, &ml_request_minimize); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_maximize, &ml_request_maximize); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_move, &ml_request_move); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_resize, &ml_request_resize); + wl_signal_add(&mp_wlr_xwayland_surface->events.set_override_redirect, &ml_set_override_redirect); + wl_signal_add(&mp_wlr_xwayland_surface->events.set_title, &ml_set_title); + wl_signal_add(&mp_wlr_xwayland_surface->events.set_class, &ml_set_class); + wl_signal_add(&mp_wlr_xwayland_surface->events.set_hints, &ml_set_hints); + wl_signal_add(&mp_wlr_xwayland_surface->events.destroy, &ml_destroy); +} XWaylandView::~XWaylandView() {} +void +XWaylandView::format_uid() +{ + std::stringstream uid_ss; + uid_ss << "0x" << std::hex << uid() << std::dec; + uid_ss << " [" << m_title; + uid_ss << "; " << m_class; + uid_ss << "; " << m_instance; + uid_ss << ", " << m_pid << "]"; + uid_ss << " (XM)"; + m_uid_formatted = uid_ss.str(); +} + Region XWaylandView::constraints() { @@ -67,10 +102,7 @@ pid_t XWaylandView::pid() { TRACE(); - - struct wlr_xwayland_surface* wlr_xwayland_surface - = wlr_xwayland_surface_from_wlr_surface(mp_wlr_surface); - return wlr_xwayland_surface->pid; + return mp_wlr_xwayland_surface->pid; } bool @@ -78,212 +110,623 @@ XWaylandView::prefers_floating() { TRACE(); + struct wlr_xwayland_surface* xwayland_surface = mp_wlr_xwayland_surface; + + if (xwayland_surface->modal) + return true; + + for (std::size_t i = 0; i < xwayland_surface->window_type_len; ++i) { + xcb_atom_t type = xwayland_surface->window_type[i]; + + if (type == mp_xwayland->m_atoms[XWayland::NET_WM_WINDOW_TYPE_DIALOG] + || type == mp_xwayland->m_atoms[XWayland::NET_WM_WINDOW_TYPE_UTILITY] + || type == mp_xwayland->m_atoms[XWayland::NET_WM_WINDOW_TYPE_TOOLBAR] + || type == mp_xwayland->m_atoms[XWayland::NET_WM_WINDOW_TYPE_SPLASH]) + { + return true; + } + } + + struct wlr_xwayland_surface_size_hints* size_hints = xwayland_surface->size_hints; + if (size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 + && (size_hints->max_width == size_hints->min_width + || size_hints->max_height == size_hints->min_height)) + { + return true; + } + + return false; } -View_ptr -XWaylandView::is_transient_for() +void +XWaylandView::focus(Toggle toggle) { TRACE(); + switch (toggle) { + case Toggle::On: + { + if (focused()) + return; + + set_focused(true); + activate(toggle); + render_decoration(); + raise(); + break; + } + case Toggle::Off: + { + if (!focused()) + return; + + set_focused(false); + activate(toggle); + render_decoration(); + break; + } + case Toggle::Reverse: + { + focus( + focused() + ? Toggle::Off + : Toggle::On + ); + return; + } + default: break; + } } void -XWaylandView::focus(Toggle) +XWaylandView::activate(Toggle toggle) { TRACE(); + switch (toggle) { + case Toggle::On: + { + if (activated()) + return; + + set_activated(true); + + struct wlr_keyboard* keyboard = wlr_seat_get_keyboard(mp_seat->mp_wlr_seat); + if (keyboard) + wlr_seat_keyboard_notify_enter( + mp_seat->mp_wlr_seat, + mp_wlr_surface, + keyboard->keycodes, + keyboard->num_keycodes, + &keyboard->modifiers + ); + else + wlr_seat_keyboard_notify_enter( + mp_seat->mp_wlr_seat, + mp_wlr_surface, + nullptr, + 0, + nullptr + ); + + wlr_xwayland_surface_activate(mp_wlr_xwayland_surface, true); + wlr_xwayland_surface_restack( + mp_wlr_xwayland_surface, + nullptr, + XCB_STACK_MODE_ABOVE + ); + break; + } + case Toggle::Off: + { + if (!activated()) + return; + + set_activated(false); + wlr_xwayland_surface_activate(mp_wlr_xwayland_surface, false); + break; + } + case Toggle::Reverse: + { + activate( + activated() + ? Toggle::Off + : Toggle::On + ); + return; + } + default: break; + } } void -XWaylandView::activate(Toggle) +XWaylandView::set_fullscreen(Toggle) { TRACE(); - + // TODO } void -XWaylandView::set_tiled(Toggle) +XWaylandView::configure(Region const& region, Extents const& extents, bool interactive) { TRACE(); + wlr_scene_node_set_position(mp_scene, region.pos.x, region.pos.y); + wlr_scene_node_set_position(mp_scene_surface, extents.left, extents.top); + wlr_scene_rect_set_size(m_protrusions[0], region.dim.w, extents.top); + wlr_scene_rect_set_size(m_protrusions[1], region.dim.w, extents.bottom); + wlr_scene_rect_set_size(m_protrusions[2], extents.left, region.dim.h - extents.top - extents.bottom); + wlr_scene_rect_set_size(m_protrusions[3], extents.right, region.dim.h - extents.top - extents.bottom); + wlr_scene_node_set_position(&m_protrusions[0]->node, 0, 0); + wlr_scene_node_set_position(&m_protrusions[1]->node, 0, region.dim.h - extents.bottom); + wlr_scene_node_set_position(&m_protrusions[2]->node, 0, extents.top); + wlr_scene_node_set_position(&m_protrusions[3]->node, region.dim.w - extents.right, extents.top); + + wlr_xwayland_surface_configure( + mp_wlr_xwayland_surface, + region.pos.x, + region.pos.y, + region.dim.w - extents.left - extents.right, + region.dim.h - extents.top - extents.bottom + ); + + m_resize = 0; } void -XWaylandView::set_fullscreen(Toggle) +XWaylandView::close() { TRACE(); - + wlr_xwayland_surface_close(mp_wlr_xwayland_surface); } void -XWaylandView::set_resizing(Toggle) +XWaylandView::close_popups() { TRACE(); } void -XWaylandView::configure(Region const& region, Extents const& extents, bool interactive) +XWaylandView::handle_map(struct wl_listener* listener, void* data) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_map); + Server_ptr server = view->mp_server; + Model_ptr model = view->mp_model; + + view->m_pid = view->pid(); + view->format_uid(); + + view->set_floating(view->prefers_floating()); + + struct wlr_xwayland_surface* xwayland_surface + = reinterpret_cast<struct wlr_xwayland_surface*>(data); + view->mp_wlr_surface = xwayland_surface->surface; + + Dim preferred_dim = Dim{ + .w = xwayland_surface->width, + .h = xwayland_surface->height, + }; + + Extents const& extents = view->free_decoration().extents(); + preferred_dim.w += extents.left + extents.right; + preferred_dim.h += extents.top + extents.bottom; + view->set_preferred_dim(preferred_dim); + + view->set_free_region(Region{ + .pos = Pos{ + .x = xwayland_surface->x - extents.left, + .y = xwayland_surface->y - extents.top + }, + .dim = preferred_dim + }); + view->set_tile_region(view->free_region()); + + view->m_app_id = view->m_class = xwayland_surface->class_ + ? xwayland_surface->class_ + : "N/a"; + view->m_instance = xwayland_surface->instance + ? xwayland_surface->instance + : "N/a"; + view->m_title = xwayland_surface->title + ? xwayland_surface->title + : "N/a"; + view->m_title_formatted = view->m_title; // TODO: format title + + view->mp_scene = &wlr_scene_tree_create( + server->m_scene_layers[SCENE_LAYER_TILE] + )->node; + + view->mp_wlr_surface->data = view->mp_scene_surface = wlr_scene_subsurface_tree_create( + view->mp_scene, + view->mp_wlr_surface + ); + view->mp_scene_surface->data = view; + + view->relayer( + view->floating() + ? SCENE_LAYER_FREE + : SCENE_LAYER_TILE + ); + + for (std::size_t i = 0; i < 4; ++i) { + view->m_protrusions[i] = wlr_scene_rect_create( + view->mp_scene, + 0, 0, + view->active_decoration().colorscheme.unfocused.values + ); + view->m_protrusions[i]->node.data = view; + wlr_scene_rect_set_color( + view->m_protrusions[i], + view->active_decoration().colorscheme.unfocused.values + ); + wlr_scene_node_lower_to_bottom(&view->m_protrusions[i]->node); + } + + wl_signal_add(&xwayland_surface->surface->events.commit, &view->ml_commit); + + Workspace_ptr workspace = model->mp_workspace; + + view->set_mapped(true); + view->render_decoration(); + model->register_view(view, workspace); } void -XWaylandView::close() +XWaylandView::handle_unmap(struct wl_listener* listener, void*) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_unmap); + + wl_list_remove(&view->ml_commit.link); + + view->activate(Toggle::Off); + view->mp_model->unregister_view(view); + + wlr_scene_node_destroy(view->mp_scene); + view->mp_wlr_surface = nullptr; + view->set_managed(false); + + if (view->mp_model->mp_workspace) + view->mp_model->apply_layout(view->mp_workspace); } void -XWaylandView::close_popups() +XWaylandView::handle_commit(struct wl_listener*, void*) { TRACE(); } void -XWaylandView::destroy() +XWaylandView::handle_request_activate(struct wl_listener* listener, void*) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_request_activate); + struct wlr_xwayland_surface* xwayland_surface = view->mp_wlr_xwayland_surface; + + if (!xwayland_surface->mapped) + return; + + view->mp_model->focus_view(view); } void -XWaylandView::handle_commit(struct wl_listener* listener, void* data) +XWaylandView::handle_request_configure(struct wl_listener* listener, void* data) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_request_configure); + struct wlr_xwayland_surface_configure_event* event + = reinterpret_cast<struct wlr_xwayland_surface_configure_event*>(data); + struct wlr_xwayland_surface* xwayland_surface = view->mp_wlr_xwayland_surface; + + if (!xwayland_surface->mapped) { + wlr_xwayland_surface_configure( + xwayland_surface, + event->x, event->y, + event->width, event->height + ); + + return; + } + + if (view->mp_model->is_free(view)) { + view->set_preferred_dim(Dim{ + .w = event->width, + .h = event->height, + }); + + view->set_free_region(Region{ + .pos = Pos{ + .x = event->x, + .y = event->y + }, + .dim = view->preferred_dim() + }); + + view->configure( + view->free_region(), + FREE_DECORATION.extents(), + false + ); + } else + view->configure( + view->active_region(), + view->active_decoration().extents(), + false + ); } void -XWaylandView::handle_request_move(struct wl_listener* listener, void* data) +XWaylandView::handle_request_fullscreen(struct wl_listener*, void*) { TRACE(); - + // TODO } void -XWaylandView::handle_request_resize(struct wl_listener* listener, void* data) +XWaylandView::handle_request_minimize(struct wl_listener*, void*) { TRACE(); + // TODO +} +void +XWaylandView::handle_request_maximize(struct wl_listener*, void*) +{ + TRACE(); + // TODO } void -XWaylandView::handle_request_maximize(struct wl_listener* listener, void* data) +XWaylandView::handle_request_move(struct wl_listener* listener, void*) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_request_move); + struct wlr_xwayland_surface* xwayland_surface = view->mp_wlr_xwayland_surface; + + if (!xwayland_surface->mapped) + return; + + if (!view->mp_model->is_free(view) || view->fullscreen()) + return; + + view->mp_model->cursor_interactive(Cursor::Mode::Move, view); } void -XWaylandView::handle_request_minimize(struct wl_listener* listener, void* data) +XWaylandView::handle_request_resize(struct wl_listener* listener, void* data) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_request_resize); + struct wlr_xwayland_surface* xwayland_surface = view->mp_wlr_xwayland_surface; + + if (!xwayland_surface->mapped) + return; + + if (!view->mp_model->is_free(view) || view->fullscreen()) + return; + + struct wlr_xwayland_resize_event* event + = reinterpret_cast<struct wlr_xwayland_resize_event*>(data); + + view->mp_seat->mp_cursor->initiate_cursor_interactive( + Cursor::Mode::Resize, + view, + event->edges + ); } void -XWaylandView::handle_request_configure(struct wl_listener* listener, void* data) +XWaylandView::handle_set_override_redirect(struct wl_listener* listener, void* data) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_set_override_redirect); + struct wlr_xwayland_surface* xwayland_surface + = reinterpret_cast<struct wlr_xwayland_surface*>(data); + + if (xwayland_surface->mapped) + handle_unmap(&view->ml_unmap, nullptr); + + handle_destroy(&view->ml_destroy, view); + xwayland_surface->data = nullptr; + + XWaylandUnmanaged_ptr unmanaged = view->mp_model->create_xwayland_unmanaged( + xwayland_surface, + view->mp_seat, + view->mp_xwayland + ); + + if (xwayland_surface->mapped) + XWaylandUnmanaged::handle_map(&unmanaged->ml_map, xwayland_surface); } void -XWaylandView::handle_request_fullscreen(struct wl_listener* listener, void* data) +XWaylandView::handle_set_title(struct wl_listener* listener, void*) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_set_title); + view->m_title = view->mp_wlr_xwayland_surface->title + ? view->mp_wlr_xwayland_surface->title + : "N/a"; + view->m_title_formatted = view->m_title; // TODO: format title + view->format_uid(); } void -XWaylandView::handle_request_activate(struct wl_listener* listener, void* data) +XWaylandView::handle_set_class(struct wl_listener* listener, void*) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_set_class); + view->m_class = view->mp_wlr_xwayland_surface->class_ + ? view->mp_wlr_xwayland_surface->class_ + : "N/a"; + view->format_uid(); } void -XWaylandView::handle_set_title(struct wl_listener* listener, void* data) +XWaylandView::handle_set_hints(struct wl_listener* listener, void*) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_set_hints); + struct wlr_xwayland_surface* xwayland_surface = view->mp_wlr_xwayland_surface; + + if (!xwayland_surface->mapped) + return; + + const bool urgent = xwayland_surface->hints_urgency; + + if (urgent) { + view->set_urgent(true); + view->render_decoration(); + } } void -XWaylandView::handle_set_class(struct wl_listener* listener, void* data) +XWaylandView::handle_destroy(struct wl_listener* listener, void*) { TRACE(); + XWaylandView_ptr view = wl_container_of(listener, view, ml_destroy); + + wl_list_remove(&view->ml_map.link); + wl_list_remove(&view->ml_unmap.link); + wl_list_remove(&view->ml_request_activate.link); + wl_list_remove(&view->ml_request_configure.link); + wl_list_remove(&view->ml_request_fullscreen.link); + wl_list_remove(&view->ml_request_minimize.link); + wl_list_remove(&view->ml_request_maximize.link); + wl_list_remove(&view->ml_request_move.link); + wl_list_remove(&view->ml_request_resize.link); + wl_list_remove(&view->ml_set_override_redirect.link); + wl_list_remove(&view->ml_set_title.link); + wl_list_remove(&view->ml_set_class.link); + wl_list_remove(&view->ml_set_hints.link); + wl_list_remove(&view->ml_destroy.link); + + view->mp_wlr_xwayland_surface = nullptr; + view->mp_model->destroy_view(view); +} + + +XWaylandUnmanaged::XWaylandUnmanaged( + struct wlr_xwayland_surface* wlr_xwayland_surface, + Server_ptr server, + Model_ptr model, + Seat_ptr seat, + XWayland_ptr xwayland +) + : Node(Type::XWaylandUnmanaged, reinterpret_cast<std::uintptr_t>(wlr_xwayland_surface)), + mp_wlr_xwayland_surface(wlr_xwayland_surface), + mp_server(server), + mp_model(model), + mp_seat(seat), + mp_output(nullptr), + mp_xwayland(xwayland), + m_region({}), + ml_map({ .notify = handle_map }), + ml_unmap({ .notify = handle_unmap }), + ml_commit({ .notify = handle_commit }), + ml_set_override_redirect({ .notify = handle_set_override_redirect }), + ml_set_geometry({ .notify = handle_set_geometry }), + ml_request_activate({ .notify = handle_request_activate }), + ml_request_configure({ .notify = handle_request_configure }), + ml_request_fullscreen({ .notify = handle_request_fullscreen }), + ml_destroy({ .notify = handle_destroy }) +{ + wl_signal_add(&mp_wlr_xwayland_surface->events.map, &ml_map); + wl_signal_add(&mp_wlr_xwayland_surface->events.unmap, &ml_unmap); + wl_signal_add(&mp_wlr_xwayland_surface->events.set_override_redirect, &ml_set_override_redirect); + wl_signal_add(&mp_wlr_xwayland_surface->events.set_geometry, &ml_set_geometry); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_activate, &ml_request_activate); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_configure, &ml_request_configure); + wl_signal_add(&mp_wlr_xwayland_surface->events.request_fullscreen, &ml_request_fullscreen); + wl_signal_add(&mp_wlr_xwayland_surface->events.destroy, &ml_destroy); +} + +XWaylandUnmanaged::~XWaylandUnmanaged() +{} + +void +XWaylandUnmanaged::format_uid() +{ + std::stringstream uid_ss; + uid_ss << "0x" << std::hex << uid() << std::dec; + uid_ss << " (XU)"; + m_uid_formatted = uid_ss.str(); } void -XWaylandView::handle_set_role(struct wl_listener* listener, void* data) +XWaylandUnmanaged::handle_map(struct wl_listener*, void*) { TRACE(); } void -XWaylandView::handle_set_window_type(struct wl_listener* listener, void* data) +XWaylandUnmanaged::handle_unmap(struct wl_listener*, void*) { TRACE(); } void -XWaylandView::handle_set_hints(struct wl_listener* listener, void* data) +XWaylandUnmanaged::handle_commit(struct wl_listener*, void*) { TRACE(); } void -XWaylandView::handle_set_decorations(struct wl_listener* listener, void* data) +XWaylandUnmanaged::handle_set_override_redirect(struct wl_listener*, void*) { TRACE(); } void -XWaylandView::handle_map(struct wl_listener* listener, void* data) +XWaylandUnmanaged::handle_set_geometry(struct wl_listener*, void*) { TRACE(); } void -XWaylandView::handle_unmap(struct wl_listener* listener, void* data) +XWaylandUnmanaged::handle_request_activate(struct wl_listener*, void*) { TRACE(); } void -XWaylandView::handle_destroy(struct wl_listener* listener, void* data) +XWaylandUnmanaged::handle_request_configure(struct wl_listener*, void*) { TRACE(); } void -XWaylandView::handle_override_redirect(struct wl_listener* listener, void* data) +XWaylandUnmanaged::handle_request_fullscreen(struct wl_listener*, void*) { TRACE(); } -XWaylandUnmanaged::XWaylandUnmanaged( - struct wlr_xwayland_surface* wlr_xwayland_surface, - XWayland_ptr xwayland -) - : mp_wlr_xwayland_surface(wlr_xwayland_surface), - mp_xwayland(xwayland) -{} +void +XWaylandUnmanaged::handle_destroy(struct wl_listener*, void*) +{ + TRACE(); -XWaylandUnmanaged::~XWaylandUnmanaged() -{} +} #endif diff --git a/src/kranewl/xwayland.cc b/src/kranewl/xwayland.cc @@ -1,7 +1,9 @@ #include <trace.hh> #include <kranewl/input/seat.hh> +#include <kranewl/model.hh> #include <kranewl/server.hh> +#include <kranewl/tree/xwayland-view.hh> #include <kranewl/xwayland.hh> // https://github.com/swaywm/wlroots/issues/682 @@ -25,11 +27,18 @@ extern "C" { #include <cstdlib> -XWayland::XWayland(struct wlr_xwayland* xwayland, Server_ptr server) +XWayland::XWayland( + struct wlr_xwayland* xwayland, + Server_ptr server, + Model_ptr model, + Seat_ptr seat +) : mp_wlr_xwayland(xwayland), mp_cursor_manager(wlr_xcursor_manager_create(nullptr, 24)), m_atoms({}), mp_server(server), + mp_model(model), + mp_seat(seat), ml_ready({ .notify = XWayland::handle_ready }), ml_new_surface({ .notify = XWayland::handle_new_surface }) { @@ -115,7 +124,7 @@ XWayland::handle_ready(struct wl_listener* listener, void*) wlr_xwayland_set_seat( xwayland->mp_wlr_xwayland, - xwayland->mp_server->m_seat.mp_wlr_seat + xwayland->mp_seat->mp_wlr_seat ); struct wlr_xcursor* xcursor; @@ -139,4 +148,13 @@ XWayland::handle_new_surface(struct wl_listener* listener, void* data) { TRACE(); + XWayland_ptr xwayland = wl_container_of(listener, xwayland, ml_new_surface); + struct wlr_xwayland_surface* xwayland_surface + = reinterpret_cast<struct wlr_xwayland_surface*>(data); + + XWaylandView_ptr xwayland_view = xwayland->mp_model->create_xwayland_view( + xwayland_surface, + xwayland->mp_seat, + xwayland + ); }