diff --git a/plugins/animate/animate.cpp b/plugins/animate/animate.cpp index ab7871ae3..b1ffdf961 100644 --- a/plugins/animate/animate.cpp +++ b/plugins/animate/animate.cpp @@ -7,10 +7,13 @@ #include #include #include +#include "animate.hpp" #include "system_fade.hpp" #include "basic_animations.hpp" #include "fire/fire.hpp" +#include "unmapped-view-node.hpp" #include "wayfire/plugin.hpp" +#include "wayfire/scene-operations.hpp" #include "wayfire/scene.hpp" #include "wayfire/signal-provider.hpp" #include @@ -69,13 +72,23 @@ struct animation_hook : public animation_hook_base std::string name; wf::output_t *current_output = nullptr; std::unique_ptr animation; + std::shared_ptr unmapped_contents; + + void damage_whole_view() + { + view->damage(); + if (unmapped_contents) + { + wf::scene::damage_node(unmapped_contents, unmapped_contents->get_bounding_box()); + } + } /* Update animation right before each frame */ wf::effect_hook_t update_animation_hook = [=] () { - view->damage(); + damage_whole_view(); bool result = animation->step(); - view->damage(); + damage_whole_view(); if (!result) { @@ -122,21 +135,62 @@ struct animation_hook : public animation_hook_base /* Animation is driven by the output render cycle the view is on. * Thus, we need to keep in sync with the current output. */ view->connect(&on_set_output); + + // Take a ref on the view, so that it remains available for as long as the animation runs. + wf::scene::set_node_enabled(view->get_root_node(), true); + view->take_ref(); + + if (type == ANIMATION_TYPE_UNMAP) + { + set_unmapped_contents(); + } } void stop_hook(bool detached) override { - /* We don't want to change the state of the view if it was detached */ - if ((type == ANIMATION_TYPE_MINIMIZE) && !detached) + view->erase_data(name); + } + + // When showing the final unmap animation, we show a ``fake'' node instead of the actual view contents, + // because the underlying (sub)surfaces might be destroyed. + // + // The unmapped contents have to be visible iff the view is in an unmap animation. + void set_unmapped_contents() + { + if (!unmapped_contents) { - view->set_minimized(true); + unmapped_contents = std::make_shared(view); + auto parent = dynamic_cast( + view->get_surface_root_node()->parent()); + + if (parent) + { + wf::scene::add_front( + std::dynamic_pointer_cast(parent->shared_from_this()), + unmapped_contents); + } } + } - view->erase_data(name); + void unset_unmapped_contents() + { + if (unmapped_contents) + { + wf::scene::remove_child(unmapped_contents); + unmapped_contents.reset(); + } } void reverse(wf_animation_type type) override { + if (type == ANIMATION_TYPE_UNMAP) + { + set_unmapped_contents(); + } else + { + unset_unmapped_contents(); + } + this->type = type; if (animation) { @@ -160,12 +214,9 @@ struct animation_hook : public animation_hook_base on_set_output.disconnect(); this->animation.reset(); - // remove from list - if (type == ANIMATION_TYPE_UNMAP) - { - wf::scene::set_node_enabled(view->get_root_node(), false); - view->unref(); - } + unset_unmapped_contents(); + wf::scene::set_node_enabled(view->get_root_node(), false); + view->unref(); } animation_hook(const animation_hook &) = delete; @@ -309,13 +360,6 @@ class wayfire_animation : public wf::plugin_interface_t, private wf::per_output_ { name = "animation-hook-" + name; - if (type == ANIMATION_TYPE_UNMAP) - { - wf::scene::set_node_enabled(view->get_root_node(), true); - view->take_ref(); - view->take_snapshot(); - } - if (type == ANIMATION_TYPE_MAP) { if (try_reverse(view, type, name, SHOWN)) @@ -399,12 +443,14 @@ class wayfire_animation : public wf::plugin_interface_t, private wf::per_output_ { if (ev->state) { - ev->carried_out = true; set_animation(ev->view, ANIMATION_TYPE_MINIMIZE, default_duration, "minimize"); } else { set_animation(ev->view, ANIMATION_TYPE_RESTORE, default_duration, "minimize"); } + + // ev->carried_out should remain false, so that core also does the automatic minimize/restore and + // refocus }; wf::signal::connection_t on_render_start = diff --git a/plugins/animate/basic_animations.hpp b/plugins/animate/basic_animations.hpp index 19f02f4ef..0905ab7a3 100644 --- a/plugins/animate/basic_animations.hpp +++ b/plugins/animate/basic_animations.hpp @@ -1,4 +1,5 @@ #include "animate.hpp" +#include "wayfire/toplevel-view.hpp" #include #include #include @@ -96,13 +97,15 @@ class zoom_animation : public animation_base if (type & MINIMIZE_STATE_ANIMATION) { - auto hint = view->get_minimize_hint(); + auto toplevel = wf::toplevel_cast(view); + wf::dassert(toplevel != nullptr, "We cannot minimize non-toplevel views!"); + auto hint = toplevel->get_minimize_hint(); if ((hint.width > 0) && (hint.height > 0)) { int hint_cx = hint.x + hint.width / 2; int hint_cy = hint.y + hint.height / 2; - auto bbox = view->get_wm_geometry(); + auto bbox = toplevel->get_geometry(); int view_cx = bbox.x + bbox.width / 2; int view_cy = bbox.y + bbox.height / 2; diff --git a/plugins/animate/unmapped-view-node.hpp b/plugins/animate/unmapped-view-node.hpp new file mode 100644 index 000000000..64bd1fa07 --- /dev/null +++ b/plugins/animate/unmapped-view-node.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "wayfire/geometry.hpp" +#include "wayfire/opengl.hpp" +#include "wayfire/region.hpp" +#include "wayfire/scene-render.hpp" +#include "wayfire/scene.hpp" +#include + +namespace wf +{ +class unmapped_view_snapshot_node : public wf::scene::node_t +{ + wf::render_target_t snapshot; + wf::geometry_t bbox; + + public: + unmapped_view_snapshot_node(wayfire_view view) : node_t(false) + { + view->take_snapshot(snapshot); + bbox = view->get_surface_root_node()->get_bounding_box(); + } + + ~unmapped_view_snapshot_node() + { + OpenGL::render_begin(); + snapshot.release(); + OpenGL::render_end(); + } + + wf::geometry_t get_bounding_box() override + { + return bbox; + } + + void gen_render_instances(std::vector& instances, + scene::damage_callback push_damage, wf::output_t *shown_on) override + { + instances.push_back(std::make_unique(this, push_damage, shown_on)); + } + + private: + class rinstance_t : public wf::scene::simple_render_instance_t + { + public: + using simple_render_instance_t::simple_render_instance_t; + void render(const wf::render_target_t& target, const wf::region_t& region) + { + OpenGL::render_begin(target); + for (auto& box : region) + { + target.logic_scissor(wlr_box_from_pixman_box(box)); + OpenGL::render_texture(self->snapshot.tex, target, self->get_bounding_box()); + } + + OpenGL::render_end(); + } + }; +}; +} diff --git a/plugins/blur/blur.cpp b/plugins/blur/blur.cpp index 8214dd8fa..b2a33330f 100644 --- a/plugins/blur/blur.cpp +++ b/plugins/blur/blur.cpp @@ -80,9 +80,9 @@ class blur_render_instance_t : public transformer_render_instance_t { if (self->get_children().size() == 1) { - if (auto vnode = dynamic_cast(self->get_children().front().get())) + if (auto opaque = dynamic_cast(self->get_children().front().get())) { - return (damage ^ vnode->get_opaque_region()).empty(); + return (damage ^ opaque->get_opaque_region()).empty(); } } @@ -93,11 +93,11 @@ class blur_render_instance_t : public transformer_render_instance_t { if (self->get_children().size() == 1) { - if (auto vnode = dynamic_cast(self->get_children().front().get())) + if (auto opaque = dynamic_cast(self->get_children().front().get())) { const int padding = calculate_damage_padding(target, self->provider()->calculate_blur_radius()); - auto opaque_region = vnode->get_opaque_region(); + auto opaque_region = opaque->get_opaque_region(); opaque_region.expand_edges(-padding); wf::region_t translucent_region = damage ^ opaque_region; diff --git a/plugins/common/wayfire/plugins/common/move-drag-interface.hpp b/plugins/common/wayfire/plugins/common/move-drag-interface.hpp index 6c4987018..7d3ca19ee 100644 --- a/plugins/common/wayfire/plugins/common/move-drag-interface.hpp +++ b/plugins/common/wayfire/plugins/common/move-drag-interface.hpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace wf @@ -94,7 +95,7 @@ struct drag_done_signal struct view_t { /** Dragged view. */ - wayfire_view view; + wayfire_toplevel_view view; /** * The position relative to the view where the grab was. @@ -107,7 +108,7 @@ struct drag_done_signal std::vector all_views; /** The main view which was dragged. */ - wayfire_view main_view; + wayfire_toplevel_view main_view; /** * The position of the input when the view was dropped. @@ -256,7 +257,7 @@ static const std::string move_drag_transformer = "move-drag-transformer"; struct dragged_view_t { // The view being dragged - wayfire_view view; + wayfire_toplevel_view view; // Its transformer std::shared_ptr transformer; @@ -267,7 +268,7 @@ struct dragged_view_t wf::geometry_t last_bbox; }; -inline wayfire_view get_toplevel(wayfire_view view) +inline wayfire_toplevel_view get_toplevel(wayfire_toplevel_view view) { while (view->parent) { @@ -277,10 +278,10 @@ inline wayfire_view get_toplevel(wayfire_view view) return view; } -inline std::vector get_target_views(wayfire_view grabbed, +inline std::vector get_target_views(wayfire_toplevel_view grabbed, bool join_views) { - std::vector r = {grabbed}; + std::vector r = {grabbed}; if (join_views) { r = grabbed->enumerate_views(); @@ -419,7 +420,7 @@ class core_drag_t : public signal::provider_t * Rebuild the wobbly model after a change in the scaling, so that the wobbly * model does not try to animate the scaling change itself. */ - void rebuild_wobbly(wayfire_view view, wf::point_t grab, wf::pointf_t relative) + void rebuild_wobbly(wayfire_toplevel_view view, wf::point_t grab, wf::pointf_t relative) { auto dim = wf::dimensions(wf::view_bounding_box_up_to(view, "wobbly")); modify_wobbly(view, find_geometry_around(dim, grab, relative)); @@ -433,7 +434,7 @@ class core_drag_t : public signal::provider_t * @param grab_position The position of the input, in output-layout coordinates. * @param relative The position of the grab_position relative to view. */ - void start_drag(wayfire_view grab_view, wf::point_t grab_position, + void start_drag(wayfire_toplevel_view grab_view, wf::point_t grab_position, wf::pointf_t relative, const drag_options_t& options) { @@ -501,8 +502,7 @@ class core_drag_t : public signal::provider_t } } - void start_drag(wayfire_view view, wf::point_t grab_position, - const drag_options_t& options) + void start_drag(wayfire_toplevel_view view, wf::point_t grab_position, const drag_options_t& options) { if (options.join_views) { @@ -511,8 +511,7 @@ class core_drag_t : public signal::provider_t auto bbox = view->get_transformed_node()->get_bounding_box() + wf::origin(view->get_output()->get_layout_geometry()); - start_drag(view, grab_position, - find_relative_grab(bbox, grab_position), options); + start_drag(view, grab_position, find_relative_grab(bbox, grab_position), options); } void handle_motion(wf::point_t to) @@ -631,7 +630,7 @@ class core_drag_t : public signal::provider_t } // View currently being moved. - wayfire_view view; + wayfire_toplevel_view view; // Output where the action is happening. wf::output_t *current_output = NULL; @@ -744,7 +743,7 @@ inline void adjust_view_on_output(drag_done_signal *ev) } auto bbox = wf::view_bounding_box_up_to(v.view, "wobbly"); - auto wm = v.view->get_wm_geometry(); + auto wm = v.view->get_geometry(); wf::point_t wm_offset = wf::origin(wm) + -wf::origin(bbox); bbox = wf::move_drag::find_geometry_around( @@ -752,12 +751,12 @@ inline void adjust_view_on_output(drag_done_signal *ev) wf::point_t target = wf::origin(bbox) + wm_offset; v.view->move(target.x, target.y); - if (v.view->fullscreen) + if (v.view->pending_fullscreen()) { - v.view->fullscreen_request(ev->focused_output, true, target_ws); - } else if (v.view->tiled_edges) + wf::get_core().default_wm->fullscreen_request(v.view, ev->focused_output, true, target_ws); + } else if (v.view->pending_tiled_edges()) { - v.view->tile_request(v.view->tiled_edges, target_ws); + wf::get_core().default_wm->tile_request(v.view, v.view->pending_tiled_edges(), target_ws); } // check focus timestamp and select the last focused view to (re)focus @@ -779,11 +778,11 @@ inline void adjust_view_on_output(drag_done_signal *ev) /** * Adjust the view's state after snap-off. */ -inline void adjust_view_on_snap_off(wayfire_view view) +inline void adjust_view_on_snap_off(wayfire_toplevel_view view) { - if (view->tiled_edges && !view->fullscreen) + if (view->pending_tiled_edges() && !view->pending_fullscreen()) { - view->tile_request(0); + wf::get_core().default_wm->tile_request(view, 0); } } } diff --git a/plugins/decor/deco-subsurface.cpp b/plugins/decor/deco-subsurface.cpp index b1a834a91..14a1a92d3 100644 --- a/plugins/decor/deco-subsurface.cpp +++ b/plugins/decor/deco-subsurface.cpp @@ -4,6 +4,7 @@ #include "wayfire/scene-render.hpp" #include "wayfire/scene.hpp" #include "wayfire/signal-provider.hpp" +#include "wayfire/toplevel.hpp" #include #define GLM_FORCE_RADIANS #include @@ -14,12 +15,13 @@ #include #include #include -#include #include #include +#include #include "deco-subsurface.hpp" #include "deco-layout.hpp" #include "deco-theme.hpp" +#include #include @@ -28,7 +30,7 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_interaction_t, public wf::touch_interaction_t { - wayfire_view view; + wayfire_toplevel_view view; wf::signal::connection_t title_set = [=] (wf::view_title_changed_signal *ev) { @@ -58,17 +60,17 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in std::string current_text = ""; } title_texture; + public: wf::decor::decoration_theme_t theme; wf::decor::decoration_layout_t layout; wf::region_t cached_region; wf::dimensions_t size; - public: int current_thickness; int current_titlebar; - simple_decoration_node_t(wayfire_view view) : + simple_decoration_node_t(wayfire_toplevel_view view) : node_t(false), theme{}, layout{theme, [=] (wlr_box box) { wf::scene::damage_node(shared_from_this(), box + get_offset()); }} @@ -194,9 +196,9 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in wf::geometry_t get_bounding_box() override { - if (view->fullscreen) + if (view->pending_fullscreen()) { - return view->get_wm_geometry(); + return view->get_geometry(); } else { return wf::construct_box(get_offset(), size); @@ -236,27 +238,27 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in switch (action.action) { case wf::decor::DECORATION_ACTION_MOVE: - return view->move_request(); + return wf::get_core().default_wm->move_request(view); case wf::decor::DECORATION_ACTION_RESIZE: - return view->resize_request(action.edges); + return wf::get_core().default_wm->resize_request(view, action.edges); case wf::decor::DECORATION_ACTION_CLOSE: return view->close(); case wf::decor::DECORATION_ACTION_TOGGLE_MAXIMIZE: - if (view->tiled_edges) + if (view->pending_tiled_edges()) { - view->tile_request(0); + return wf::get_core().default_wm->tile_request(view, 0); } else { - view->tile_request(wf::TILED_EDGES_ALL); + return wf::get_core().default_wm->tile_request(view, wf::TILED_EDGES_ALL); } break; case wf::decor::DECORATION_ACTION_MINIMIZE: - view->minimize_request(true); + return wf::get_core().default_wm->minimize_request(view, true); break; default: @@ -287,7 +289,7 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in view->damage(); size = dims; layout.resize(size.width, size.height); - if (!view->fullscreen) + if (!view->toplevel()->current().fullscreen) { this->cached_region = layout.calculate_region(); } @@ -297,7 +299,7 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in void update_decoration_size() { - if (view->fullscreen) + if (view->toplevel()->current().fullscreen) { current_thickness = 0; current_titlebar = 0; @@ -312,66 +314,55 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in } }; -class simple_decorator_t : public wf::decorator_frame_t_t +wf::simple_decorator_t::simple_decorator_t(wayfire_toplevel_view view) { - wayfire_view view; - std::shared_ptr deco; + this->view = view; + deco = std::make_shared(view); + deco->resize(wf::dimensions(view->get_pending_geometry())); + wf::scene::add_back(view->get_surface_root_node(), deco); + + view->connect(&on_view_activated); + view->connect(&on_view_geometry_changed); + view->connect(&on_view_fullscreen); - wf::signal::connection_t on_view_activated = [&] (auto) + on_view_activated = [this] (auto) { - view->damage(); + wf::scene::damage_node(deco, deco->get_bounding_box()); }; - wf::signal::connection_t on_view_geometry_changed = [&] (auto) + on_view_geometry_changed = [this] (auto) { - deco->resize(wf::dimensions(view->get_wm_geometry())); + deco->resize(wf::dimensions(this->view->get_geometry())); }; - wf::signal::connection_t on_view_fullscreen = [&] (auto) + on_view_fullscreen = [this] (auto) { deco->update_decoration_size(); - if (!view->fullscreen) + if (!this->view->toplevel()->current().fullscreen) { - deco->resize(wf::dimensions(view->get_wm_geometry())); + deco->resize(wf::dimensions(this->view->get_geometry())); } }; +} - public: - simple_decorator_t(wayfire_view view) - { - this->view = view; - deco = std::make_shared(view); - wf::scene::add_back(view->get_surface_root_node(), deco); - - view->connect(&on_view_activated); - view->connect(&on_view_geometry_changed); - view->connect(&on_view_fullscreen); - } - - ~simple_decorator_t() - { - wf::scene::remove_child(deco); - } - - /* frame implementation */ - virtual wf::decoration_margins_t get_margins() override - { - return wf::decoration_margins_t{ - .left = deco->current_thickness, - .right = deco->current_thickness, - .bottom = deco->current_thickness, - .top = deco->current_titlebar, - }; - } -}; - -void init_view(wayfire_view view) +wf::simple_decorator_t::~simple_decorator_t() { - auto decor = std::make_unique(view); - view->set_decoration(std::move(decor)); + wf::scene::remove_child(deco); } -void deinit_view(wayfire_view view) +wf::decoration_margins_t wf::simple_decorator_t::get_margins(const wf::toplevel_state_t& state) { - view->set_decoration(nullptr); + if (state.fullscreen) + { + return {0, 0, 0, 0}; + } + + const int thickness = deco->theme.get_border_size(); + const int titlebar = deco->theme.get_title_height() + deco->theme.get_border_size(); + return wf::decoration_margins_t{ + .left = thickness, + .right = thickness, + .bottom = thickness, + .top = titlebar, + }; } diff --git a/plugins/decor/deco-subsurface.hpp b/plugins/decor/deco-subsurface.hpp index 091c545ca..6c9636d4d 100644 --- a/plugins/decor/deco-subsurface.hpp +++ b/plugins/decor/deco-subsurface.hpp @@ -1,9 +1,31 @@ #ifndef DECO_SUBSURFACE_HPP #define DECO_SUBSURFACE_HPP -#include +#include "wayfire/object.hpp" +#include "wayfire/toplevel.hpp" +#include +#include -void init_view(wayfire_view view); -void deinit_view(wayfire_view view); +class simple_decoration_node_t; +namespace wf +{ +/** + * A decorator object attached as custom data to a toplevel object. + */ +class simple_decorator_t : public wf::custom_data_t +{ + wayfire_toplevel_view view; + std::shared_ptr deco; + + wf::signal::connection_t on_view_activated; + wf::signal::connection_t on_view_geometry_changed; + wf::signal::connection_t on_view_fullscreen; + + public: + simple_decorator_t(wayfire_toplevel_view view); + ~simple_decorator_t(); + wf::decoration_margins_t get_margins(const wf::toplevel_state_t& state); +}; +} #endif /* end of include guard: DECO_SUBSURFACE_HPP */ diff --git a/plugins/decor/decoration.cpp b/plugins/decor/decoration.cpp index 885b29106..1f568f9d2 100644 --- a/plugins/decor/decoration.cpp +++ b/plugins/decor/decoration.cpp @@ -4,19 +4,51 @@ #include #include #include +#include #include "deco-subsurface.hpp" #include "wayfire/core.hpp" #include "wayfire/plugin.hpp" #include "wayfire/signal-provider.hpp" +#include "wayfire/toplevel-view.hpp" +#include "wayfire/toplevel.hpp" class wayfire_decoration : public wf::plugin_interface_t { wf::view_matcher_t ignore_views{"decoration/ignore_views"}; - wf::signal::connection_t on_view_mapped = [=] (wf::view_mapped_signal *ev) + wf::signal::connection_t on_new_tx = + [=] (wf::txn::new_transaction_signal *ev) { - update_view_decoration(ev->view); + // For each transaction, we need to consider what happens with participating views + for (const auto& obj : ev->tx->get_objects()) + { + if (auto toplevel = std::dynamic_pointer_cast(obj)) + { + // First check whether the toplevel already has decoration + // In that case, we should just set the correct margins + if (auto deco = toplevel->get_data()) + { + toplevel->pending().margins = deco->get_margins(toplevel->pending()); + continue; + } + + // Second case: the view is already mapped, or the transaction does not map it. + // The view is not being decorated, so nothing to do here. + if (toplevel->current().mapped || !toplevel->pending().mapped) + { + continue; + } + + // Third case: the transaction will map the toplevel. + auto view = wf::find_view_for_toplevel(toplevel); + wf::dassert(view != nullptr, "Mapping a toplevel means there must be a corresponding view!"); + if (should_decorate_view(view)) + { + adjust_new_decorations(view); + } + } + } }; wf::signal::connection_t on_decoration_state_changed = @@ -29,7 +61,7 @@ class wayfire_decoration : public wf::plugin_interface_t void init() override { wf::get_core().connect(&on_decoration_state_changed); - wf::get_core().connect(&on_view_mapped); + wf::get_core().tx_manager->connect(&on_new_tx); for (auto& view : wf::get_core().get_all_views()) { @@ -41,7 +73,11 @@ class wayfire_decoration : public wf::plugin_interface_t { for (auto view : wf::get_core().get_all_views()) { - deinit_view(view); + if (auto toplevel = wf::toplevel_cast(view)) + { + remove_decoration(toplevel); + wf::get_core().tx_manager->schedule_object(toplevel->toplevel()); + } } } @@ -57,14 +93,51 @@ class wayfire_decoration : public wf::plugin_interface_t return ignore_views.matches(view); } - void update_view_decoration(wayfire_view view) + bool should_decorate_view(wayfire_toplevel_view view) { - if (view->should_be_decorated() && !ignore_decoration_of_view(view)) + return view->should_be_decorated() && !ignore_decoration_of_view(view); + } + + void adjust_new_decorations(wayfire_toplevel_view view) + { + auto toplevel = view->toplevel(); + + toplevel->store_data(std::make_unique(view)); + auto deco = toplevel->get_data(); + auto& pending = toplevel->pending(); + pending.margins = deco->get_margins(pending); + + if (!pending.fullscreen && !pending.tiled_edges) { - init_view(view); - } else + pending.geometry = wf::expand_geometry_by_margins(pending.geometry, pending.margins); + } + } + + void remove_decoration(wayfire_toplevel_view view) + { + view->toplevel()->erase_data(); + auto& pending = view->toplevel()->pending(); + if (!pending.fullscreen && !pending.tiled_edges) { - deinit_view(view); + pending.geometry = wf::shrink_geometry_by_margins(pending.geometry, pending.margins); + } + + pending.margins = {0, 0, 0, 0}; + } + + void update_view_decoration(wayfire_view view) + { + if (auto toplevel = wf::toplevel_cast(view)) + { + if (should_decorate_view(toplevel)) + { + adjust_new_decorations(toplevel); + } else + { + remove_decoration(toplevel); + } + + wf::get_core().tx_manager->schedule_object(toplevel->toplevel()); } } }; diff --git a/plugins/grid/grid.cpp b/plugins/grid/grid.cpp index 5bf1a8f58..97be59789 100644 --- a/plugins/grid/grid.cpp +++ b/plugins/grid/grid.cpp @@ -13,6 +13,7 @@ #include #include "wayfire/plugins/grid.hpp" #include "wayfire/plugins/crossfade.hpp" +#include #include #include @@ -25,7 +26,7 @@ class wf_grid_slot_data : public wf::custom_data_t int slot; }; -nonstd::observer_ptr ensure_grid_view(wayfire_view view) +nonstd::observer_ptr ensure_grid_view(wayfire_toplevel_view view) { if (!view->has_data()) { @@ -117,14 +118,13 @@ class wayfire_grid : public wf::per_output_plugin_instance_t return false; } - auto view = output->get_active_view(); - if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL)) + auto view = toplevel_cast(output->get_active_view()); + if (!view) { return false; } - view->tile_request(0); - + wf::get_core().default_wm->tile_request(view, 0); return true; }; @@ -136,7 +136,7 @@ class wayfire_grid : public wf::per_output_plugin_instance_t keys[i].load_option("grid/slot_" + slots[i]); bindings[i] = [=] (auto) { - auto view = output->get_active_view(); + auto view = toplevel_cast(output->get_active_view()); if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL)) { return false; @@ -163,13 +163,13 @@ class wayfire_grid : public wf::per_output_plugin_instance_t output->connect(&on_fullscreen_signal); } - bool can_adjust_view(wayfire_view view) + bool can_adjust_view(wayfire_toplevel_view view) { uint32_t req_actions = wf::VIEW_ALLOW_MOVE | wf::VIEW_ALLOW_RESIZE; return (view->get_allowed_actions() & req_actions) == req_actions; } - void handle_slot(wayfire_view view, int slot, wf::point_t delta = {0, 0}) + void handle_slot(wayfire_toplevel_view view, int slot, wf::point_t delta = {0, 0}) { if (!can_adjust_view(view)) { @@ -222,8 +222,8 @@ class wayfire_grid : public wf::per_output_plugin_instance_t auto data = view->get_data_safe(); /* Detect if the view was maximized outside of the grid plugin */ - auto wm = view->get_wm_geometry(); - if (view->tiled_edges && (wm.width == ev->old_workarea.width) && + auto wm = view->get_pending_geometry(); + if (view->pending_tiled_edges() && (wm.width == ev->old_workarea.width) && (wm.height == ev->old_workarea.height)) { data->slot = wf::grid::SLOT_CENTER; diff --git a/plugins/grid/wayfire/plugins/crossfade.hpp b/plugins/grid/wayfire/plugins/crossfade.hpp index 61b5cba68..4deaafe8f 100644 --- a/plugins/grid/wayfire/plugins/crossfade.hpp +++ b/plugins/grid/wayfire/plugins/crossfade.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "wayfire/core.hpp" #include "wayfire/geometry.hpp" #include "wayfire/opengl.hpp" #include "wayfire/region.hpp" @@ -10,11 +11,15 @@ #include "wayfire/signal-provider.hpp" #include #include +#include #include #include #include #include #include +#include +#include +#include namespace wf { @@ -37,15 +42,15 @@ class crossfade_node_t : public scene::view_2d_transformer_t wf::geometry_t displayed_geometry; double overlay_alpha; - crossfade_node_t(wayfire_view view) : view_2d_transformer_t(view) + crossfade_node_t(wayfire_toplevel_view view) : view_2d_transformer_t(view) { - displayed_geometry = view->get_wm_geometry(); + displayed_geometry = view->get_geometry(); this->view = view; auto root_node = view->get_surface_root_node(); const wf::geometry_t bbox = root_node->get_bounding_box(); - original_buffer.geometry = view->get_wm_geometry(); + original_buffer.geometry = view->get_geometry(); original_buffer.scale = view->get_output()->handle->scale; OpenGL::render_begin(); @@ -173,7 +178,7 @@ class grid_animation_t : public wf::custom_data_t * @param type Indicates which animation method to use. * @param duration Indicates the duration of the animation (only for crossfade) */ - grid_animation_t(wayfire_view view, type_t type, + grid_animation_t(wayfire_toplevel_view view, type_t type, wf::option_sptr_t duration) { this->view = view; @@ -194,18 +199,20 @@ class grid_animation_t : public wf::custom_data_t * animation. If target_edges are -1, then the tiled edges of the view will * not be changed. */ - void adjust_target_geometry(wf::geometry_t geometry, int32_t target_edges) + void adjust_target_geometry(wf::geometry_t geometry, int32_t target_edges, wf::txn::transaction_uptr& tx) { // Apply the desired attributes to the view - const auto& set_state = [=] () + const auto& set_state = [&] () { if (target_edges >= 0) { - view->set_fullscreen(false); - view->set_tiled(target_edges); + wf::get_core().default_wm->update_last_windowed_geometry(view); + view->toplevel()->pending().fullscreen = false; + view->toplevel()->pending().tiled_edges = target_edges; } - view->set_geometry(geometry); + view->toplevel()->pending().geometry = geometry; + tx->add_object(view->toplevel()); }; if (type != CROSSFADE) @@ -223,7 +230,7 @@ class grid_animation_t : public wf::custom_data_t } // Crossfade animation - original = view->get_wm_geometry(); + original = view->get_geometry(); animation.set_start(original); animation.set_end(geometry); animation.start(); @@ -236,6 +243,13 @@ class grid_animation_t : public wf::custom_data_t set_state(); } + void adjust_target_geometry(wf::geometry_t geometry, int32_t target_edges) + { + auto tx = wf::txn::transaction_t::create(); + adjust_target_geometry(geometry, target_edges, tx); + wf::get_core().tx_manager->schedule_transaction(std::move(tx)); + } + ~grid_animation_t() { view->get_transformed_node()->rem_transformer(); @@ -255,9 +269,9 @@ class grid_animation_t : public wf::custom_data_t return destroy(); } - if (view->get_wm_geometry() != original) + if (view->get_geometry() != original) { - original = view->get_wm_geometry(); + original = view->get_geometry(); animation.set_end(original); } @@ -265,7 +279,7 @@ class grid_animation_t : public wf::custom_data_t view->damage(); tr->displayed_geometry = animation; - auto geometry = view->get_wm_geometry(); + auto geometry = view->get_geometry(); tr->scale_x = animation.width / geometry.width; tr->scale_y = animation.height / geometry.height; @@ -284,7 +298,7 @@ class grid_animation_t : public wf::custom_data_t } wf::geometry_t original; - wayfire_view view; + wayfire_toplevel_view view; wf::output_t *output; wf::signal::connection_t on_disappear = [=] (view_disappeared_signal *ev) { diff --git a/plugins/grid/wayfire/plugins/grid.hpp b/plugins/grid/wayfire/plugins/grid.hpp index c85aac886..55ccc7f74 100644 --- a/plugins/grid/wayfire/plugins/grid.hpp +++ b/plugins/grid/wayfire/plugins/grid.hpp @@ -47,7 +47,7 @@ struct grid_query_geometry_signal */ struct grid_snap_view_signal { - wayfire_view view; + wayfire_toplevel_view view; slot_t slot; }; } diff --git a/plugins/ipc/demo-ipc.cpp b/plugins/ipc/demo-ipc.cpp index e32c0aa92..35037bd0c 100644 --- a/plugins/ipc/demo-ipc.cpp +++ b/plugins/ipc/demo-ipc.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "ipc-helpers.hpp" @@ -81,8 +82,13 @@ class wayfire_demo_ipc : public wf::plugin_interface_t { if (auto geometry = wf::ipc::geometry_from_json(data["geometry"])) { - view->set_geometry(geometry.value()); - return wf::ipc::json_ok(); + if (auto toplevel = toplevel_cast(view)) + { + toplevel->set_geometry(geometry.value()); + return wf::ipc::json_ok(); + } + + return wf::ipc::json_error("view is not toplevel"); } return wf::ipc::json_error("geometry incorrect"); @@ -119,8 +125,10 @@ class wayfire_demo_ipc : public wf::plugin_interface_t description["id"] = view->get_id(); description["app-id"] = view->get_app_id(); description["title"] = view->get_title(); - description["geometry"] = wf::ipc::geometry_to_json(view->get_wm_geometry()); - description["output"] = view->get_output() ? view->get_output()->get_id() : -1; + auto toplevel = wf::toplevel_cast(view); + description["geometry"] = + wf::ipc::geometry_to_json(toplevel ? toplevel->get_geometry() : view->get_bounding_box()); + description["output"] = view->get_output() ? view->get_output()->get_id() : -1; return description; } }; diff --git a/plugins/ipc/stipc.cpp b/plugins/ipc/stipc.cpp index 6ca86fdbb..3c1784c15 100644 --- a/plugins/ipc/stipc.cpp +++ b/plugins/ipc/stipc.cpp @@ -1,6 +1,8 @@ #include "ipc-method-repository.hpp" #include "wayfire/plugin.hpp" #include "wayfire/plugins/common/shared-core-data.hpp" +#include "wayfire/toplevel-view.hpp" +#include "wayfire/unstable/wlr-surface-node.hpp" #include "wayfire/util.hpp" #include "wayfire/view-helpers.hpp" #include @@ -349,6 +351,24 @@ class stipc_plugin_t : public wf::plugin_interface_t return false; } + static wf::geometry_t get_view_base_geometry(wayfire_view view) + { + auto sroot = view->get_surface_root_node(); + for (auto& ch : sroot->get_children()) + { + if (auto wlr_surf = dynamic_cast(ch.get())) + { + auto bbox = wlr_surf->get_bounding_box(); + wf::pointf_t origin = sroot->to_global({0, 0}); + bbox.x = origin.x; + bbox.y = origin.y; + return bbox; + } + } + + return sroot->get_bounding_box(); + } + ipc::method_callback list_views = [] (nlohmann::json) { auto response = nlohmann::json::array(); @@ -359,16 +379,21 @@ class stipc_plugin_t : public wf::plugin_interface_t v["id"] = view->get_id(); v["title"] = view->get_title(); v["app-id"] = view->get_app_id(); - v["geometry"] = wf::ipc::geometry_to_json(view->get_wm_geometry()); - v["base-geometry"] = wf::ipc::geometry_to_json(view->get_output_geometry()); - v["state"] = { - {"tiled", view->tiled_edges}, - {"fullscreen", view->fullscreen}, - {"minimized", view->minimized}, - }; + v["base-geometry"] = wf::ipc::geometry_to_json(get_view_base_geometry(view)); + v["state"] = {}; - v["layer"] = layer_to_string(get_view_layer(view)); + if (auto toplevel = toplevel_cast(view)) + { + v["geometry"] = wf::ipc::geometry_to_json(toplevel->get_geometry()); + v["state"]["tiled"] = toplevel->pending_tiled_edges(); + v["state"]["fullscreen"] = toplevel->pending_fullscreen(); + v["state"]["minimized"] = toplevel->minimized; + } else + { + v["geometry"] = wf::ipc::geometry_to_json(view->get_bounding_box()); + } + v["layer"] = layer_to_string(get_view_layer(view)); response.push_back(v); } @@ -398,6 +423,13 @@ class stipc_plugin_t : public wf::plugin_interface_t std::to_string((int)v["id"])); } + auto toplevel = toplevel_cast(*it); + if (!toplevel) + { + return wf::ipc::json_error("View is not toplevel view id " + + std::to_string((int)v["id"])); + } + if (v.contains("output")) { WFJSON_EXPECT_FIELD(v, "output", string); @@ -407,11 +439,11 @@ class stipc_plugin_t : public wf::plugin_interface_t return wf::ipc::json_error("Unknown output " + (std::string)v["output"]); } - move_view_to_output(*it, wo, false); + move_view_to_output(toplevel, wo, false); } wf::geometry_t g{v["x"], v["y"], v["width"], v["height"]}; - (*it)->set_geometry(g); + toplevel->set_geometry(g); } return wf::ipc::json_ok(); diff --git a/plugins/protocols/foreign-toplevel.cpp b/plugins/protocols/foreign-toplevel.cpp index 13bbd5ad8..d010e6e36 100644 --- a/plugins/protocols/foreign-toplevel.cpp +++ b/plugins/protocols/foreign-toplevel.cpp @@ -5,20 +5,22 @@ #include #include #include +#include +#include #include "gtk-shell.hpp" #include "config.h" class wayfire_foreign_toplevel; -using foreign_toplevel_map_type = std::map>; +using foreign_toplevel_map_type = std::map>; class wayfire_foreign_toplevel { - wayfire_view view; + wayfire_toplevel_view view; wlr_foreign_toplevel_handle_v1 *handle; foreign_toplevel_map_type *view_to_toplevel; public: - wayfire_foreign_toplevel(wayfire_view view, wlr_foreign_toplevel_handle_v1 *handle, + wayfire_foreign_toplevel(wayfire_toplevel_view view, wlr_foreign_toplevel_handle_v1 *handle, foreign_toplevel_map_type *view_to_toplevel) { this->view = view; @@ -96,10 +98,11 @@ class wayfire_foreign_toplevel void toplevel_send_state() { - wlr_foreign_toplevel_handle_v1_set_maximized(handle, view->tiled_edges == wf::TILED_EDGES_ALL); + wlr_foreign_toplevel_handle_v1_set_maximized(handle, + view->pending_tiled_edges() == wf::TILED_EDGES_ALL); wlr_foreign_toplevel_handle_v1_set_activated(handle, view->activated); wlr_foreign_toplevel_handle_v1_set_minimized(handle, view->minimized); - wlr_foreign_toplevel_handle_v1_set_fullscreen(handle, view->fullscreen); + wlr_foreign_toplevel_handle_v1_set_fullscreen(handle, view->pending_fullscreen()); /* update parent as well */ auto it = view_to_toplevel->find(view->parent); @@ -178,18 +181,18 @@ class wayfire_foreign_toplevel toplevel_handle_v1_maximize_request.set_callback([&] (void *data) { auto ev = static_cast(data); - view->tile_request(ev->maximized ? wf::TILED_EDGES_ALL : 0); + wf::get_core().default_wm->tile_request(view, ev->maximized ? wf::TILED_EDGES_ALL : 0); }); toplevel_handle_v1_minimize_request.set_callback([&] (void *data) { auto ev = static_cast(data); - view->minimize_request(ev->minimized); + wf::get_core().default_wm->minimize_request(view, ev->minimized); }); toplevel_handle_v1_activate_request.set_callback([&] (auto) { - view->focus_request(); + wf::get_core().default_wm->focus_request(view); }); toplevel_handle_v1_close_request.set_callback([&] (auto) @@ -216,12 +219,12 @@ class wayfire_foreign_toplevel { auto ev = static_cast(data); auto wo = wf::get_core().output_layout->find_output(ev->output); - view->fullscreen_request(wo, ev->fullscreen); + wf::get_core().default_wm->fullscreen_request(view, wo, ev->fullscreen); }); } - void handle_minimize_hint(wf::view_interface_t *view, wf::view_interface_t *relative_to, - const wlr_box& hint) + void handle_minimize_hint(wf::toplevel_view_interface_t *view, wf::view_interface_t *relative_to, + wlr_box hint) { if (relative_to->get_output() != view->get_output()) { @@ -229,7 +232,10 @@ class wayfire_foreign_toplevel /* TODO: translate coordinates in case minimize hint is on another output */ } - view->set_minimize_hint(hint + wf::origin(relative_to->get_output_geometry())); + wf::pointf_t relative = relative_to->get_surface_root_node()->to_global({0, 0}); + hint.x += relative.x; + hint.y += relative.y; + view->set_minimize_hint(hint); } }; @@ -254,21 +260,21 @@ class wayfire_foreign_toplevel_protocol_impl : public wf::plugin_interface_t private: wf::signal::connection_t on_view_mapped = [=] (wf::view_mapped_signal *ev) { - if (ev->view->role == wf::VIEW_ROLE_TOPLEVEL) + if (auto toplevel = wf::toplevel_cast(ev->view)) { auto handle = wlr_foreign_toplevel_handle_v1_create(toplevel_manager); - handle_for_view[ev->view] = - std::make_unique(ev->view, handle, &handle_for_view); + handle_for_view[toplevel] = + std::make_unique(toplevel, handle, &handle_for_view); } }; wf::signal::connection_t on_view_unmapped = [=] (wf::view_unmapped_signal *ev) { - handle_for_view.erase(ev->view); + handle_for_view.erase(toplevel_cast(ev->view)); }; wlr_foreign_toplevel_manager_v1 *toplevel_manager; - std::map> handle_for_view; + std::map> handle_for_view; }; DECLARE_WAYFIRE_PLUGIN(wayfire_foreign_toplevel_protocol_impl); diff --git a/plugins/protocols/gtk-shell.cpp b/plugins/protocols/gtk-shell.cpp index 410dcd67a..a1c1d3864 100644 --- a/plugins/protocols/gtk-shell.cpp +++ b/plugins/protocols/gtk-shell.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "gtk-shell.hpp" @@ -138,37 +139,37 @@ static void append_to_array(wl_array *array, uint32_t value) * Tells the client about the window state in more detail than xdg_surface. * This currently only includes which edges are tiled. */ -static void send_gtk_surface_configure(wf_gtk_surface *surface, wayfire_view view) +static void send_gtk_surface_configure(wf_gtk_surface *surface, wayfire_toplevel_view view) { int version = wl_resource_get_version(surface->resource); wl_array states; wl_array_init(&states); - if (view->tiled_edges) + if (view->pending_tiled_edges()) { append_to_array(&states, GTK_SURFACE1_STATE_TILED); } if ((version >= GTK_SURFACE1_STATE_TILED_TOP_SINCE_VERSION) && - (view->tiled_edges & WLR_EDGE_TOP)) + (view->pending_tiled_edges() & WLR_EDGE_TOP)) { append_to_array(&states, GTK_SURFACE1_STATE_TILED_TOP); } if ((version >= GTK_SURFACE1_STATE_TILED_RIGHT_SINCE_VERSION) && - (view->tiled_edges & WLR_EDGE_RIGHT)) + (view->pending_tiled_edges() & WLR_EDGE_RIGHT)) { append_to_array(&states, GTK_SURFACE1_STATE_TILED_RIGHT); } if ((version >= GTK_SURFACE1_STATE_TILED_BOTTOM_SINCE_VERSION) && - (view->tiled_edges & WLR_EDGE_BOTTOM)) + (view->pending_tiled_edges() & WLR_EDGE_BOTTOM)) { append_to_array(&states, GTK_SURFACE1_STATE_TILED_BOTTOM); } if ((version >= GTK_SURFACE1_STATE_TILED_LEFT_SINCE_VERSION) && - (view->tiled_edges & WLR_EDGE_LEFT)) + (view->pending_tiled_edges() & WLR_EDGE_LEFT)) { append_to_array(&states, GTK_SURFACE1_STATE_TILED_LEFT); } @@ -180,13 +181,12 @@ static void send_gtk_surface_configure(wf_gtk_surface *surface, wayfire_view vie /** * Tells gtk which edges should be resizable. */ -static void send_gtk_surface_configure_edges(wf_gtk_surface *surface, - wayfire_view view) +static void send_gtk_surface_configure_edges(wf_gtk_surface *surface, wayfire_toplevel_view view) { wl_array edges; wl_array_init(&edges); - if (!view->tiled_edges) + if (!view->pending_tiled_edges()) { append_to_array(&edges, GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_TOP); append_to_array(&edges, GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_RIGHT); @@ -203,7 +203,7 @@ static void send_gtk_surface_configure_edges(wf_gtk_surface *surface, */ static void handle_xdg_surface_on_configure(wf_gtk_surface *surface) { - wayfire_view view = wf::wl_surface_to_wayfire_view(surface->wl_surface); + wayfire_toplevel_view view = wf::toplevel_cast(wf::wl_surface_to_wayfire_view(surface->wl_surface)); if (view) { send_gtk_surface_configure(surface, view); diff --git a/plugins/protocols/wayfire-shell.cpp b/plugins/protocols/wayfire-shell.cpp index aea511d3a..3d47173d0 100644 --- a/plugins/protocols/wayfire-shell.cpp +++ b/plugins/protocols/wayfire-shell.cpp @@ -357,18 +357,9 @@ class wfs_surface } ~wfs_surface() = default; - - wfs_surface(const wfs_surface &) = delete; - wfs_surface(wfs_surface &&) = delete; - wfs_surface& operator =(const wfs_surface&) = delete; - wfs_surface& operator =(wfs_surface&&) = delete; - void interactive_move() { - if (view) - { - view->move_request(); - } + LOGE("Interactive move no longer supported!"); } }; diff --git a/plugins/scale/scale-title-filter.cpp b/plugins/scale/scale-title-filter.cpp index d052e6826..a78e27829 100644 --- a/plugins/scale/scale-title-filter.cpp +++ b/plugins/scale/scale-title-filter.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -155,7 +156,7 @@ class scale_title_filter : public wf::per_output_plugin_instance_t update_overlay(); } - scale_filter_views(ev, [this] (wayfire_view v) + scale_filter_views(ev, [this] (wayfire_toplevel_view v) { return !should_show_view(v); }); diff --git a/plugins/scale/scale-title-overlay.cpp b/plugins/scale/scale-title-overlay.cpp index cdb5d8edf..f4e36ccd9 100644 --- a/plugins/scale/scale-title-overlay.cpp +++ b/plugins/scale/scale-title-overlay.cpp @@ -1,5 +1,6 @@ #include "scale.hpp" #include "scale-title-overlay.hpp" +#include "wayfire/core.hpp" #include "wayfire/debug.hpp" #include "wayfire/geometry.hpp" #include "wayfire/output.hpp" @@ -21,7 +22,7 @@ /** * Get the topmost parent of a view. */ -static wayfire_view find_toplevel_parent(wayfire_view view) +static wayfire_toplevel_view find_toplevel_parent(wayfire_toplevel_view view) { while (view->parent) { @@ -36,11 +37,11 @@ static wayfire_view find_toplevel_parent(wayfire_view view) */ struct view_title_texture_t : public wf::custom_data_t { - wayfire_view view; + wayfire_toplevel_view view; wf::cairo_text_t overlay; wf::cairo_text_t::params par; bool overflow = false; - wayfire_view dialog; /* the texture should be rendered on top of this dialog */ + wayfire_toplevel_view dialog; /* the texture should be rendered on top of this dialog */ /** * Render the overlay text in our texture, cropping it to the size by @@ -67,7 +68,7 @@ struct view_title_texture_t : public wf::custom_data_t } }; - view_title_texture_t(wayfire_view v, int font_size, const wf::color_t& bg_color, + view_title_texture_t(wayfire_toplevel_view v, int font_size, const wf::color_t& bg_color, const wf::color_t& text_color, float output_scale) : view(v) { par.font_size = font_size; @@ -95,7 +96,7 @@ class title_overlay_node_t : public node_t }; /* save the transformed view, since we need it in the destructor */ - wayfire_view view; + wayfire_toplevel_view view; /* the position on the screen we currently render to */ wf::geometry_t geometry{0, 0, 0, 0}; scale_show_title_t& parent; @@ -109,7 +110,7 @@ class title_overlay_node_t : public node_t /** * Gets the overlay texture stored with the given view. */ - view_title_texture_t& get_overlay_texture(wayfire_view view) + view_title_texture_t& get_overlay_texture(wayfire_toplevel_view view) { auto data = view->get_data(); if (!data) @@ -124,14 +125,14 @@ class title_overlay_node_t : public node_t return *data.get(); } - wf::geometry_t get_scaled_bbox(wayfire_view v) + wf::geometry_t get_scaled_bbox(wayfire_toplevel_view v) { auto tr = v->get_transformed_node()-> get_transformer("scale"); if (tr) { - auto wm_geometry = v->get_wm_geometry(); + auto wm_geometry = v->get_geometry(); return get_bbox_for_node(tr, wm_geometry); } @@ -245,7 +246,7 @@ class title_overlay_node_t : public node_t public: title_overlay_node_t( - wayfire_view view_, position pos_, scale_show_title_t& parent_) : + wayfire_toplevel_view view_, position pos_, scale_show_title_t& parent_) : node_t(false), view(view_), parent(parent_), pos(pos_) { auto parent = find_toplevel_parent(view); @@ -491,9 +492,7 @@ void scale_show_title_t::update_title_overlay_opt() void scale_show_title_t::update_title_overlay_mouse() { - wayfire_view v = - scale_find_view_at(wf::get_core().get_cursor_position(), output); - + wayfire_toplevel_view v = scale_find_view_at(wf::get_core().get_cursor_position(), output); if (v) { v = find_toplevel_parent(v); diff --git a/plugins/scale/scale.cpp b/plugins/scale/scale.cpp index b1d43d145..fe1d95d56 100644 --- a/plugins/scale/scale.cpp +++ b/plugins/scale/scale.cpp @@ -32,6 +32,7 @@ #include "wayfire/scene-input.hpp" #include "wayfire/scene.hpp" #include "wayfire/signal-provider.hpp" +#include "wayfire/toplevel-view.hpp" #include "wayfire/view.hpp" using namespace wf::animation; @@ -97,12 +98,12 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, wf::point_t initial_workspace; bool active, hook_set; /* View that was active before scale began. */ - wayfire_view initial_focus_view; + wayfire_toplevel_view initial_focus_view; /* View that has active focus. */ - wayfire_view current_focus_view; + wayfire_toplevel_view current_focus_view; // View over which the last input press happened, might become dangling - wayfire_view last_selected_view; - std::map scale_data; + wayfire_toplevel_view last_selected_view; + std::map scale_data; wf::option_wrapper_t spacing{"scale/spacing"}; wf::option_wrapper_t middle_click_close{"scale/middle_click_close"}; wf::option_wrapper_t inactive_alpha{"scale/inactive_alpha"}; @@ -152,10 +153,8 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, void setup_workspace_switching() { - workspace_bindings = - std::make_unique(output); - workspace_bindings->setup([&] (wf::point_t delta, - wayfire_view view, bool only_view) + workspace_bindings = std::make_unique(output); + workspace_bindings->setup([&] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) { if (!output->is_plugin_active(grab_interface.name)) { @@ -177,7 +176,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, auto ws = output->wset()->get_current_workspace() + delta; // vswitch picks the top view, we want the focused one - std::vector fixed_views; + std::vector fixed_views; if (view && !all_workspaces) { fixed_views.push_back(current_focus_view); @@ -190,7 +189,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Add a transformer that will be used to scale the view */ - bool add_transformer(wayfire_view view) + bool add_transformer(wayfire_toplevel_view view) { if (view->get_transformed_node()->get_transformer("scale")) { @@ -216,7 +215,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Remove the scale transformer from the view */ - void pop_transformer(wayfire_view view) + void pop_transformer(wayfire_toplevel_view view) { /* signal that a transformer was added to this view */ scale_transformer_removed_signal data; @@ -342,7 +341,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /** Return the topmost parent */ - wayfire_view get_top_parent(wayfire_view view) + wayfire_toplevel_view get_top_parent(wayfire_toplevel_view view) { while (view && view->parent) { @@ -354,7 +353,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, /* Fade all views' alpha to inactive alpha except the * view argument */ - void fade_out_all_except(wayfire_view view) + void fade_out_all_except(wayfire_toplevel_view view) { for (auto& e : scale_data) { @@ -374,7 +373,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Fade in view alpha */ - void fade_in(wayfire_view view) + void fade_in(wayfire_toplevel_view view) { if (!view || !scale_data.count(view)) { @@ -391,7 +390,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Fade out view alpha */ - void fade_out(wayfire_view view) + void fade_out(wayfire_toplevel_view view) { if (!view) { @@ -413,7 +412,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Switch to the workspace for the untransformed view geometry */ - void select_view(wayfire_view view) + void select_view(wayfire_toplevel_view view) { if (!view) { @@ -425,11 +424,11 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Updates current and initial view focus variables accordingly */ - void check_focus_view(wayfire_view view) + void check_focus_view(wayfire_toplevel_view view) { if (view == current_focus_view) { - current_focus_view = output->get_active_view(); + current_focus_view = toplevel_cast(output->get_active_view()); } if (view == initial_focus_view) @@ -439,7 +438,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Remove transformer from view and remove view from the scale_data map */ - void remove_view(wayfire_view view) + void remove_view(wayfire_toplevel_view view) { if (!view) { @@ -552,7 +551,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Get the workspace for the center point of the untransformed view geometry */ - wf::point_t get_view_main_workspace(wayfire_view view) + wf::point_t get_view_main_workspace(wayfire_toplevel_view view) { while (view->parent) { @@ -561,7 +560,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, auto ws = output->wset()->get_current_workspace(); auto og = output->get_layout_geometry(); - auto vg = view->get_wm_geometry(); + auto vg = view->get_geometry(); auto center = wf::point_t{vg.x + vg.width / 2, vg.y + vg.height / 2}; return wf::point_t{ @@ -571,7 +570,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, /* Given row and column, return a view at this position in the scale grid, * or the first scaled view if none is found */ - wayfire_view find_view_in_grid(int row, int col) + wayfire_toplevel_view find_view_in_grid(int row, int col) { for (auto& view : scale_data) { @@ -593,7 +592,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, void handle_keyboard_key(wf::seat_t*, wlr_keyboard_key_event ev) override { - auto view = output->get_active_view(); + auto view = toplevel_cast(output->get_active_view()); if (!view) { view = current_focus_view; @@ -734,18 +733,18 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Returns a list of views for all workspaces */ - std::vector get_all_workspace_views() + std::vector get_all_workspace_views() { return output->wset()->get_views(wf::WSET_EXCLUDE_MINIMIZED | wf::WSET_MAPPED_ONLY); } /* Returns a list of views for the current workspace */ - std::vector get_current_workspace_views() + std::vector get_current_workspace_views() { - std::vector views; + std::vector views; for (auto& view : get_all_workspace_views()) { - auto vg = view->get_wm_geometry(); + auto vg = view->get_geometry(); auto og = output->get_relative_geometry(); wf::region_t wr{og}; wf::point_t center{vg.x + vg.width / 2, vg.y + vg.height / 2}; @@ -760,9 +759,9 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Returns a list of views to be scaled */ - std::vector get_views() + std::vector get_views() { - std::vector views; + std::vector views; if (all_workspaces) { @@ -778,7 +777,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, /** * @return true if the view is to be scaled. */ - bool should_scale_view(wayfire_view view) + bool should_scale_view(wayfire_toplevel_view view) { auto views = get_views(); @@ -809,28 +808,28 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, target_alpha); } - static bool view_compare_x(const wayfire_view& a, const wayfire_view& b) + static bool view_compare_x(const wayfire_toplevel_view& a, const wayfire_toplevel_view& b) { - auto vg_a = a->get_wm_geometry(); + auto vg_a = a->get_geometry(); std::vector a_coords = {vg_a.x, vg_a.width, vg_a.y, vg_a.height}; - auto vg_b = b->get_wm_geometry(); + auto vg_b = b->get_geometry(); std::vector b_coords = {vg_b.x, vg_b.width, vg_b.y, vg_b.height}; return a_coords < b_coords; } - static bool view_compare_y(const wayfire_view& a, const wayfire_view& b) + static bool view_compare_y(const wayfire_toplevel_view& a, const wayfire_toplevel_view& b) { - auto vg_a = a->get_wm_geometry(); + auto vg_a = a->get_geometry(); std::vector a_coords = {vg_a.y, vg_a.height, vg_a.x, vg_a.width}; - auto vg_b = b->get_wm_geometry(); + auto vg_b = b->get_geometry(); std::vector b_coords = {vg_b.y, vg_b.height, vg_b.x, vg_b.width}; return a_coords < b_coords; } - std::vector> view_sort( - std::vector& views) + std::vector> view_sort( + std::vector& views) { - std::vector> view_grid; + std::vector> view_grid; // First ensure a consistent sorting of all views using a persistent // identifier before sorting by geometry. // This is so that if two views have exactly the same geometry, @@ -856,9 +855,9 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } /* Filter the views to be arranged by layout_slots() */ - void filter_views(std::vector& views) + void filter_views(std::vector& views) { - std::vector filtered_views; + std::vector filtered_views; scale_filter_signal signal(views, filtered_views); output->emit(&signal); @@ -887,7 +886,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, if (!current_focus_view) { - std::sort(views.begin(), views.end(), [=] (wayfire_view a, wayfire_view b) + std::sort(views.begin(), views.end(), [=] (wayfire_toplevel_view a, wayfire_toplevel_view b) { return wf::get_focus_timestamp(a) > wf::get_focus_timestamp(b); }); @@ -900,7 +899,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, /* Compute target scale layout geometry for all the view transformers * and start animating. Initial code borrowed from the compiz scale * plugin algorithm */ - void layout_slots(std::vector views) + void layout_slots(std::vector views) { if (!views.size()) { @@ -970,7 +969,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, }; add_transformer(view); - auto geom = view->get_wm_geometry(); + auto geom = view->get_geometry(); double view_scale = calculate_scale({geom.width, geom.height}); for (auto& child : view->enumerate_views(false)) { @@ -1007,9 +1006,8 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, continue; } - auto vg = child->get_wm_geometry(); - wf::pointf_t center = {vg.x + vg.width / 2.0, - vg.y + vg.height / 2.0}; + auto vg = child->get_geometry(); + wf::pointf_t center = {vg.x + vg.width / 2.0, vg.y + vg.height / 2.0}; // Take padding into account double scale = calculate_scale({vg.width, vg.height}); @@ -1081,7 +1079,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, layout_slots(get_views()); }; - void handle_new_view(wayfire_view view) + void handle_new_view(wayfire_toplevel_view view) { if (!should_scale_view(view)) { @@ -1094,15 +1092,21 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, wf::signal::connection_t on_view_set_output = [=] (wf::view_set_output_signal *ev) { - handle_new_view(ev->view); + if (auto toplevel = wf::toplevel_cast(ev->view)) + { + handle_new_view(toplevel); + } }; wf::signal::connection_t on_view_mapped = [=] (wf::view_mapped_signal *ev) { - handle_new_view(ev->view); + if (auto toplevel = wf::toplevel_cast(ev->view)) + { + handle_new_view(toplevel); + } }; - void handle_view_disappeared(wayfire_view view) + void handle_view_disappeared(wayfire_toplevel_view view) { if (scale_data.count(get_top_parent(view)) != 0) { @@ -1123,7 +1127,10 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, wf::signal::connection_t view_disappeared = [=] (wf::view_disappeared_signal *ev) { - handle_view_disappeared(ev->view); + if (auto toplevel = toplevel_cast(ev->view)) + { + handle_view_disappeared(toplevel); + } }; /* Workspace changed */ @@ -1166,15 +1173,18 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, /* View unmapped */ wf::signal::connection_t view_unmapped = [=] (wf::view_unmapped_signal *ev) { - check_focus_view(ev->view); + if (auto toplevel = wf::toplevel_cast(ev->view)) + { + check_focus_view(toplevel); + } }; /* View focused. This handler makes sure our view remains focused */ wf::signal::connection_t view_focused = [=] (wf::focus_view_signal *ev) { - fade_out_all_except(ev->view); - fade_in(ev->view); - current_focus_view = ev->view; + fade_out_all_except(toplevel_cast(ev->view)); + fade_in(toplevel_cast(ev->view)); + current_focus_view = toplevel_cast(ev->view); }; /* Our own refocus that uses untransformed coordinates */ @@ -1188,7 +1198,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, return; } - wayfire_view next_focus = nullptr; + wayfire_toplevel_view next_focus = nullptr; auto views = get_current_workspace_views(); for (auto v : views) @@ -1307,7 +1317,7 @@ class wayfire_scale : public wf::per_output_plugin_instance_t, } initial_workspace = output->wset()->get_current_workspace(); - initial_focus_view = output->get_active_view(); + initial_focus_view = toplevel_cast(output->get_active_view()); current_focus_view = initial_focus_view ?: views.front(); // Make sure no leftover events from the activation binding // trigger an action in scale diff --git a/plugins/scale/scale.hpp b/plugins/scale/scale.hpp index 85e36b541..ef985c251 100644 --- a/plugins/scale/scale.hpp +++ b/plugins/scale/scale.hpp @@ -1,9 +1,10 @@ +#include "wayfire/toplevel-view.hpp" #include #include #include #include -inline wayfire_view scale_find_view_at(wf::pointf_t at, wf::output_t *output) +inline wayfire_toplevel_view scale_find_view_at(wf::pointf_t at, wf::output_t *output) { auto offset = wf::origin(output->get_layout_geometry()); at.x -= offset.x; @@ -12,7 +13,7 @@ inline wayfire_view scale_find_view_at(wf::pointf_t at, wf::output_t *output) auto node = output->wset()->get_node()->find_node_at(at); if (node) { - return wf::node_to_view(node->node->shared_from_this()); + return wf::toplevel_cast(wf::node_to_view(node->node->shared_from_this())); } return nullptr; diff --git a/plugins/scale/wayfire/plugins/scale-signal.hpp b/plugins/scale/wayfire/plugins/scale-signal.hpp index 581013e23..0b320322e 100644 --- a/plugins/scale/wayfire/plugins/scale-signal.hpp +++ b/plugins/scale/wayfire/plugins/scale-signal.hpp @@ -28,10 +28,10 @@ */ struct scale_filter_signal { - std::vector& views_shown; - std::vector& views_hidden; - scale_filter_signal(std::vector& shown, - std::vector& hidden) : views_shown(shown), views_hidden(hidden) + std::vector& views_shown; + std::vector& views_hidden; + scale_filter_signal(std::vector& shown, + std::vector& hidden) : views_shown(shown), views_hidden(hidden) {} }; @@ -42,7 +42,7 @@ template void scale_filter_views(scale_filter_signal *signal, pred&& p) { auto it = std::remove_if(signal->views_shown.begin(), signal->views_shown.end(), - [signal, &p] (wayfire_view v) + [signal, &p] (wayfire_toplevel_view v) { bool r = p(v); if (r) @@ -86,12 +86,12 @@ struct scale_update_signal */ struct scale_transformer_added_signal { - wayfire_view view; + wayfire_toplevel_view view; }; struct scale_transformer_removed_signal { - wayfire_view view; + wayfire_toplevel_view view; }; #endif diff --git a/plugins/single_plugins/expo.cpp b/plugins/single_plugins/expo.cpp index 5d74e0c97..017cec8df 100644 --- a/plugins/single_plugins/expo.cpp +++ b/plugins/single_plugins/expo.cpp @@ -390,7 +390,7 @@ class wayfire_expo : public wf::per_output_plugin_instance_t, public wf::keyboar } } - void start_moving(wayfire_view view, wf::point_t grab) + void start_moving(wayfire_toplevel_view view, wf::point_t grab) { if (!(view->get_allowed_actions() & (wf::VIEW_ALLOW_WS_CHANGE | wf::VIEW_ALLOW_MOVE))) { @@ -408,7 +408,7 @@ class wayfire_expo : public wf::per_output_plugin_instance_t, public wf::keyboar wf::move_drag::drag_options_t opts; opts.initial_scale = std::max(vw, vh); opts.enable_snap_off = move_enable_snap_off && - (view->fullscreen || view->tiled_edges); + (view->pending_fullscreen() || view->pending_tiled_edges()); opts.snap_off_threshold = move_snap_off_threshold; opts.join_views = move_join_views; @@ -620,7 +620,7 @@ class wayfire_expo : public wf::per_output_plugin_instance_t, public wf::keyboar }; } - wayfire_view find_view_at_coordinates(int gx, int gy) + wayfire_toplevel_view find_view_at_coordinates(int gx, int gy) { auto local = input_coordinates_to_output_local_coordinates({gx, gy}); wf::pointf_t localf = {1.0 * local.x, 1.0 * local.y}; @@ -630,7 +630,7 @@ class wayfire_expo : public wf::per_output_plugin_instance_t, public wf::keyboar auto isec = output->node_for_layer((wf::scene::layer)i)->find_node_at(localf); auto node = isec ? isec->node.get() : nullptr; - if (auto view = wf::node_to_view(node)) + if (auto view = wf::toplevel_cast(wf::node_to_view(node))) { auto all_views = output->wset()->get_views(); if (std::find(all_views.begin(), all_views.end(), view) != all_views.end()) diff --git a/plugins/single_plugins/extra-gestures.cpp b/plugins/single_plugins/extra-gestures.cpp index 6f68ee9da..d2aecf679 100644 --- a/plugins/single_plugins/extra-gestures.cpp +++ b/plugins/single_plugins/extra-gestures.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include @@ -87,7 +89,13 @@ class extra_gestures_plugin_t : public per_output_plugin_instance_t touch_and_hold_move = std::make_unique(std::move(actions), [=] () { - execute_view_action([] (wayfire_view view) { view->move_request(); }); + execute_view_action([] (wayfire_view view) + { + if (auto toplevel = wf::toplevel_cast(view)) + { + wf::get_core().default_wm->move_request(toplevel); + } + }); }); } diff --git a/plugins/single_plugins/fast-switcher.cpp b/plugins/single_plugins/fast-switcher.cpp index ef9f1c0d1..ae119d789 100644 --- a/plugins/single_plugins/fast-switcher.cpp +++ b/plugins/single_plugins/fast-switcher.cpp @@ -10,6 +10,7 @@ #include #include #include +#include /* * This plugin provides abilities to switch between views. @@ -22,7 +23,7 @@ class wayfire_fast_switcher : public wf::per_output_plugin_instance_t, public wf wf::option_wrapper_t activate_key_backward{ "fast-switcher/activate_backward"}; wf::option_wrapper_t inactive_alpha{"fast-switcher/inactive_alpha"}; - std::vector views; // all views on current viewport + std::vector views; // all views on current viewport size_t current_view_index = 0; // the modifiers which were used to activate switcher uint32_t activating_modifiers = 0; @@ -117,7 +118,7 @@ class wayfire_fast_switcher : public wf::per_output_plugin_instance_t, public wf { views = output->wset()->get_views( wf::WSET_CURRENT_WORKSPACE | wf::WSET_MAPPED_ONLY | wf::WSET_EXCLUDE_MINIMIZED); - std::sort(views.begin(), views.end(), [] (wayfire_view& a, wayfire_view& b) + std::sort(views.begin(), views.end(), [] (wayfire_toplevel_view& a, wayfire_toplevel_view& b) { return wf::get_focus_timestamp(a) > wf::get_focus_timestamp(b); }); diff --git a/plugins/single_plugins/idle.cpp b/plugins/single_plugins/idle.cpp index 1e1d74f6b..c6ab15e38 100644 --- a/plugins/single_plugins/idle.cpp +++ b/plugins/single_plugins/idle.cpp @@ -194,13 +194,13 @@ class wayfire_idle_plugin : public wf::per_output_plugin_instance_t output->connect(&fullscreen_state_changed); disable_on_fullscreen.set_callback(disable_on_fullscreen_changed); - if (output->get_active_view() && output->get_active_view()->fullscreen) + if (auto toplevel = toplevel_cast(output->get_active_view())) { /* Currently, the fullscreen count would always be 0 or 1, * since fullscreen-layer-focused is only emitted on changes between 0 * and 1 **/ - has_fullscreen = true; + has_fullscreen = toplevel->pending_fullscreen(); } update_fullscreen(); diff --git a/plugins/single_plugins/move.cpp b/plugins/single_plugins/move.cpp index 4799bd76d..3e8beba8a 100644 --- a/plugins/single_plugins/move.cpp +++ b/plugins/single_plugins/move.cpp @@ -1,3 +1,4 @@ +#include "wayfire/debug.hpp" #include "wayfire/plugins/common/input-grab.hpp" #include "wayfire/scene-input.hpp" #include "wayfire/signal-provider.hpp" @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -155,7 +157,7 @@ class wayfire_move : public wf::per_output_plugin_instance_t, activate_binding = [=] (auto) { - auto view = wf::get_core().get_cursor_focus_view(); + auto view = toplevel_cast(wf::get_core().get_cursor_focus_view()); if (view && (view->role != wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)) { initiate(view, get_global_input_coords()); @@ -222,7 +224,7 @@ class wayfire_move : public wf::per_output_plugin_instance_t, * Activation flags ignore input inhibitors if the view is in the desktop * widget layer (i.e OSKs) */ - uint32_t get_act_flags(wayfire_view view) + uint32_t get_act_flags(wayfire_toplevel_view view) { auto view_layer = wf::get_view_layer(view).value_or(wf::scene::layer::WORKSPACE); /* Allow moving an on-screen keyboard while screen is locked */ @@ -242,7 +244,7 @@ class wayfire_move : public wf::per_output_plugin_instance_t, * Usually, this is the view itself or its topmost parent if the join_views * option is set. */ - wayfire_view get_target_view(wayfire_view view) + wayfire_toplevel_view get_target_view(wayfire_toplevel_view view) { while (view && view->parent && join_views) { @@ -252,7 +254,7 @@ class wayfire_move : public wf::per_output_plugin_instance_t, return view; } - bool can_move_view(wayfire_view view) + bool can_move_view(wayfire_toplevel_view view) { if (!view || !view->is_mapped() || !(view->get_allowed_actions() & wf::VIEW_ALLOW_MOVE)) { @@ -263,7 +265,7 @@ class wayfire_move : public wf::per_output_plugin_instance_t, return output->can_activate_plugin(&grab_interface, get_act_flags(view)); } - bool grab_input(wayfire_view view) + bool grab_input(wayfire_toplevel_view view) { view = view ?: drag_helper->view; if (!view) @@ -281,7 +283,7 @@ class wayfire_move : public wf::per_output_plugin_instance_t, return true; } - bool initiate(wayfire_view view, wf::point_t grab_position) + bool initiate(wayfire_toplevel_view view, wf::point_t grab_position) { // First, make sure that the view is on the output the input is. auto target_output = wf::get_core().output_layout->get_output_at(grab_position.x, grab_position.y); @@ -291,14 +293,14 @@ class wayfire_move : public wf::per_output_plugin_instance_t, -wf::origin(target_output->get_layout_geometry()); move_view_to_output(view, target_output, false); - view->move(view->get_wm_geometry().x + offset.x, view->get_wm_geometry().y + offset.y); + view->move(view->get_geometry().x + offset.x, view->get_geometry().y + offset.y); // On the new output - view->move_request(); + wf::get_core().default_wm->move_request(view); return false; } - wayfire_view grabbed_view = view; + wayfire_toplevel_view grabbed_view = view; view = get_target_view(view); if (!can_move_view(view)) { @@ -312,7 +314,7 @@ class wayfire_move : public wf::per_output_plugin_instance_t, wf::move_drag::drag_options_t opts; opts.enable_snap_off = move_enable_snap_off && - (view->fullscreen || view->tiled_edges); + (view->pending_fullscreen() || view->pending_tiled_edges()); opts.snap_off_threshold = move_snap_off_threshold; opts.join_views = join_views; @@ -537,7 +539,7 @@ class wayfire_move : public wf::per_output_plugin_instance_t, // retain their fullscreen state (but they can be moved to other // workspaces). Unsetting the fullscreen state can break some // Xwayland games. - if (drag_helper->view->fullscreen) + if (drag_helper->view->pending_fullscreen()) { return false; } diff --git a/plugins/single_plugins/oswitch.cpp b/plugins/single_plugins/oswitch.cpp index 39d2ee3ce..ac9e13260 100644 --- a/plugins/single_plugins/oswitch.cpp +++ b/plugins/single_plugins/oswitch.cpp @@ -1,4 +1,5 @@ #include "wayfire/plugin.hpp" +#include "wayfire/toplevel-view.hpp" #include #include #include @@ -28,7 +29,7 @@ class wayfire_oswitch : public wf::plugin_interface_t { auto current_output = wf::get_core().get_active_output(); auto next = wf::get_core().output_layout->get_next_output(current_output); - auto view = current_output->get_active_view(); + auto view = wf::toplevel_cast(current_output->get_active_view()); if (!view) { switch_output(wf::activator_data_t{}); diff --git a/plugins/single_plugins/place.cpp b/plugins/single_plugins/place.cpp index 9d1c9c28b..80ffb9940 100644 --- a/plugins/single_plugins/place.cpp +++ b/plugins/single_plugins/place.cpp @@ -1,16 +1,20 @@ #include "wayfire/signal-provider.hpp" +#include "wayfire/toplevel-view.hpp" #include #include #include #include +#include #include class wayfire_place_window : public wf::per_output_plugin_instance_t { wf::signal::connection_t on_view_mapped = [=] (wf::view_mapped_signal *ev) { - if ((ev->view->role != wf::VIEW_ROLE_TOPLEVEL) || ev->view->parent || - ev->view->fullscreen || ev->view->tiled_edges || ev->is_positioned) + auto toplevel = wf::toplevel_cast(ev->view); + + if (!toplevel || toplevel->parent || toplevel->pending_fullscreen() || + toplevel->pending_tiled_edges() || ev->is_positioned) { return; } @@ -21,16 +25,16 @@ class wayfire_place_window : public wf::per_output_plugin_instance_t std::string mode = placement_mode; if (mode == "cascade") { - cascade(ev->view, workarea); + cascade(toplevel, workarea); } else if (mode == "maximize") { - maximize(ev->view, workarea); + maximize(toplevel, workarea); } else if (mode == "random") { - random(ev->view, workarea); + random(toplevel, workarea); } else { - center(ev->view, workarea); + center(toplevel, workarea); } }; @@ -65,9 +69,9 @@ class wayfire_place_window : public wf::per_output_plugin_instance_t output->connect(&on_view_mapped); } - void cascade(wayfire_view & view, wf::geometry_t workarea) + void cascade(wayfire_toplevel_view & view, wf::geometry_t workarea) { - wf::geometry_t window = view->get_wm_geometry(); + wf::geometry_t window = view->get_pending_geometry(); if ((cascade_x + window.width > workarea.x + workarea.width) || (cascade_y + window.height > workarea.y + workarea.height)) @@ -82,9 +86,9 @@ class wayfire_place_window : public wf::per_output_plugin_instance_t cascade_y += workarea.height * .03; } - void random(wayfire_view & view, wf::geometry_t workarea) + void random(wayfire_toplevel_view & view, wf::geometry_t workarea) { - wf::geometry_t window = view->get_wm_geometry(); + wf::geometry_t window = view->get_pending_geometry(); wf::geometry_t area; int pos_x, pos_y; @@ -106,17 +110,17 @@ class wayfire_place_window : public wf::per_output_plugin_instance_t view->move(pos_x, pos_y); } - void center(wayfire_view & view, wf::geometry_t workarea) + void center(wayfire_toplevel_view & view, wf::geometry_t workarea) { - wf::geometry_t window = view->get_wm_geometry(); + wf::geometry_t window = view->get_pending_geometry(); window.x = workarea.x + (workarea.width / 2) - (window.width / 2); window.y = workarea.y + (workarea.height / 2) - (window.height / 2); view->move(window.x, window.y); } - void maximize(wayfire_view & view, wf::geometry_t workarea) + void maximize(wayfire_toplevel_view & view, wf::geometry_t workarea) { - view->tile_request(wf::TILED_EDGES_ALL); + wf::get_core().default_wm->tile_request(view, wf::TILED_EDGES_ALL); } }; diff --git a/plugins/single_plugins/preserve-output.cpp b/plugins/single_plugins/preserve-output.cpp index dc3e3dec2..406176be9 100644 --- a/plugins/single_plugins/preserve-output.cpp +++ b/plugins/single_plugins/preserve-output.cpp @@ -1,6 +1,7 @@ #include "wayfire/nonstd/observer_ptr.h" #include "wayfire/object.hpp" #include "wayfire/output.hpp" +#include "wayfire/toplevel-view.hpp" #include "wayfire/util.hpp" #include "wayfire/view-helpers.hpp" #include @@ -14,6 +15,7 @@ #include #include #include +#include #include @@ -44,14 +46,14 @@ std::string make_output_identifier(wf::output_t *output) return identifier; } -void view_store_data(wayfire_view view, wf::output_t *output, int z_order) +void view_store_data(wayfire_toplevel_view view, wf::output_t *output, int z_order) { auto view_data = view->get_data_safe(); view_data->output_identifier = make_output_identifier(output); - view_data->geometry = view->get_wm_geometry(); - view_data->fullscreen = view->fullscreen; + view_data->geometry = view->get_pending_geometry(); + view_data->fullscreen = view->pending_fullscreen(); view_data->minimized = view->minimized; - view_data->tiled_edges = view->tiled_edges; + view_data->tiled_edges = view->pending_tiled_edges(); view_data->z_order = z_order; if (view == output->get_active_view()) { @@ -59,7 +61,7 @@ void view_store_data(wayfire_view view, wf::output_t *output, int z_order) } } -nonstd::observer_ptr view_get_data(wayfire_view view) +nonstd::observer_ptr view_get_data(wayfire_toplevel_view view) { return view->get_data(); } @@ -212,10 +214,10 @@ class wayfire_preserve_output : public wf::per_output_plugin_instance_t } // Make a list of views to move to this output - auto views = std::vector(); + auto views = std::vector(); for (auto& view : wf::get_core().get_all_views()) { - if (!view->is_mapped()) + if (!view->is_mapped() || !toplevel_cast(view)) { continue; } @@ -225,16 +227,16 @@ class wayfire_preserve_output : public wf::per_output_plugin_instance_t continue; } - auto last_output_info = view_get_data(view); + auto last_output_info = view_get_data(toplevel_cast(view)); if (last_output_info->output_identifier == identifier) { - views.push_back(view); + views.push_back(wf::toplevel_cast(view)); } } // Sorts with the views closest to front last std::sort(views.begin(), views.end(), - [=] (wayfire_view & view1, wayfire_view & view2) + [=] (wayfire_toplevel_view & view1, wayfire_toplevel_view & view2) { return view_get_data(view1)->z_order > view_get_data(view2)->z_order; }); @@ -247,11 +249,11 @@ class wayfire_preserve_output : public wf::per_output_plugin_instance_t view->get_title(), " to: ", output->to_string()); move_view_to_output(view, output, false); - view->set_fullscreen(last_output_info->fullscreen); + view->toplevel()->pending().fullscreen = last_output_info->fullscreen; view->set_minimized(last_output_info->minimized); if (last_output_info->tiled_edges != 0) { - view->tile_request(last_output_info->tiled_edges); + wf::get_core().default_wm->tile_request(view, last_output_info->tiled_edges); } view->set_geometry(last_output_info->geometry); @@ -281,7 +283,7 @@ class wayfire_preserve_output : public wf::per_output_plugin_instance_t auto view = signal_data->view; // Ignore event if geometry didn't actually change - if (signal_data->old_geometry == view->get_wm_geometry()) + if (signal_data->old_geometry == view->get_geometry()) { return; } diff --git a/plugins/single_plugins/resize.cpp b/plugins/single_plugins/resize.cpp index e936c21ac..57e1b7dad 100644 --- a/plugins/single_plugins/resize.cpp +++ b/plugins/single_plugins/resize.cpp @@ -1,5 +1,8 @@ +#include "wayfire/geometry.hpp" #include "wayfire/plugins/common/input-grab.hpp" #include "wayfire/scene-input.hpp" +#include "wayfire/txn/transaction-manager.hpp" +#include #include #include #include @@ -10,6 +13,7 @@ #include #include #include +#include class wayfire_resize : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t, public wf::touch_interaction_t @@ -47,7 +51,7 @@ class wayfire_resize : public wf::per_output_plugin_instance_t, public wf::point wf::button_callback activate_binding; - wayfire_view view; + wayfire_toplevel_view view; bool was_client_request, is_using_touch; wf::point_t grab_start; @@ -68,7 +72,7 @@ class wayfire_resize : public wf::per_output_plugin_instance_t, public wf::point activate_binding = [=] (auto) { - auto view = wf::get_core().get_cursor_focus_view(); + auto view = toplevel_cast(wf::get_core().get_cursor_focus_view()); if (view) { is_using_touch = false; @@ -174,10 +178,10 @@ class wayfire_resize : public wf::per_output_plugin_instance_t, public wf::point return edges; } - bool initiate(wayfire_view view, uint32_t forced_edges = 0) + bool initiate(wayfire_toplevel_view view, uint32_t forced_edges = 0) { if (!view || (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT) || - !view->is_mapped() || view->fullscreen) + !view->is_mapped() || view->pending_fullscreen()) { return false; } @@ -199,18 +203,10 @@ class wayfire_resize : public wf::per_output_plugin_instance_t, public wf::point input_grab->grab_input(wf::scene::layer::OVERLAY); grab_start = get_input_coords(); - grabbed_geometry = view->get_wm_geometry(); - - if ((edges & WLR_EDGE_LEFT) || (edges & WLR_EDGE_TOP)) + grabbed_geometry = view->get_geometry(); + if (view->pending_tiled_edges()) { - view->set_moving(true); - } - - view->set_resizing(true, edges); - - if (view->tiled_edges) - { - view->set_tiled(0); + view->toplevel()->pending().tiled_edges = 0; } this->view = view; @@ -247,13 +243,6 @@ class wayfire_resize : public wf::per_output_plugin_instance_t, public wf::point if (view) { - if ((edges & WLR_EDGE_LEFT) || - (edges & WLR_EDGE_TOP)) - { - view->set_moving(false); - } - - view->set_resizing(false); end_wobbly(view); wf::view_change_workspace_signal workspace_may_changed; @@ -264,33 +253,64 @@ class wayfire_resize : public wf::per_output_plugin_instance_t, public wf::point } } + // Convert resize edges to gravity + uint32_t calculate_gravity() + { + uint32_t gravity = 0; + if (edges & WLR_EDGE_LEFT) + { + gravity |= WLR_EDGE_RIGHT; + } + + if (edges & WLR_EDGE_RIGHT) + { + gravity |= WLR_EDGE_LEFT; + } + + if (edges & WLR_EDGE_TOP) + { + gravity |= WLR_EDGE_BOTTOM; + } + + if (edges & WLR_EDGE_BOTTOM) + { + gravity |= WLR_EDGE_TOP; + } + + return gravity; + } + void input_motion() { auto input = get_input_coords(); int dx = input.x - grab_start.x; int dy = input.y - grab_start.y; - int width = grabbed_geometry.width; - int height = grabbed_geometry.height; + wf::geometry_t desired = grabbed_geometry; if (edges & WLR_EDGE_LEFT) { - width -= dx; + desired.x += dx; + desired.width -= dx; } else if (edges & WLR_EDGE_RIGHT) { - width += dx; + desired.width += dx; } if (edges & WLR_EDGE_TOP) { - height -= dy; + desired.y += dy; + desired.height -= dy; } else if (edges & WLR_EDGE_BOTTOM) { - height += dy; + desired.height += dy; } - height = std::max(height, 1); - width = std::max(width, 1); - view->resize(width, height); + desired.width = std::max(desired.width, 1); + desired.height = std::max(desired.height, 1); + + view->toplevel()->pending().gravity = calculate_gravity(); + view->toplevel()->pending().geometry = desired; + wf::get_core().tx_manager->schedule_object(view->toplevel()); } void fini() override diff --git a/plugins/single_plugins/switcher.cpp b/plugins/single_plugins/switcher.cpp index 0974d786b..f202156f5 100644 --- a/plugins/single_plugins/switcher.cpp +++ b/plugins/single_plugins/switcher.cpp @@ -63,7 +63,7 @@ static constexpr bool view_expired(int view_position) struct SwitcherView { - wayfire_view view; + wayfire_toplevel_view view; SwitcherPaintAttribs attribs; int position; @@ -244,10 +244,13 @@ class WayfireSwitcher : public wf::per_output_plugin_instance_t, public wf::keyb wf::signal::connection_t view_disappeared = [=] (wf::view_disappeared_signal *ev) { - handle_view_removed(ev->view); + if (auto toplevel = toplevel_cast(ev->view)) + { + handle_view_removed(toplevel); + } }; - void handle_view_removed(wayfire_view view) + void handle_view_removed(wayfire_toplevel_view view) { // not running at all, don't care if (!output->is_plugin_active(grab_interface.name)) @@ -448,7 +451,7 @@ class WayfireSwitcher : public wf::per_output_plugin_instance_t, public wf::keyb } /* Calculate alpha for the view when switcher is inactive. */ - float get_view_normal_alpha(wayfire_view view) + float get_view_normal_alpha(wayfire_toplevel_view view) { /* Usually views are visible, but if they were minimized, * and we aren't restoring the view, it has target alpha 0.0 */ @@ -493,7 +496,7 @@ class WayfireSwitcher : public wf::per_output_plugin_instance_t, public wf::keyb } // returns a list of mapped views - std::vector get_workspace_views() const + std::vector get_workspace_views() const { return output->wset()->get_views(wf::WSET_MAPPED_ONLY | wf::WSET_CURRENT_WORKSPACE); } @@ -554,7 +557,7 @@ class WayfireSwitcher : public wf::per_output_plugin_instance_t, public wf::keyb * of the unfocused view. When dearranging those copies, they overlap. * If the view is translucent, this means that the view gets darker than * it really is. * To circumvent this, we just fade out one of the copies */ - wayfire_view fading_view = nullptr; + wayfire_toplevel_view fading_view = nullptr; if (count_different_active_views() == 2) { fading_view = get_unfocused_view(); @@ -624,7 +627,7 @@ class WayfireSwitcher : public wf::per_output_plugin_instance_t, public wf::keyb } } - SwitcherView create_switcher_view(wayfire_view view) + SwitcherView create_switcher_view(wayfire_toplevel_view view) { /* we add a view transform if there isn't any. * @@ -655,8 +658,7 @@ class WayfireSwitcher : public wf::per_output_plugin_instance_t, public wf::keyb void render_view_scene(wayfire_view view, const wf::render_target_t& buffer) { std::vector instances; - view->get_transformed_node()->gen_render_instances( - instances, [] (auto) {}); + view->get_transformed_node()->gen_render_instances(instances, [] (auto) {}); wf::scene::render_pass_params_t params; params.instances = &instances; @@ -819,7 +821,7 @@ class WayfireSwitcher : public wf::per_output_plugin_instance_t, public wf::keyb int count_different_active_views() { - std::set active_views; + std::set active_views; for (auto& sv : views) { active_views.insert(sv.view); @@ -829,7 +831,7 @@ class WayfireSwitcher : public wf::per_output_plugin_instance_t, public wf::keyb } /* Move the last view in the given slot so that it becomes invalid */ - wayfire_view invalidate_last_in_slot(int slot) + wayfire_toplevel_view invalidate_last_in_slot(int slot) { for (int i = views.size() - 1; i >= 0; i--) { @@ -845,7 +847,7 @@ class WayfireSwitcher : public wf::per_output_plugin_instance_t, public wf::keyb } /* Returns the non-focused view in the case where there is only 1 view */ - wayfire_view get_unfocused_view() + wayfire_toplevel_view get_unfocused_view() { for (auto& sv : views) { diff --git a/plugins/single_plugins/wrot.cpp b/plugins/single_plugins/wrot.cpp index dbeff1043..c825fd897 100644 --- a/plugins/single_plugins/wrot.cpp +++ b/plugins/single_plugins/wrot.cpp @@ -41,7 +41,7 @@ class wf_wrot : public wf::per_output_plugin_instance_t, public wf::pointer_inte wf::option_wrapper_t invert{"wrot/invert"}; wf::pointf_t last_position; - wayfire_view current_view = nullptr; + wayfire_toplevel_view current_view = nullptr; std::unique_ptr input_grab; mode current_mode = mode::NONE; @@ -67,7 +67,7 @@ class wf_wrot : public wf::per_output_plugin_instance_t, public wf::pointer_inte return false; } - current_view = wf::get_core().get_cursor_focus_view(); + current_view = toplevel_cast(wf::get_core().get_cursor_focus_view()); if (!current_view || (current_view->role != wf::VIEW_ROLE_TOPLEVEL)) { output->deactivate_plugin(&grab_interface); @@ -116,7 +116,7 @@ class wf_wrot : public wf::per_output_plugin_instance_t, public wf::pointer_inte current_view, wf::TRANSFORMER_2D, transformer_2d, current_view); current_view->damage(); - auto g = current_view->get_wm_geometry(); + auto g = current_view->get_geometry(); double cx = g.x + g.width / 2.0; double cy = g.y + g.height / 2.0; @@ -183,7 +183,7 @@ class wf_wrot : public wf::per_output_plugin_instance_t, public wf::pointer_inte return false; } - current_view = wf::get_core().get_cursor_focus_view(); + current_view = toplevel_cast(wf::get_core().get_cursor_focus_view()); if (!current_view || (current_view->role != wf::VIEW_ROLE_TOPLEVEL)) { output->deactivate_plugin(&grab_interface); diff --git a/plugins/single_plugins/wsets.cpp b/plugins/single_plugins/wsets.cpp index 21a1af555..1a3198fee 100644 --- a/plugins/single_plugins/wsets.cpp +++ b/plugins/single_plugins/wsets.cpp @@ -249,13 +249,13 @@ class wayfire_wsets_plugin_t : public wf::plugin_interface_t void send_window_to(int index) { auto wo = wf::get_core().get_active_output(); - if (!wo || !wo->get_active_view()) + if (!wo) { return; } - auto view = wo->get_active_view(); - if (view->role != wf::VIEW_ROLE_TOPLEVEL) + auto view = toplevel_cast(wo->get_active_view()); + if (!view) { return; } diff --git a/plugins/tile/tile-plugin.cpp b/plugins/tile/tile-plugin.cpp index d291f593f..1b61a8c7e 100644 --- a/plugins/tile/tile-plugin.cpp +++ b/plugins/tile/tile-plugin.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "tree-controller.hpp" #include "tree.hpp" @@ -20,17 +22,32 @@ #include "wayfire/scene-operations.hpp" #include "wayfire/scene.hpp" #include "wayfire/signal-provider.hpp" +#include "wayfire/toplevel-view.hpp" #include "wayfire/view-helpers.hpp" +#include "wayfire/txn/transaction-manager.hpp" -namespace wf -{ -static bool can_tile_view(wayfire_view view) +struct autocommit_transaction_t { - if (view->role != wf::VIEW_ROLE_TOPLEVEL) + public: + wf::txn::transaction_uptr tx; + autocommit_transaction_t() { - return false; + tx = wf::txn::transaction_t::create(); } + ~autocommit_transaction_t() + { + if (!tx->get_objects().empty()) + { + wf::get_core().tx_manager->schedule_transaction(std::move(tx)); + } + } +}; + +namespace wf +{ +static bool can_tile_view(wayfire_toplevel_view view) +{ if (view->parent) { return false; @@ -128,13 +145,12 @@ class tile_workspace_set_data_t : public wf::custom_data_t void update_root_size() { - if (!wset.lock()->get_attached_output()) - { - return; - } + auto wo = wset.lock()->get_attached_output(); + wf::geometry_t workarea = wo ? wo->workarea->get_workarea() : tile::default_output_resolution; + + wf::geometry_t output_geometry = + wset.lock()->get_last_output_geometry().value_or(tile::default_output_resolution); - wf::geometry_t workarea = wset.lock()->get_attached_output()->workarea->get_workarea(); - wf::geometry_t output_geometry = wset.lock()->get_attached_output()->get_relative_geometry(); auto wsize = wset.lock()->get_workspace_grid_size(); for (int i = 0; i < wsize.width; i++) { @@ -144,7 +160,9 @@ class tile_workspace_set_data_t : public wf::custom_data_t auto vp_geometry = workarea; vp_geometry.x += i * output_geometry.width; vp_geometry.y += j * output_geometry.height; - roots[i][j]->set_geometry(vp_geometry); + + autocommit_transaction_t tx; + roots[i][j]->set_geometry(vp_geometry, tx.tx); } } } @@ -176,8 +194,9 @@ class tile_workspace_set_data_t : public wf::custom_data_t { for (auto& root : col) { - root->set_gaps(gaps); - root->set_geometry(root->geometry); + autocommit_transaction_t tx; + root->set_gaps(gaps, tx.tx); + root->set_geometry(root->geometry, tx.tx); } } }; @@ -188,7 +207,8 @@ class tile_workspace_set_data_t : public wf::custom_data_t { for (auto& root : col) { - tile::flatten_tree(root); + autocommit_transaction_t tx; + tile::flatten_tree(root, tx.tx); } } } @@ -226,7 +246,7 @@ class tile_workspace_set_data_t : public wf::custom_data_t std::weak_ptr wset; - void attach_view(wayfire_view view, wf::point_t vp = {-1, -1}) + void attach_view(wayfire_toplevel_view view, wf::point_t vp = {-1, -1}) { view->set_allowed_actions(VIEW_ALLOW_WS_CHANGE); @@ -236,7 +256,10 @@ class tile_workspace_set_data_t : public wf::custom_data_t } auto view_node = std::make_unique(view); - roots[vp.x][vp.y]->as_split_node()->add_child(std::move(view_node)); + { + autocommit_transaction_t tx; + roots[vp.x][vp.y]->as_split_node()->add_child(std::move(view_node), tx.tx); + } auto node = view->get_root_node(); wf::scene::readd_front(tiled_sublayer[vp.x][vp.y], node); @@ -249,13 +272,16 @@ class tile_workspace_set_data_t : public wf::custom_data_t { auto wview = view->view; wview->set_allowed_actions(VIEW_ALLOW_ALL); - view->parent->remove_child(view); + { + autocommit_transaction_t tx; + view->parent->remove_child(view, tx.tx); + } /* View node is invalid now */ flatten_roots(); - if (wview->fullscreen && wview->is_mapped()) + if (wview->pending_fullscreen() && wview->is_mapped()) { - wview->fullscreen_request(nullptr, false); + wf::get_core().default_wm->fullscreen_request(wview, nullptr, false); } /* Remove from special sublayer */ @@ -312,9 +338,9 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom bool has_fullscreen_view() { int count_fullscreen = 0; - for_each_view(tile_workspace_set_data_t::get_current_root(output), [&] (wayfire_view view) + for_each_view(tile_workspace_set_data_t::get_current_root(output), [&] (wayfire_toplevel_view view) { - count_fullscreen += view->fullscreen; + count_fullscreen += view->pending_fullscreen(); }); return count_fullscreen > 0; @@ -366,12 +392,12 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom controller = get_default_controller(); } - bool tile_window_by_default(wayfire_view view) + bool tile_window_by_default(wayfire_toplevel_view view) { return tile_by_default.matches(view) && can_tile_view(view); } - void attach_view(wayfire_view view, wf::point_t vp = {-1, -1}) + void attach_view(wayfire_toplevel_view view, wf::point_t vp = {-1, -1}) { if (!view->get_wset()) { @@ -390,10 +416,13 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom wf::signal::connection_t on_view_mapped = [=] (view_mapped_signal *ev) { - if (tile_window_by_default(ev->view)) + if (auto toplevel = toplevel_cast(ev->view)) { - stop_controller(true); - tile_workspace_set_data_t::get(output).attach_view(ev->view); + if (tile_window_by_default(toplevel)) + { + stop_controller(true); + tile_workspace_set_data_t::get(output).attach_view(toplevel); + } } }; @@ -418,10 +447,10 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom ev->carried_out = true; }; - void set_view_fullscreen(wayfire_view view, bool fullscreen) + void set_view_fullscreen(wayfire_toplevel_view view, bool fullscreen) { - /* Set fullscreen, and trigger resizing of the views */ - view->set_fullscreen(fullscreen); + /* Set fullscreen, and trigger resizing of the views (which will commit the view) */ + view->toplevel()->pending().fullscreen = fullscreen; tile_workspace_set_data_t::get(output).update_root_size(); } @@ -439,11 +468,18 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom wf::signal::connection_t on_focus_changed = [=] (focus_view_signal *ev) { - if (ev->view && tile::view_node_t::get_node(ev->view) && !ev->view->fullscreen) + if (!toplevel_cast(ev->view)) + { + return; + } + + auto toplevel = toplevel_cast(ev->view); + if (tile::view_node_t::get_node(ev->view) && !toplevel->pending_fullscreen()) { - for_each_view(tile_workspace_set_data_t::get_current_root(output), [&] (wayfire_view view) + for_each_view(tile_workspace_set_data_t::get_current_root(output), [&] ( + wayfire_toplevel_view view) { - if (view->fullscreen) + if (view->pending_fullscreen()) { set_view_fullscreen(view, false); } @@ -451,7 +487,7 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom } }; - void change_view_workspace(wayfire_view view, wf::point_t vp = {-1, -1}) + void change_view_workspace(wayfire_toplevel_view view, wf::point_t vp = {-1, -1}) { auto existing_node = wf::tile::view_node_t::get_node(view); if (existing_node) @@ -493,10 +529,10 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom * @param need_tiled Whether the view needs to be tiled */ bool conditioned_view_execute(bool need_tiled, - std::function func) + std::function func) { auto view = output->get_active_view(); - if (!view) + if (!toplevel_cast(view)) { return false; } @@ -508,7 +544,7 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom if (output->can_activate_plugin(&grab_interface)) { - func(view); + func(toplevel_cast(view)); return true; } @@ -517,13 +553,13 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom wf::key_callback on_toggle_tiled_state = [=] (auto) { - return conditioned_view_execute(false, [=] (wayfire_view view) + return conditioned_view_execute(false, [=] (wayfire_toplevel_view view) { auto existing_node = tile::view_node_t::get_node(view); if (existing_node) { detach_view(existing_node); - view->tile_request(0); + wf::get_core().default_wm->tile_request(view, 0); } else { attach_view(view); @@ -533,12 +569,12 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom bool focus_adjacent(tile::split_insertion_t direction) { - return conditioned_view_execute(true, [=] (wayfire_view view) + return conditioned_view_execute(true, [=] (wayfire_toplevel_view view) { auto adjacent = tile::find_first_view_in_direction( tile::view_node_t::get_node(view), direction); - bool was_fullscreen = view->fullscreen; + bool was_fullscreen = view->pending_fullscreen(); if (adjacent) { /* This will lower the fullscreen status of the view */ @@ -546,7 +582,7 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom if (was_fullscreen && keep_fullscreen_on_adjacent) { - adjacent->view->fullscreen_request(output, true); + wf::get_core().default_wm->fullscreen_request(adjacent->view, output, true); } } }); @@ -662,6 +698,18 @@ class tile_plugin_t : public wf::plugin_interface_t, wf::per_output_tracker_mixi } } + void stop_controller(std::shared_ptr wset) + { + if (auto wo = wset->get_attached_output()) + { + auto tile = wo->get_data(); + if (tile) + { + tile->stop_controller(true); + } + } + } + wf::signal::connection_t on_view_pre_moved_to_wset = [=] (view_pre_moved_to_wset_signal *ev) { @@ -671,11 +719,7 @@ class tile_plugin_t : public wf::plugin_interface_t, wf::per_output_tracker_mixi ev->view->store_data(std::make_unique()); if (ev->old_wset) { - if (auto wo = ev->old_wset->get_attached_output()) - { - wo->get_data()->stop_controller(true); - } - + stop_controller(ev->old_wset); tile_workspace_set_data_t::get(ev->old_wset).detach_view(node); } } @@ -686,11 +730,7 @@ class tile_plugin_t : public wf::plugin_interface_t, wf::per_output_tracker_mixi { if (ev->view->has_data() && ev->new_wset) { - if (auto wo = ev->new_wset->get_attached_output()) - { - wo->get_data()->stop_controller(true); - } - + stop_controller(ev->new_wset); tile_workspace_set_data_t::get(ev->new_wset).attach_view(ev->view); } }; diff --git a/plugins/tile/tree-controller.cpp b/plugins/tile/tree-controller.cpp index d426fb17d..47bdf56ad 100644 --- a/plugins/tile/tree-controller.cpp +++ b/plugins/tile/tree-controller.cpp @@ -8,13 +8,14 @@ #include #include #include +#include namespace wf { namespace tile { void for_each_view(nonstd::observer_ptr root, - std::function callback) + std::function callback) { if (root->as_view_node()) { @@ -218,7 +219,7 @@ move_view_controller_t::~move_view_controller_t() if (this->preview) { this->preview->set_target_geometry( - get_output_local_coordinates(output, current_input), 0.0, true); + get_wset_local_coordinates(output->wset(), current_input), 0.0, true); } } @@ -261,18 +262,17 @@ void move_view_controller_t::input_motion(wf::point_t input) /* No view, no preview */ if (this->preview) { - preview->set_target_geometry( - get_output_local_coordinates(output, input), 0.0); + preview->set_target_geometry(get_wset_local_coordinates(output->wset(), input), 0.0); } return; } auto split = calculate_insert_type(view, input); - ensure_preview(get_output_local_coordinates(output, input)); + ensure_preview(get_wset_local_coordinates(output->wset(), input)); auto preview_geometry = calculate_split_preview(view, split); - preview_geometry = get_output_local_coordinates(output, preview_geometry); + preview_geometry = get_wset_local_coordinates(output->wset(), preview_geometry); this->preview->set_target_geometry(preview_geometry, 1.0); } @@ -302,6 +302,8 @@ void move_view_controller_t::input_released() return; } + auto tx = wf::txn::transaction_t::create(); + if (split == INSERT_SWAP) { std::swap(grabbed_view->geometry, dropped_at->geometry); @@ -318,8 +320,8 @@ void move_view_controller_t::input_released() std::swap(*it1, *it2); - p1->set_geometry(p1->geometry); - p2->set_geometry(p2->geometry); + p1->set_geometry(p1->geometry, tx); + p2->set_geometry(p2->geometry, tx); return; } @@ -329,7 +331,7 @@ void move_view_controller_t::input_released() if (dropped_at->parent->get_split_direction() == split_type) { /* We can simply add the dragged view as a sibling of the target view */ - auto view = grabbed_view->parent->remove_child(grabbed_view); + auto view = grabbed_view->parent->remove_child(grabbed_view, tx); int idx = find_idx(dropped_at); if ((split == INSERT_RIGHT) || (split == INSERT_BELOW)) @@ -337,7 +339,7 @@ void move_view_controller_t::input_released() ++idx; } - dropped_at->parent->add_child(std::move(view), idx); + dropped_at->parent->add_child(std::move(view), tx, idx); } else { /* Case 2: we need a new split just for the dropped on and the dragged @@ -345,32 +347,33 @@ void move_view_controller_t::input_released() auto new_split = std::make_unique(split_type); /* The size will be autodetermined by the tree structure, but we set * some valid size here to avoid UB */ - new_split->set_geometry(dropped_at->geometry); + new_split->set_geometry(dropped_at->geometry, tx); /* Find the position of the dropped view and its parent */ int idx = find_idx(dropped_at); auto dropped_parent = dropped_at->parent; /* Remove both views */ - auto dropped_view = dropped_at->parent->remove_child(dropped_at); - auto dragged_view = grabbed_view->parent->remove_child(grabbed_view); + auto dropped_view = dropped_at->parent->remove_child(dropped_at, tx); + auto dragged_view = grabbed_view->parent->remove_child(grabbed_view, tx); if ((split == INSERT_ABOVE) || (split == INSERT_LEFT)) { - new_split->add_child(std::move(dragged_view)); - new_split->add_child(std::move(dropped_view)); + new_split->add_child(std::move(dragged_view), tx); + new_split->add_child(std::move(dropped_view), tx); } else { - new_split->add_child(std::move(dropped_view)); - new_split->add_child(std::move(dragged_view)); + new_split->add_child(std::move(dropped_view), tx); + new_split->add_child(std::move(dragged_view), tx); } /* Put them in place */ - dropped_parent->add_child(std::move(new_split), idx); + dropped_parent->add_child(std::move(new_split), tx, idx); } /* Clean up tree structure */ - flatten_tree(this->root); + flatten_tree(this->root, tx); + wf::get_core().tx_manager->schedule_transaction(std::move(tx)); } wf::geometry_t eval(nonstd::observer_ptr node) @@ -537,6 +540,7 @@ void resize_view_controller_t::input_motion(wf::point_t input) return; } + auto tx = wf::txn::transaction_t::create(); if (horizontal_pair.first && horizontal_pair.second) { int dy = input.y - last_point.y; @@ -545,8 +549,8 @@ void resize_view_controller_t::input_motion(wf::point_t input) auto g2 = horizontal_pair.second->geometry; adjust_geometry(g1.y, g1.height, g2.y, g2.height, dy); - horizontal_pair.first->set_geometry(g1); - horizontal_pair.second->set_geometry(g2); + horizontal_pair.first->set_geometry(g1, tx); + horizontal_pair.second->set_geometry(g2, tx); } if (vertical_pair.first && vertical_pair.second) @@ -557,10 +561,11 @@ void resize_view_controller_t::input_motion(wf::point_t input) auto g2 = vertical_pair.second->geometry; adjust_geometry(g1.x, g1.width, g2.x, g2.width, dx); - vertical_pair.first->set_geometry(g1); - vertical_pair.second->set_geometry(g2); + vertical_pair.first->set_geometry(g1, tx); + vertical_pair.second->set_geometry(g2, tx); } + wf::get_core().tx_manager->schedule_transaction(std::move(tx)); this->last_point = input; } } diff --git a/plugins/tile/tree-controller.hpp b/plugins/tile/tree-controller.hpp index a0283fab7..46b300448 100644 --- a/plugins/tile/tree-controller.hpp +++ b/plugins/tile/tree-controller.hpp @@ -14,7 +14,7 @@ namespace tile * Run callback for each view in the tree */ void for_each_view(nonstd::observer_ptr root, - std::function callback); + std::function callback); enum split_insertion_t { diff --git a/plugins/tile/tree.cpp b/plugins/tile/tree.cpp index 5efea0d2c..555fd2544 100644 --- a/plugins/tile/tree.cpp +++ b/plugins/tile/tree.cpp @@ -1,4 +1,7 @@ #include "tree.hpp" +#include "wayfire/core.hpp" +#include "wayfire/geometry.hpp" +#include "wayfire/toplevel-view.hpp" #include #include @@ -8,12 +11,15 @@ #include #include #include +#include +#include +#include namespace wf { namespace tile { -void tree_node_t::set_geometry(wf::geometry_t geometry) +void tree_node_t::set_geometry(wf::geometry_t geometry, wf::txn::transaction_uptr&) { this->geometry = geometry; } @@ -28,19 +34,18 @@ nonstd::observer_ptr tree_node_t::as_view_node() return nonstd::make_observer(dynamic_cast(this)); } -wf::point_t get_output_local_coordinates(wf::output_t *output, wf::point_t p) +wf::point_t get_wset_local_coordinates(std::shared_ptr wset, wf::point_t p) { - auto vp = output->wset()->get_current_workspace(); - auto size = output->get_screen_size(); + auto vp = wset->get_current_workspace(); + auto size = wset->get_last_output_geometry().value_or(default_output_resolution); p.x -= vp.x * size.width; p.y -= vp.y * size.height; - return p; } -wf::geometry_t get_output_local_coordinates(wf::output_t *output, wf::geometry_t g) +wf::geometry_t get_wset_local_coordinates(std::shared_ptr wset, wf::geometry_t g) { - auto new_tl = get_output_local_coordinates(output, wf::point_t{g.x, g.y}); + auto new_tl = get_wset_local_coordinates(wset, wf::point_t{g.x, g.y}); g.x = new_tl.x; g.y = new_tl.y; @@ -87,7 +92,7 @@ int32_t split_node_t::calculate_splittable() const return calculate_splittable(this->geometry); } -void split_node_t::recalculate_children(wf::geometry_t available) +void split_node_t::recalculate_children(wf::geometry_t available, wf::txn::transaction_uptr& tx) { if (this->children.empty()) { @@ -110,7 +115,7 @@ void split_node_t::recalculate_children(wf::geometry_t available) return (current / old_child_sum) * total_splittable; }; - set_gaps(this->gaps); + set_gaps(this->gaps, tx); /* For each child, assign its percentage of the whole. */ for (auto& child : this->children) @@ -123,11 +128,11 @@ void split_node_t::recalculate_children(wf::geometry_t available) /* Set new size */ int32_t child_size = child_end - child_start; - child->set_geometry(get_child_geometry(child_start, child_size)); + child->set_geometry(get_child_geometry(child_start, child_size), tx); } } -void split_node_t::add_child(std::unique_ptr child, int index) +void split_node_t::add_child(std::unique_ptr child, wf::txn::transaction_uptr& tx, int index) { /* * Strategy: @@ -160,14 +165,14 @@ void split_node_t::add_child(std::unique_ptr child, int index) this->children.emplace(this->children.begin() + index, std::move(child)); - set_gaps(this->gaps); + set_gaps(this->gaps, tx); /* Recalculate geometry */ - recalculate_children(geometry); + recalculate_children(geometry, tx); } std::unique_ptr split_node_t::remove_child( - nonstd::observer_ptr child) + nonstd::observer_ptr child, wf::txn::transaction_uptr& tx) { /* Remove child */ std::unique_ptr result; @@ -186,19 +191,19 @@ std::unique_ptr split_node_t::remove_child( } /* Remaining children have the full geometry */ - recalculate_children(this->geometry); + recalculate_children(this->geometry, tx); result->parent = nullptr; return result; } -void split_node_t::set_geometry(wf::geometry_t geometry) +void split_node_t::set_geometry(wf::geometry_t geometry, wf::txn::transaction_uptr& tx) { - tree_node_t::set_geometry(geometry); - recalculate_children(geometry); + tree_node_t::set_geometry(geometry, tx); + recalculate_children(geometry, tx); } -void split_node_t::set_gaps(const gap_size_t& gaps) +void split_node_t::set_gaps(const gap_size_t& gaps, wf::txn::transaction_uptr& tx) { this->gaps = gaps; for (const auto& child : this->children) @@ -234,7 +239,7 @@ void split_node_t::set_gaps(const gap_size_t& gaps) *second_edge = gaps.internal; } - child->set_gaps(child_gaps); + child->set_gaps(child_gaps, tx); } } @@ -269,7 +274,7 @@ struct view_node_t::scale_transformer_t : public wf::scene::view_2d_transformer_ { wf::geometry_t box; - scale_transformer_t(wayfire_view view, wf::geometry_t box) : + scale_transformer_t(wayfire_toplevel_view view, wf::geometry_t box) : wf::scene::view_2d_transformer_t(view) { set_box(box); @@ -281,7 +286,7 @@ struct view_node_t::scale_transformer_t : public wf::scene::view_2d_transformer_ this->view->damage(); - auto current = this->view->get_wm_geometry(); + auto current = toplevel_cast(this->view)->get_geometry(); if ((current.width <= 0) || (current.height <= 0)) { /* view possibly unmapped?? */ @@ -327,7 +332,7 @@ class tile_view_animation_t : public wf::grid::grid_animation_t tile_view_animation_t& operator =(tile_view_animation_t&&) = delete; }; -view_node_t::view_node_t(wayfire_view view) +view_node_t::view_node_t(wayfire_toplevel_view view) { this->view = view; view->store_data(std::make_unique(this)); @@ -336,17 +341,12 @@ view_node_t::view_node_t(wayfire_view view) { update_transformer(); }); - this->on_decoration_changed.set_callback([=] (auto) - { - set_geometry(geometry); - }); on_adjust_transformer.set_callback([=] (auto) { update_transformer(); }); view->connect(&on_geometry_changed); - view->connect(&on_decoration_changed); view->connect(&on_adjust_transformer); } @@ -356,7 +356,7 @@ view_node_t::~view_node_t() view->erase_data(); } -void view_node_t::set_gaps(const gap_size_t& size) +void view_node_t::set_gaps(const gap_size_t& size, wf::txn::transaction_uptr& tx) { if ((this->gaps.top != size.top) || (this->gaps.bottom != size.bottom) || @@ -371,21 +371,19 @@ wf::geometry_t view_node_t::calculate_target_geometry() { /* Calculate view geometry in coordinates local to the active workspace, * because tree coordinates are kept in workspace-agnostic coordinates. */ - auto output = view->get_output(); - auto local_geometry = get_output_local_coordinates( - view->get_output(), geometry); + auto wset = view->get_wset(); + auto local_geometry = get_wset_local_coordinates(wset, geometry); local_geometry.x += gaps.left; local_geometry.y += gaps.top; local_geometry.width -= gaps.left + gaps.right; local_geometry.height -= gaps.top + gaps.bottom; - auto size = output->get_screen_size(); + auto size = wset->get_last_output_geometry().value_or(default_output_resolution); /* If view is maximized, we want to use the full available geometry */ - if (view->fullscreen) + if (view->pending_fullscreen()) { - auto vp = output->wset()->get_current_workspace(); - + auto vp = wset->get_current_workspace(); int view_vp_x = std::floor(1.0 * geometry.x / size.width); int view_vp_y = std::floor(1.0 * geometry.y / size.height); @@ -399,10 +397,8 @@ wf::geometry_t view_node_t::calculate_target_geometry() if (view->sticky) { - local_geometry.x = - (local_geometry.x % size.width + size.width) % size.width; - local_geometry.y = - (local_geometry.y % size.height + size.height) % size.height; + local_geometry.x = (local_geometry.x % size.width + size.width) % size.width; + local_geometry.y = (local_geometry.y % size.height + size.height) % size.height; } return local_geometry; @@ -430,7 +426,7 @@ bool view_node_t::needs_crossfade() } static nonstd::observer_ptr ensure_animation( - wayfire_view view, wf::option_sptr_t duration) + wayfire_toplevel_view view, wf::option_sptr_t duration) { if (!view->has_data()) { @@ -442,26 +438,29 @@ static nonstd::observer_ptr ensure_animation( return view->get_data(); } -void view_node_t::set_geometry(wf::geometry_t geometry) +void view_node_t::set_geometry(wf::geometry_t geometry, wf::txn::transaction_uptr& tx) { - tree_node_t::set_geometry(geometry); + tree_node_t::set_geometry(geometry, tx); if (!view->is_mapped()) { return; } - view->set_tiled(TILED_EDGES_ALL); + wf::get_core().default_wm->update_last_windowed_geometry(view); + view->toplevel()->pending().tiled_edges = TILED_EDGES_ALL; + tx->add_object(view->toplevel()); auto target = calculate_target_geometry(); - if (this->needs_crossfade() && (target != view->get_wm_geometry())) + if (this->needs_crossfade() && (target != view->get_geometry())) { view->get_transformed_node()->rem_transformer(scale_transformer_name); ensure_animation(view, animation_duration) - ->adjust_target_geometry(target, -1); + ->adjust_target_geometry(target, -1, tx); } else { - view->set_geometry(target); + view->toplevel()->pending().geometry = target; + tx->add_object(view->toplevel()); } } @@ -479,7 +478,7 @@ void view_node_t::update_transformer() return; } - auto wm = view->get_wm_geometry(); + auto wm = view->get_geometry(); if (wm != target_geometry) { auto tr = ensure_named_transformer(view, @@ -502,7 +501,7 @@ nonstd::observer_ptr view_node_t::get_node(wayfire_view view) } /* ----------------- Generic tree operations implementation ----------------- */ -void flatten_tree(std::unique_ptr& root) +void flatten_tree(std::unique_ptr& root, txn::transaction_uptr& tx) { /* Cannot flatten a view node */ if (root->as_view_node()) @@ -515,7 +514,7 @@ void flatten_tree(std::unique_ptr& root) { for (auto& child : root->children) { - flatten_tree(child); + flatten_tree(child, tx); } return; @@ -541,8 +540,7 @@ void flatten_tree(std::unique_ptr& root) } /* Rewire the tree, skipping the current root */ - auto child = root->as_split_node()->remove_child(child_ptr); - + auto child = root->as_split_node()->remove_child(child_ptr, tx); child->parent = root->parent; root = std::move(child); // overwrite root with the child } diff --git a/plugins/tile/tree.hpp b/plugins/tile/tree.hpp index 8aaba1ce4..a82985fa6 100644 --- a/plugins/tile/tree.hpp +++ b/plugins/tile/tree.hpp @@ -2,8 +2,10 @@ #define WF_TILE_PLUGIN_TREE #include "wayfire/signal-definitions.hpp" +#include "wayfire/workspace-set.hpp" #include #include +#include namespace wf { @@ -46,10 +48,10 @@ struct tree_node_t wf::geometry_t geometry; /** Set the geometry available for the node and its subnodes. */ - virtual void set_geometry(wf::geometry_t geometry); + virtual void set_geometry(wf::geometry_t geometry, wf::txn::transaction_uptr& tx); /** Set the gaps for the node and subnodes. */ - virtual void set_gaps(const gap_size_t& gaps) = 0; + virtual void set_gaps(const gap_size_t& gaps, wf::txn::transaction_uptr& tx) = 0; virtual ~tree_node_t() {} @@ -88,26 +90,26 @@ struct split_node_t : public tree_node_t * @param index The index at which to insert the new child, or -1 for * adding to the end of the child list. */ - void add_child(std::unique_ptr child, int index = -1); + void add_child(std::unique_ptr child, wf::txn::transaction_uptr& tx, int index = -1); /** * Remove a child from the node, and return its unique_ptr */ std::unique_ptr remove_child( - nonstd::observer_ptr child); + nonstd::observer_ptr child, wf::txn::transaction_uptr& tx); /** * Set the total geometry available to the node. This will recursively * resize the children nodes, so that they fit inside the new geometry and * have a size proportional to their old size. */ - void set_geometry(wf::geometry_t geometry) override; + void set_geometry(wf::geometry_t geometry, wf::txn::transaction_uptr& tx) override; /** * Set the gaps for the subnodes. The internal gap will override * the corresponding edges for each child. */ - void set_gaps(const gap_size_t& gaps) override; + void set_gaps(const gap_size_t& gaps, wf::txn::transaction_uptr& tx) override; split_node_t(split_direction_t direction); split_direction_t get_split_direction() const; @@ -119,7 +121,7 @@ struct split_node_t : public tree_node_t * Resize the children so that they fit inside the given * available_geometry. */ - void recalculate_children(wf::geometry_t available_geometry); + void recalculate_children(wf::geometry_t available_geometry, wf::txn::transaction_uptr& tx); /** * Calculate the geometry of a child if it has child_size as one @@ -147,10 +149,10 @@ struct tile_adjust_transformer_signal */ struct view_node_t : public tree_node_t { - view_node_t(wayfire_view view); + view_node_t(wayfire_toplevel_view view); ~view_node_t(); - wayfire_view view; + wayfire_toplevel_view view; /** * Set the geometry of the node and the contained view. * @@ -158,13 +160,13 @@ struct view_node_t : public tree_node_t * geometry of the node. For example, a fullscreen view will always have * the geometry of the whole output. */ - void set_geometry(wf::geometry_t geometry) override; + void set_geometry(wf::geometry_t geometry, wf::txn::transaction_uptr& tx) override; /** * Set the gaps for non-fullscreen mode. * The gap sizes will be subtracted from all edges of the view's geometry. */ - void set_gaps(const gap_size_t& gaps) override; + void set_gaps(const gap_size_t& gaps, wf::txn::transaction_uptr& tx) override; /* Return the tree node corresponding to the view, or nullptr if none */ static nonstd::observer_ptr get_node(wayfire_view view); @@ -174,7 +176,6 @@ struct view_node_t : public tree_node_t nonstd::observer_ptr transformer; wf::signal::connection_t on_geometry_changed; - wf::signal::connection_t on_decoration_changed; wf::signal::connection_t on_adjust_transformer; wf::option_wrapper_t animation_duration{"simple-tile/animation_duration"}; @@ -198,7 +199,7 @@ struct view_node_t : public tree_node_t * Note: this will potentially invalidate pointers to the tree and modify * the given parameter. */ -void flatten_tree(std::unique_ptr& root); +void flatten_tree(std::unique_ptr& root, wf::txn::transaction_uptr& tx); /** * Get the root of the tree which node is part of @@ -206,11 +207,15 @@ void flatten_tree(std::unique_ptr& root); nonstd::observer_ptr get_root(nonstd::observer_ptr node); /** - * Transform coordinates from the tiling trees coordinate system to output-local - * coordinates. + * Transform coordinates from the tiling trees coordinate system to wset-local coordinates. */ -wf::geometry_t get_output_local_coordinates(wf::output_t *output, wf::geometry_t g); -wf::point_t get_output_local_coordinates(wf::output_t *output, wf::point_t g); +wf::geometry_t get_wset_local_coordinates(std::shared_ptr wset, wf::geometry_t g); +wf::point_t get_wset_local_coordinates(std::shared_ptr wset, wf::point_t g); + +// Since wsets may not have been attached to any output yet, they may not have a native 'resolution'. +// In this case, we use a default resolution of 1920x1080 in order to layout views. This resolution will be +// automatically adjusted once the wset is added to an output. +static constexpr wf::geometry_t default_output_resolution = {0, 0, 1920, 1080}; } } diff --git a/plugins/vswitch/vswitch.cpp b/plugins/vswitch/vswitch.cpp index e84868709..0a213b7c1 100644 --- a/plugins/vswitch/vswitch.cpp +++ b/plugins/vswitch/vswitch.cpp @@ -53,7 +53,7 @@ class vswitch : public wf::per_output_plugin_instance_t [=] () { output->deactivate_plugin(&grab_interface); }); bindings = std::make_unique(output); - bindings->setup([this] (wf::point_t delta, wayfire_view view, bool only_view) + bindings->setup([this] (wf::point_t delta, wayfire_toplevel_view view, bool only_view) { // Do not switch workspace with sticky view, they are on all // workspaces anyway @@ -76,9 +76,8 @@ class vswitch : public wf::per_output_plugin_instance_t for (auto& v : view->enumerate_views(false)) { - auto origin = wf::origin(v->get_wm_geometry()); - v->move(origin.x + delta.x * size.width, - origin.y + delta.y * size.height); + auto origin = wf::origin(v->get_pending_geometry()); + v->move(origin.x + delta.x * size.width, origin.y + delta.y * size.height); } wf::view_change_workspace_signal data; @@ -159,7 +158,7 @@ class vswitch : public wf::per_output_plugin_instance_t view = nullptr; } - algorithm->set_overlay_view(view); + algorithm->set_overlay_view(toplevel_cast(view)); algorithm->set_target_workspace( output->wset()->get_current_workspace() + delta); diff --git a/plugins/vswitch/wayfire/plugins/vswitch.hpp b/plugins/vswitch/wayfire/plugins/vswitch.hpp index 3ecd95803..1957bea4c 100644 --- a/plugins/vswitch/wayfire/plugins/vswitch.hpp +++ b/plugins/vswitch/wayfire/plugins/vswitch.hpp @@ -3,6 +3,7 @@ #include "wayfire/render-manager.hpp" #include "wayfire/scene-render.hpp" #include "wayfire/scene.hpp" +#include "wayfire/toplevel-view.hpp" #include #include #include @@ -89,7 +90,7 @@ class workspace_switch_t animation.dy.set(animation.dy + cws.y - workspace.y, 0); animation.start(); - std::vector fixed_views; + std::vector fixed_views; if (overlay_view) { fixed_views.push_back(overlay_view); @@ -109,7 +110,7 @@ class workspace_switch_t * @param view The desired overlay view, or NULL if the overlay view needs * to be unset. */ - virtual void set_overlay_view(wayfire_view view) + virtual void set_overlay_view(wayfire_toplevel_view view) { if (this->overlay_view == view) { @@ -180,7 +181,7 @@ class workspace_switch_t std::unique_ptr wall; const std::string vswitch_view_transformer_name = "vswitch-transformer"; - wayfire_view overlay_view; + wayfire_toplevel_view overlay_view; bool running = false; wf::signal::connection_t on_frame = [=] (wall_frame_event_t *ev) @@ -315,7 +316,7 @@ class control_bindings_t * guaranteed that @view will not be nullptr if this is true. */ using binding_callback_t = std::function; + wf::point_t delta, wayfire_toplevel_view view, bool window_only)>; /** * Connect bindings on the output. @@ -476,9 +477,9 @@ class control_bindings_t wf::output_t *output; /** Find the view to switch workspace with */ - virtual wayfire_view get_target_view() + virtual wayfire_toplevel_view get_target_view() { - auto view = output->get_active_view(); + auto view = toplevel_cast(output->get_active_view()); while (view && view->parent) { view = view->parent; @@ -502,7 +503,7 @@ class control_bindings_t * determined by the current workspace, target direction and wraparound * mode. */ - virtual bool handle_dir(wf::point_t dir, wayfire_view view, bool window_only, + virtual bool handle_dir(wf::point_t dir, wayfire_toplevel_view view, bool window_only, binding_callback_t callback) { if (!view && window_only) diff --git a/plugins/window-rules/view-action-interface.cpp b/plugins/window-rules/view-action-interface.cpp index ea6ee2d9e..6e5cdb951 100644 --- a/plugins/window-rules/view-action-interface.cpp +++ b/plugins/window-rules/view-action-interface.cpp @@ -1,6 +1,7 @@ #include "view-action-interface.hpp" #include "wayfire/output.hpp" +#include "wayfire/toplevel-view.hpp" #include "wayfire/view.hpp" #include "wayfire/workspace-set.hpp" #include "wayfire/plugins/grid.hpp" @@ -9,6 +10,7 @@ #include "wayfire/output-layout.hpp" #include "../wm-actions/wm-actions-signals.hpp" #include +#include #include #include @@ -216,19 +218,19 @@ bool view_action_interface_t::execute(const std::string & name, return true; } -void view_action_interface_t::set_view(wayfire_view view) +void view_action_interface_t::set_view(wayfire_toplevel_view view) { _view = view; } void view_action_interface_t::_maximize() { - _view->tile_request(wf::TILED_EDGES_ALL); + wf::get_core().default_wm->tile_request(_view, wf::TILED_EDGES_ALL); } void view_action_interface_t::_unmaximize() { - _view->tile_request(0); + wf::get_core().default_wm->tile_request(_view, 0); } void view_action_interface_t::_minimize() @@ -239,6 +241,10 @@ void view_action_interface_t::_minimize() void view_action_interface_t::_unminimize() { _view->set_minimized(false); + if (_view->get_output()) + { + _view->get_output()->focus_view(_view, true); + } } void view_action_interface_t::_make_sticky() @@ -484,7 +490,7 @@ void view_action_interface_t::_move(int x, int y) if (output != nullptr) { auto grid = this->_get_workspace_grid_geometry(output); - auto view_geometry = _view->get_wm_geometry(); + auto view_geometry = _view->get_pending_geometry(); view_geometry.x = x; view_geometry.y = y; @@ -516,7 +522,7 @@ void view_action_interface_t::_assign_ws(wf::point_t point) auto delta = point - output->wset()->get_current_workspace(); auto size = output->get_screen_size(); - auto wm = _view->get_wm_geometry(); + auto wm = _view->get_pending_geometry(); _view->move(wm.x + delta.x * size.width, wm.y + delta.y * size.height); } } // End namespace wf. diff --git a/plugins/window-rules/view-action-interface.hpp b/plugins/window-rules/view-action-interface.hpp index a9ad6f426..2a3b6b91d 100644 --- a/plugins/window-rules/view-action-interface.hpp +++ b/plugins/window-rules/view-action-interface.hpp @@ -17,7 +17,7 @@ class view_action_interface_t : public action_interface_t virtual bool execute(const std::string & name, const std::vector & args) override; - void set_view(wayfire_view view); + void set_view(wayfire_toplevel_view view); private: void _maximize(); @@ -54,7 +54,7 @@ class view_action_interface_t : public action_interface_t wf::geometry_t _get_workspace_grid_geometry(wf::output_t *output) const; - wayfire_view _view; + wayfire_toplevel_view _view; }; } // End namespace wf. diff --git a/plugins/window-rules/window-rules.cpp b/plugins/window-rules/window-rules.cpp index 3a7a1fc21..5bcc4a1c4 100644 --- a/plugins/window-rules/window-rules.cpp +++ b/plugins/window-rules/window-rules.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "lambda-rules-registration.hpp" #include "view-action-interface.hpp" @@ -25,7 +26,7 @@ class wayfire_window_rules_t : public wf::per_output_plugin_instance_t public: void init() override; void fini() override; - void apply(const std::string & signal, wayfire_view view); + void apply(const std::string & signal, wayfire_toplevel_view view); private: void setup_rules_from_config(); @@ -34,7 +35,7 @@ class wayfire_window_rules_t : public wf::per_output_plugin_instance_t // Created rule handler. wf::signal::connection_t on_view_mapped = [=] (wf::view_mapped_signal *ev) { - apply("created", ev->view); + apply("created", toplevel_cast(ev->view)); }; // Maximized rule handler. @@ -94,21 +95,19 @@ void wayfire_window_rules_t::fini() } } -void wayfire_window_rules_t::apply(const std::string & signal, wayfire_view view) +void wayfire_window_rules_t::apply(const std::string & signal, wayfire_toplevel_view view) { if (view == nullptr) { - LOGE("View is null."); - return; } - if ((signal == "maximized") && (view->tiled_edges != wf::TILED_EDGES_ALL)) + if ((signal == "maximized") && (view->pending_tiled_edges() != wf::TILED_EDGES_ALL)) { return; } - if ((signal == "unmaximized") && (view->tiled_edges == wf::TILED_EDGES_ALL)) + if ((signal == "unmaximized") && (view->pending_tiled_edges() == wf::TILED_EDGES_ALL)) { return; } diff --git a/plugins/wm-actions/wm-actions.cpp b/plugins/wm-actions/wm-actions.cpp index 6b135c73d..fe81182a3 100644 --- a/plugins/wm-actions/wm-actions.cpp +++ b/plugins/wm-actions/wm-actions.cpp @@ -8,6 +8,8 @@ #include "wayfire/scene.hpp" #include "wayfire/signal-definitions.hpp" #include "wayfire/signal-provider.hpp" +#include "wayfire/toplevel-view.hpp" +#include "wayfire/window-manager.hpp" #include "wm-actions-signals.hpp" class always_on_top_root_node_t : public wf::scene::output_node_t @@ -77,22 +79,18 @@ class wayfire_wm_actions_t : public wf::per_output_plugin_instance_t * Find the selected toplevel view, or nullptr if the selected view is not * toplevel. */ - wayfire_view choose_view(wf::activator_source_t source) + wayfire_toplevel_view choose_view(wf::activator_source_t source) { wayfire_view view; if (source == wf::activator_source_t::BUTTONBINDING) { view = wf::get_core().get_cursor_focus_view(); - } - - view = output->get_active_view(); - if (!view || (view->role != wf::VIEW_ROLE_TOPLEVEL)) - { - return nullptr; } else { - return view; + view = output->get_active_view(); } + + return wf::toplevel_cast(view); } /** @@ -197,7 +195,7 @@ class wayfire_wm_actions_t : public wf::per_output_plugin_instance_t * Execute for_view on the selected view, if available. */ bool execute_for_selected_view(wf::activator_source_t source, - std::function for_view) + std::function for_view) { auto view = choose_view(source); if (!view || !output->can_activate_plugin(&grab_interface)) @@ -220,35 +218,36 @@ class wayfire_wm_actions_t : public wf::per_output_plugin_instance_t wf::activator_callback on_minimize = [=] (auto ev) -> bool { - return execute_for_selected_view(ev.source, [] (wayfire_view view) + return execute_for_selected_view(ev.source, [] (wayfire_toplevel_view view) { - view->minimize_request(!view->minimized); + wf::get_core().default_wm->minimize_request(view, !view->minimized); return true; }); }; wf::activator_callback on_toggle_maximize = [=] (auto ev) -> bool { - return execute_for_selected_view(ev.source, [] (wayfire_view view) + return execute_for_selected_view(ev.source, [] (wayfire_toplevel_view view) { - view->tile_request(view->tiled_edges == - wf::TILED_EDGES_ALL ? 0 : wf::TILED_EDGES_ALL); + wf::get_core().default_wm->tile_request(view, + view->pending_tiled_edges() == wf::TILED_EDGES_ALL ? 0 : wf::TILED_EDGES_ALL); return true; }); }; wf::activator_callback on_toggle_fullscreen = [=] (auto ev) -> bool { - return execute_for_selected_view(ev.source, [] (wayfire_view view) + return execute_for_selected_view(ev.source, [] (wayfire_toplevel_view view) { - view->fullscreen_request(view->get_output(), !view->fullscreen); + wf::get_core().default_wm->fullscreen_request(view, view->get_output(), + !view->pending_fullscreen()); return true; }); }; wf::activator_callback on_toggle_sticky = [=] (auto ev) -> bool { - return execute_for_selected_view(ev.source, [] (wayfire_view view) + return execute_for_selected_view(ev.source, [] (wayfire_toplevel_view view) { view->set_sticky(view->sticky ^ 1); return true; @@ -265,10 +264,8 @@ class wayfire_wm_actions_t : public wf::per_output_plugin_instance_t { if (!view->minimized) { - view->minimize_request(true); - view->store_data( - std::make_unique(), - "wm-actions-showdesktop"); + wf::get_core().default_wm->minimize_request(view, true); + view->store_data(std::make_unique(), "wm-actions-showdesktop"); } } @@ -340,7 +337,7 @@ class wayfire_wm_actions_t : public wf::per_output_plugin_instance_t if (view->has_data("wm-actions-showdesktop")) { view->erase_data("wm-actions-showdesktop"); - view->minimize_request(false); + wf::get_core().default_wm->minimize_request(view, false); } } diff --git a/plugins/wobbly/wayfire/plugins/wobbly/wobbly-signal.hpp b/plugins/wobbly/wayfire/plugins/wobbly/wobbly-signal.hpp index 0a2fd97c6..e7ef538de 100644 --- a/plugins/wobbly/wayfire/plugins/wobbly/wobbly-signal.hpp +++ b/plugins/wobbly/wayfire/plugins/wobbly/wobbly-signal.hpp @@ -22,7 +22,7 @@ enum wobbly_event */ struct wobbly_signal { - wayfire_view view; + wayfire_toplevel_view view; wobbly_event events; @@ -41,7 +41,7 @@ struct wobbly_signal /** * Start wobblying when the view is being grabbed, for ex. when moving it */ -inline void start_wobbly(wayfire_view view, int grab_x, int grab_y) +inline void start_wobbly(wayfire_toplevel_view view, int grab_x, int grab_y) { wobbly_signal sig; sig.view = view; @@ -55,7 +55,7 @@ inline void start_wobbly(wayfire_view view, int grab_x, int grab_y) * Start wobblying when the view is being grabbed, for ex. when moving it. * The position is relative to the view, i.e [0.5, 0.5] is the midpoint. */ -inline void start_wobbly_rel(wayfire_view view, wf::pointf_t rel_grab) +inline void start_wobbly_rel(wayfire_toplevel_view view, wf::pointf_t rel_grab) { wobbly_signal sig; sig.view = view; @@ -71,7 +71,7 @@ inline void start_wobbly_rel(wayfire_view view, wf::pointf_t rel_grab) /** * Release the wobbly grab */ -inline void end_wobbly(wayfire_view view) +inline void end_wobbly(wayfire_toplevel_view view) { wobbly_signal sig; sig.view = view; @@ -82,7 +82,7 @@ inline void end_wobbly(wayfire_view view) /** * Indicate that the grab has moved (i.e cursor moved, touch moved, etc.) */ -inline void move_wobbly(wayfire_view view, int grab_x, int grab_y) +inline void move_wobbly(wayfire_toplevel_view view, int grab_x, int grab_y) { wobbly_signal sig; sig.view = view; @@ -95,7 +95,7 @@ inline void move_wobbly(wayfire_view view, int grab_x, int grab_y) * Temporarily activate wobbly on the view. * This is useful when animating some transition like fullscreening, tiling, etc. */ -inline void activate_wobbly(wayfire_view view) +inline void activate_wobbly(wayfire_toplevel_view view) { if (!view->get_transformed_node()->get_transformer("wobbly")) { @@ -109,7 +109,7 @@ inline void activate_wobbly(wayfire_view view) /** * Translate the wobbly model (and its grab point, if any). */ -inline void translate_wobbly(wayfire_view view, wf::point_t delta) +inline void translate_wobbly(wayfire_toplevel_view view, wf::point_t delta) { wobbly_signal sig; sig.view = view; @@ -123,7 +123,7 @@ inline void translate_wobbly(wayfire_view view, wf::point_t delta) * This means that its four corners will be held in place, until the model is * untiled. */ -inline void set_tiled_wobbly(wayfire_view view, bool tiled) +inline void set_tiled_wobbly(wayfire_toplevel_view view, bool tiled) { wobbly_signal sig; sig.view = view; @@ -134,7 +134,7 @@ inline void set_tiled_wobbly(wayfire_view view, bool tiled) /** * Change the wobbly model geometry, without re-activating the springs. */ -inline void modify_wobbly(wayfire_view view, wf::geometry_t target) +inline void modify_wobbly(wayfire_toplevel_view view, wf::geometry_t target) { wobbly_signal sig; sig.view = view; diff --git a/plugins/wobbly/wobbly.cpp b/plugins/wobbly/wobbly.cpp index 17e45042b..947bdb79e 100644 --- a/plugins/wobbly/wobbly.cpp +++ b/plugins/wobbly/wobbly.cpp @@ -243,7 +243,7 @@ class iwobbly_state_t * This isn't really meant to be used standalone, only subclasses should * be instantiated. */ - iwobbly_state_t(const wobbly_model_t& m, wayfire_view v) : + iwobbly_state_t(const wobbly_model_t& m, wayfire_toplevel_view v) : view(v), model(m) { bounding_box = {model->x, model->y, model->width, model->height}; @@ -283,7 +283,7 @@ class iwobbly_state_t } protected: - wayfire_view view; + wayfire_toplevel_view view; const wobbly_model_t& model; wf::geometry_t bounding_box; }; @@ -471,7 +471,7 @@ class wobbly_state_floating_t : public iwobbly_state_t /* Synchronize view position with the model */ auto new_bbox = view->get_transformed_node()->get_transformer("wobbly") ->get_children_bounding_box(); - auto wm = view->get_wm_geometry(); + auto wm = view->get_geometry(); int target_x = model->x + wm.x - new_bbox.x; int target_y = model->y + wm.y - new_bbox.y; @@ -548,7 +548,7 @@ class wobbly_state_free_t : public iwobbly_state_t class wobbly_transformer_node_t : public wf::scene::floating_inner_node_t { public: - wobbly_transformer_node_t(wayfire_view view) : floating_inner_node_t(false) + wobbly_transformer_node_t(wayfire_toplevel_view view) : floating_inner_node_t(false) { this->view = view; init_model(); @@ -609,7 +609,7 @@ class wobbly_transformer_node_t : public wf::scene::floating_inner_node_t } private: - wayfire_view view; + wayfire_toplevel_view view; wf::effect_hook_t pre_hook; wf::signal::connection_t on_view_unmap = [=] (wf::view_unmapped_signal*) @@ -740,7 +740,7 @@ class wobbly_transformer_node_t : public wf::scene::floating_inner_node_t tiled = force_tile; } else { - tiled = (force_tile || view->tiled_edges) || view->fullscreen; + tiled = (force_tile || view->pending_tiled_edges()) || view->pending_fullscreen(); } uint32_t next_state_mask = 0; diff --git a/src/api/wayfire/compositor-view.hpp b/src/api/wayfire/compositor-view.hpp index 7c8efd647..6fad3b975 100644 --- a/src/api/wayfire/compositor-view.hpp +++ b/src/api/wayfire/compositor-view.hpp @@ -1,6 +1,7 @@ #ifndef COMPOSITOR_VIEW_HPP #define COMPOSITOR_VIEW_HPP +#include "wayfire/geometry.hpp" #include "wayfire/view.hpp" #include @@ -42,15 +43,15 @@ class color_rect_view_t : public wf::view_interface_t /** Set the border width. */ virtual void set_border(int width); + /** Set the view geometry. */ + virtual void set_geometry(wf::geometry_t geometry); + virtual wf::geometry_t get_geometry(); + /* required for view_interface_t */ - virtual void move(int x, int y) override; - virtual void resize(int w, int h) override; - virtual wf::geometry_t get_output_geometry() override; virtual bool is_mapped() const override; virtual wlr_surface *get_keyboard_focus_surface() override; virtual bool is_focusable() const override; - virtual bool should_be_decorated() override; }; } diff --git a/src/api/wayfire/core.hpp b/src/api/wayfire/core.hpp index 36822abb1..8f7068716 100644 --- a/src/api/wayfire/core.hpp +++ b/src/api/wayfire/core.hpp @@ -21,6 +21,8 @@ namespace wf { class view_interface_t; +class toplevel_view_interface_t; +class window_manager_t; namespace scene { @@ -40,6 +42,7 @@ struct gesture_state_t; } using wayfire_view = nonstd::observer_ptr; +using wayfire_toplevel_view = nonstd::observer_ptr; namespace wf { @@ -106,6 +109,7 @@ class compositor_core_t : public wf::object_base_t, public signal::provider_t std::unique_ptr bindings; std::unique_ptr seat; std::unique_ptr tx_manager; + std::unique_ptr default_wm; /** * Various protocols supported by wlroots @@ -322,7 +326,7 @@ class compositor_core_t : public wf::object_base_t, public signal::provider_t * set, it will adjust the view geometry for the new output and clamp * it to the output geometry so it is at an expected size and position. */ -void move_view_to_output(wayfire_view v, wf::output_t *new_output, bool reconfigure); +void move_view_to_output(wayfire_toplevel_view v, wf::output_t *new_output, bool reconfigure); /** * Simply a convenience function to call wf::compositor_core_t::get() diff --git a/src/api/wayfire/debug.hpp b/src/api/wayfire/debug.hpp index 2850b6b09..7521be79b 100644 --- a/src/api/wayfire/debug.hpp +++ b/src/api/wayfire/debug.hpp @@ -74,6 +74,8 @@ enum class logging_category : size_t WSET = 6, // Keyboard-related events KBD = 7, + // Xwayland-related events + XWL = 8, TOTAL, }; diff --git a/src/api/wayfire/decorator.hpp b/src/api/wayfire/decorator.hpp deleted file mode 100644 index 331f66fd7..000000000 --- a/src/api/wayfire/decorator.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef DECORATOR_HPP -#define DECORATOR_HPP - -#include - -namespace wf -{ -/** - * Describes the size of the decoration frame around a toplevel. - */ -struct decoration_margins_t -{ - int left; - int right; - int bottom; - int top; -}; - -class decorator_frame_t_t -{ - public: - virtual decoration_margins_t get_margins() = 0; - - wf::geometry_t expand_wm_geometry(wf::geometry_t contained_wm_geometry) - { - auto margins = get_margins(); - contained_wm_geometry.x -= margins.left; - contained_wm_geometry.y -= margins.top; - contained_wm_geometry.width += margins.left + margins.right; - contained_wm_geometry.height += margins.top + margins.bottom; - return contained_wm_geometry; - } - - void calculate_resize_size(int& target_width, int& target_height) - { - auto margins = get_margins(); - target_width -= margins.left + margins.right; - target_height -= margins.top + margins.bottom; - target_width = std::max(target_width, 1); - target_height = std::max(target_height, 1); - } - - virtual ~decorator_frame_t_t() = default; -}; -} - -#endif /* end of include guard: DECORATOR_HPP */ diff --git a/src/api/wayfire/signal-definitions.hpp b/src/api/wayfire/signal-definitions.hpp index 8579c7c80..0a398d530 100644 --- a/src/api/wayfire/signal-definitions.hpp +++ b/src/api/wayfire/signal-definitions.hpp @@ -319,7 +319,7 @@ struct workspace_change_request_signal * Note that the views might still be moved if a previous workspace change * request is being serviced. */ - std::vector fixed_views; + std::vector fixed_views; }; /** @@ -426,7 +426,7 @@ struct view_parent_changed_signal */ struct view_minimized_signal { - wayfire_view view; + wayfire_toplevel_view view; }; /** @@ -437,7 +437,7 @@ struct view_minimized_signal */ struct view_minimize_request_signal { - wayfire_view view; + wayfire_toplevel_view view; /** true is minimized, false is restored */ bool state; @@ -462,7 +462,7 @@ struct view_activated_state_signal */ struct view_tiled_signal { - wayfire_view view; + wayfire_toplevel_view view; /** Previously tiled edges */ uint32_t old_edges; @@ -478,7 +478,7 @@ struct view_tiled_signal */ struct view_tile_request_signal { - wayfire_view view; + wayfire_toplevel_view view; /** The desired edges */ uint32_t edges; @@ -508,7 +508,7 @@ struct view_tile_request_signal */ struct view_fullscreen_signal { - wayfire_view view; + wayfire_toplevel_view view; bool state; }; @@ -520,7 +520,7 @@ struct view_fullscreen_signal */ struct view_fullscreen_request_signal { - wayfire_view view; + wayfire_toplevel_view view; /** The desired fullscreen state */ bool state; @@ -567,7 +567,7 @@ struct view_focus_request_signal */ struct view_set_sticky_signal { - wayfire_view view; + wayfire_toplevel_view view; }; /** @@ -606,7 +606,7 @@ struct view_show_window_menu_signal */ struct view_geometry_changed_signal { - wayfire_view view; + wayfire_toplevel_view view; /** The old wm geometry */ wf::geometry_t old_geometry; @@ -619,7 +619,7 @@ struct view_geometry_changed_signal */ struct view_change_workspace_signal { - wayfire_view view; + wayfire_toplevel_view view; wf::point_t from, to; @@ -636,17 +636,7 @@ struct view_change_workspace_signal */ struct view_decoration_state_updated_signal { - wayfire_view view; -}; - -/** - * on: view - * when: Whenever the view's decoration changes. - * argument: unused. - */ -struct view_decoration_changed_signal -{ - wayfire_view view; + wayfire_toplevel_view view; }; /** @@ -671,7 +661,7 @@ struct view_ping_timeout_signal struct view_pre_moved_to_wset_signal { /* The view being moved */ - wayfire_view view; + wayfire_toplevel_view view; /* The old wset the view was on, may be NULL. */ std::shared_ptr old_wset; /* The new wset the view is being moved to. */ @@ -685,7 +675,7 @@ struct view_pre_moved_to_wset_signal struct view_moved_to_wset_signal { /* The view being moved */ - wayfire_view view; + wayfire_toplevel_view view; /* The old wset the view was on, may be NULL. */ std::shared_ptr old_wset; /* The new wset the view is being moved to. */ @@ -731,7 +721,7 @@ struct focus_view_signal */ struct view_move_request_signal { - wayfire_view view; + wayfire_toplevel_view view; }; /** @@ -741,7 +731,7 @@ struct view_move_request_signal */ struct view_resize_request_signal { - wayfire_view view; + wayfire_toplevel_view view; /** The requested resize edges */ uint32_t edges; diff --git a/src/api/wayfire/toplevel-view.hpp b/src/api/wayfire/toplevel-view.hpp new file mode 100644 index 000000000..851fbdd9e --- /dev/null +++ b/src/api/wayfire/toplevel-view.hpp @@ -0,0 +1,211 @@ +#pragma once + +#include "wayfire/nonstd/observer_ptr.h" +#include +#include + +namespace wf +{ +class toplevel_t; +class toplevel_view_interface_t; +class window_manager_t; +} + +using wayfire_toplevel_view = nonstd::observer_ptr; + +namespace wf +{ +/** + * A list of standard actions which may be allowed on a view. + */ +enum view_allowed_actions_t +{ + // None of the actions below are allowed. + VIEW_ALLOW_NONE = 0, + // It is allowed to move the view anywhere on the screen. + VIEW_ALLOW_MOVE = (1 << 0), + // It is allowed to resize the view arbitrarily. + VIEW_ALLOW_RESIZE = (1 << 1), + // It is allowed to move the view to another workspace. + VIEW_ALLOW_WS_CHANGE = (1 << 2), + // All of the actions above are allowed. + VIEW_ALLOW_ALL = VIEW_ALLOW_MOVE | VIEW_ALLOW_RESIZE | VIEW_ALLOW_WS_CHANGE, +}; + +/** + * A bitmask consisting of all tiled edges. + * This corresponds to a maximized state. + */ +constexpr uint32_t TILED_EDGES_ALL = + WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT; + +/** + * Toplevel views are a subtype of views which have an associated toplevel object. As such, they may be moved, + * resized, etc. freely by plugins and have many additional operations when compared to other view types. + */ +class toplevel_view_interface_t : public wf::view_interface_t +{ + public: + /** + * Get the toplevel object associated with the view. + */ + const std::shared_ptr& toplevel() const; + + /** + * The toplevel parent of the view, for ex. the main view of a file chooser + * dialogue. + */ + wayfire_toplevel_view parent = nullptr; + + /** + * A list of the children views (typically dialogs). + */ + std::vector children; + + /** + * Set the toplevel parent of the view, and adjust the children's list of + * the parent. + */ + void set_toplevel_parent(wayfire_toplevel_view parent); + + /** + * Generate a list of all views in the view's tree. + * This includes the view itself, its @children and so on. + * + * @param mapped_only Whether to include only mapped views. + * + * @return A list of all views in the view's tree. This includes the view + * itself, its @children and so on. + */ + std::vector enumerate_views(bool mapped_only = true); + + /** + * A wrapper function for updating the toplevel's position. + * Equivalent to setting the pending coordinates of the toplevel and committing it in a new transaction. + */ + void move(int x, int y); + + /** + * A wrapper function for updating the toplevel's dimensions. + * Equivalent to setting the pending dimensions of the toplevel and committing it in a new transaction. + */ + void resize(int w, int h); + + /** + * A wrapper function for updating the toplevel's geometry. + * Equivalent to setting the pending geometry of the toplevel and committing it in a new transaction. + */ + void set_geometry(wf::geometry_t g); + + /** + * Request that the view resizes to its native size. + */ + virtual void request_native_size(); + + /** Whether the view is in activated state, usually you want to use either + * set_activated() or focus_request() */ + bool activated = false; + /** Whether the view is in minimized state, usually you want to use either + * set_minimized() or minimize_request() */ + bool minimized = false; + /** Whether the view is sticky. If a view is sticky it will not be affected + * by changes of the current workspace. */ + bool sticky = false; + + /** Set the minimized state of the view. */ + virtual void set_minimized(bool minimized); + /** Set the view's activated state. */ + virtual void set_activated(bool active); + /** Set the view's sticky state. */ + virtual void set_sticky(bool sticky); + + inline uint32_t pending_tiled_edges() const + { + return toplevel()->pending().tiled_edges; + } + + inline bool pending_fullscreen() const + { + return toplevel()->pending().fullscreen; + } + + inline wf::geometry_t get_geometry() const + { + return toplevel()->current().geometry; + } + + inline wf::geometry_t get_pending_geometry() const + { + return toplevel()->pending().geometry; + } + + /** + * Get the allowed actions for this view. By default, all actions are allowed, but plugins may disable + * individual actions. + * + * The allowed actions are a bitmask of @view_allowed_actions_t. + */ + uint32_t get_allowed_actions() const; + + /** + * Set the allowed actions for the view. + * + * @param actions The allowed actions, a bitmask of @view_allowed_actions_t. + */ + void set_allowed_actions(uint32_t actions) const; + + /** + * Get the minimize target for this view, i.e when displaying a minimize + * animation, where the animation's target should be. Defaults to {0,0,0,0}. + * + * @return the minimize target + */ + virtual wlr_box get_minimize_hint(); + + /** + * Sets the minimize target for this view, i.e when displaying a minimize + * animation, where the animation's target should be. + * @param hint The new minimize target rectangle, in output-local coordinates. + */ + virtual void set_minimize_hint(wlr_box hint); + + /** @return true if the view needs decorations */ + virtual bool should_be_decorated(); + + /** + * Set the view's output. + * + * If the new output is different from the previous, the view will be + * removed from the layer it was on the old output. + */ + virtual void set_output(wf::output_t *new_output) override; + + /** + * Get the workspace set the view is attached to, if any. + */ + std::shared_ptr get_wset(); + + virtual ~toplevel_view_interface_t(); + + protected: + /** + * When a view is being destroyed, all associated objects like subsurfaces, + * transformers and custom data are destroyed. + * + * In general, we want to make sure that these associated objects are freed + * before the actual view object destruction starts. Thus, deinitialize() + * is called from core just before destroying the view. + */ + void deinitialize() override; +}; + +inline wayfire_toplevel_view toplevel_cast(wayfire_view view) +{ + return dynamic_cast(view.get()); +} + +// Find the view which has the given toplevel, if such a view exists. +// The view might not exist if it was destroyed, but a plugin holds on to a stale toplevel pointer. +wayfire_toplevel_view find_view_for_toplevel( + std::shared_ptr toplevel); +} diff --git a/src/api/wayfire/toplevel.hpp b/src/api/wayfire/toplevel.hpp new file mode 100644 index 000000000..cefa95fbb --- /dev/null +++ b/src/api/wayfire/toplevel.hpp @@ -0,0 +1,148 @@ +#pragma once + +#include +#include "wayfire/geometry.hpp" +#include "wayfire/object.hpp" +#include + +namespace wf +{ +/** + * Describes the size of the decoration frame around a toplevel. + */ +struct decoration_margins_t +{ + int left; + int right; + int bottom; + int top; +}; + +struct toplevel_state_t +{ + /** + * Mapped toplevel objects are ready to be presented to the user and can interact with input. + * Unmapped toplevels usually are not displayed and do not interact with any plugins until they are mapped + * at a later point in time. + */ + bool mapped = false; + + /** + * The geometry of the toplevel, as seen by the 'window manager'. This includes for example decorations, + * but excludes shadows or subsurfaces sticking out of the main surface. + */ + wf::geometry_t geometry = {100, 100, 0, 0}; + + /** + * A bitmask of WLR_EDGE_* values. Indicates the edge or corner of the toplevel which should stay immobile + * if the client resizes in a way not indicated by the compositor. + * + * The default gravity is the top-left corner, which stays immobile if the client for example resizes + * itself or does not obey a resize request sent by the compositor. + */ + uint32_t gravity = WLR_EDGE_LEFT | WLR_EDGE_TOP; + + /** + * The tiled edges of the toplevel. + * Tiled edges are edges of the toplevel that are aligned to other objects (output edge, other toplevels, + * etc.). Clients usually draw no shadows, rounded corners and similar decorations on tiled edges. + * + * Usually, when all edges are tiled, the toplevel is considered maximized. + */ + uint32_t tiled_edges = 0; + + /** + * The fullscreen state of the view. Fullscreen clients are typically shown above panels and take up the + * full size of their primary output. + */ + bool fullscreen = false; + + /** + * The size of the server-side decorations around the view. + * + * Note that the margin values should be updated by decoration plugins before the toplevel state is + * committed, for example during the new_transaction_signal. As a result, the pending margins are not + * always meaningful for plugins, and they should avoid reading these values as they likely will not be + * finalized before the view is actually committed. + */ + decoration_margins_t margins = {0, 0, 0, 0}; +}; + +/** + * Toplevels are a kind of views which can be moved, resized and whose state can change (fullscreen, tiled, + * etc). Most of the toplevel's attributes are double-buffered and are changed via transactions. + */ +class toplevel_t : public wf::txn::transaction_object_t, public wf::object_base_t +{ + public: + /** + * The current state of the toplevel, as was last committed by the client. The main surface's buffers + * contents correspond to the current state. + */ + const toplevel_state_t& current() + { + return _current; + } + + /** + * The committed state of the toplevel, that is, the state which the compositor has requested from the + * client. This state may be different than the current state in case the client has not committed in + * response to the compositor's request. + */ + const toplevel_state_t& committed() + { + return _committed; + } + + /** + * The pending state of a toplevel. It may be changed by plugins. The pending state, however, will not be + * applied until the toplevel is committed as a part of a transaction. + */ + toplevel_state_t& pending() + { + return _pending; + } + + protected: + toplevel_state_t _current; + toplevel_state_t _pending; + toplevel_state_t _committed; + + std::optional last_windowed_geometry; +}; + +// Helper functions when working with toplevel state +inline wf::dimensions_t expand_dimensions_by_margins(wf::dimensions_t dim, + const decoration_margins_t& margins) +{ + dim.width += margins.left + margins.right; + dim.height += margins.top + margins.bottom; + return dim; +} + +inline wf::dimensions_t shrink_dimensions_by_margins(wf::dimensions_t dim, + const decoration_margins_t& margins) +{ + dim.width -= margins.left + margins.right; + dim.height -= margins.top + margins.bottom; + return dim; +} + +inline wf::geometry_t expand_geometry_by_margins(wf::geometry_t geometry, const decoration_margins_t& margins) +{ + geometry.x -= margins.left; + geometry.y -= margins.top; + geometry.width += margins.left + margins.right; + geometry.height += margins.top + margins.bottom; + return geometry; +} + +inline wf::geometry_t shrink_geometry_by_margins(wf::geometry_t geometry, const decoration_margins_t& margins) +{ + geometry.x += margins.left; + geometry.y += margins.top; + geometry.width -= margins.left + margins.right; + geometry.height -= margins.top + margins.bottom; + return geometry; +} +} diff --git a/src/api/wayfire/unstable/translation-node.hpp b/src/api/wayfire/unstable/translation-node.hpp index 4b5ae3c89..215add19f 100644 --- a/src/api/wayfire/unstable/translation-node.hpp +++ b/src/api/wayfire/unstable/translation-node.hpp @@ -38,5 +38,26 @@ class translation_node_t : public wf::scene::floating_inner_node_t protected: wf::point_t offset = {0, 0}; }; + +class translation_node_instance_t : public render_instance_t +{ + protected: + std::vector children; + damage_callback push_damage; + translation_node_t *self; + wf::signal::connection_t on_node_damage; + + public: + translation_node_instance_t(translation_node_t *self, + damage_callback push_damage, wf::output_t *shown_on); + + // Implementation of render_instance_t + void schedule_instructions(std::vector& instructions, + const wf::render_target_t& target, wf::region_t& damage) override; + void render(const wf::render_target_t& target, const wf::region_t& region) override; + void presentation_feedback(wf::output_t *output) override; + wf::scene::direct_scanout try_scanout(wf::output_t *output) override; + void compute_visibility(wf::output_t *output, wf::region_t& visible) override; +}; } } diff --git a/src/api/wayfire/unstable/wlr-surface-node.hpp b/src/api/wayfire/unstable/wlr-surface-node.hpp index c65e3b784..b7ba943bb 100644 --- a/src/api/wayfire/unstable/wlr-surface-node.hpp +++ b/src/api/wayfire/unstable/wlr-surface-node.hpp @@ -65,6 +65,7 @@ class wlr_surface_node_t : public node_t, public zero_copy_texturable_node_t wlr_surface *get_surface() const; void apply_state(surface_state_t&& state); + void send_frame_done(); private: std::unique_ptr ptr_interaction; @@ -77,7 +78,6 @@ class wlr_surface_node_t : public node_t, public zero_copy_texturable_node_t const bool autocommit; surface_state_t current_state; - void send_frame_done(); void apply_current_surface_state(); }; } diff --git a/src/api/wayfire/view-transform.hpp b/src/api/wayfire/view-transform.hpp index 95d93201b..032bd8859 100644 --- a/src/api/wayfire/view-transform.hpp +++ b/src/api/wayfire/view-transform.hpp @@ -28,6 +28,20 @@ class zero_copy_texturable_node_t } }; +class opaque_region_node_t +{ + public: + virtual ~opaque_region_node_t() = default; + + /** + * Get the opaque region of the node in its parent's coordinate system (same as get_bounding_box()). + */ + virtual wf::region_t get_opaque_region() const + { + return {}; + } +}; + /** * A helper class for implementing transformer nodes. * Transformer nodes usually operate on views and implement special effects, like diff --git a/src/api/wayfire/view.hpp b/src/api/wayfire/view.hpp index 1b84f53c1..719433cfb 100644 --- a/src/api/wayfire/view.hpp +++ b/src/api/wayfire/view.hpp @@ -10,7 +10,6 @@ #include "wayfire/scene-input.hpp" #include "wayfire/scene-render.hpp" #include "wayfire/geometry.hpp" -#include "wayfire/decorator.hpp" #include "wayfire/view-transform.hpp" #include #include @@ -21,7 +20,7 @@ namespace wf { class view_interface_t; class workspace_set_t; -class decorator_frame_t_t; +class decorator_frame_t; } using wayfire_view = nonstd::observer_ptr; @@ -55,30 +54,6 @@ enum view_role_t VIEW_ROLE_DESKTOP_ENVIRONMENT, }; -/** - * A list of standard actions which may be allowed on a view. - */ -enum view_allowed_actions_t -{ - // None of the actions below are allowed. - VIEW_ALLOW_NONE = 0, - // It is allowed to move the view anywhere on the screen. - VIEW_ALLOW_MOVE = (1 << 0), - // It is allowed to resize the view arbitrarily. - VIEW_ALLOW_RESIZE = (1 << 1), - // It is allowed to move the view to another workspace. - VIEW_ALLOW_WS_CHANGE = (1 << 2), - // All of the actions above are allowed. - VIEW_ALLOW_ALL = VIEW_ALLOW_MOVE | VIEW_ALLOW_RESIZE | VIEW_ALLOW_WS_CHANGE, -}; - -/** - * A bitmask consisting of all tiled edges. - * This corresponds to a maximized state. - */ -constexpr uint32_t TILED_EDGES_ALL = - WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT; - /** * view_interface_t is the base class for all "toplevel windows", i.e surfaces * which have no parent. @@ -106,34 +81,6 @@ class view_interface_t : public wf::signal::provider_t, public wf::object_base_t */ const scene::floating_inner_ptr& get_surface_root_node() const; - /** - * The toplevel parent of the view, for ex. the main view of a file chooser - * dialogue. - */ - wayfire_view parent = nullptr; - - /** - * A list of the children views. - */ - std::vector children; - - /** - * Generate a list of all views in the view's tree. - * This includes the view itself, its @children and so on. - * - * @param mapped_only Whether to include only mapped views. - * - * @return A list of all views in the view's tree. This includes the view - * itself, its @children and so on. - */ - std::vector enumerate_views(bool mapped_only = true); - - /** - * Set the toplevel parent of the view, and adjust the children's list of - * the parent. - */ - void set_toplevel_parent(wayfire_view parent); - /** The current view role. */ view_role_t role = VIEW_ROLE_TOPLEVEL; @@ -159,89 +106,15 @@ class view_interface_t : public wf::signal::provider_t, public wf::object_base_t */ virtual wf::output_t *get_output(); - /** - * Get the workspace set the view is attached to, if any. - */ - std::shared_ptr get_wset(); - - /** Move the view to the given output-local coordinates. */ - virtual void move(int x, int y) = 0; - - /** - * Request that the view change its size to the given dimensions. The view - * is not obliged to assume the given dimensions. - * - * Maximized and tiled views typically do obey the resize request. - */ - virtual void resize(int w, int h); - - /** - * A convenience function, has the same effect as calling move and resize - * atomically. - */ - virtual void set_geometry(wf::geometry_t g); - - /** - * Start a resizing mode for this view. While a view is resizing, one edge - * or corner of the view is made immobile (exactly the edge/corner opposite - * to the edges which are set as resizing) - * - * @param resizing whether to enable or disable resizing mode - * @param edges the edges which are being resized - */ - virtual void set_resizing(bool resizing, uint32_t edges = 0); - - /** - * Set the view in moving mode. - * - * @param moving whether to enable or disable moving mode - */ - virtual void set_moving(bool moving); - - /** - * Request that the view resizes to its native size. - */ - virtual void request_native_size(); - /** Request that the view closes. */ virtual void close(); - /** - * Get the allowed actions for this view. By default, all actions are allowed, but plugins may disable - * individual actions. - * - * The allowed actions are a bitmask of @view_allowed_actions_t. - */ - uint32_t get_allowed_actions() const; - - /** - * Set the allowed actions for the view. - * - * @param actions The allowed actions, a bitmask of @view_allowed_actions_t. - */ - void set_allowed_actions(uint32_t actions) const; - /** * Ping the view's client. * If the ping request times out, `ping-timeout` event will be emitted. */ virtual void ping(); - /** - * The wm geometry of the view is the portion of the view surface that - * contains the actual contents, for example, without the view shadows, etc. - * - * @return The wm geometry of the view. - */ - virtual wf::geometry_t get_wm_geometry(); - - /** - * @return the geometry of the view. Coordinates are relative to the current - * workspace of the view's output, or with undefined origin if the view is - * not on any output. This doesn't take into account the view's transformers. - */ - virtual wf::geometry_t get_output_geometry() = 0; - /** * @return The bounding box of the view, which includes all (sub)surfaces, * menus, etc. after applying the view transformations. @@ -268,68 +141,6 @@ class view_interface_t : public wf::signal::provider_t, public wf::object_base_t */ virtual bool is_focusable() const; - /** Whether the view is in fullscreen state, usually you want to use either - * set_fullscreen() or fullscreen_request() */ - bool fullscreen = false; - /** Whether the view is in activated state, usually you want to use either - * set_activated() or focus_request() */ - bool activated = false; - /** Whether the view is in minimized state, usually you want to use either - * set_minimized() or minimize_request() */ - bool minimized = false; - bool pending_minimized = false; - /** Whether the view is sticky. If a view is sticky it will not be affected - * by changes of the current workspace. */ - bool sticky = false; - /** The tiled edges of the view, usually you want to use set_tiled(). - * If the view is tiled to all edges, it is considered maximized. */ - uint32_t tiled_edges = 0; - - /** Set the minimized state of the view. */ - virtual void set_minimized(bool minimized); - /** Set the tiled edges of the view */ - virtual void set_tiled(uint32_t edges); - /** Set the fullscreen state of the view */ - virtual void set_fullscreen(bool fullscreen); - /** Set the view's activated state. */ - virtual void set_activated(bool active); - /** Set the view's sticky state. */ - virtual void set_sticky(bool sticky); - - /** Request that an interactive move starts for this view */ - virtual void move_request(); - /** Request that the view is focused on its output */ - virtual void focus_request(); - /** Request that an interactive resize starts for this view */ - virtual void resize_request(uint32_t edges = 0); - /** Request that the view is (un)minimized */ - virtual void minimize_request(bool minimized); - /** - * Request that the view is (un)tiled. - * - * If the view is being tiled, the caller should ensure thaat the view is on - * the correct workspace. - * - * Note: by default, any tiled edges means that the view gets the full - * workarea. - */ - virtual void tile_request(uint32_t tiled_edges); - - /** - * Request that the view is (un)tiled on the given workspace. - */ - virtual void tile_request(uint32_t tiled_edges, wf::point_t ws); - - /** Request that the view is (un)fullscreened on the given output */ - virtual void fullscreen_request(wf::output_t *output, bool state); - - /** - * Request that the view is (un)fullscreened on the given output - * and workspace. - */ - virtual void fullscreen_request(wf::output_t *output, bool state, - wf::point_t ws); - /** Damage the whole view and add the damage to its output */ virtual void damage(); @@ -345,49 +156,13 @@ class view_interface_t : public wf::signal::provider_t, public wf::object_base_t return ""; } - /** - * Get the minimize target for this view, i.e when displaying a minimize - * animation, where the animation's target should be. Defaults to {0,0,0,0}. - * - * @return the minimize target - */ - virtual wlr_box get_minimize_hint(); - - /** - * Sets the minimize target for this view, i.e when displaying a minimize - * animation, where the animation's target should be. - * @param hint The new minimize target rectangle, in output-local coordinates. - */ - virtual void set_minimize_hint(wlr_box hint); - /** @return true if the view needs decorations */ - virtual bool should_be_decorated(); - - /** - * Set the decoration surface for the view. - * - * @param frame The surface to be set as a decoration. - * - * The life-time of the decoration frame is managed by the view itself, so after - * calling this function you probably want to drop any references that you - * hold (excluding the default one) - */ - virtual void set_decoration(std::unique_ptr frame); - - /** - * Get the decoration frame for a view. May be nullptr. - */ - virtual nonstd::observer_ptr get_decoration(); - /** @return true if the view has active transformers */ bool has_transformer(); /** - * A snapshot of the view is a copy of the view's contents into a - * framebuffer. It is used to get an image of the view while it is mapped, - * and continue displaying it afterwards. Additionally, return the captured - * framebuffter + * A snapshot of the view is a copy of the view's contents into a framebuffer. */ - virtual const wf::render_target_t& take_snapshot(); + virtual void take_snapshot(wf::render_target_t& target); /** * View lifetime is managed by reference counting. To take a reference, @@ -451,13 +226,6 @@ class view_interface_t : public wf::signal::provider_t, public wf::object_base_t */ virtual void deinitialize(); - /** - * @return the bounding box of the view before transformers, - * in output-local coordinates - */ - virtual wf::geometry_t get_untransformed_bounding_box(); - - /** * Called when the reference count reaches 0. * It destructs the object and deletes it, so "this" may not be @@ -503,81 +271,21 @@ wayfire_view node_to_view(wf::scene::node_t *node); class view_node_tag_t { public: - virtual ~view_node_tag_t() = default; - virtual wayfire_view get_view() const = 0; -}; - -namespace scene -{ -/** - * A node in the scenegraph representing a single view and its surfaces. - * - * A view is always contained in a floating_inner_node_t responsible for it - * and its view tree. This is necessary, because that floating parent also - * contains inner nodes for each child view (e.g. dialogs). - * - * An example of the structure of a main view with two dialogs, - * one of the dialog having a nested dialog in turn, is the following: - * - * floating_inner_node_t(main_view): - * - view_node_t(main_view) - * - floating_inner_node_t(dialog1): - * - view_node_t(dialog1) - * - floating_inner_node_t(dialog2): - * - view_node_t(dialog2) - * - floating_inner_node_t(dialog2.1): - * - view_node_t(dialog2.1) - * - * Each view node is a structure node for its floating parent (e.g. cannot be - * removed from it). Instead, plugins should reorder/move the view's parent node, - * therefore ensuring that each view moves in the scenegraph together with its - * children. - * - * Each view_node_t also exposes its surfaces as children. - * The children's coordinate system is transformed to match the transformers - * currently applied on the view and then offsetted to start at the top-left - * corner of the view's main surface. - */ -class view_node_t : public scene::floating_inner_node_t, - public zero_copy_texturable_node_t, public view_node_tag_t -{ - public: - view_node_t(wayfire_view view); - std::optional find_node_at(const wf::pointf_t& at) override; - wf::keyboard_focus_node_t keyboard_refocus(wf::output_t *output) override; - std::string stringify() const override; + view_node_tag_t(wayfire_view view) + { + this->view = view; + } - wf::pointf_t to_local(const wf::pointf_t& point) override; - wf::pointf_t to_global(const wf::pointf_t& point) override; + virtual ~view_node_tag_t() = default; - wayfire_view get_view() const override + wayfire_view get_view() const { return view; } - wf::region_t get_opaque_region() const; - - keyboard_interaction_t& keyboard_interaction() override; - - /** - * Views currently gather damage, etc. manually from the surfaces, - * and sometimes render them, sometimes not ... - */ - void gen_render_instances( - std::vector& instances, - damage_callback push_damage, - wf::output_t *output) override; - - std::optional to_texture() const override; - wf::geometry_t get_bounding_box() override; - - protected: - view_node_t(); + private: wayfire_view view; - std::unique_ptr kb_interaction; - wf::signal::connection_t on_view_destroy; }; } -} #endif diff --git a/src/api/wayfire/window-manager.hpp b/src/api/wayfire/window-manager.hpp new file mode 100644 index 000000000..2592424e2 --- /dev/null +++ b/src/api/wayfire/window-manager.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include "wayfire/core.hpp" +#include "wayfire/geometry.hpp" +namespace wf +{ +/** + * An interface which describes basic window management operations on toplevels. + */ +class window_manager_t +{ + public: + virtual ~window_manager_t() = default; + + /** + * Update the remembered last windowed geometry. + * + * When a view is being tiled or fullscreened, we usually want to remember its size and position so that + * it can be restored to that geometry after unfullscreening/untiling. window-manager implementations keep + * track of this when a plugin calls update_last_windowed_geometry(). + */ + virtual void update_last_windowed_geometry(wayfire_toplevel_view view); + + /** + * Get the stored last_windowed_geometry, if it was stored at all. + */ + virtual std::optional get_last_windowed_geometry(wayfire_toplevel_view view); + + /** + * Request that an interactive move starts for the given view. + */ + virtual void move_request(wayfire_toplevel_view view); + + /** + * Request that an interactive resize starts for the given view. + */ + virtual void resize_request(wayfire_toplevel_view view, uint32_t edges = 0); + + /** + * Try to focus the view and its output. + */ + virtual void focus_request(wayfire_toplevel_view view); + + /** Request that the view is (un)minimized */ + virtual void minimize_request(wayfire_toplevel_view view, bool minimized); + + /** + * Request that the view is (un)tiled on the given workspace of its primary output. + * + * Note: by default, any tiled edges means that the view gets the full workarea. + * + * @param ws If no workspace is provided, the view will be tiled on the current workspace. Otherwise, + * the view will be tiled on the provided workspace. + */ + virtual void tile_request(wayfire_toplevel_view view, uint32_t tiled_edges, + std::optional ws = {}); + + /** + * Request that the view is (un)fullscreened on the given workspace of its primary output. + * + * @param ws If no workspace is provided, the view will be fullscreened or restored to the current + * workspace of its primary output. Otherwise, the operation will be done for the given workspace. + */ + virtual void fullscreen_request(wayfire_toplevel_view view, wf::output_t *output, bool state, + std::optional ws = {}); +}; +} diff --git a/src/api/wayfire/workspace-set.hpp b/src/api/wayfire/workspace-set.hpp index 135900427..a3818cc48 100644 --- a/src/api/wayfire/workspace-set.hpp +++ b/src/api/wayfire/workspace-set.hpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include namespace wf { @@ -124,13 +124,13 @@ class workspace_set_t : public wf::signal::provider_t, public wf::object_base_t, * node of the workspace set (i.e. @get_node()). Plugins adding these views have to ensure that the views * are disabled if the workspace set is not active on any output. */ - void add_view(wayfire_view view); + void add_view(wayfire_toplevel_view view); /** * Remove the view from the workspace set. * Note that the view will remain associated with the last output the workspace set was on. */ - void remove_view(wayfire_view view); + void remove_view(wayfire_toplevel_view view); /** * Get a list of all views currently in the workspace set. @@ -143,7 +143,8 @@ class workspace_set_t : public wf::signal::provider_t, public wf::object_base_t, * included in the return value. WSET_CURRENT_WORKSPACE takes higher precedence than this value if * specified. */ - std::vector get_views(uint32_t flags = 0, std::optional workspace = {}); + std::vector get_views(uint32_t flags = 0, + std::optional workspace = {}); /** * Get the main workspace for a view. @@ -151,18 +152,18 @@ class workspace_set_t : public wf::signal::provider_t, public wf::object_base_t, * * If the center is on an invalid workspace, the closest workspace will be returned. */ - wf::point_t get_view_main_workspace(wayfire_view view); + wf::point_t get_view_main_workspace(wayfire_toplevel_view view); /** * Check if the given view is visible on the given workspace */ - bool view_visible_on(wayfire_view view, wf::point_t ws); + bool view_visible_on(wayfire_toplevel_view view, wf::point_t ws); /** * Ensure that the view's wm_geometry is visible on the workspace ws. This * involves moving the view as appropriate. */ - void move_to_workspace(wayfire_view view, wf::point_t ws); + void move_to_workspace(wayfire_toplevel_view view, wf::point_t ws); /** * Directly change the active workspace. @@ -174,7 +175,7 @@ class workspace_set_t : public wf::signal::provider_t, public wf::object_base_t, * current workspace. */ void set_workspace(wf::point_t ws, - const std::vector& fixed_views = {}); + const std::vector& fixed_views = {}); /** * Switch to the given workspace. @@ -186,7 +187,7 @@ class workspace_set_t : public wf::signal::provider_t, public wf::object_base_t, * workspace-change-request-signal. */ void request_workspace(wf::point_t ws, - const std::vector& fixed_views = {}); + const std::vector& fixed_views = {}); /** * @return The given workspace @@ -225,10 +226,10 @@ class workspace_set_t : public wf::signal::provider_t, public wf::object_base_t, }; // A helper function to emit view-pre-moved-to-wset -void emit_view_pre_moved_to_wset_pre(wayfire_view view, +void emit_view_pre_moved_to_wset_pre(wayfire_toplevel_view view, std::shared_ptr old_wset, std::shared_ptr new_wset); // A helper function to emit view-moved-to-wset -void emit_view_moved_to_wset(wayfire_view view, +void emit_view_moved_to_wset(wayfire_toplevel_view view, std::shared_ptr old_wset, std::shared_ptr new_wset); } diff --git a/src/core/core.cpp b/src/core/core.cpp index adb0b1717..2ed9b9035 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -1,9 +1,10 @@ /* Needed for pipe2 */ #ifndef _GNU_SOURCE #define _GNU_SOURCE - #include "wayfire/scene.hpp" + #include "wayfire/core.hpp" #endif +#include "wayfire/scene.hpp" #include #include "wayfire/scene-operations.hpp" #include "wayfire/txn/transaction-manager.hpp" @@ -43,6 +44,7 @@ #include "../output/output-impl.hpp" #include "main.hpp" #include "seat/drag-icon.hpp" +#include #include "core-impl.hpp" @@ -81,6 +83,7 @@ void wf::compositor_core_impl_t::init() { this->scene_root = std::make_shared(); this->tx_manager = std::make_unique(); + this->default_wm = std::make_unique(); wlr_renderer_init_wl_display(renderer, display); @@ -520,7 +523,7 @@ std::string wf::compositor_core_impl_t::get_xwayland_display() return xwayland_get_display(); } -void wf::move_view_to_output(wayfire_view v, wf::output_t *new_output, bool reconfigure) +void wf::move_view_to_output(wayfire_toplevel_view v, wf::output_t *new_output, bool reconfigure) { auto old_output = v->get_output(); auto old_wset = v->get_wset(); @@ -534,9 +537,9 @@ void wf::move_view_to_output(wayfire_view v, wf::output_t *new_output, bool reco if (reconfigure) { - edges = v->tiled_edges; - fullscreen = v->fullscreen; - view_g = v->get_wm_geometry(); + edges = v->pending_tiled_edges(); + fullscreen = v->pending_fullscreen(); + view_g = v->get_pending_geometry(); old_output_g = old_output->get_relative_geometry(); new_output_g = new_output->get_relative_geometry(); auto ratio_x = (double)new_output_g.width / old_output_g.width; @@ -563,10 +566,10 @@ void wf::move_view_to_output(wayfire_view v, wf::output_t *new_output, bool reco { if (fullscreen) { - v->fullscreen_request(new_output, true); + wf::get_core().default_wm->fullscreen_request(v, new_output, true); } else if (edges) { - v->tile_request(edges); + wf::get_core().default_wm->tile_request(v, edges); } else { auto new_g = wf::clamp(view_g, new_output->workarea->get_workarea()); diff --git a/src/core/output-layout.cpp b/src/core/output-layout.cpp index 0259836bd..e84ec3817 100644 --- a/src/core/output-layout.cpp +++ b/src/core/output-layout.cpp @@ -160,7 +160,7 @@ void transfer_views(wf::output_t *from, wf::output_t *to) std::vector non_ws_views; for (auto& view : wf::get_core().get_all_views()) { - if ((view->get_output() == from) && !view->get_wset()) + if ((view->get_output() == from) && (!toplevel_cast(view) || !toplevel_cast(view)->get_wset())) { non_ws_views.push_back(view); // Take a ref, so that the view doesn't get destroyed while we're doing operations on the views diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index c05c267ce..07d948185 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -2,6 +2,7 @@ #include "seat-impl.hpp" #include "wayfire/util.hpp" #include "wayfire/signal-definitions.hpp" +#include "wayfire/view.hpp" #include #include @@ -24,9 +25,9 @@ class input_method_relay wf::signal::connection_t keyboard_focus_changed = [=] (wf::keyboard_focus_changed_signal *ev) { - if (auto vnode = dynamic_cast(ev->new_focus.get())) + if (auto view = wf::node_to_view(ev->new_focus)) { - set_focus(vnode->get_view()->get_wlr_surface()); + set_focus(view->get_wlr_surface()); } else { set_focus(nullptr); diff --git a/src/core/seat/seat.cpp b/src/core/seat/seat.cpp index 2b7cd9cf4..ede232f5d 100644 --- a/src/core/seat/seat.cpp +++ b/src/core/seat/seat.cpp @@ -127,6 +127,12 @@ wf::seat_t::seat_t(wl_display *display, std::string name) : seat(wlr_seat_create priv->on_wlr_pointer_grab_end.set_callback([&] (void*) { + if (priv->drag_active) + { + // Drag is handled separately. + return; + } + if (auto focus = priv->lpointer->get_focus()) { if (dynamic_cast(&focus->pointer_interaction())) diff --git a/src/core/txn/transaction-manager.cpp b/src/core/txn/transaction-manager.cpp index bf13e2713..b1500cfd4 100644 --- a/src/core/txn/transaction-manager.cpp +++ b/src/core/txn/transaction-manager.cpp @@ -1,6 +1,7 @@ #include #include #include "transaction-manager-impl.hpp" +#include "wayfire/debug.hpp" #include "wayfire/txn/transaction.hpp" wf::txn::transaction_manager_t::transaction_manager_t() diff --git a/src/core/view-access-interface.cpp b/src/core/view-access-interface.cpp index 3949c09b0..4d6197025 100644 --- a/src/core/view-access-interface.cpp +++ b/src/core/view-access-interface.cpp @@ -1,5 +1,6 @@ #include "wayfire/condition/access_interface.hpp" #include "wayfire/output.hpp" +#include "wayfire/toplevel-view.hpp" #include "wayfire/view-helpers.hpp" #include "wayfire/view.hpp" #include "wayfire/view-access-interface.hpp" @@ -8,6 +9,7 @@ #include #include #include +#include namespace wf { @@ -33,6 +35,7 @@ variant_t view_access_interface_t::get(const std::string & identifier, bool & er return out; } + uint32_t view_tiled_edges = toplevel_cast(_view) ? toplevel_cast(_view)->pending_tiled_edges() : 0; if (identifier == "app_id") { out = _view->get_app_id(); @@ -64,13 +67,13 @@ variant_t view_access_interface_t::get(const std::string & identifier, bool & er } } else if (identifier == "fullscreen") { - out = _view->fullscreen; + out = toplevel_cast(_view) ? toplevel_cast(_view)->pending_fullscreen() : false; } else if (identifier == "activated") { - out = _view->activated; + out = toplevel_cast(_view) ? toplevel_cast(_view)->activated : false; } else if (identifier == "minimized") { - out = _view->minimized; + out = toplevel_cast(_view) ? toplevel_cast(_view)->minimized : false; } else if (identifier == "focusable") { out = _view->is_focusable(); @@ -79,22 +82,22 @@ variant_t view_access_interface_t::get(const std::string & identifier, bool & er out = _view->is_mapped(); } else if (identifier == "tiled-left") { - out = (_view->tiled_edges & WLR_EDGE_LEFT) > 0; + out = ((view_tiled_edges & WLR_EDGE_LEFT) > 0); } else if (identifier == "tiled-right") { - out = (_view->tiled_edges & WLR_EDGE_RIGHT) > 0; + out = ((view_tiled_edges & WLR_EDGE_RIGHT) > 0); } else if (identifier == "tiled-top") { - out = (_view->tiled_edges & WLR_EDGE_TOP) > 0; + out = ((view_tiled_edges & WLR_EDGE_TOP) > 0); } else if (identifier == "tiled-bottom") { - out = (_view->tiled_edges & WLR_EDGE_BOTTOM) > 0; + out = ((view_tiled_edges & WLR_EDGE_BOTTOM) > 0); } else if (identifier == "maximized") { - out = _view->tiled_edges == TILED_EDGES_ALL; + out = (view_tiled_edges == TILED_EDGES_ALL); } else if (identifier == "floating") { - out = _view->tiled_edges == 0; + out = toplevel_cast(_view) ? (toplevel_cast(_view)->pending_tiled_edges() == 0) : false; } else if (identifier == "type") { do { diff --git a/src/core/window-manager.cpp b/src/core/window-manager.cpp new file mode 100644 index 000000000..71bd97082 --- /dev/null +++ b/src/core/window-manager.cpp @@ -0,0 +1,247 @@ +#include "wayfire/core.hpp" +#include "wayfire/geometry.hpp" +#include "wayfire/object.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace wf +{ +class windowed_geometry_data_t : public wf::custom_data_t +{ + public: + /** Last geometry the view has had in non-tiled and non-fullscreen state. + * -1 as width/height means that no such geometry has been stored. */ + wf::geometry_t last_windowed_geometry = {0, 0, -1, -1}; + + /** + * The workarea when last_windowed_geometry was stored. This is used + * for ex. when untiling a view to determine its geometry relative to the + * (potentially changed) workarea of its output. + */ + wf::geometry_t windowed_geometry_workarea = {0, 0, -1, -1}; +}; + +void wf::window_manager_t::update_last_windowed_geometry(wayfire_toplevel_view view) +{ + if (!view->is_mapped() || view->pending_tiled_edges() || view->pending_fullscreen()) + { + return; + } + + auto windowed = view->get_data_safe(); + + windowed->last_windowed_geometry = view->toplevel()->pending().geometry; + if (view->get_output()) + { + windowed->windowed_geometry_workarea = view->get_output()->workarea->get_workarea(); + } else + { + windowed->windowed_geometry_workarea = {0, 0, -1, -1}; + } +} + +std::optional wf::window_manager_t::get_last_windowed_geometry(wayfire_toplevel_view view) +{ + auto windowed = view->get_data_safe(); + + if ((windowed->windowed_geometry_workarea.width <= 0) || (windowed->last_windowed_geometry.width <= 0)) + { + return {}; + } + + if (!view->get_output()) + { + return windowed->last_windowed_geometry; + } + + const auto& geom = windowed->last_windowed_geometry; + const auto& old_area = windowed->windowed_geometry_workarea; + const auto& new_area = view->get_output()->workarea->get_workarea(); + return wf::geometry_t{ + .x = new_area.x + (geom.x - old_area.x) * new_area.width / old_area.width, + .y = new_area.y + (geom.y - old_area.y) * new_area.height / old_area.height, + .width = geom.width * new_area.width / old_area.width, + .height = geom.height * new_area.height / old_area.height + }; +} + +void window_manager_t::move_request(wayfire_toplevel_view view) +{ + if (view->get_output()) + { + view_move_request_signal data; + data.view = view; + view->get_output()->emit(&data); + } +} + +void window_manager_t::resize_request(wayfire_toplevel_view view, uint32_t edges) +{ + if (view->get_output()) + { + view_resize_request_signal data; + data.view = view; + data.edges = edges; + view->get_output()->emit(&data); + } +} + +void window_manager_t::focus_request(wayfire_toplevel_view view) +{ + if (view->get_output()) + { + view_focus_request_signal data; + data.view = view; + data.self_request = false; + + view->emit(&data); + wf::get_core().emit(&data); + if (!data.carried_out) + { + wf::get_core().focus_output(view->get_output()); + view->get_output()->ensure_visible(view); + view->get_output()->focus_view(view, true); + } + } +} + +void window_manager_t::minimize_request(wayfire_toplevel_view view, bool minimized) +{ + if ((view->minimized == minimized) || !view->is_mapped()) + { + return; + } + + view_minimize_request_signal data; + data.view = view; + data.state = minimized; + + if (view->get_output()) + { + view->get_output()->emit(&data); + } + + if (!data.carried_out) + { + /* Do the default minimization */ + view->set_minimized(minimized); + if (!minimized && view->get_output()) + { + view->get_output()->focus_view(view, true); + } + } +} + +/** + * Put a view on the given workspace. + */ +static void move_to_workspace(wayfire_toplevel_view view, wf::point_t workspace) +{ + auto output = view->get_output(); + auto wm_geometry = view->get_pending_geometry(); + auto delta = workspace - output->wset()->get_current_workspace(); + auto scr_size = output->get_screen_size(); + + wm_geometry.x += scr_size.width * delta.x; + wm_geometry.y += scr_size.height * delta.y; + view->move(wm_geometry.x, wm_geometry.y); +} + +void window_manager_t::tile_request(wayfire_toplevel_view view, + uint32_t tiled_edges, std::optional ws) +{ + if (view->pending_fullscreen() || !view->get_output()) + { + return; + } + + const wf::point_t workspace = ws.value_or(view->get_output()->wset()->get_current_workspace()); + + view_tile_request_signal data; + data.view = view; + data.edges = tiled_edges; + data.workspace = workspace; + data.desired_size = tiled_edges ? view->get_output()->workarea->get_workarea() : + get_last_windowed_geometry(view).value_or(wf::geometry_t{0, 0, -1, -1}); + + update_last_windowed_geometry(view); + view->toplevel()->pending().tiled_edges = tiled_edges; + if (view->is_mapped()) + { + view->get_output()->emit(&data); + } + + if (!data.carried_out) + { + if (data.desired_size.width > 0) + { + // set geometry will commit the state + view->set_geometry(data.desired_size); + } else + { + view->request_native_size(); + wf::get_core().tx_manager->schedule_object(view->toplevel()); + } + + move_to_workspace(view, workspace); + } +} + +void window_manager_t::fullscreen_request(wayfire_toplevel_view view, + wf::output_t *output, bool state, std::optional ws) +{ + wf::output_t *wo = output ?: (view->get_output() ?: wf::get_core().get_active_output()); + const wf::point_t workspace = ws.value_or(wo->wset()->get_current_workspace()); + wf::dassert(wo != nullptr, "Fullscreening should not happen with null output!"); + + /* TODO: what happens if the view is moved to the other output, but not + * fullscreened? We should make sure that it stays visible there */ + if (view->get_output() != wo) + { + // TODO: move_view_to_output seems like a good candidate for inclusion in window-manager + wf::move_view_to_output(view, wo, false); + } + + view_fullscreen_request_signal data; + data.view = view; + data.state = state; + data.workspace = workspace; + data.desired_size = wo->get_relative_geometry(); + + if (!state) + { + data.desired_size = view->pending_tiled_edges() ? wo->workarea->get_workarea() : + get_last_windowed_geometry(view).value_or(wf::geometry_t{0, 0, -1, -1}); + } else + { + update_last_windowed_geometry(view); + } + + view->toplevel()->pending().fullscreen = state; + if (view->is_mapped()) + { + wo->emit(&data); + } + + if (!data.carried_out) + { + if (data.desired_size.width > 0) + { + // set geometry will commit the state + view->set_geometry(data.desired_size); + } else + { + view->request_native_size(); + wf::get_core().tx_manager->schedule_object(view->toplevel()); + } + + move_to_workspace(view, workspace); + } +} +} // namespace wf diff --git a/src/main.cpp b/src/main.cpp index 17092c38d..ac772b0a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -211,6 +211,10 @@ void parse_extended_debugging(const std::vector& categories) { LOGD("Enabling extended debugging for keyboard events"); wf::log::enabled_categories.set((size_t)wf::log::logging_category::KBD, 1); + } else if (cat == "xwayland") + { + LOGD("Enabling extended debugging for xwayland events"); + wf::log::enabled_categories.set((size_t)wf::log::logging_category::XWL, 1); } else { LOGE("Unrecognized debugging category \"", cat, "\""); diff --git a/src/meson.build b/src/meson.build index 6d4ef81bb..5e62ed202 100644 --- a/src/meson.build +++ b/src/meson.build @@ -3,6 +3,7 @@ wayfire_sources = ['geometry.cpp', 'debug.cpp', 'util.cpp', + 'core/window-manager.cpp', 'core/output-layout.cpp', 'core/plugin-loader.cpp', 'core/matcher.cpp', @@ -36,13 +37,18 @@ wayfire_sources = ['geometry.cpp', 'view/surface.cpp', 'view/subsurface.cpp', 'view/view.cpp', + 'view/toplevel-view.cpp', 'view/view-impl.cpp', + 'view/toplevel-node.cpp', 'view/xdg-shell.cpp', + 'view/xdg-shell/xdg-toplevel.cpp', 'view/xdg-shell/xdg-toplevel-view.cpp', 'view/xwayland.cpp', 'view/xwayland/xwayland-toplevel-view.cpp', + 'view/xwayland/xwayland-toplevel.cpp', 'view/xwayland/xwayland-helpers.cpp', - 'view/layer-shell.cpp', + 'view/layer-shell/layer-shell.cpp', + 'view/layer-shell/layer-shell-node.cpp', 'view/view-3d.cpp', 'view/compositor-view.cpp', 'view/wlr-surface-node.cpp', diff --git a/src/output/output-impl.hpp b/src/output/output-impl.hpp index c577d87ca..84a4b8b3e 100644 --- a/src/output/output-impl.hpp +++ b/src/output/output-impl.hpp @@ -62,7 +62,7 @@ class output_impl_t : public output_t virtual ~output_impl_t(); wayfire_view active_view = nullptr; - wayfire_view last_active_toplevel = nullptr; + wayfire_toplevel_view last_active_toplevel = nullptr; /** * Implementations of the public APIs diff --git a/src/output/output.cpp b/src/output/output.cpp index 20c4539a0..3c2e8f044 100644 --- a/src/output/output.cpp +++ b/src/output/output.cpp @@ -8,6 +8,7 @@ #include "wayfire/scene-input.hpp" #include "wayfire/scene-operations.hpp" #include "wayfire/scene.hpp" +#include "wayfire/toplevel-view.hpp" #include "wayfire/view-helpers.hpp" #include "wayfire/view.hpp" #include "../core/core-impl.hpp" @@ -23,6 +24,7 @@ #include #include #include +#include #include #include @@ -38,6 +40,7 @@ void wf::output_impl_t::handle_view_removed(wayfire_view view) if (this->last_active_toplevel == view) { + last_active_toplevel->set_activated(false); this->last_active_toplevel = nullptr; } @@ -281,7 +284,17 @@ bool wf::output_t::ensure_visible(wayfire_view v) static wayfire_view pick_topmost_focusable(wayfire_view view) { - auto all_views = view->enumerate_views(); + if (!wf::toplevel_cast(view)) + { + if (view->get_keyboard_focus_surface()) + { + return view; + } + + return nullptr; + } + + auto all_views = toplevel_cast(view)->enumerate_views(); auto it = std::find_if(all_views.begin(), all_views.end(), [] (wayfire_view v) { return v->get_keyboard_focus_surface() != NULL; }); @@ -323,7 +336,7 @@ void wf::output_impl_t::focus_node(wf::scene::node_ptr new_focus) void wf::output_impl_t::update_active_view(wayfire_view v) { LOGC(KBD, "Output ", this->to_string(), ": active view becomes ", v); - if ((v == nullptr) || (v->role == wf::VIEW_ROLE_TOPLEVEL)) + if ((v == nullptr) || toplevel_cast(v)) { if (last_active_toplevel != v) { @@ -332,18 +345,33 @@ void wf::output_impl_t::update_active_view(wayfire_view v) last_active_toplevel->set_activated(false); } - if (v) + last_active_toplevel = nullptr; + if (auto toplevel = toplevel_cast(v)) { - v->set_activated(true); + toplevel->set_activated(true); + last_active_toplevel = toplevel; } - - last_active_toplevel = v; } } this->active_view = v; } +wayfire_view find_topmost_parent(wayfire_view v) +{ + if (auto toplevel = wf::toplevel_cast(v)) + { + while (toplevel->parent) + { + toplevel = toplevel->parent; + } + + return toplevel; + } + + return v; +} + void wf::output_impl_t::focus_view(wayfire_view v, uint32_t flags) { static wf::option_wrapper_t @@ -351,18 +379,17 @@ void wf::output_impl_t::focus_view(wayfire_view v, uint32_t flags) const auto& make_view_visible = [flags] (wayfire_view view) { - if (view->minimized) + view = find_topmost_parent(view); + if (auto toplevel = toplevel_cast(view)) { - view->minimize_request(false); + if (toplevel->minimized) + { + wf::get_core().default_wm->minimize_request(toplevel, false); + } } if (flags & FOCUS_VIEW_RAISE) { - while (view->parent) - { - view = view->parent; - } - view_bring_to_front(view); } }; @@ -399,9 +426,9 @@ void wf::output_impl_t::focus_view(wayfire_view v, uint32_t flags) return; } - while (all_dialogs_modal && v->parent && v->parent->is_mapped()) + if (all_dialogs_modal) { - v = v->parent; + v = find_topmost_parent(v); } pre_focus_view_signal pre_focus; diff --git a/src/output/promotion-manager.hpp b/src/output/promotion-manager.hpp index 94a234512..e9c35f3bf 100644 --- a/src/output/promotion-manager.hpp +++ b/src/output/promotion-manager.hpp @@ -1,5 +1,6 @@ #pragma once +#include "wayfire/toplevel-view.hpp" #include #include #include @@ -45,14 +46,18 @@ class promotion_manager_t update_promotion_state(); }; - wayfire_view find_top_visible_view(wf::scene::node_ptr root) + wayfire_toplevel_view find_top_visible_view(wf::scene::node_ptr root) { if (auto view = wf::node_to_view(root)) { - if (view->is_mapped() && - output->wset()->view_visible_on(view, output->wset()->get_current_workspace())) + if (!view->is_mapped() || !toplevel_cast(view)) { - return view; + return nullptr; + } + + if (output->wset()->view_visible_on(toplevel_cast(view), output->wset()->get_current_workspace())) + { + return toplevel_cast(view); } } @@ -72,8 +77,8 @@ class promotion_manager_t void update_promotion_state() { - wayfire_view candidate = find_top_visible_view(output->wset()->get_node()); - if (candidate && candidate->fullscreen) + wayfire_toplevel_view candidate = find_top_visible_view(output->wset()->get_node()); + if (candidate && candidate->toplevel()->current().fullscreen) { start_promotion(); } else diff --git a/src/output/workspace-impl.cpp b/src/output/workspace-impl.cpp index d2bd40fe1..f9f11f698 100644 --- a/src/output/workspace-impl.cpp +++ b/src/output/workspace-impl.cpp @@ -21,6 +21,7 @@ #include "wayfire/scene-input.hpp" #include "wayfire/scene.hpp" #include "wayfire/signal-provider.hpp" +#include "wayfire/toplevel-view.hpp" namespace wf { @@ -231,7 +232,7 @@ struct workspace_set_t::impl for (auto& view : get_views(WSET_MAPPED_ONLY)) { - auto wm = view->get_wm_geometry(); + auto wm = view->get_geometry(); float px = 1. * wm.x / old_w; float py = 1. * wm.y / old_h; float pw = 1. * wm.width / old_w; @@ -266,7 +267,7 @@ struct workspace_set_t::impl for (auto view : get_views(WSET_MAPPED_ONLY)) { - if (!(view->get_wm_geometry() & full_grid)) + if (!(view->get_geometry() & full_grid)) { move_to_workspace(view, get_view_main_workspace(view)); } @@ -275,7 +276,7 @@ struct workspace_set_t::impl wf::signal::connection_t on_view_destruct = [=] (view_destruct_signal *ev) { - remove_view(ev->view); + remove_view(toplevel_cast(ev->view)); }; bool visible = false; @@ -392,7 +393,7 @@ struct workspace_set_t::impl } } - void add_view(wayfire_view view) + void add_view(wayfire_toplevel_view view) { if (std::find(wset_views.begin(), wset_views.end(), view) != wset_views.end()) { @@ -406,7 +407,7 @@ struct workspace_set_t::impl view->set_output(this->output); } - void remove_view(wayfire_view view) + void remove_view(wayfire_toplevel_view view) { auto it = std::find(wset_views.begin(), wset_views.end(), view); if (it == wset_views.end()) @@ -421,7 +422,8 @@ struct workspace_set_t::impl view->priv->current_wset.reset(); } - std::vector get_views(uint32_t flags = 0, std::optional workspace = {}) + std::vector get_views(uint32_t flags = 0, + std::optional workspace = {}) { if (!flags && !workspace) { @@ -434,7 +436,7 @@ struct workspace_set_t::impl } auto views = wset_views; - auto it = std::remove_if(views.begin(), views.end(), [&] (wayfire_view view) + auto it = std::remove_if(views.begin(), views.end(), [&] (wayfire_toplevel_view view) { if ((flags & WSET_MAPPED_ONLY) && !view->is_mapped()) { @@ -462,7 +464,7 @@ struct workspace_set_t::impl if (flags & WSET_SORT_STACKING) { - std::sort(views.begin(), views.end(), [] (wayfire_view a, wayfire_view b) + std::sort(views.begin(), views.end(), [] (wayfire_toplevel_view a, wayfire_view b) { wf::scene::node_t *x = a->get_root_node().get(); wf::scene::node_t *y = b->get_root_node().get(); @@ -482,13 +484,13 @@ struct workspace_set_t::impl } private: - std::vector wset_views; + std::vector wset_views; int current_vx = 0; int current_vy = 0; public: - wf::point_t get_view_main_workspace(wayfire_view view) + wf::point_t get_view_main_workspace(wayfire_toplevel_view view) { if (!workspace_geometry) { @@ -496,7 +498,7 @@ struct workspace_set_t::impl return {0, 0}; } - auto wm = view->get_wm_geometry(); + auto wm = view->get_geometry(); wf::point_t workspace = { current_vx + (int)std::floor((wm.x + wm.width / 2.0) / workspace_geometry->width), current_vy + (int)std::floor((wm.y + wm.height / 2.0) / workspace_geometry->height) @@ -511,7 +513,7 @@ struct workspace_set_t::impl * * @return true if the view is visible on the workspace vp */ - bool view_visible_on(wayfire_view view, wf::point_t vp) + bool view_visible_on(wayfire_toplevel_view view, wf::point_t vp) { if (!workspace_geometry) { @@ -526,13 +528,13 @@ struct workspace_set_t::impl g.y += (vp.y - current_vy) * g.height; } - return g & view->get_wm_geometry(); + return g & view->get_geometry(); } /** * Moves view geometry so that it is visible on the given workspace */ - void move_to_workspace(wayfire_view view, wf::point_t ws) + void move_to_workspace(wayfire_toplevel_view view, wf::point_t ws) { if (!workspace_geometry) { @@ -547,7 +549,7 @@ struct workspace_set_t::impl ws = {current_vx, current_vy}; } - auto box = view->get_wm_geometry(); + auto box = view->get_pending_geometry(); wf::geometry_t visible = *workspace_geometry; visible.x += (ws.x - current_vx) * visible.width; visible.y += (ws.y - current_vy) * visible.height; @@ -577,7 +579,7 @@ struct workspace_set_t::impl } void set_workspace(wf::point_t nws, - const std::vector& fixed_views) + const std::vector& fixed_views) { if (!grid.is_workspace_valid(nws)) { @@ -610,7 +612,7 @@ struct workspace_set_t::impl auto dx = (data.old_viewport.x - nws.x) * screen.width; auto dy = (data.old_viewport.y - nws.y) * screen.height; - std::vector> + std::vector> old_fixed_view_workspaces; old_fixed_view_workspaces.reserve(fixed_views.size()); @@ -626,7 +628,7 @@ struct workspace_set_t::impl { for (auto v : view->enumerate_views()) { - v->move(v->get_wm_geometry().x + dx, v->get_wm_geometry().y + dy); + v->move(v->get_pending_geometry().x + dx, v->get_pending_geometry().y + dy); } } } @@ -681,43 +683,43 @@ void workspace_set_t::set_visible(bool visible) } /* Just pass to the appropriate function from above */ -wf::point_t workspace_set_t::get_view_main_workspace(wayfire_view view) +wf::point_t workspace_set_t::get_view_main_workspace(wayfire_toplevel_view view) { return pimpl->get_view_main_workspace(view); } -bool workspace_set_t::view_visible_on(wayfire_view view, wf::point_t ws) +bool workspace_set_t::view_visible_on(wayfire_toplevel_view view, wf::point_t ws) { return pimpl->view_visible_on(view, ws); } -void workspace_set_t::move_to_workspace(wayfire_view view, wf::point_t ws) +void workspace_set_t::move_to_workspace(wayfire_toplevel_view view, wf::point_t ws) { return pimpl->move_to_workspace(view, ws); } -void workspace_set_t::add_view(wayfire_view view) +void workspace_set_t::add_view(wayfire_toplevel_view view) { pimpl->add_view(view); } -std::vector workspace_set_t::get_views(uint32_t flags, std::optional ws) +std::vector workspace_set_t::get_views(uint32_t flags, std::optional ws) { return pimpl->get_views(flags, ws); } -void workspace_set_t::remove_view(wayfire_view view) +void workspace_set_t::remove_view(wayfire_toplevel_view view) { pimpl->remove_view(view); } void workspace_set_t::set_workspace(wf::point_t ws, - const std::vector& fixed_views) + const std::vector& fixed_views) { return pimpl->set_workspace(ws, fixed_views); } -void workspace_set_t::request_workspace(wf::point_t ws, const std::vector& views) +void workspace_set_t::request_workspace(wf::point_t ws, const std::vector& views) { if (!pimpl->output) { @@ -774,7 +776,7 @@ std::optional workspace_set_t::get_last_output_geometry() return pimpl->workspace_geometry; } -void emit_view_pre_moved_to_wset_pre(wayfire_view view, +void emit_view_pre_moved_to_wset_pre(wayfire_toplevel_view view, std::shared_ptr old_wset, std::shared_ptr new_wset) { view_pre_moved_to_wset_signal data; @@ -784,7 +786,7 @@ void emit_view_pre_moved_to_wset_pre(wayfire_view view, wf::get_core().emit(&data); } -void emit_view_moved_to_wset(wayfire_view view, +void emit_view_moved_to_wset(wayfire_toplevel_view view, std::shared_ptr old_wset, std::shared_ptr new_wset) { view_moved_to_wset_signal data; diff --git a/src/view/compositor-view.cpp b/src/view/compositor-view.cpp index d1e4a82ad..e3e8a3f1d 100644 --- a/src/view/compositor-view.cpp +++ b/src/view/compositor-view.cpp @@ -1,3 +1,5 @@ +#include "view/view-impl.hpp" +#include "wayfire/geometry.hpp" #include "wayfire/scene-render.hpp" #include "wayfire/scene.hpp" #include "wayfire/view.hpp" @@ -14,14 +16,8 @@ static void render_colored_rect(const wf::render_target_t& fb, int x, int y, int w, int h, const wf::color_t& color) { - wf::color_t premultiply{ - color.r * color.a, - color.g * color.a, - color.b * color.a, - color.a}; - - OpenGL::render_rectangle({x, y, w, h}, premultiply, - fb.get_orthographic_projection()); + wf::color_t premultiply{color.r * color.a, color.g * color.a, color.b * color.a, color.a}; + OpenGL::render_rectangle({x, y, w, h}, premultiply, fb.get_orthographic_projection()); } class wf::color_rect_view_t::color_rect_node_t : public wf::scene::floating_inner_node_t @@ -94,7 +90,7 @@ class wf::color_rect_view_t::color_rect_node_t : public wf::scene::floating_inne { if (view) { - return view->get_output_geometry(); + return view->get_geometry(); } else { return {0, 0, 0, 0}; @@ -143,35 +139,16 @@ bool wf::color_rect_view_t::is_mapped() const return _is_mapped; } -void wf::color_rect_view_t::move(int x, int y) +void wf::color_rect_view_t::set_geometry(wf::geometry_t g) { damage(); - view_geometry_changed_signal data; - data.old_geometry = get_wm_geometry(); - - this->geometry.x = x; - this->geometry.y = y; - + this->geometry = g; damage(); - emit(&data); } -void wf::color_rect_view_t::resize(int w, int h) +wf::geometry_t wf::color_rect_view_t::get_geometry() { - damage(); - view_geometry_changed_signal data; - data.old_geometry = get_wm_geometry(); - - this->geometry.width = w; - this->geometry.height = h; - - damage(); - emit(&data); -} - -wf::geometry_t wf::color_rect_view_t::get_output_geometry() -{ - return geometry; + return this->geometry; } wlr_surface*wf::color_rect_view_t::get_keyboard_focus_surface() @@ -184,11 +161,6 @@ bool wf::color_rect_view_t::is_focusable() const return false; } -bool wf::color_rect_view_t::should_be_decorated() -{ - return false; -} - void wf::color_rect_view_t::initialize() { view_interface_t::initialize(); diff --git a/src/view/layer-shell/layer-shell-node.cpp b/src/view/layer-shell/layer-shell-node.cpp new file mode 100644 index 000000000..f8b1e4141 --- /dev/null +++ b/src/view/layer-shell/layer-shell-node.cpp @@ -0,0 +1,206 @@ +#include "layer-shell-node.hpp" +#include "../view-keyboard-interaction.hpp" +#include "wayfire/scene-input.hpp" +#include "../core/core-impl.hpp" +#include "../core/seat/seat-impl.hpp" +#include "wayfire/scene-render.hpp" +#include "wayfire/unstable/translation-node.hpp" +#include + +wf::layer_shell_node_t::layer_shell_node_t(wayfire_view view) : view_node_tag_t(view) +{ + this->kb_interaction = std::make_unique(view); + on_view_destroy = [=] (view_destruct_signal *ev) + { + this->view = nullptr; + this->kb_interaction = std::make_unique(); + }; + + view->connect(&on_view_destroy); + this->view = view; +} + +std::string wf::layer_shell_node_t::stringify() const +{ + std::ostringstream out; + out << this->view; + return out.str() + " " + stringify_flags(); +} + +wf::keyboard_interaction_t& wf::layer_shell_node_t::keyboard_interaction() +{ + return *kb_interaction; +} + +wf::keyboard_focus_node_t wf::layer_shell_node_t::keyboard_refocus(wf::output_t *output) +{ + if (!view || !view->get_keyboard_focus_surface()) + { + return wf::keyboard_focus_node_t{}; + } + + // Layer-shell views are treated differently. + // Usually, they should not be focused at all. The only case we want to + // focus them is when they were already focused, and should continue to + // have focus, or when they have an active grab. + if (auto surf = view->get_wlr_surface()) + { + if (wlr_surface_is_layer_surface(surf)) + { + auto lsurf = wlr_layer_surface_v1_from_wlr_surface(surf); + if (lsurf->current.keyboard_interactive == + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) + { + // Active grab + return wf::keyboard_focus_node_t{ + .node = this, + .importance = focus_importance::HIGH, + .allow_focus_below = false + }; + } + } + } + + if (output != view->get_output()) + { + return wf::keyboard_focus_node_t{}; + } + + const uint64_t output_last_ts = view->get_output()->get_last_focus_timestamp(); + const uint64_t our_ts = keyboard_interaction().last_focus_timestamp; + + auto cur_focus = wf::get_core_impl().seat->priv->keyboard_focus.get(); + bool has_focus = (cur_focus == this) || (our_ts == output_last_ts); + if (has_focus) + { + return wf::keyboard_focus_node_t{this, focus_importance::REGULAR}; + } + + return wf::keyboard_focus_node_t{}; +} + +wf::region_t wf::layer_shell_node_t::get_opaque_region() const +{ + if (view && view->is_mapped() && view->get_wlr_surface()) + { + auto surf = view->get_wlr_surface(); + + wf::region_t region{&surf->opaque_region}; + region += this->get_offset(); + return region; + } + + return {}; +} + +std::optional wf::layer_shell_node_t::to_texture() const +{ + if (!view || !view->is_mapped() || (get_children().size() != 1)) + { + return {}; + } + + if (auto texturable = dynamic_cast(get_children().front().get())) + { + return texturable->to_texture(); + } + + return {}; +} + +class layer_shell_render_instance_t : public wf::scene::translation_node_instance_t +{ + wf::layer_shell_node_t *sself; + + public: + layer_shell_render_instance_t(wf::layer_shell_node_t *self, + wf::scene::damage_callback push_damage, wf::output_t *shown_on) : + translation_node_instance_t(self, push_damage_on_all_workspaces(push_damage), shown_on) + { + sself = self; + } + + wf::scene::damage_callback push_damage_on_all_workspaces(wf::scene::damage_callback push_damage) + { + return [=] (const wf::region_t& region) + { + if (!sself->get_view()) + { + return; + } + + auto view = sself->get_view(); + auto output = view->get_output(); + if (!output) + { + push_damage(region); + return; + } + + auto wsize = output->wset()->get_workspace_grid_size(); + auto cws = output->wset()->get_current_workspace(); + + /* Damage only the visible region of the shell view. + * This prevents hidden panels from spilling damage onto other workspaces */ + wlr_box ws_box = output->get_relative_geometry(); + wf::region_t full_damage; + + for (int i = 0; i < wsize.width; i++) + { + for (int j = 0; j < wsize.height; j++) + { + const int dx = (i - cws.x) * ws_box.width; + const int dy = (j - cws.y) * ws_box.height; + full_damage |= region + wf::point_t{dx, dy}; + } + } + + push_damage(full_damage); + }; + } + + void schedule_instructions(std::vector& instructions, + const wf::render_target_t& target, wf::region_t& damage) override + { + if (!sself->get_view()) + { + return; + } + + wf::render_target_t our_target = target; + wf::point_t offset = {0, 0}; + + if (sself->get_view()->get_output()) + { + // Adjust geometry of damage/target so that it is visible on all workspaces + auto output_size = sself->get_view()->get_output()->get_screen_size(); + offset = { + (target.geometry.x % output_size.width) - target.geometry.x, + (target.geometry.y % output_size.height) - target.geometry.y, + }; + + our_target = target.translated(offset); + } + + damage += offset; + translation_node_instance_t::schedule_instructions(instructions, our_target, damage); + damage += -offset; + } +}; + +void wf::layer_shell_node_t::gen_render_instances(std::vector & instances, + scene::damage_callback push_damage, wf::output_t *shown_on) +{ + if (!view) + { + return; + } + + // Special case: layer-shell views live only inside their outputs and should not be shown on other outputs + if (shown_on && (this->view->get_output() != shown_on)) + { + return; + } + + instances.push_back(std::make_unique(this, push_damage, shown_on)); +} diff --git a/src/view/layer-shell/layer-shell-node.hpp b/src/view/layer-shell/layer-shell-node.hpp new file mode 100644 index 000000000..ec5cd329e --- /dev/null +++ b/src/view/layer-shell/layer-shell-node.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "wayfire/geometry.hpp" +#include "wayfire/signal-provider.hpp" +#include +#include +#include +#include + +namespace wf +{ +/** + * A surface root node for toplevel views. + */ +class layer_shell_node_t : public wf::scene::translation_node_t, + public scene::zero_copy_texturable_node_t, public scene::opaque_region_node_t, public view_node_tag_t +{ + public: + layer_shell_node_t(wayfire_view view); + + wf::keyboard_focus_node_t keyboard_refocus(wf::output_t *output) override; + keyboard_interaction_t& keyboard_interaction() override; + std::string stringify() const override; + + void gen_render_instances(std::vector& instances, + scene::damage_callback push_damage, wf::output_t *output) override; + + std::optional to_texture() const override; + wf::region_t get_opaque_region() const override; + + protected: + wayfire_view view; + std::unique_ptr kb_interaction; + wf::signal::connection_t on_view_destroy; +}; +} diff --git a/src/view/layer-shell.cpp b/src/view/layer-shell/layer-shell.cpp similarity index 96% rename from src/view/layer-shell.cpp rename to src/view/layer-shell/layer-shell.cpp index d12e45ab5..58e765bac 100644 --- a/src/view/layer-shell.cpp +++ b/src/view/layer-shell/layer-shell.cpp @@ -4,11 +4,12 @@ #include #include +#include "view/layer-shell/layer-shell-node.hpp" #include "wayfire/geometry.hpp" #include "wayfire/scene-operations.hpp" #include "wayfire/util.hpp" #include "wayfire/view.hpp" -#include "xdg-shell.hpp" +#include "../xdg-shell.hpp" #include "wayfire/core.hpp" #include "wayfire/debug.hpp" #include @@ -16,7 +17,7 @@ #include "wayfire/output.hpp" #include "wayfire/workspace-set.hpp" #include "wayfire/output-layout.hpp" -#include "view-impl.hpp" +#include "../view-impl.hpp" static const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; @@ -33,6 +34,7 @@ class wayfire_layer_shell_view : public wf::view_interface_t wf::wl_listener_wrapper on_surface_commit; std::shared_ptr main_surface; + std::shared_ptr surface_root_node; std::unique_ptr surface_controller; /** @@ -92,22 +94,11 @@ class wayfire_layer_shell_view : public wf::view_interface_t } /* Functions which are further specialized for the different shells */ - void move(int x, int y) override + void move(int x, int y) { - auto old_geometry = geometry; + surface_root_node->set_offset({x, y}); this->geometry.x = x; this->geometry.y = y; - wf::emit_geometry_changed_signal(self(), old_geometry); - } - - wf::geometry_t get_wm_geometry() override - { - return geometry; - } - - wf::geometry_t get_output_geometry() override - { - return geometry; } wlr_surface *get_keyboard_focus_surface() override @@ -119,11 +110,6 @@ class wayfire_layer_shell_view : public wf::view_interface_t return NULL; } - - bool should_be_decorated() override - { - return false; - } }; wf::output_workarea_manager_t::anchored_edge anchor_to_edge(uint32_t edges) @@ -390,13 +376,13 @@ wayfire_layer_shell_view::wayfire_layer_shell_view(wlr_layer_surface_v1 *lsurf) { on_surface_commit.set_callback([&] (void*) { commit(); }); this->main_surface = std::make_shared(lsurf->surface, true); + this->surface_root_node = std::make_shared(self()); + this->set_surface_root_node(surface_root_node); - LOGD("Create a layer surface: namespace ", lsurf->namespace_t, - " layer ", lsurf->current.layer); + LOGD("Create a layer surface: namespace ", lsurf->namespace_t, " layer ", lsurf->current.layer); role = wf::VIEW_ROLE_DESKTOP_ENVIRONMENT; std::memset(&this->prev_state, 0, sizeof(prev_state)); - sticky = true; /* If we already have an output set, then assign it before core assigns us * an output */ @@ -502,9 +488,7 @@ void wayfire_layer_shell_view::map() { { this->app_id = nonull(lsurface->namespace_t); - wf::view_app_id_changed_signal data; - data.view = self(); - emit(&data); + wf::emit_app_id_changed_signal(self()); } // Disconnect, from now on regular commits will work @@ -523,6 +507,8 @@ void wayfire_layer_shell_view::map() { get_output()->refocus(); } + + emit_view_map(); } void wayfire_layer_shell_view::unmap() @@ -543,11 +529,9 @@ void wayfire_layer_shell_view::commit() wf::dimensions_t new_size{lsurface->surface->current.width, lsurface->surface->current.height}; if (new_size != wf::dimensions(geometry)) { - auto old_geometry = geometry; this->geometry.width = new_size.width; this->geometry.height = new_size.height; - wf::emit_geometry_changed_signal(self(), old_geometry); - view_damage_raw(self(), last_bounding_box); + wf::scene::damage_node(get_root_node(), last_bounding_box); } this->last_bounding_box = get_bounding_box(); diff --git a/src/view/toplevel-node.cpp b/src/view/toplevel-node.cpp new file mode 100644 index 000000000..3a0cf4dd1 --- /dev/null +++ b/src/view/toplevel-node.cpp @@ -0,0 +1,156 @@ +#include "toplevel-node.hpp" +#include "wayfire/view.hpp" +#include +#include "view-keyboard-interaction.hpp" + +wf::toplevel_view_node_t::toplevel_view_node_t(wayfire_toplevel_view view) : view_node_tag_t(view) +{ + this->kb_interaction = std::make_unique(view); + on_view_destroy = [=] (view_destruct_signal *ev) + { + this->view = nullptr; + this->kb_interaction = std::make_unique(); + }; + + view->connect(&on_view_destroy); + this->view = view; +} + +/** + * Minimal percentage of the view which needs to be visible on a workspace + * for it to count to be on that workspace. + */ +static constexpr double MIN_VISIBILITY_PC = 0.1; + +wf::keyboard_focus_node_t wf::toplevel_view_node_t::keyboard_refocus(wf::output_t *output) +{ + if (!view) + { + return wf::keyboard_focus_node_t{}; + } + + if (!this->view->is_mapped() || !this->view->get_keyboard_focus_surface() || + this->view->minimized || !this->view->get_output()) + { + return wf::keyboard_focus_node_t{}; + } + + static wf::option_wrapper_t remove_output_limits{"workarounds/remove_output_limits"}; + bool foreign_output = !remove_output_limits && (output != view->get_output()); + if (foreign_output) + { + return wf::keyboard_focus_node_t{}; + } + + // When refocusing, we consider each view visible on the output. + // However, we want to filter out views which are 'barely visible', that is, + // views where only a small area is visible, because the user typically does + // not want to focus these views (they might be visible by mistake, or have + // just a single pixel visible, etc). + // + // These views request a LOW focus_importance. + // + // NB: we refocus based on the pending geometry, because the new geometry might not have been applied + // immediately after switching workspaces. + auto output_box = output->get_layout_geometry(); + auto view_box = view->get_pending_geometry() + wf::origin(view->get_output()->get_layout_geometry()); + + auto intersection = wf::geometry_intersection(output_box, view_box); + double area = 1.0 * intersection.width * intersection.height; + area /= 1.0 * view_box.width * view_box.height; + + if (area >= MIN_VISIBILITY_PC) + { + return wf::keyboard_focus_node_t{this, focus_importance::REGULAR}; + } else if (area > 0) + { + return wf::keyboard_focus_node_t{this, focus_importance::LOW}; + } else + { + return wf::keyboard_focus_node_t{}; + } +} + +wf::keyboard_interaction_t& wf::toplevel_view_node_t::keyboard_interaction() +{ + return *kb_interaction; +} + +std::string wf::toplevel_view_node_t::stringify() const +{ + std::ostringstream out; + out << this->view; + return out.str() + " " + stringify_flags(); +} + +class toplevel_view_render_instance_t : public wf::scene::translation_node_instance_t +{ + public: + using translation_node_instance_t::translation_node_instance_t; + + wf::scene::direct_scanout try_scanout(wf::output_t *output) override + { + wf::toplevel_view_node_t *tnode = dynamic_cast(self); + auto view = tnode->get_view(); + + if (!view) + { + return wf::scene::direct_scanout::SKIP; + } + + auto og = output->get_relative_geometry(); + if (!(view->get_bounding_box() & og)) + { + return wf::scene::direct_scanout::SKIP; + } + + auto result = try_scanout_from_list(children, output); + if (result == wf::scene::direct_scanout::SUCCESS) + { + LOGC(SCANOUT, "Scanned out ", view, " on output ", output->to_string()); + return wf::scene::direct_scanout::SUCCESS; + } else + { + LOGC(SCANOUT, "Failed to scan out ", view, " on output ", output->to_string()); + return wf::scene::direct_scanout::OCCLUSION; + } + + return result; + } +}; + +void wf::toplevel_view_node_t::gen_render_instances( + std::vector& instances, + scene::damage_callback push_damage, wf::output_t *output) +{ + instances.push_back(std::make_unique(this, push_damage, output)); +} + +std::optional wf::toplevel_view_node_t::to_texture() const +{ + if (!view || !view->is_mapped() || (get_children().size() != 1)) + { + return {}; + } + + if (auto texturable = dynamic_cast(get_children().front().get())) + { + return texturable->to_texture(); + } + + return {}; +} + +wf::region_t wf::toplevel_view_node_t::get_opaque_region() const +{ + if (view && view->is_mapped() && view->get_wlr_surface()) + { + auto surf = view->get_wlr_surface(); + + wf::region_t region{&surf->opaque_region}; + region += get_offset(); + return region; + } + + return {}; +} diff --git a/src/view/toplevel-node.hpp b/src/view/toplevel-node.hpp new file mode 100644 index 000000000..38a224890 --- /dev/null +++ b/src/view/toplevel-node.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "wayfire/geometry.hpp" +#include "wayfire/signal-provider.hpp" +#include "wayfire/toplevel-view.hpp" +#include +#include +#include +#include + +namespace wf +{ +/** + * A surface root node for toplevel views. + */ +class toplevel_view_node_t : public wf::scene::translation_node_t, + public scene::zero_copy_texturable_node_t, public scene::opaque_region_node_t, public view_node_tag_t +{ + public: + toplevel_view_node_t(wayfire_toplevel_view view); + + wf::keyboard_focus_node_t keyboard_refocus(wf::output_t *output) override; + keyboard_interaction_t& keyboard_interaction() override; + std::string stringify() const override; + + void gen_render_instances(std::vector& instances, + scene::damage_callback push_damage, wf::output_t *output) override; + + std::optional to_texture() const override; + wf::region_t get_opaque_region() const override; + + protected: + wayfire_toplevel_view view; + std::unique_ptr kb_interaction; + wf::signal::connection_t on_view_destroy; +}; +} diff --git a/src/view/toplevel-view.cpp b/src/view/toplevel-view.cpp new file mode 100644 index 000000000..e42bd3022 --- /dev/null +++ b/src/view/toplevel-view.cpp @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "view-impl.hpp" +#include "wayfire/core.hpp" +#include "wayfire/view.hpp" + +static void reposition_relative_to_parent(wayfire_toplevel_view view) +{ + if (!view->parent) + { + return; + } + + auto parent_geometry = view->parent->get_pending_geometry(); + auto wm_geometry = view->get_pending_geometry(); + auto scr_size = view->get_output()->get_screen_size(); + // Guess which workspace the parent is on + wf::point_t center = { + parent_geometry.x + parent_geometry.width / 2, + parent_geometry.y + parent_geometry.height / 2, + }; + wf::point_t parent_ws = { + (int)std::floor(1.0 * center.x / scr_size.width), + (int)std::floor(1.0 * center.y / scr_size.height), + }; + + auto workarea = view->get_output()->render->get_ws_box( + view->get_output()->wset()->get_current_workspace() + parent_ws); + if (view->parent->is_mapped()) + { + auto parent_g = view->parent->get_pending_geometry(); + wm_geometry.x = parent_g.x + (parent_g.width - wm_geometry.width) / 2; + wm_geometry.y = parent_g.y + (parent_g.height - wm_geometry.height) / 2; + } else + { + /* if we have a parent which still isn't mapped, we cannot determine + * the view's position, so we center it on the screen */ + wm_geometry.x = workarea.width / 2 - wm_geometry.width / 2; + wm_geometry.y = workarea.height / 2 - wm_geometry.height / 2; + } + + /* make sure view is visible afterwards */ + wm_geometry = wf::clamp(wm_geometry, workarea); + view->move(wm_geometry.x, wm_geometry.y); + if ((wm_geometry.width != view->get_pending_geometry().width) || + (wm_geometry.height != view->get_pending_geometry().height)) + { + view->resize(wm_geometry.width, wm_geometry.height); + } +} + +static void unset_toplevel_parent(wayfire_toplevel_view view) +{ + if (view->parent) + { + auto& container = view->parent->children; + auto it = std::remove(container.begin(), container.end(), view); + container.erase(it, container.end()); + wf::scene::remove_child(view->get_root_node()); + } +} + +static wayfire_toplevel_view find_toplevel_parent(wayfire_toplevel_view view) +{ + while (view->parent) + { + view = view->parent; + } + + return view; +} + +/** + * Check whether the toplevel parent needs refocus. + * This may be needed because when focusing a view, its topmost child is given + * keyboard focus. When the parent-child relations change, it may happen that + * the parent needs to be focused again, this time with a different keyboard + * focus surface. + */ +static void check_refocus_parent(wayfire_toplevel_view view) +{ + view = find_toplevel_parent(view); + if (view->get_output() && (view->get_output()->get_active_view() == view)) + { + view->get_output()->focus_view(view, false); + } +} + +void wf::toplevel_view_interface_t::set_toplevel_parent(wayfire_toplevel_view new_parent) +{ + auto old_parent = parent; + if (parent != new_parent) + { + /* Erase from the old parent */ + unset_toplevel_parent({this}); + + /* Add in the list of the new parent */ + if (new_parent) + { + new_parent->children.insert(new_parent->children.begin(), {this}); + } + + parent = new_parent; + view_parent_changed_signal ev; + this->emit(&ev); + } + + if (parent) + { + /* Make sure the view is available only as a child */ + if (this->get_output()) + { + this->get_output()->wset()->remove_view({this}); + } + + this->set_output(parent->get_output()); + /* if the view isn't mapped, then it will be positioned properly in map() */ + if (is_mapped()) + { + reposition_relative_to_parent({this}); + } + + wf::scene::readd_front(parent->get_root_node(), this->get_root_node()); + check_refocus_parent(parent); + } else if (old_parent) + { + /* At this point, we are a regular view. */ + if (this->get_output()) + { + wf::scene::readd_front(get_output()->wset()->get_node(), get_root_node()); + get_output()->wset()->add_view({this}); + check_refocus_parent(old_parent); + } + } +} + +std::vector wf::toplevel_view_interface_t::enumerate_views(bool mapped_only) +{ + if (!this->is_mapped() && mapped_only) + { + return {}; + } + + std::vector result; + result.reserve(priv->last_view_cnt); + for (auto& v : this->children) + { + auto cdr = v->enumerate_views(mapped_only); + result.insert(result.end(), cdr.begin(), cdr.end()); + } + + result.push_back({this}); + priv->last_view_cnt = result.size(); + return result; +} + +void wf::toplevel_view_interface_t::set_output(wf::output_t *new_output) +{ + wf::view_interface_t::set_output(new_output); + for (auto& view : this->children) + { + view->set_output(new_output); + } +} + +void wf::toplevel_view_interface_t::move(int x, int y) +{ + toplevel()->pending().geometry.x = x; + toplevel()->pending().geometry.y = y; + wf::get_core().tx_manager->schedule_object(toplevel()); +} + +void wf::toplevel_view_interface_t::resize(int w, int h) +{ + toplevel()->pending().geometry.width = w; + toplevel()->pending().geometry.height = h; + wf::get_core().tx_manager->schedule_object(toplevel()); +} + +void wf::toplevel_view_interface_t::set_geometry(wf::geometry_t geometry) +{ + toplevel()->pending().geometry = geometry; + wf::get_core().tx_manager->schedule_object(toplevel()); +} + +void wf::toplevel_view_interface_t::request_native_size() +{ + /* no-op */ +} + +void wf::toplevel_view_interface_t::set_minimized(bool minim) +{ + if (minim == minimized) + { + return; + } + + this->minimized = minim; + wf::scene::set_node_enabled(get_root_node(), !minimized); + + view_minimized_signal data; + data.view = {this}; + this->emit(&data); + if (get_output()) + { + get_output()->emit(&data); + view_disappeared_signal data; + data.view = self(); + get_output()->emit(&data); + } +} + +void wf::toplevel_view_interface_t::set_sticky(bool sticky) +{ + if (this->sticky == sticky) + { + return; + } + + damage(); + this->sticky = sticky; + damage(); + + wf::view_set_sticky_signal data; + data.view = {this}; + + this->emit(&data); + if (this->get_output()) + { + this->get_output()->emit(&data); + } +} + +void wf::toplevel_view_interface_t::set_activated(bool active) +{ + activated = active; + view_activated_state_signal ev; + this->emit(&ev); +} + +wlr_box wf::toplevel_view_interface_t::get_minimize_hint() +{ + return this->priv->minimize_hint; +} + +void wf::toplevel_view_interface_t::set_minimize_hint(wlr_box hint) +{ + this->priv->minimize_hint = hint; +} + +bool wf::toplevel_view_interface_t::should_be_decorated() +{ + return false; +} + +void wf::toplevel_view_interface_t::deinitialize() +{ + auto children = this->children; + for (auto ch : children) + { + ch->set_toplevel_parent(nullptr); + } + + view_interface_t::deinitialize(); +} + +wf::toplevel_view_interface_t::~toplevel_view_interface_t() +{ + /* Note: at this point, it is invalid to call most functions */ + unset_toplevel_parent({this}); +} + +void wf::toplevel_view_interface_t::set_allowed_actions(uint32_t actions) const +{ + priv->allowed_actions = actions; +} + +uint32_t wf::toplevel_view_interface_t::get_allowed_actions() const +{ + return priv->allowed_actions; +} + +std::shared_ptr wf::toplevel_view_interface_t::get_wset() +{ + return priv->current_wset.lock(); +} + +const std::shared_ptr& wf::toplevel_view_interface_t::toplevel() const +{ + return priv->toplevel; +} + +wayfire_toplevel_view wf::find_view_for_toplevel( + std::shared_ptr toplevel) +{ + // FIXME: this could be a lot more efficient if we simply store a custom data on the toplevel. + for (auto& view : wf::get_core().get_all_views()) + { + if (auto tview = toplevel_cast(view)) + { + if (tview->toplevel() == toplevel) + { + return tview; + } + } + } + + return nullptr; +} diff --git a/src/view/translation-node.cpp b/src/view/translation-node.cpp index 5cdcefbee..69ea70d68 100644 --- a/src/view/translation-node.cpp +++ b/src/view/translation-node.cpp @@ -23,111 +23,101 @@ std::string wf::scene::translation_node_t::stringify() const return "translation by " + x + "," + y + " " + stringify_flags(); } -namespace wf -{ -namespace scene -{ -class translation_node_instance_t : public wf::scene::render_instance_t +void wf::scene::translation_node_t::gen_render_instances( + std::vector& instances, + scene::damage_callback damage, wf::output_t *output) { - std::vector children; - wf::scene::damage_callback push_damage; - wf::scene::translation_node_t *self; + instances.push_back(std::make_unique(this, damage, output)); +} - public: - translation_node_instance_t(wf::scene::translation_node_t *self, - wf::scene::damage_callback push_damage, wf::output_t *shown_on) - { - this->self = self; - this->push_damage = push_damage; +wf::geometry_t wf::scene::translation_node_t::get_bounding_box() +{ + return get_children_bounding_box() + get_offset(); +} - self->connect(&on_node_damage); +wf::point_t wf::scene::translation_node_t::get_offset() const +{ + return offset; +} - auto push_damage_child = [=] (wf::region_t child_damage) - { - child_damage += self->get_offset(); - push_damage(child_damage); - }; +void wf::scene::translation_node_t::set_offset(wf::point_t offset) +{ + this->offset = offset; +} - for (auto& ch : self->get_children()) - { - if (ch->is_enabled()) - { - ch->gen_render_instances(children, push_damage_child, shown_on); - } - } - } +// ----------------------------------------- Render instance ------------------------------------------------- +wf::scene::translation_node_instance_t::translation_node_instance_t( + translation_node_t *self, damage_callback push_damage, wf::output_t *shown_on) +{ + this->self = self; + this->push_damage = push_damage; - wf::signal::connection_t on_node_damage = - [=] (wf::scene::node_damage_signal *data) + on_node_damage = [=] (wf::scene::node_damage_signal *data) { push_damage(data->region); }; + self->connect(&on_node_damage); - void schedule_instructions(std::vector& instructions, - const wf::render_target_t& target, wf::region_t& damage) override + auto push_damage_child = [=] (wf::region_t child_damage) { - wf::point_t offset = self->get_offset(); - damage += -offset; + child_damage += self->get_offset(); + push_damage(child_damage); + }; - auto our_target = target.translated(-offset); - for (auto& ch : this->children) + for (auto& ch : self->get_children()) + { + if (ch->is_enabled()) { - ch->schedule_instructions(instructions, our_target, damage); + ch->gen_render_instances(children, push_damage_child, shown_on); } - - damage += offset; } +} - void render(const wf::render_target_t& target, - const wf::region_t& region) override +void wf::scene::translation_node_instance_t::schedule_instructions( + std::vector& instructions, + const wf::render_target_t& target, wf::region_t& damage) +{ + wf::region_t our_damage = damage & self->get_bounding_box(); + if (!our_damage.empty()) { - wf::dassert(false, "Rendering a translation node?"); - } + wf::point_t offset = self->get_offset(); + damage += -offset; + auto our_target = target.translated(-offset); - void presentation_feedback(wf::output_t *output) override - { for (auto& ch : this->children) { - ch->presentation_feedback(output); - } - } - - wf::scene::direct_scanout try_scanout(wf::output_t *output) override - { - if (self->get_offset() != wf::point_t{0, 0}) - { - return wf::scene::direct_scanout::OCCLUSION; + ch->schedule_instructions(instructions, our_target, damage); } - return try_scanout_from_list(this->children, output); - } - - void compute_visibility(wf::output_t *output, wf::region_t& visible) override - { - compute_visibility_from_list(children, output, visible, self->get_offset()); + damage += offset; } -}; -} } -void wf::scene::translation_node_t::gen_render_instances( - std::vector& instances, - scene::damage_callback damage, wf::output_t *output) +void wf::scene::translation_node_instance_t::render( + const wf::render_target_t& target, const wf::region_t& region) { - instances.push_back(std::make_unique(this, damage, output)); + wf::dassert(false, "Rendering a translation node?"); } -wf::geometry_t wf::scene::translation_node_t::get_bounding_box() +void wf::scene::translation_node_instance_t::presentation_feedback(wf::output_t *output) { - return get_children_bounding_box() + get_offset(); + for (auto& ch : this->children) + { + ch->presentation_feedback(output); + } } -wf::point_t wf::scene::translation_node_t::get_offset() const +wf::scene::direct_scanout wf::scene::translation_node_instance_t::try_scanout(wf::output_t *output) { - return offset; + if (self->get_offset() != wf::point_t{0, 0}) + { + return wf::scene::direct_scanout::OCCLUSION; + } + + return try_scanout_from_list(this->children, output); } -void wf::scene::translation_node_t::set_offset(wf::point_t offset) +void wf::scene::translation_node_instance_t::compute_visibility(wf::output_t *output, wf::region_t& visible) { - this->offset = offset; + compute_visibility_from_list(children, output, visible, self->get_offset()); } diff --git a/src/view/view-3d.cpp b/src/view/view-3d.cpp index d6747a185..7d29d99c9 100644 --- a/src/view/view-3d.cpp +++ b/src/view/view-3d.cpp @@ -3,6 +3,7 @@ #include "wayfire/region.hpp" #include "wayfire/scene-input.hpp" #include "wayfire/scene.hpp" +#include "wayfire/toplevel-view.hpp" #include "wayfire/view-transform.hpp" #include "wayfire/opengl.hpp" #include "wayfire/core.hpp" @@ -104,6 +105,17 @@ static wf::pointf_t get_center(wf::geometry_t view) }; } +static wf::pointf_t get_center(wayfire_view view) +{ + if (auto toplevel = toplevel_cast(view)) + { + return get_center(toplevel->get_geometry()); + } else + { + return get_center(view->get_surface_root_node()->get_bounding_box()); + } +} + static void rotate_xy(double& x, double& y, double angle) { const auto cs = std::cos(angle); @@ -113,7 +125,7 @@ static void rotate_xy(double& x, double& y, double angle) wf::pointf_t view_2d_transformer_t::to_local(const wf::pointf_t& point) { - auto midpoint = get_center(view->get_wm_geometry()); + auto midpoint = get_center(view); auto result = point - midpoint; result.x -= translation_x; result.y -= translation_y; @@ -126,7 +138,7 @@ wf::pointf_t view_2d_transformer_t::to_local(const wf::pointf_t& point) wf::pointf_t view_2d_transformer_t::to_global(const wf::pointf_t& point) { - auto midpoint = get_center(view->get_wm_geometry()); + auto midpoint = get_center(view); auto result = point - midpoint; result.x *= scale_x; @@ -177,7 +189,7 @@ class view_2d_render_instance_t : auto bbox = self->get_children_bounding_box(); auto tex = this->get_texture(target.scale); - auto midpoint = get_center(self->view->get_wm_geometry()); + auto midpoint = get_center(self->view); auto center_at = glm::translate(glm::mat4(1.0), {-midpoint.x, -midpoint.y, 0.0}); auto scale = glm::scale(glm::mat4(1.0), diff --git a/src/view/view-impl.cpp b/src/view/view-impl.cpp index 17b48f088..97590ade1 100644 --- a/src/view/view-impl.cpp +++ b/src/view/view-impl.cpp @@ -2,7 +2,6 @@ #include "wayfire/core.hpp" #include "../core/core-impl.hpp" #include "view-impl.hpp" -#include "wayfire/decorator.hpp" #include "wayfire/scene-input.hpp" #include "wayfire/scene-render.hpp" #include "wayfire/scene.hpp" @@ -17,262 +16,6 @@ #include "xdg-shell.hpp" -wf::wlr_view_t::wlr_view_t() : wf::view_interface_t() -{ - on_surface_commit.set_callback([&] (void*) { commit(); }); -} - -void wf::wlr_view_t::handle_app_id_changed(std::string new_app_id) -{ - this->app_id = new_app_id; - view_app_id_changed_signal data; - data.view = self(); - emit(&data); -} - -std::string wf::wlr_view_t::get_app_id() -{ - return this->app_id; -} - -void wf::wlr_view_t::handle_title_changed(std::string new_title) -{ - this->title = new_title; - view_title_changed_signal data; - data.view = self(); - emit(&data); -} - -std::string wf::wlr_view_t::get_title() -{ - return this->title; -} - -void wf::wlr_view_t::set_position(int x, int y, - wf::geometry_t old_geometry, bool send_signal) -{ - auto obox = get_output_geometry(); - auto wm = get_wm_geometry(); - - view_geometry_changed_signal data; - data.view = self(); - data.old_geometry = old_geometry; - - view_damage_raw(self(), last_bounding_box); - /* obox.x - wm.x is the current difference in the output and wm geometry */ - geometry.x = x + obox.x - wm.x; - geometry.y = y + obox.y - wm.y; - - /* Make sure that if we move the view while it is unmapped, its snapshot - * is still valid coordinates */ - priv->offscreen_buffer = priv->offscreen_buffer.translated({ - x - data.old_geometry.x, y - data.old_geometry.y, - }); - - damage(); - - if (send_signal) - { - emit(&data); - wf::get_core().emit(&data); - if (get_output()) - { - get_output()->emit(&data); - } - } - - last_bounding_box = get_bounding_box(); - scene::update(this->get_surface_root_node(), scene::update_flag::GEOMETRY); -} - -void wf::wlr_view_t::move(int x, int y) -{ - set_position(x, y, get_wm_geometry(), true); -} - -void wf::wlr_view_t::adjust_anchored_edge(wf::dimensions_t new_size) -{ - if (priv->edges) - { - auto wm = get_wm_geometry(); - if (priv->edges & WLR_EDGE_LEFT) - { - wm.x += geometry.width - new_size.width; - } - - if (priv->edges & WLR_EDGE_TOP) - { - wm.y += geometry.height - new_size.height; - } - - set_position(wm.x, wm.y, - get_wm_geometry(), false); - } -} - -void wf::wlr_view_t::update_size() -{ - if (!is_mapped()) - { - return; - } - - auto current_size = get_size(); - if ((current_size.width == geometry.width) && - (current_size.height == geometry.height)) - { - return; - } - - /* Damage current size */ - view_damage_raw(self(), last_bounding_box); - adjust_anchored_edge(current_size); - - view_geometry_changed_signal data; - data.view = self(); - data.old_geometry = get_wm_geometry(); - - geometry.width = current_size.width; - geometry.height = current_size.height; - - /* Damage new size */ - last_bounding_box = get_bounding_box(); - view_damage_raw(self(), last_bounding_box); - emit(&data); - wf::get_core().emit(&data); - if (get_output()) - { - get_output()->emit(&data); - } - - scene::update(this->get_surface_root_node(), scene::update_flag::GEOMETRY); -} - -bool wf::wlr_view_t::should_resize_client( - wf::dimensions_t request, wf::dimensions_t current_geometry) -{ - /* - * Do not send a configure if the client will retain its size. - * This is needed if a client starts with one size and immediately resizes - * again. - * - * If we do configure it with the given size, then it will think that we - * are requesting the given size, and won't resize itself again. - */ - if (this->last_size_request == wf::dimensions_t{0, 0}) - { - return request != current_geometry; - } else - { - return request != last_size_request; - } -} - -wf::geometry_t wf::wlr_view_t::get_output_geometry() -{ - return geometry; -} - -wf::geometry_t wf::wlr_view_t::get_wm_geometry() -{ - if (priv->frame) - { - return priv->frame->expand_wm_geometry(geometry); - } else - { - return geometry; - } -} - -wlr_surface*wf::wlr_view_t::get_keyboard_focus_surface() -{ - if (is_mapped() && priv->keyboard_focus_enabled) - { - return priv->wsurface; - } - - return NULL; -} - -bool wf::wlr_view_t::should_be_decorated() -{ - return role == wf::VIEW_ROLE_TOPLEVEL && !has_client_decoration; -} - -void wf::wlr_view_t::set_decoration_mode(bool use_csd) -{ - bool was_decorated = should_be_decorated(); - this->has_client_decoration = use_csd; - if ((was_decorated != should_be_decorated()) && is_mapped()) - { - wf::view_decoration_state_updated_signal data; - data.view = self(); - - this->emit(&data); - wf::get_core().emit(&data); - } -} - -void wf::wlr_view_t::commit() -{ - wf::region_t dmg; - wlr_surface_get_effective_damage(priv->wsurface, dmg.to_pixman()); - wf::scene::damage_node(this->get_surface_root_node(), dmg); - - update_size(); - - /* Clear the resize edges. - * This is must be done here because if the user(or plugin) resizes too fast, - * the shell client might still haven't configured the surface, and in this - * case the next commit(here) needs to still have access to the gravity */ - if (!priv->in_continuous_resize) - { - priv->edges = 0; - } - - this->last_bounding_box = get_bounding_box(); -} - -void wf::wlr_view_t::map(wlr_surface *surface) -{ - on_surface_commit.connect(&surface->events.commit); - this->main_surface = std::make_shared(surface, true); - priv->set_mapped_surface_contents(main_surface); - priv->set_mapped(true); - - update_size(); - - if (role == VIEW_ROLE_TOPLEVEL) - { - if (!parent) - { - wf::scene::readd_front(get_output()->wset()->get_node(), get_root_node()); - get_output()->wset()->add_view(self()); - } - - get_output()->focus_view(self(), true); - } - - damage(); - emit_view_map(); - /* Might trigger repositioning */ - set_toplevel_parent(this->parent); -} - -void wf::wlr_view_t::unmap() -{ - damage(); - emit_view_pre_unmap(); - set_decoration(nullptr); - - main_surface = nullptr; - priv->unset_mapped_surface_contents(); - on_surface_commit.disconnect(); - - emit_view_unmap(); - priv->set_mapped(false); -} - void wf::emit_view_map_signal(wayfire_view view, bool has_position) { wf::view_mapped_signal data; @@ -291,7 +34,7 @@ void wf::emit_ping_timeout_signal(wayfire_view view) view->emit(&data); } -void wf::emit_geometry_changed_signal(wayfire_view view, wf::geometry_t old_geometry) +void wf::emit_geometry_changed_signal(wayfire_toplevel_view view, wf::geometry_t old_geometry) { wf::view_geometry_changed_signal data; data.view = view; @@ -342,10 +85,52 @@ void wf::view_interface_t::emit_view_pre_unmap() wf::get_core().emit(&data); } -void wf::wlr_view_t::destroy() +void wf::emit_title_changed_signal(wayfire_view view) +{ + view_title_changed_signal data; + data.view = view; + view->emit(&data); +} + +void wf::emit_app_id_changed_signal(wayfire_view view) { - /* Drop the internal reference */ - unref(); + view_app_id_changed_signal data; + data.view = view; + view->emit(&data); +} + +void wf::emit_toplevel_state_change_signals(wayfire_toplevel_view view, const wf::toplevel_state_t& old_state) +{ + if (view->toplevel()->current().geometry != old_state.geometry) + { + emit_geometry_changed_signal(view, old_state.geometry); + } + + if (view->toplevel()->current().tiled_edges != old_state.tiled_edges) + { + wf::view_tiled_signal data; + data.view = view; + data.old_edges = old_state.tiled_edges; + data.new_edges = view->toplevel()->current().tiled_edges; + + view->emit(&data); + if (view->get_output()) + { + view->get_output()->emit(&data); + } + } + + if (view->toplevel()->current().fullscreen != old_state.fullscreen) + { + view_fullscreen_signal data; + data.view = view; + data.state = view->toplevel()->current().fullscreen; + view->emit(&data); + if (view->get_output()) + { + view->get_output()->emit(&data); + } + } } void wf::init_desktop_apis() @@ -392,38 +177,10 @@ wayfire_view wf::wl_surface_to_wayfire_view(wl_resource *resource) #endif - wf::view_interface_t *view = static_cast(handle); + wf::view_interface_t *view = static_cast(handle); return view ? view->self() : nullptr; } -wf::dimensions_t wf::wlr_view_t::get_size() const -{ - if (!is_mapped()) - { - return {0, 0}; - } - - return { - priv->wsurface->current.width, - priv->wsurface->current.height, - }; -} - -bool wf::wlr_view_t::is_mapped() const -{ - return priv->wsurface != nullptr; -} - -wlr_buffer*wf::wlr_view_t::get_buffer() -{ - if (priv->wsurface && wlr_surface_has_buffer(priv->wsurface)) - { - return &priv->wsurface->buffer->base; - } - - return nullptr; -} - void wf::view_interface_t::view_priv_impl::set_mapped_surface_contents( std::shared_ptr content) { @@ -540,3 +297,19 @@ std::vector wf::collect_views_from_output(wf::output_t *output, return views; } + +void wf::adjust_geometry_for_gravity(wf::toplevel_state_t& desired_state, wf::dimensions_t actual_size) +{ + if (desired_state.gravity & WLR_EDGE_RIGHT) + { + desired_state.geometry.x += desired_state.geometry.width - actual_size.width; + } + + if (desired_state.gravity & WLR_EDGE_BOTTOM) + { + desired_state.geometry.y += desired_state.geometry.height - actual_size.height; + } + + desired_state.geometry.width = actual_size.width; + desired_state.geometry.height = actual_size.height; +} diff --git a/src/view/view-impl.hpp b/src/view/view-impl.hpp index 072e24da9..41dd72ed0 100644 --- a/src/view/view-impl.hpp +++ b/src/view/view-impl.hpp @@ -5,8 +5,11 @@ #include #include #include +#include #include "surface-impl.hpp" +#include "wayfire/core.hpp" +#include "wayfire/signal-provider.hpp" #include "wayfire/unstable/wlr-surface-node.hpp" #include "wayfire/output.hpp" #include "wayfire/scene.hpp" @@ -14,6 +17,7 @@ #include "wayfire/view-transform.hpp" #include #include +#include struct wlr_seat; namespace wf @@ -30,28 +34,9 @@ class view_interface_t::view_priv_impl size_t last_view_cnt = 0; bool keyboard_focus_enabled = true; - - /** - * Calculate the windowed geometry relative to the output's workarea. - */ - wf::geometry_t calculate_windowed_geometry(wf::output_t *output); - - /** - * Update the stored window geometry and workarea, if the current view - * state is not-tiled and not-moving. - */ - void update_windowed_geometry(wayfire_view self, wf::geometry_t geometry); - - std::unique_ptr frame = nullptr; - - uint32_t allowed_actions = VIEW_ALLOW_ALL; + uint32_t allowed_actions = VIEW_ALLOW_ALL; uint32_t edges = 0; - int in_continuous_move = 0; - int in_continuous_resize = 0; - int visibility_counter = 1; - - wf::render_target_t offscreen_buffer; wlr_box minimize_hint = {0, 0, 0, 0}; scene::floating_inner_ptr root_node; @@ -65,117 +50,27 @@ class view_interface_t::view_priv_impl void unset_mapped_surface_contents(); std::weak_ptr current_wset; - private: - /** Last geometry the view has had in non-tiled and non-fullscreen state. - * -1 as width/height means that no such geometry has been stored. */ - wf::geometry_t last_windowed_geometry = {0, 0, -1, -1}; - - /** - * The workarea when last_windowed_geometry was stored. This is used - * for ex. when untiling a view to determine its geometry relative to the - * (potentially changed) workarea of its output. - */ - wf::geometry_t windowed_geometry_workarea = {0, 0, -1, -1}; + std::shared_ptr toplevel; }; /** - * Damage the given box, assuming the damage belongs to the given view. - * The given box is assumed to have been transformed with the view's - * transformers. - * - * The main difference with directly damaging the output is that this will - * add the damage to all workspaces the view is visible on, in case of shell - * views. + * Adjust the position of the view according to the new size of its buffer and the geometry. */ -void view_damage_raw(wayfire_view view, const wlr_box& box); - -/** - * Implementation of a view backed by a wlr_* shell struct. - */ -class wlr_view_t : public view_interface_t -{ - public: - wlr_view_t(); - virtual ~wlr_view_t() = default; - - virtual std::string get_app_id() override final; - virtual std::string get_title() override final; - - /* Functions which are further specialized for the different shells */ - virtual void move(int x, int y) override; - virtual wf::geometry_t get_wm_geometry() override; - virtual wf::geometry_t get_output_geometry() override; - - virtual wlr_surface *get_keyboard_focus_surface() override; - - virtual bool should_be_decorated() override; - virtual void set_decoration_mode(bool use_csd); - bool has_client_decoration = true; - - protected: - std::string title, app_id; - /** Used by view implementations when the app id changes */ - void handle_app_id_changed(std::string new_app_id); - /** Used by view implementations when the title changes */ - void handle_title_changed(std::string new_title); - - /** - * The bounding box of the view the last time it was rendered. - * - * This is used to damage the view when it is resized, because when a - * transformer changes because the view is resized, we can't reliably - * calculate the old view region to damage. - */ - wf::geometry_t last_bounding_box{0, 0, 0, 0}; - - /** - * Adjust the view position when resizing the view so that its apparent - * position doesn't change when resizing. - */ - void adjust_anchored_edge(wf::dimensions_t new_size); - - /** The output geometry of the view */ - wf::geometry_t geometry{100, 100, 0, 0}; - - /** Set the view position and optionally send the geometry changed signal - * @param old_geometry The geometry to report as previous, in case the - * signal is sent. */ - virtual void set_position(int x, int y, wf::geometry_t old_geometry, - bool send_geometry_signal); - /** Update the view size to the actual dimensions of its surface */ - virtual void update_size(); - - /** Last request to the client */ - wf::dimensions_t last_size_request = {0, 0}; - virtual bool should_resize_client(wf::dimensions_t request, - wf::dimensions_t current_size); - - virtual void commit(); - virtual void map(wlr_surface *surface); - virtual void unmap(); - - /* Handle the destruction of the underlying wlroots object */ - virtual void destroy(); - - wf::wl_listener_wrapper on_surface_commit; - std::shared_ptr main_surface; - - public: - /* Just pass to the default wlr surface implementation */ - bool is_mapped() const override; - wf::dimensions_t get_size() const; - - wlr_buffer *get_buffer(); -}; +void adjust_geometry_for_gravity(wf::toplevel_state_t& desired_state, wf::dimensions_t actual_size); /** Emit the map signal for the given view */ void emit_view_map_signal(wayfire_view view, bool has_position); void emit_ping_timeout_signal(wayfire_view view); -void emit_geometry_changed_signal(wayfire_view view, wf::geometry_t old_geometry); +void emit_geometry_changed_signal(wayfire_toplevel_view view, wf::geometry_t old_geometry); + +void emit_title_changed_signal(wayfire_view view); +void emit_app_id_changed_signal(wayfire_view view); void init_xdg_shell(); void init_xwayland(); void init_layer_shell(); +void emit_toplevel_state_change_signals( + wayfire_toplevel_view view, const wf::toplevel_state_t& old_state); std::string xwayland_get_display(); void xwayland_update_default_cursor(); diff --git a/src/view/view-node.cpp b/src/view/view-node.cpp deleted file mode 100644 index 6150d6dcb..000000000 --- a/src/view/view-node.cpp +++ /dev/null @@ -1,432 +0,0 @@ -#include "view-keyboard-interaction.hpp" -#include -#include -#include -#include "../core/core-impl.hpp" -#include "../core/seat/input-manager.hpp" -#include "wayfire/geometry.hpp" -#include "wayfire/opengl.hpp" -#include "wayfire/option-wrapper.hpp" -#include "wayfire/region.hpp" -#include "wayfire/scene-input.hpp" -#include "wayfire/scene-render.hpp" -#include "wayfire/scene.hpp" -#include "wayfire/signal-provider.hpp" -#include "wayfire/view-helpers.hpp" -#include "wayfire/view-transform.hpp" -#include "wayfire/view.hpp" -#include "wayfire/workspace-set.hpp" -#include "wlr-layer-shell-unstable-v1-protocol.h" -#include "view-impl.hpp" - -wf::scene::view_node_t::view_node_t(wayfire_view _view) : - floating_inner_node_t(false), view(_view) -{ - this->kb_interaction = std::make_unique(view); - on_view_destroy = [=] (view_destruct_signal *ev) - { - this->view = nullptr; - this->kb_interaction = std::make_unique(); - }; - - view->connect(&on_view_destroy); -} - -wf::scene::view_node_t::view_node_t() : floating_inner_node_t(false) -{} - -std::string wf::scene::view_node_t::stringify() const -{ - std::ostringstream out; - out << this->view; - return out.str() + " " + stringify_flags(); -} - -wf::keyboard_interaction_t& wf::scene::view_node_t::keyboard_interaction() -{ - return *kb_interaction; -} - -wf::pointf_t wf::scene::view_node_t::to_local(const wf::pointf_t& point) -{ - if (!view) - { - return point; - } - - return point - wf::pointf_t(wf::origin(view->get_output_geometry())); -} - -wf::pointf_t wf::scene::view_node_t::to_global(const wf::pointf_t& point) -{ - if (!view) - { - return point; - } - - return point + wf::pointf_t(wf::origin(view->get_output_geometry())); -} - -std::optional wf::scene::view_node_t::find_node_at( - const wf::pointf_t& at) -{ - if (!view) - { - return {}; - } - - auto& input_manager = wf::get_core_impl().input; - if (input_manager->exclusive_client && (view->get_client() != input_manager->exclusive_client)) - { - /* We have exclusive focus surface, for ex. a lockscreen. - * The only kind of things we can focus are OSKs and similar */ - if (view) - { - auto layer = get_view_layer(view); - if (layer.has_value() && (layer.value() == wf::scene::layer::DWIDGET)) - { - return floating_inner_node_t::find_node_at(at); - } - } - - return {}; - } - - if (view->minimized) - { - return {}; - } - - return floating_inner_node_t::find_node_at(at); -} - -/** - * Minimal percentage of the view which needs to be visible on a workspace - * for it to count to be on that workspace. - */ -static constexpr double MIN_VISIBILITY_PC = 0.1; - -wf::keyboard_focus_node_t wf::scene::view_node_t::keyboard_refocus( - wf::output_t *output) -{ - if (!view) - { - return wf::keyboard_focus_node_t{}; - } - - if (!this->view->is_mapped() || - !this->view->get_keyboard_focus_surface() || - this->view->minimized || - !this->view->get_output()) - { - return wf::keyboard_focus_node_t{}; - } - - static wf::option_wrapper_t remove_output_limits{ - "workarounds/remove_output_limits"}; - bool foreign_output = !remove_output_limits && (output != view->get_output()); - - const uint64_t output_last_ts = view->get_output()->get_last_focus_timestamp(); - const uint64_t our_ts = keyboard_interaction().last_focus_timestamp; - - auto cur_focus = wf::get_core_impl().seat->priv->keyboard_focus.get(); - bool has_focus = (cur_focus == this) || (our_ts == output_last_ts); - - const auto cur_layer = get_view_layer(view).value_or(wf::scene::layer::UNMANAGED); - if (cur_layer != wf::scene::layer::WORKSPACE) - { - // Non-workspace views are treated differently. - // Usually, they should not be focused at all. The only case we want to - // focus them is when they were already focused, and should continue to - // have focus, or when they have an active grab. - if (auto surf = view->get_wlr_surface()) - { - if (wlr_surface_is_layer_surface(surf)) - { - auto lsurf = wlr_layer_surface_v1_from_wlr_surface(surf); - if (lsurf->current.keyboard_interactive == - ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) - { - // Active grab - return wf::keyboard_focus_node_t{ - .node = this, - .importance = focus_importance::HIGH, - .allow_focus_below = false - }; - } - } - } - - if (has_focus && !foreign_output) - { - return wf::keyboard_focus_node_t{this, focus_importance::REGULAR}; - } - - return wf::keyboard_focus_node_t{}; - } - - if (foreign_output) - { - return wf::keyboard_focus_node_t{}; - } - - // When refocusing, we consider each view visible on the output. - // However, we want to filter out views which are 'barely visible', that is, - // views where only a small area is visible, because the user typically does - // not want to focus these views (they might be visible by mistake, or have - // just a single pixel visible, etc). - // - // These views request a LOW focus_importance. - auto output_box = output->get_layout_geometry(); - auto view_box = view->get_wm_geometry() + - wf::origin(view->get_output()->get_layout_geometry()); - - auto intersection = wf::geometry_intersection(output_box, view_box); - double area = 1.0 * intersection.width * intersection.height; - area /= 1.0 * view_box.width * view_box.height; - - if (area >= MIN_VISIBILITY_PC) - { - return wf::keyboard_focus_node_t{this, focus_importance::REGULAR}; - } else if (area > 0) - { - return wf::keyboard_focus_node_t{this, focus_importance::LOW}; - } else - { - return wf::keyboard_focus_node_t{}; - } -} - -namespace wf -{ -namespace scene -{ -class view_render_instance_t : public render_instance_t -{ - std::vector children; - wayfire_view view; - damage_callback push_damage; - wf::output_t *visible_on; - - public: - view_render_instance_t(wayfire_view view, damage_callback push_damage, wf::output_t *visible_on) - { - this->view = view; - this->push_damage = push_damage; - this->visible_on = visible_on; - view->get_surface_root_node()->connect(&on_view_damage); - - auto push_damage_child = [=] (wf::region_t child_damage) - { - if (!view) - { - return; - } - - child_damage += wf::origin(view->get_output_geometry()); - push_damage(child_damage); - }; - - for (auto& ch : view->get_surface_root_node()->get_children()) - { - if (ch->is_enabled()) - { - ch->gen_render_instances(children, push_damage_child, visible_on); - } - } - } - - // FIXME: once transformers are proper nodes, this should be - // done in the surfaces and then bubbled up. - wf::signal::connection_t on_view_damage = - [=] (node_damage_signal *data) - { - push_damage(data->region); - }; - - void schedule_instructions(std::vector& instructions, - const wf::render_target_t& target, wf::region_t& damage) override - { - if (!view) - { - return; - } - - wf::render_target_t our_target = target; - wf::point_t offset = {0, 0}; - - if (view->sticky && view->get_output()) - { - // Adjust geometry of damage/target so that view is always visible if - // sticky - auto output_size = view->get_output()->get_screen_size(); - offset = { - (target.geometry.x % output_size.width) - target.geometry.x, - (target.geometry.y % output_size.height) - target.geometry.y, - }; - - our_target = target.translated(offset); - } - - damage += offset; - - auto bbox = view->get_surface_root_node()->get_bounding_box(); - wf::region_t our_damage = damage & bbox; - if (!our_damage.empty()) - { - if (!view->is_mapped()) - { - instructions.push_back(render_instruction_t{ - .instance = this, - .target = our_target, - .damage = std::move(our_damage), - }); - } else - { - auto surface_offset = wf::origin(view->get_output_geometry()); - damage += -surface_offset; - - our_target = our_target.translated(-surface_offset); - for (auto& ch : this->children) - { - ch->schedule_instructions(instructions, our_target, damage); - } - - damage += surface_offset; - } - } - - damage += -offset; - } - - void render(const wf::render_target_t& target, - const wf::region_t& region) override - { - OpenGL::render_begin(target); - for (auto& box : region) - { - target.logic_scissor(wlr_box_from_pixman_box(box)); - OpenGL::render_transformed_texture( - view->priv->offscreen_buffer.tex, - view->priv->offscreen_buffer.geometry, - target.get_orthographic_projection()); - } - - OpenGL::render_end(); - } - - void presentation_feedback(wf::output_t *output) override - { - for (auto& ch : this->children) - { - ch->presentation_feedback(output); - } - } - - direct_scanout try_scanout(wf::output_t *output) override - { - if (!view) - { - return direct_scanout::SKIP; - } - - auto og = output->get_relative_geometry(); - if (!(this->view->get_bounding_box() & og)) - { - return direct_scanout::SKIP; - } - - auto result = try_scanout_from_list(children, output); - if (result == direct_scanout::SUCCESS) - { - LOGC(SCANOUT, "Scanned out ", view, " on output ", output->to_string()); - return direct_scanout::SUCCESS; - } else - { - LOGC(SCANOUT, "Failed to scan out ", view, " on output ", output->to_string()); - return direct_scanout::OCCLUSION; - } - - return result; - } - - void compute_visibility(wf::output_t *output, wf::region_t& visible) override - { - if (!view || !view->is_mapped()) - { - return; - } - - compute_visibility_from_list(children, output, visible, wf::origin(view->get_output_geometry())); - } -}; - -void view_node_t::gen_render_instances(std::vector & instances, - damage_callback push_damage, wf::output_t *shown_on) -{ - if (!view) - { - return; - } - - if ((this->view->role == VIEW_ROLE_DESKTOP_ENVIRONMENT) && - this->view->sticky) - { - // FIXME: this code should be layer-shell-node-specific - // Special case: layer-shell views live only inside their outputs and - // have no benefit from being rendered on other outputs. - if (shown_on && (this->view->get_output() != shown_on)) - { - return; - } - } - - instances.push_back(std::make_unique( - this->view, push_damage, shown_on)); -} - -std::optional view_node_t::to_texture() const -{ - if (!view || !view->is_mapped() || (get_children().size() != 1)) - { - return {}; - } - - if (auto texturable = dynamic_cast(get_children().front().get())) - { - return texturable->to_texture(); - } - - return {}; -} -} -} - -wf::geometry_t wf::scene::view_node_t::get_bounding_box() -{ - if (!view) - { - return {0, 0, 0, 0}; - } - - if (!view->is_mapped()) - { - return view->priv->offscreen_buffer.geometry; - } - - return get_children_bounding_box() + wf::origin(view->get_output_geometry()); -} - -wf::region_t wf::scene::view_node_t::get_opaque_region() const -{ - if (view && view->is_mapped() && view->get_wlr_surface()) - { - auto surf = view->get_wlr_surface(); - - wf::region_t region{&surf->opaque_region}; - region += wf::origin(view->get_output_geometry()); - return region; - } - - return {}; -} diff --git a/src/view/view.cpp b/src/view/view.cpp index 05f155ff3..b6176942a 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -3,6 +3,7 @@ #include #include "../core/core-impl.hpp" #include "view-impl.hpp" +#include "wayfire/debug.hpp" #include "wayfire/geometry.hpp" #include "wayfire/opengl.hpp" #include "wayfire/output.hpp" @@ -20,158 +21,6 @@ #include "wayfire/signal-definitions.hpp" #include -static void reposition_relative_to_parent(wayfire_view view) -{ - if (!view->parent) - { - return; - } - - auto parent_geometry = view->parent->get_wm_geometry(); - auto wm_geometry = view->get_wm_geometry(); - auto scr_size = view->get_output()->get_screen_size(); - // Guess which workspace the parent is on - wf::point_t center = { - parent_geometry.x + parent_geometry.width / 2, - parent_geometry.y + parent_geometry.height / 2, - }; - wf::point_t parent_ws = { - (int)std::floor(1.0 * center.x / scr_size.width), - (int)std::floor(1.0 * center.y / scr_size.height), - }; - - auto workarea = view->get_output()->render->get_ws_box( - view->get_output()->wset()->get_current_workspace() + parent_ws); - if (view->parent->is_mapped()) - { - auto parent_g = view->parent->get_wm_geometry(); - wm_geometry.x = parent_g.x + (parent_g.width - wm_geometry.width) / 2; - wm_geometry.y = parent_g.y + (parent_g.height - wm_geometry.height) / 2; - } else - { - /* if we have a parent which still isn't mapped, we cannot determine - * the view's position, so we center it on the screen */ - wm_geometry.x = workarea.width / 2 - wm_geometry.width / 2; - wm_geometry.y = workarea.height / 2 - wm_geometry.height / 2; - } - - /* make sure view is visible afterwards */ - wm_geometry = wf::clamp(wm_geometry, workarea); - view->move(wm_geometry.x, wm_geometry.y); - if ((wm_geometry.width != view->get_wm_geometry().width) || - (wm_geometry.height != view->get_wm_geometry().height)) - { - view->resize(wm_geometry.width, wm_geometry.height); - } -} - -static void unset_toplevel_parent(wayfire_view view) -{ - if (view->parent) - { - auto& container = view->parent->children; - auto it = std::remove(container.begin(), container.end(), view); - container.erase(it, container.end()); - wf::scene::remove_child(view->get_root_node()); - } -} - -static wayfire_view find_toplevel_parent(wayfire_view view) -{ - while (view->parent) - { - view = view->parent; - } - - return view; -} - -/** - * Check whether the toplevel parent needs refocus. - * This may be needed because when focusing a view, its topmost child is given - * keyboard focus. When the parent-child relations change, it may happen that - * the parent needs to be focused again, this time with a different keyboard - * focus surface. - */ -static void check_refocus_parent(wayfire_view view) -{ - view = find_toplevel_parent(view); - if (view->get_output() && (view->get_output()->get_active_view() == view)) - { - view->get_output()->focus_view(view, false); - } -} - -void wf::view_interface_t::set_toplevel_parent(wayfire_view new_parent) -{ - auto old_parent = parent; - if (parent != new_parent) - { - /* Erase from the old parent */ - unset_toplevel_parent(self()); - - /* Add in the list of the new parent */ - if (new_parent) - { - new_parent->children.insert(new_parent->children.begin(), self()); - } - - parent = new_parent; - view_parent_changed_signal ev; - this->emit(&ev); - } - - if (parent) - { - /* Make sure the view is available only as a child */ - if (this->get_output()) - { - this->get_output()->wset()->remove_view(self()); - } - - this->set_output(parent->get_output()); - /* if the view isn't mapped, then it will be positioned properly in map() */ - if (is_mapped()) - { - reposition_relative_to_parent(self()); - } - - wf::scene::readd_front(parent->get_root_node(), this->get_root_node()); - check_refocus_parent(parent); - } else if (old_parent) - { - /* At this point, we are a regular view. */ - if (this->get_output()) - { - wf::scene::readd_front(get_output()->wset()->get_node(), get_root_node()); - get_output()->wset()->add_view(self()); - check_refocus_parent(old_parent); - } - } -} - -std::vector wf::view_interface_t::enumerate_views( - bool mapped_only) -{ - if (!this->is_mapped() && mapped_only) - { - return {}; - } - - std::vector result; - result.reserve(priv->last_view_cnt); - for (auto& v : this->children) - { - auto cdr = v->enumerate_views(mapped_only); - result.insert(result.end(), cdr.begin(), cdr.end()); - } - - result.push_back(self()); - priv->last_view_cnt = result.size(); - - return result; -} - void wf::view_interface_t::set_role(view_role_t new_role) { role = new_role; @@ -212,11 +61,6 @@ void wf::view_interface_t::set_output(wf::output_t *new_output) } wf::get_core().emit(&data); - - for (auto& view : this->children) - { - view->set_output(new_output); - } } wf::output_t*wf::view_interface_t::get_output() @@ -224,52 +68,6 @@ wf::output_t*wf::view_interface_t::get_output() return priv->output; } -void wf::view_interface_t::resize(int w, int h) -{ - /* no-op */ -} - -void wf::view_interface_t::set_geometry(wf::geometry_t g) -{ - move(g.x, g.y); - resize(g.width, g.height); -} - -void wf::view_interface_t::set_resizing(bool resizing, uint32_t edges) -{ - priv->update_windowed_geometry(self(), get_wm_geometry()); - /* edges are reset on the next commit */ - if (resizing) - { - this->priv->edges = edges; - } - - auto& in_resize = this->priv->in_continuous_resize; - in_resize += resizing ? 1 : -1; - - if (in_resize < 0) - { - LOGE("in_continuous_resize counter dropped below 0!"); - } -} - -void wf::view_interface_t::set_moving(bool moving) -{ - priv->update_windowed_geometry(self(), get_wm_geometry()); - auto& in_move = this->priv->in_continuous_move; - - in_move += moving ? 1 : -1; - if (in_move < 0) - { - LOGE("in_continuous_move counter dropped below 0!"); - } -} - -void wf::view_interface_t::request_native_size() -{ - /* no-op */ -} - void wf::view_interface_t::ping() { // Do nothing, specialized in the various shells @@ -280,11 +78,6 @@ void wf::view_interface_t::close() /* no-op */ } -wf::geometry_t wf::view_interface_t::get_wm_geometry() -{ - return get_output_geometry(); -} - wlr_box wf::view_interface_t::get_bounding_box() { return get_transformed_node()->get_bounding_box(); @@ -295,412 +88,9 @@ bool wf::view_interface_t::is_focusable() const return priv->keyboard_focus_enabled; } -void wf::view_interface_t::set_minimized(bool minim) -{ - this->minimized = minim; - view_minimized_signal data; - data.view = self(); - this->emit(&data); - get_output()->emit(&data); - - if (pending_minimized == minim) - { - return; - } - - this->pending_minimized = minim; - minimized = minim; - if (minimized) - { - view_disappeared_signal data; - data.view = self(); - get_output()->emit(&data); - wf::scene::set_node_enabled(get_root_node(), false); - } else - { - wf::scene::set_node_enabled(get_root_node(), true); - get_output()->focus_view(self(), true); - } -} - -void wf::view_interface_t::set_sticky(bool sticky) -{ - if (this->sticky == sticky) - { - return; - } - - damage(); - this->sticky = sticky; - damage(); - - wf::view_set_sticky_signal data; - data.view = self(); - - this->emit(&data); - if (this->get_output()) - { - this->get_output()->emit(&data); - } -} - -void wf::view_interface_t::set_tiled(uint32_t edges) -{ - if (edges) - { - priv->update_windowed_geometry(self(), get_wm_geometry()); - } - - wf::view_tiled_signal data; - data.view = self(); - data.old_edges = this->tiled_edges; - data.new_edges = edges; - - this->tiled_edges = edges; - this->emit(&data); - if (this->get_output()) - { - get_output()->emit(&data); - } -} - -void wf::view_interface_t::set_fullscreen(bool full) -{ - /* When fullscreening a view, we want to store the last geometry it had - * before getting fullscreen so that we can restore to it */ - if (full && !fullscreen) - { - priv->update_windowed_geometry(self(), get_wm_geometry()); - } - - fullscreen = full; - - view_fullscreen_signal data; - data.view = self(); - data.state = full; - if (get_output()) - { - get_output()->emit(&data); - } - - this->emit(&data); -} - -void wf::view_interface_t::set_activated(bool active) -{ - activated = active; - view_activated_state_signal ev; - this->emit(&ev); -} - -void wf::view_interface_t::move_request() -{ - view_move_request_signal data; - data.view = self(); - get_output()->emit(&data); -} - -void wf::view_interface_t::focus_request() -{ - if (get_output()) - { - view_focus_request_signal data; - data.view = self(); - data.self_request = false; - - emit(&data); - wf::get_core().emit(&data); - if (!data.carried_out) - { - wf::get_core().focus_output(get_output()); - get_output()->ensure_visible(self()); - get_output()->focus_view(self(), true); - } - } -} - -void wf::view_interface_t::resize_request(uint32_t edges) -{ - view_resize_request_signal data; - data.view = self(); - data.edges = edges; - get_output()->emit(&data); -} - -void wf::view_interface_t::tile_request(uint32_t edges) -{ - if (get_output()) - { - tile_request(edges, get_output()->wset()->get_current_workspace()); - } -} - -/** - * Put a view on the given workspace. - */ -static void move_to_workspace(wf::view_interface_t *view, wf::point_t workspace) -{ - auto output = view->get_output(); - auto wm_geometry = view->get_wm_geometry(); - auto delta = workspace - output->wset()->get_current_workspace(); - auto scr_size = output->get_screen_size(); - - wm_geometry.x += scr_size.width * delta.x; - wm_geometry.y += scr_size.height * delta.y; - view->move(wm_geometry.x, wm_geometry.y); -} - -void wf::view_interface_t::view_priv_impl::update_windowed_geometry( - wayfire_view self, wf::geometry_t geometry) -{ - if (!self->is_mapped() || self->tiled_edges || this->in_continuous_move || - this->in_continuous_resize) - { - return; - } - - this->last_windowed_geometry = geometry; - if (self->get_output()) - { - this->windowed_geometry_workarea = - self->get_output()->workarea->get_workarea(); - } else - { - this->windowed_geometry_workarea = {0, 0, -1, -1}; - } -} - -wf::geometry_t wf::view_interface_t::view_priv_impl::calculate_windowed_geometry( - wf::output_t *output) -{ - if (!output || (windowed_geometry_workarea.width <= 0)) - { - return last_windowed_geometry; - } - - const auto& geom = last_windowed_geometry; - const auto& old_area = windowed_geometry_workarea; - const auto& new_area = output->workarea->get_workarea(); - return { - .x = new_area.x + (geom.x - old_area.x) * new_area.width / - old_area.width, - .y = new_area.y + (geom.y - old_area.y) * new_area.height / - old_area.height, - .width = geom.width * new_area.width / old_area.width, - .height = geom.height * new_area.height / old_area.height - }; -} - -void wf::view_interface_t::tile_request(uint32_t edges, wf::point_t workspace) -{ - if (fullscreen || !get_output()) - { - return; - } - - view_tile_request_signal data; - data.view = self(); - data.edges = edges; - data.workspace = workspace; - data.desired_size = edges ? get_output()->workarea->get_workarea() : - priv->calculate_windowed_geometry(get_output()); - - set_tiled(edges); - if (is_mapped()) - { - get_output()->emit(&data); - } - - if (!data.carried_out) - { - if (data.desired_size.width > 0) - { - set_geometry(data.desired_size); - } else - { - request_native_size(); - } - - move_to_workspace(this, workspace); - } -} - -void wf::view_interface_t::minimize_request(bool state) -{ - if ((state == minimized) || !is_mapped()) - { - return; - } - - view_minimize_request_signal data; - data.view = self(); - data.state = state; - - if (is_mapped()) - { - get_output()->emit(&data); - /* Some plugin (e.g animate) will take care of the request, so we need - * to just send proper state to foreign-toplevel clients */ - if (data.carried_out) - { - minimized = state; - get_output()->refocus(); - } else - { - /* Do the default minimization */ - set_minimized(state); - } - } -} - -void wf::view_interface_t::fullscreen_request(wf::output_t *out, bool state) -{ - auto wo = (out ?: (get_output() ?: wf::get_core().get_active_output())); - if (wo) - { - fullscreen_request(wo, state, - wo->wset()->get_current_workspace()); - } -} - -void wf::view_interface_t::fullscreen_request(wf::output_t *out, bool state, - wf::point_t workspace) -{ - auto wo = (out ?: (get_output() ?: wf::get_core().get_active_output())); - assert(wo); - - /* TODO: what happens if the view is moved to the other output, but not - * fullscreened? We should make sure that it stays visible there */ - if (get_output() != wo) - { - wf::move_view_to_output(self(), wo, false); - } - - view_fullscreen_request_signal data; - data.view = self(); - data.state = state; - data.workspace = workspace; - data.desired_size = get_output()->get_relative_geometry(); - - if (!state) - { - data.desired_size = this->tiled_edges ? - this->get_output()->workarea->get_workarea() : - this->priv->calculate_windowed_geometry(get_output()); - } - - set_fullscreen(state); - if (is_mapped()) - { - wo->emit(&data); - } - - if (!data.carried_out) - { - if (data.desired_size.width > 0) - { - set_geometry(data.desired_size); - } else - { - request_native_size(); - } - - move_to_workspace(this, workspace); - } -} - void wf::view_interface_t::damage() { - auto bbox = get_untransformed_bounding_box(); - view_damage_raw(self(), bbox); -} - -wlr_box wf::view_interface_t::get_minimize_hint() -{ - return this->priv->minimize_hint; -} - -void wf::view_interface_t::set_minimize_hint(wlr_box hint) -{ - this->priv->minimize_hint = hint; -} - -bool wf::view_interface_t::should_be_decorated() -{ - return false; -} - -nonstd::observer_ptr wf::view_interface_t::get_decoration() -{ - return this->priv->frame.get(); -} - -void wf::view_interface_t::set_decoration( - std::unique_ptr frame) -{ - if (!frame) - { - damage(); - - // Take wm geometry as it was with the decoration. - const auto wm = get_wm_geometry(); - - // Drop the owned frame. - priv->frame = nullptr; - - // Grow the tiled view to fill its old expanded geometry that included - // the decoration. - if (!fullscreen && this->tiled_edges && (wm != get_wm_geometry())) - { - set_geometry(wm); - } - - view_decoration_changed_signal data; - data.view = self(); - emit(&data); - return; - } - - // Take wm geometry as it was before adding the frame */ - auto wm = get_wm_geometry(); - - damage(); - // Drop the old frame if any and assign the new one. - priv->frame = std::move(frame); - - /* Calculate the wm geometry of the view after adding the decoration. - * - * If the view is neither maximized nor fullscreen, then we want to expand - * the view geometry so that the actual view contents retain their size. - * - * For fullscreen and maximized views we want to "shrink" the view contents - * so that the total wm geometry remains the same as before. */ - wf::geometry_t target_wm_geometry; - if (!fullscreen && !this->tiled_edges) - { - target_wm_geometry = priv->frame->expand_wm_geometry(wm); - // make sure that the view doesn't go outside of the screen or such - auto wa = get_output()->workarea->get_workarea(); - auto visible = wf::geometry_intersection(target_wm_geometry, wa); - if (visible != target_wm_geometry) - { - target_wm_geometry.x = wm.x; - target_wm_geometry.y = wm.y; - } - } else if (fullscreen) - { - target_wm_geometry = get_output()->get_relative_geometry(); - } else if (this->tiled_edges) - { - target_wm_geometry = get_output()->workarea->get_workarea(); - } - - set_geometry(target_wm_geometry); - damage(); - - view_decoration_changed_signal ev; - ev.view = self(); - emit(&ev); + wf::scene::damage_node(get_surface_root_node(), get_surface_root_node()->get_bounding_box()); } bool wf::view_interface_t::has_transformer() @@ -709,29 +99,18 @@ bool wf::view_interface_t::has_transformer() return !ch.empty() && ch.front() != get_surface_root_node(); } -wf::geometry_t wf::view_interface_t::get_untransformed_bounding_box() -{ - return get_surface_root_node()->get_bounding_box(); -} - -const wf::render_target_t& wf::view_interface_t::take_snapshot() +void wf::view_interface_t::take_snapshot(wf::render_target_t& target) { - if (!is_mapped()) - { - return priv->offscreen_buffer; - } - - wf::render_target_t& offscreen_buffer = priv->offscreen_buffer; auto root_node = get_surface_root_node(); const wf::geometry_t bbox = root_node->get_bounding_box(); float scale = get_output()->handle->scale; OpenGL::render_begin(); - offscreen_buffer.allocate(bbox.width * scale, bbox.height * scale); + target.allocate(bbox.width * scale, bbox.height * scale); OpenGL::render_end(); - offscreen_buffer.geometry = root_node->get_bounding_box(); - offscreen_buffer.scale = scale; + target.geometry = root_node->get_bounding_box(); + target.scale = scale; std::vector instances; root_node->gen_render_instances(instances, [] (auto) {}, get_output()); @@ -739,11 +118,9 @@ const wf::render_target_t& wf::view_interface_t::take_snapshot() scene::render_pass_params_t params; params.background_color = {0, 0, 0, 0}; params.damage = bbox; - params.target = offscreen_buffer; + params.target = target; params.instances = &instances; - scene::run_render_pass(params, scene::RPASS_CLEAR_BACKGROUND); - return offscreen_buffer; } wf::view_interface_t::view_interface_t() @@ -774,16 +151,12 @@ void wf::view_interface_t::unref() class view_root_node_t : public wf::scene::floating_inner_node_t, public wf::view_node_tag_t { public: - view_root_node_t(wf::view_interface_t *_view) : floating_inner_node_t(false), view(_view) + view_root_node_t(wf::view_interface_t *_view) : floating_inner_node_t(false), + view_node_tag_t(_view), view(_view) { view->connect(&on_destruct); } - wayfire_view get_view() const override - { - return view; - } - std::string stringify() const override { if (view) @@ -807,14 +180,12 @@ class view_root_node_t : public wf::scene::floating_inner_node_t, public wf::vie void wf::view_interface_t::initialize() { + wf::dassert(priv->surface_root_node != nullptr, + "View implementations should set the surface root node immediately after creating the view!"); + priv->root_node = std::make_shared(this); priv->transformed_node = std::make_shared(); - if (!priv->surface_root_node) - { - priv->surface_root_node = std::make_shared(this); - } - // Set up view content to scene. priv->transformed_node->set_children_list({priv->surface_root_node}); priv->root_node->set_children_list({priv->transformed_node}); @@ -823,65 +194,10 @@ void wf::view_interface_t::initialize() void wf::view_interface_t::deinitialize() { - auto children = this->children; - for (auto ch : children) - { - ch->set_toplevel_parent(nullptr); - } - - set_decoration(nullptr); this->_clear_data(); - - OpenGL::render_begin(); - this->priv->offscreen_buffer.release(); - OpenGL::render_end(); -} - -wf::view_interface_t::~view_interface_t() -{ - /* Note: at this point, it is invalid to call most functions */ - unset_toplevel_parent(self()); } -void wf::view_damage_raw(wayfire_view view, const wlr_box& box) -{ - auto output = view->get_output(); - if (!output) - { - return; - } - - wf::scene::node_damage_signal data; - - /* Sticky views are visible on all workspaces. */ - if (view->sticky) - { - auto wsize = output->wset()->get_workspace_grid_size(); - auto cws = output->wset()->get_current_workspace(); - - /* Damage only the visible region of the shell view. - * This prevents hidden panels from spilling damage onto other workspaces */ - wlr_box ws_box = output->get_relative_geometry(); - wlr_box visible_damage = geometry_intersection(box, ws_box); - for (int i = 0; i < wsize.width; i++) - { - for (int j = 0; j < wsize.height; j++) - { - const int dx = (i - cws.x) * ws_box.width; - const int dy = (j - cws.y) * ws_box.height; - data.region |= visible_damage + wf::point_t{dx, dy}; - } - } - } else - { - data.region |= box; - } - - view->get_transformed_node()->emit(&data); - // FIXME: node should be pushed from surfaces up, but we can't do that yet, - // because transformers are not in the scenegraph yet - view->get_surface_root_node()->emit(&data); -} +wf::view_interface_t::~view_interface_t() = default; void wf::view_interface_t::destruct() { @@ -926,9 +242,6 @@ wayfire_view wf::node_to_view(wf::scene::node_ptr node) return node_to_view(node.get()); } -// FIXME: Consider splitting to header + source file -#include "view-node.cpp" - wl_client*wf::view_interface_t::get_client() { if (priv->wsurface) @@ -943,18 +256,3 @@ wlr_surface*wf::view_interface_t::get_wlr_surface() { return priv->wsurface; } - -void wf::view_interface_t::set_allowed_actions(uint32_t actions) const -{ - priv->allowed_actions = actions; -} - -uint32_t wf::view_interface_t::get_allowed_actions() const -{ - return priv->allowed_actions; -} - -std::shared_ptr wf::view_interface_t::get_wset() -{ - return priv->current_wset.lock(); -} diff --git a/src/view/wlr-surface-node.cpp b/src/view/wlr-surface-node.cpp index 3169c6d64..dd6b1df95 100644 --- a/src/view/wlr-surface-node.cpp +++ b/src/view/wlr-surface-node.cpp @@ -43,6 +43,12 @@ wf::scene::surface_state_t& wf::scene::surface_state_t::operator =(surface_state void wf::scene::surface_state_t::merge_state(wlr_surface *surface) { + // NB: lock the new buffer first, in case it is the same as the old one + if (surface->buffer) + { + wlr_buffer_lock(&surface->buffer->base); + } + if (current_buffer) { wlr_buffer_unlock(current_buffer); @@ -50,14 +56,13 @@ void wf::scene::surface_state_t::merge_state(wlr_surface *surface) if (surface->buffer) { - wlr_buffer_lock(&surface->buffer->base); this->current_buffer = &surface->buffer->base; this->texture = surface->buffer->texture; this->size = {surface->current.width, surface->current.height}; } else { - this->current_buffer = nullptr; - this->texture = nullptr; + this->current_buffer = NULL; + this->texture = NULL; this->size = {0, 0}; } diff --git a/src/view/xdg-shell.cpp b/src/view/xdg-shell.cpp index fff625661..e7a3dabae 100644 --- a/src/view/xdg-shell.cpp +++ b/src/view/xdg-shell.cpp @@ -1,11 +1,13 @@ +#include #include #include #include "view/view-impl.hpp" #include "wayfire/core.hpp" #include "surface-impl.hpp" +#include "wayfire/geometry.hpp" #include "wayfire/output.hpp" -#include "wayfire/decorator.hpp" #include "wayfire/scene-operations.hpp" +#include "wayfire/scene-render.hpp" #include "wayfire/scene.hpp" #include "wayfire/view-helpers.hpp" #include "wayfire/view.hpp" @@ -18,15 +20,32 @@ #include "xdg-shell/xdg-toplevel-view.hpp" #include "view-keyboard-interaction.hpp" -wayfire_xdg_popup::wayfire_xdg_popup(wlr_xdg_popup *popup) : - wf::wlr_view_t() +class wayfire_xdg_popup_node : public wf::scene::translation_node_t +{ + public: + wayfire_xdg_popup_node(uint64_t view_id) : id(view_id) + {} + + std::string stringify() const override + { + return "xdg-popup view id=" + std::to_string(id) + " " + stringify_flags(); + } + + private: + uint64_t id = 0; +}; + +wayfire_xdg_popup::wayfire_xdg_popup(wlr_xdg_popup *popup) : wf::view_interface_t() { this->popup_parent = wf::wl_surface_to_wayfire_view(popup->parent->resource).get(); this->popup = popup; this->role = wf::VIEW_ROLE_UNMANAGED; this->priv->keyboard_focus_enabled = false; + this->surface_root_node = std::make_shared(this->get_id()); + this->set_surface_root_node(surface_root_node); this->set_output(popup_parent->get_output()); + if (!dynamic_cast(popup_parent.get())) { // 'toplevel' popups are responsible for closing their popup tree when the parent loses focus. @@ -41,17 +60,20 @@ wayfire_xdg_popup::wayfire_xdg_popup(wlr_xdg_popup *popup) : wf::get_core().connect(&on_keyboard_focus_changed); } + + on_surface_commit.set_callback([&] (void*) { commit(); }); } +wayfire_xdg_popup::~wayfire_xdg_popup() = default; + void wayfire_xdg_popup::initialize() { - wf::wlr_view_t::initialize(); + wf::view_interface_t::initialize(); + this->main_surface = std::make_shared(popup->base->surface, true); + LOGI("New xdg popup"); - on_map.set_callback([&] (void*) { map(this->popup->base->surface); }); - on_unmap.set_callback([&] (void*) - { - unmap(); - }); + on_map.set_callback([&] (void*) { map(); }); + on_unmap.set_callback([&] (void*) { unmap(); }); on_destroy.set_callback([&] (void*) { destroy(); }); on_new_popup.set_callback([&] (void *data) { @@ -89,7 +111,7 @@ void wayfire_xdg_popup::initialize() unconstrain(); } -void wayfire_xdg_popup::map(wlr_surface *surface) +void wayfire_xdg_popup::map() { wf::scene::layer parent_layer = wf::get_view_layer(popup_parent).value_or(wf::scene::layer::WORKSPACE); auto target_layer = wf::scene::layer::UNMANAGED; @@ -99,13 +121,33 @@ void wayfire_xdg_popup::map(wlr_surface *surface) } wf::scene::readd_front(get_output()->node_for_layer(target_layer), get_root_node()); - wlr_view_t::map(surface); + + on_surface_commit.connect(&popup->base->surface->events.commit); + priv->set_mapped_surface_contents(main_surface); + priv->set_mapped(true); + update_size(); + + damage(); + emit_view_map(); update_position(); } +void wayfire_xdg_popup::unmap() +{ + damage(); + emit_view_pre_unmap(); + + main_surface = nullptr; + priv->unset_mapped_surface_contents(); + on_surface_commit.disconnect(); + + emit_view_unmap(); + priv->set_mapped(false); +} + void wayfire_xdg_popup::commit() { - wlr_view_t::commit(); + update_size(); update_position(); } @@ -116,6 +158,7 @@ void wayfire_xdg_popup::update_position() return; } + // Offset relative to the parent surface wf::pointf_t popup_offset = {1.0 * popup->current.geometry.x, 1.0 * popup->current.geometry.y}; if (wlr_surface_is_xdg_surface(popup->parent)) { @@ -125,9 +168,12 @@ void wayfire_xdg_popup::update_position() popup_offset.y += box.y; } - auto parent_geometry = popup_parent->get_output_geometry(); - popup_offset.x += parent_geometry.x - popup->base->current.geometry.x; - popup_offset.y += parent_geometry.y - popup->base->current.geometry.y; + // Get the {0, 0} of the parent view in output coordinates + popup_offset += popup_parent->get_surface_root_node()->to_global({0, 0}); + + // Subtract shadows, etc. + popup_offset.x -= popup->base->current.geometry.x; + popup_offset.y -= popup->base->current.geometry.y; // Apply transformers to the popup position auto node = popup_parent->get_surface_root_node()->parent(); @@ -161,10 +207,9 @@ void wayfire_xdg_popup::unconstrain() } auto box = get_output()->get_relative_geometry(); - auto wm = toplevel_parent->get_output_geometry(); - box.x -= wm.x; - box.y -= wm.y; - + auto parent_offset = toplevel_parent->get_surface_root_node()->to_global({0, 0}); + box.x -= parent_offset.x; + box.y -= parent_offset.y; wlr_xdg_popup_unconstrain_from_box(popup, &box); } @@ -175,8 +220,8 @@ void wayfire_xdg_popup::destroy() on_destroy.disconnect(); on_new_popup.disconnect(); on_ping_timeout.disconnect(); - - wlr_view_t::destroy(); + /* Drop the internal reference */ + unref(); } void wayfire_xdg_popup::close() @@ -195,6 +240,78 @@ void wayfire_xdg_popup::ping() } } +void wayfire_xdg_popup::update_size() +{ + if (!is_mapped()) + { + return; + } + + wf::dimensions_t current_size{popup->base->surface->current.width, popup->base->surface->current.height}; + if (current_size == wf::dimensions(geometry)) + { + return; + } + + /* Damage current size */ + wf::scene::damage_node(get_root_node(), last_bounding_box); + geometry.width = current_size.width; + geometry.height = current_size.height; + + /* Damage new size */ + last_bounding_box = get_bounding_box(); + wf::scene::damage_node(get_root_node(), last_bounding_box); + wf::scene::update(this->get_surface_root_node(), wf::scene::update_flag::GEOMETRY); +} + +bool wayfire_xdg_popup::is_mapped() const +{ + return priv->wsurface != nullptr; +} + +void wayfire_xdg_popup::handle_app_id_changed(std::string new_app_id) +{ + this->app_id = new_app_id; + wf::emit_app_id_changed_signal(self()); +} + +void wayfire_xdg_popup::handle_title_changed(std::string new_title) +{ + this->title = new_title; + wf::emit_title_changed_signal(self()); +} + +std::string wayfire_xdg_popup::get_app_id() +{ + return this->app_id; +} + +std::string wayfire_xdg_popup::get_title() +{ + return this->title; +} + +void wayfire_xdg_popup::move(int x, int y) +{ + wf::scene::damage_node(get_root_node(), last_bounding_box); + surface_root_node->set_offset({x, y}); + geometry.x = x; + geometry.y = y; + damage(); + last_bounding_box = get_bounding_box(); + wf::scene::update(this->get_surface_root_node(), wf::scene::update_flag::GEOMETRY); +} + +wf::geometry_t wayfire_xdg_popup::get_geometry() +{ + return geometry; +} + +wlr_surface*wayfire_xdg_popup::get_keyboard_focus_surface() +{ + return nullptr; +} + void create_xdg_popup(wlr_xdg_popup *popup) { if (!wf::wl_surface_to_wayfire_view(popup->parent->resource)) diff --git a/src/view/xdg-shell.hpp b/src/view/xdg-shell.hpp index 8399be826..b9c718464 100644 --- a/src/view/xdg-shell.hpp +++ b/src/view/xdg-shell.hpp @@ -2,13 +2,17 @@ #define XDG_SHELL_HPP #include "view-impl.hpp" +#include "wayfire/geometry.hpp" #include "wayfire/signal-definitions.hpp" #include "wayfire/signal-provider.hpp" +#include "wayfire/view.hpp" + +class wayfire_xdg_popup_node; /** * A class for xdg-shell popups */ -class wayfire_xdg_popup : public wf::wlr_view_t +class wayfire_xdg_popup : public wf::view_interface_t { protected: wf::wl_listener_wrapper on_destroy, on_new_popup, @@ -25,14 +29,49 @@ class wayfire_xdg_popup : public wf::wlr_view_t public: wayfire_xdg_popup(wlr_xdg_popup *popup); + ~wayfire_xdg_popup(); + void initialize() override; wayfire_view popup_parent; - virtual void map(wlr_surface *surface) override; - virtual void commit() override; - virtual void destroy() override; - virtual void close() override; - void ping() final; + void map(); + void unmap(); + void commit(); + void destroy(); + void close() override; + void ping() override final; + + /* Just pass to the default wlr surface implementation */ + bool is_mapped() const override; + + std::string get_app_id() override final; + std::string get_title() override final; + + void move(int x, int y); + wf::geometry_t get_geometry(); + virtual wlr_surface *get_keyboard_focus_surface() override; + + private: + /** + * The bounding box of the view the last time it was rendered. + * + * This is used to damage the view when it is resized, because when a + * transformer changes because the view is resized, we can't reliably + * calculate the old view region to damage. + */ + wf::geometry_t last_bounding_box{0, 0, 0, 0}; + + /** The output geometry of the view */ + wf::geometry_t geometry{100, 100, 0, 0}; + + wf::wl_listener_wrapper on_surface_commit; + std::shared_ptr main_surface; + std::shared_ptr surface_root_node; + + std::string title, app_id; + void handle_app_id_changed(std::string new_app_id); + void handle_title_changed(std::string new_title); + void update_size(); }; void create_xdg_popup(wlr_xdg_popup *popup); diff --git a/src/view/xdg-shell/xdg-toplevel-view.cpp b/src/view/xdg-shell/xdg-toplevel-view.cpp index aed1d4a62..abc2de08e 100644 --- a/src/view/xdg-shell/xdg-toplevel-view.cpp +++ b/src/view/xdg-shell/xdg-toplevel-view.cpp @@ -1,5 +1,6 @@ #include "xdg-toplevel-view.hpp" #include +#include "view/toplevel-node.hpp" #include "wayfire/core.hpp" #include #include @@ -10,6 +11,8 @@ #include "wayfire/geometry.hpp" #include #include +#include +#include /** * When we get a request for setting CSD, the view might not have been @@ -22,9 +25,27 @@ wf::xdg_toplevel_view_t::xdg_toplevel_view_t(wlr_xdg_toplevel *tlvl) { this->xdg_toplevel = tlvl; this->main_surface = std::make_shared(tlvl->base->surface, false); + surface_root_node = std::make_shared(this); + this->set_surface_root_node(surface_root_node); - on_surface_commit.set_callback([&] (void*) { commit(); }); - on_map.set_callback([&] (void*) { map(); }); + this->on_toplevel_applied = [&] (xdg_toplevel_applied_state_signal *ev) + { + this->handle_toplevel_state_changed(ev->old_state); + }; + wtoplevel = std::make_shared(tlvl, main_surface); + wtoplevel->connect(&on_toplevel_applied); + this->priv->toplevel = wtoplevel; + + on_map.set_callback([&] (void*) + { + wlr_box box; + wlr_xdg_surface_get_geometry(xdg_toplevel->base, &box); + wtoplevel->pending().mapped = true; + wtoplevel->pending().geometry.width = box.width; + wtoplevel->pending().geometry.height = box.height; + priv->set_mapped_surface_contents(main_surface); + wf::get_core().tx_manager->schedule_object(wtoplevel); + }); on_unmap.set_callback([&] (void*) { unmap(); }); on_destroy.set_callback([&] (void*) { destroy(); }); on_new_popup.set_callback([&] (void *data) @@ -62,30 +83,36 @@ wf::xdg_toplevel_view_t::xdg_toplevel_view_t(wlr_xdg_toplevel *tlvl) { auto parent = xdg_toplevel->parent ? (wf::view_interface_t*)(xdg_toplevel->parent->base->data) : nullptr; - set_toplevel_parent(parent); + set_toplevel_parent(toplevel_cast(parent)); }); on_ping_timeout.set_callback([&] (void*) { wf::emit_ping_timeout_signal(self()); }); - on_request_move.set_callback([&] (void*) { move_request(); }); + on_request_move.set_callback([&] (void*) + { + wf::get_core().default_wm->move_request({this}); + }); on_request_resize.set_callback([&] (auto data) { auto ev = static_cast(data); - resize_request(ev->edges); + wf::get_core().default_wm->resize_request({this}, ev->edges); + }); + on_request_minimize.set_callback([&] (void*) + { + wf::get_core().default_wm->minimize_request({this}, true); }); - on_request_minimize.set_callback([&] (void*) { minimize_request(true); }); on_request_maximize.set_callback([&] (void *data) { - tile_request(xdg_toplevel->requested.maximized ? - wf::TILED_EDGES_ALL : 0); + wf::get_core().default_wm->tile_request({this}, + xdg_toplevel->requested.maximized ? wf::TILED_EDGES_ALL : 0); }); on_request_fullscreen.set_callback([&] (void *data) { wlr_xdg_toplevel_requested *req = &xdg_toplevel->requested; auto wo = wf::get_core().output_layout->find_output(req->fullscreen_output); - fullscreen_request(wo, req->fullscreen); + wf::get_core().default_wm->fullscreen_request({this}, wo, req->fullscreen); }); on_map.connect(&xdg_toplevel->base->events.map); @@ -107,75 +134,9 @@ wf::xdg_toplevel_view_t::xdg_toplevel_view_t(wlr_xdg_toplevel *tlvl) xdg_toplevel->base->data = dynamic_cast(this); } -void wf::xdg_toplevel_view_t::move(int x, int y) -{ - this->damage(); - - view_geometry_changed_signal geometry_changed; - geometry_changed.view = self(); - geometry_changed.old_geometry = this->wm_geometry; - - this->wm_geometry.x = x; - this->wm_geometry.y = y; - - if (priv->frame) - { - auto expanded = priv->frame->expand_wm_geometry(this->wm_geometry); - auto deco_offset = wf::origin(wm_geometry) - wf::origin(expanded); - - this->base_geometry.x = this->wm_geometry.x - wm_offset.x + deco_offset.x; - this->base_geometry.y = this->wm_geometry.y - wm_offset.y + deco_offset.y; - } else - { - this->base_geometry.x = this->wm_geometry.x - wm_offset.x; - this->base_geometry.y = this->wm_geometry.y - wm_offset.y; - } - - /* Make sure that if we move the view while it is unmapped, its snapshot - * is still valid coordinates */ - priv->offscreen_buffer = priv->offscreen_buffer.translated({ - x - geometry_changed.old_geometry.x, y - geometry_changed.old_geometry.y, - }); - - this->damage(); - - emit(&geometry_changed); - wf::get_core().emit(&geometry_changed); - if (get_output()) - { - get_output()->emit(&geometry_changed); - } - - scene::update(this->get_surface_root_node(), scene::update_flag::GEOMETRY); -} - -wf::geometry_t get_xdg_geometry(wlr_xdg_toplevel *toplevel) -{ - wlr_box xdg_geometry; - wlr_xdg_surface_get_geometry(toplevel->base, &xdg_geometry); - - return xdg_geometry; -} - -void wf::xdg_toplevel_view_t::resize(int w, int h) -{ - if (priv->frame) - { - priv->frame->calculate_resize_size(w, h); - } - - auto current_geometry = get_xdg_geometry(xdg_toplevel); - wf::dimensions_t current_size{current_geometry.width, current_geometry.height}; - if (should_resize_client({w, h}, current_size)) - { - this->last_size_request = {w, h}; - last_configure_serial = wlr_xdg_toplevel_set_size(xdg_toplevel, w, h); - } -} - void wf::xdg_toplevel_view_t::request_native_size() { - last_configure_serial = wlr_xdg_toplevel_set_size(xdg_toplevel, 0, 0); + this->wtoplevel->request_native_size(); } void wf::xdg_toplevel_view_t::close() @@ -195,16 +156,6 @@ void wf::xdg_toplevel_view_t::ping() } } -wf::geometry_t wf::xdg_toplevel_view_t::get_wm_geometry() -{ - return wm_geometry; -} - -wf::geometry_t wf::xdg_toplevel_view_t::get_output_geometry() -{ - return base_geometry; -} - wlr_surface*wf::xdg_toplevel_view_t::get_keyboard_focus_surface() { if (is_mapped()) @@ -220,25 +171,10 @@ bool wf::xdg_toplevel_view_t::is_focusable() const return true; } -void wf::xdg_toplevel_view_t::set_tiled(uint32_t edges) -{ - wlr_xdg_toplevel_set_tiled(xdg_toplevel, edges); - last_configure_serial = wlr_xdg_toplevel_set_maximized(xdg_toplevel, - (edges == wf::TILED_EDGES_ALL)); - - wf::view_interface_t::set_tiled(edges); -} - -void wf::xdg_toplevel_view_t::set_fullscreen(bool fullscreen) -{ - view_interface_t::set_fullscreen(fullscreen); - last_configure_serial = wlr_xdg_toplevel_set_fullscreen(xdg_toplevel, fullscreen); -} - void wf::xdg_toplevel_view_t::set_activated(bool active) { - view_interface_t::set_activated(active); - last_configure_serial = wlr_xdg_toplevel_set_activated(xdg_toplevel, active); + toplevel_view_interface_t::set_activated(active); + wlr_xdg_toplevel_set_activated(xdg_toplevel, active); } std::string wf::xdg_toplevel_view_t::get_app_id() @@ -258,7 +194,7 @@ bool wf::xdg_toplevel_view_t::should_be_decorated() bool wf::xdg_toplevel_view_t::is_mapped() const { - return priv->wsurface; + return wtoplevel->current().mapped && priv->wsurface; } void wf::xdg_toplevel_view_t::initialize() @@ -282,31 +218,12 @@ void wf::xdg_toplevel_view_t::initialize() if (xdg_toplevel->requested.fullscreen) { - fullscreen_request(get_output(), true); + wf::get_core().default_wm->fullscreen_request({this}, get_output(), true); } if (xdg_toplevel->requested.maximized) { - tile_request(wf::TILED_EDGES_ALL); - } -} - -bool wf::xdg_toplevel_view_t::should_resize_client(wf::dimensions_t old, wf::dimensions_t next) -{ - /* - * Do not send a configure if the client will retain its size. - * This is needed if a client starts with one size and immediately resizes - * again. - * - * If we do configure it with the given size, then it will think that we - * are requesting the given size, and won't resize itself again. - */ - if (this->last_size_request == wf::dimensions_t{0, 0}) - { - return old != next; - } else - { - return old != last_size_request; + wf::get_core().default_wm->tile_request({this}, TILED_EDGES_ALL); } } @@ -318,18 +235,14 @@ void wf::xdg_toplevel_view_t::map() this->has_client_decoration = uses_csd[surf]; } - priv->set_mapped_surface_contents(main_surface); priv->set_mapped(true); - on_surface_commit.connect(&surf->events.commit); - - update_size(); if (role == VIEW_ROLE_TOPLEVEL) { if (!parent) { wf::scene::readd_front(get_output()->wset()->get_node(), get_root_node()); - get_output()->wset()->add_view(self()); + get_output()->wset()->add_view({this}); } get_output()->focus_view(self(), true); @@ -345,96 +258,30 @@ void wf::xdg_toplevel_view_t::unmap() { damage(); emit_view_pre_unmap(); - set_decoration(nullptr); main_surface = nullptr; priv->unset_mapped_surface_contents(); - on_surface_commit.disconnect(); emit_view_unmap(); priv->set_mapped(false); } -void wf::xdg_toplevel_view_t::update_size() +void wf::xdg_toplevel_view_t::handle_toplevel_state_changed(wf::toplevel_state_t old_state) { - if (!priv->wsurface) - { - return; - } - - wf::dimensions_t current_size = {priv->wsurface->current.width, priv->wsurface->current.height}; - if ((current_size.width == base_geometry.width) && - (current_size.height == base_geometry.height)) - { - return; - } - - /* Damage current size */ - view_damage_raw(self(), base_geometry); - adjust_anchored_edge(current_size); - - view_geometry_changed_signal geometry_changed; - geometry_changed.view = self(); - geometry_changed.old_geometry = get_wm_geometry(); - - base_geometry.width = current_size.width; - base_geometry.height = current_size.height; - - auto xdg_g = get_xdg_geometry(this->xdg_toplevel); - if (priv->frame) + surface_root_node->set_offset(wf::origin(wtoplevel->calculate_base_geometry())); + if (!old_state.mapped && wtoplevel->current().mapped) { - xdg_g = priv->frame->expand_wm_geometry(xdg_g); + map(); } - wm_geometry.width = xdg_g.width; - wm_geometry.height = xdg_g.height; + wf::scene::damage_node(get_root_node(), last_bounding_box); + emit_toplevel_state_change_signals({this}, old_state); - /* Damage new size */ damage(); - - emit(&geometry_changed); - wf::get_core().emit(&geometry_changed); - if (get_output()) - { - get_output()->emit(&geometry_changed); - } - + last_bounding_box = this->get_surface_root_node()->get_bounding_box(); scene::update(this->get_surface_root_node(), scene::update_flag::GEOMETRY); } -void wf::xdg_toplevel_view_t::commit() -{ - update_size(); - - /* Clear the resize edges. - * This is must be done here because if the user(or plugin) resizes too fast, - * the shell client might still haven't configured the surface, and in this - * case the next commit(here) needs to still have access to the gravity */ - if (!priv->in_continuous_resize) - { - priv->edges = 0; - } - - /* On each commit, check whether the window geometry of the xdg_surface - * changed. In those cases, we need to adjust the view's output geometry, - * so that the apparent wm geometry doesn't change */ - auto xdg_g = get_xdg_geometry(xdg_toplevel); - if (wm_offset != wf::origin(xdg_g)) - { - wm_offset = {xdg_g.x, xdg_g.y}; - move(wm_geometry.x, wm_geometry.y); - } - - if (xdg_toplevel->base->current.configure_serial == this->last_configure_serial) - { - this->last_size_request = wf::dimensions(xdg_g); - } - - scene::surface_state_t cur_state; - cur_state.merge_state(xdg_toplevel->base->surface); - main_surface->apply_state(std::move(cur_state)); -} - void wf::xdg_toplevel_view_t::destroy() { on_map.disconnect(); @@ -460,38 +307,13 @@ void wf::xdg_toplevel_view_t::destroy() void wf::xdg_toplevel_view_t::handle_title_changed(std::string new_title) { this->title = new_title; - view_title_changed_signal data; - data.view = self(); - emit(&data); + emit_title_changed_signal(self()); } void wf::xdg_toplevel_view_t::handle_app_id_changed(std::string new_app_id) { this->app_id = new_app_id; - view_app_id_changed_signal data; - data.view = self(); - emit(&data); -} - -void wf::xdg_toplevel_view_t::adjust_anchored_edge(wf::dimensions_t new_size) -{ - if (priv->edges) - { - wf::point_t offset = {0, 0}; - if (priv->edges & WLR_EDGE_LEFT) - { - offset.x = base_geometry.width - new_size.width; - } - - if (priv->edges & WLR_EDGE_TOP) - { - offset.y = base_geometry.height - new_size.height; - } - - this->base_geometry = this->base_geometry + offset; - this->wm_geometry = this->wm_geometry + offset; - priv->offscreen_buffer = priv->offscreen_buffer.translated(offset); - } + emit_app_id_changed_signal(self()); } void wf::xdg_toplevel_view_t::set_decoration_mode(bool use_csd) @@ -501,7 +323,7 @@ void wf::xdg_toplevel_view_t::set_decoration_mode(bool use_csd) if ((was_decorated != should_be_decorated()) && is_mapped()) { wf::view_decoration_state_updated_signal data; - data.view = self(); + data.view = {this}; this->emit(&data); wf::get_core().emit(&data); diff --git a/src/view/xdg-shell/xdg-toplevel-view.hpp b/src/view/xdg-shell/xdg-toplevel-view.hpp index 86ee9c55d..c301aed4d 100644 --- a/src/view/xdg-shell/xdg-toplevel-view.hpp +++ b/src/view/xdg-shell/xdg-toplevel-view.hpp @@ -1,30 +1,29 @@ +#include "view/toplevel-node.hpp" #include "wayfire/geometry.hpp" #include #include #include "wayfire/unstable/wlr-surface-node.hpp" #include "../surface-impl.hpp" +#include +#include "wayfire/toplevel.hpp" +#include "xdg-toplevel.hpp" + namespace wf { /** * An implementation of view_interface_t for xdg-shell toplevels. */ -class xdg_toplevel_view_t : public wf::view_interface_t +class xdg_toplevel_view_t : public wf::toplevel_view_interface_t { public: xdg_toplevel_view_t(wlr_xdg_toplevel *tlvl); - void move(int x, int y) override; - void resize(int w, int h) override; void request_native_size() override; void close() override; void ping() override; - wf::geometry_t get_wm_geometry() override; - wf::geometry_t get_output_geometry() override; wlr_surface *get_keyboard_focus_surface() override; bool is_focusable() const override; - void set_tiled(uint32_t edges) override; - void set_fullscreen(bool fullscreen) override; void set_activated(bool active) override; std::string get_app_id() override; std::string get_title() override; @@ -47,26 +46,21 @@ class xdg_toplevel_view_t : public wf::view_interface_t std::string app_id; std::string title; - wf::wl_listener_wrapper on_surface_commit; std::shared_ptr main_surface; + std::shared_ptr surface_root_node; - wf::geometry_t wm_geometry = {100, 100, 0, 0}; - wf::geometry_t base_geometry = {100, 100, 0, 0}; - wf::point_t wm_offset; + std::shared_ptr wtoplevel; + wf::signal::connection_t on_toplevel_applied; + wf::geometry_t last_bounding_box = {0, 0, 0, 0}; - wf::dimensions_t last_size_request = {0, 0}; wlr_xdg_toplevel *xdg_toplevel; - uint32_t last_configure_serial; - bool should_resize_client(wf::dimensions_t old, wf::dimensions_t next); - void update_size(); - void adjust_anchored_edge(wf::dimensions_t new_size); void map(); void unmap(); - void commit(); void destroy(); void handle_title_changed(std::string new_title); void handle_app_id_changed(std::string new_app_id); + void handle_toplevel_state_changed(toplevel_state_t old_state); }; } diff --git a/src/view/xdg-shell/xdg-toplevel.cpp b/src/view/xdg-shell/xdg-toplevel.cpp new file mode 100644 index 000000000..759fa712a --- /dev/null +++ b/src/view/xdg-shell/xdg-toplevel.cpp @@ -0,0 +1,209 @@ +#include "xdg-toplevel.hpp" +#include "wayfire/core.hpp" +#include +#include +#include +#include "wayfire/geometry.hpp" +#include "wayfire/toplevel.hpp" +#include "wayfire/txn/transaction-object.hpp" +#include "../view-impl.hpp" + +wf::xdg_toplevel_t::xdg_toplevel_t(wlr_xdg_toplevel *toplevel, + std::shared_ptr main_surface) +{ + this->toplevel = toplevel; + this->main_surface = main_surface; + + on_surface_commit.set_callback([&] (void*) { handle_surface_commit(); }); + on_surface_commit.connect(&toplevel->base->surface->events.commit); + + on_toplevel_destroy.set_callback([&] (void*) + { + this->toplevel = NULL; + on_toplevel_destroy.disconnect(); + on_surface_commit.disconnect(); + emit_ready(); + }); + on_toplevel_destroy.connect(&toplevel->base->events.destroy); +} + +void wf::xdg_toplevel_t::request_native_size() +{ + // This will trigger a client-driven transaction + wlr_xdg_toplevel_set_size(toplevel, 0, 0); +} + +void wf::xdg_toplevel_t::commit() +{ + this->pending_ready = true; + _committed = _pending; + LOGC(TXNI, this, ": committing toplevel state mapped=", _pending.mapped, + " geometry=", _pending.geometry, " tiled=", _pending.tiled_edges, " fs=", _pending.fullscreen, + " margins=", _pending.margins.left, ",", _pending.margins.right, ",", + _pending.margins.top, ",", _pending.margins.bottom); + + if (!this->toplevel) + { + // No longer mapped => we can do whatever + emit_ready(); + return; + } + + wf::dimensions_t current_size = + shrink_dimensions_by_margins(wf::dimensions(_current.geometry), _current.margins); + if (_pending.mapped && !_current.mapped) + { + // We are trying to map the toplevel => check whether we should wait until it sets the proper + // geometry, or whether we are 'only' mapping without resizing. + current_size = get_current_wlr_toplevel_size(); + } + + bool wait_for_client = false; + + const wf::dimensions_t desired_size = + wf::shrink_dimensions_by_margins(wf::dimensions(_pending.geometry), _pending.margins); + + if (current_size != desired_size) + { + wait_for_client = true; + const int configure_width = std::max(1, desired_size.width); + const int configure_height = std::max(1, desired_size.height); + this->target_configure = wlr_xdg_toplevel_set_size(this->toplevel, configure_width, configure_height); + } + + if (_current.tiled_edges != _pending.tiled_edges) + { + wait_for_client = true; + wlr_xdg_toplevel_set_tiled(this->toplevel, _pending.tiled_edges); + this->target_configure = + wlr_xdg_toplevel_set_maximized(this->toplevel, (_pending.tiled_edges == wf::TILED_EDGES_ALL)); + } + + if (_current.fullscreen != _pending.fullscreen) + { + wait_for_client = true; + this->target_configure = wlr_xdg_toplevel_set_fullscreen(toplevel, _pending.fullscreen); + } + + if (wait_for_client) + { + // Send frame done to let the client know it update its state as fast as possible. + main_surface->send_frame_done(); + } else + { + emit_ready(); + } +} + +void wf::xdg_toplevel_t::apply() +{ + xdg_toplevel_applied_state_signal event_applied; + event_applied.old_state = current(); + + if (!toplevel) + { + // If toplevel does no longer exist, we can't change the size anymore. + _committed.geometry.width = _current.geometry.width; + _committed.geometry.height = _current.geometry.height; + } + + this->_current = committed(); + const bool is_pending = wf::get_core().tx_manager->is_object_pending(shared_from_this()); + if (!is_pending) + { + // Adjust for potential moves due to gravity + _pending = committed(); + } + + apply_pending_state(); + emit(&event_applied); +} + +void wf::xdg_toplevel_t::handle_surface_commit() +{ + pending_state.merge_state(toplevel->base->surface); + + const bool is_committed = wf::get_core().tx_manager->is_object_committed(shared_from_this()); + if (is_committed) + { + // TODO: handle overflow? + if (this->toplevel->base->current.configure_serial < this->target_configure) + { + // Desired state not reached => wait for the desired state to be reached. In the meantime, send a + // frame done so that the client can redraw faster. + main_surface->send_frame_done(); + return; + } + + const wf::dimensions_t real_size = + expand_dimensions_by_margins(get_current_wlr_toplevel_size(), _committed.margins); + wf::adjust_geometry_for_gravity(_committed, real_size); + emit_ready(); + return; + } + + const bool is_pending = wf::get_core().tx_manager->is_object_pending(shared_from_this()); + if (is_pending) + { + return; + } + + auto toplevel_size = + expand_dimensions_by_margins(get_current_wlr_toplevel_size(), _current.margins); + if (toplevel_size == wf::dimensions(current().geometry)) + { + // Size did not change, there are no transactions going on - apply the new texture directly + apply_pending_state(); + return; + } + + adjust_geometry_for_gravity(_pending, toplevel_size); + LOGC(VIEWS, "Client-initiated resize to geometry ", pending().geometry); + auto tx = wf::txn::transaction_t::create(); + tx->add_object(shared_from_this()); + wf::get_core().tx_manager->schedule_transaction(std::move(tx)); +} + +wf::geometry_t wf::xdg_toplevel_t::calculate_base_geometry() +{ + auto geometry = current().geometry; + geometry.x = geometry.x - wm_offset.x + _current.margins.left; + geometry.y = geometry.y - wm_offset.y + _current.margins.top; + geometry.width = main_surface->get_bounding_box().width; + geometry.height = main_surface->get_bounding_box().height; + return geometry; +} + +void wf::xdg_toplevel_t::apply_pending_state() +{ + if (toplevel) + { + pending_state.merge_state(toplevel->base->surface); + } + + main_surface->apply_state(std::move(pending_state)); + + if (toplevel) + { + wlr_box wm_box; + wlr_xdg_surface_get_geometry(toplevel->base, &wm_box); + this->wm_offset = wf::origin(wm_box); + } +} + +void wf::xdg_toplevel_t::emit_ready() +{ + if (pending_ready) + { + pending_ready = false; + emit_object_ready(this); + } +} + +wf::dimensions_t wf::xdg_toplevel_t::get_current_wlr_toplevel_size() +{ + // Size did change => Start a new transaction to change the size. + wlr_box wm_box; + wlr_xdg_surface_get_geometry(toplevel->base, &wm_box); + return wf::dimensions(wm_box); +} diff --git a/src/view/xdg-shell/xdg-toplevel.hpp b/src/view/xdg-shell/xdg-toplevel.hpp new file mode 100644 index 000000000..83ea9d5b5 --- /dev/null +++ b/src/view/xdg-shell/xdg-toplevel.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "wayfire/geometry.hpp" +#include "wayfire/util.hpp" +#include +#include +#include +#include + +namespace wf +{ +/** + * A signal emitted on the xdg_toplevel after the committed state is applied. + */ +struct xdg_toplevel_applied_state_signal +{ + toplevel_state_t old_state; +}; + +class xdg_toplevel_t : public toplevel_t, public std::enable_shared_from_this +{ + public: + xdg_toplevel_t(wlr_xdg_toplevel *toplevel, + std::shared_ptr surface); + void commit() override; + void apply() override; + wf::geometry_t calculate_base_geometry(); + void request_native_size(); + + private: + std::shared_ptr main_surface; + scene::surface_state_t pending_state; + + void apply_pending_state(); + wf::dimensions_t get_current_wlr_toplevel_size(); + + wf::wl_listener_wrapper on_surface_commit; + wf::wl_listener_wrapper on_toplevel_destroy; + wlr_xdg_toplevel *toplevel; + wf::point_t wm_offset = {0, 0}; + + void handle_surface_commit(); + uint32_t target_configure = 0; + + void emit_ready(); + bool pending_ready = false; +}; +} diff --git a/src/view/xwayland.cpp b/src/view/xwayland.cpp index 23e83c2fa..89c23a487 100644 --- a/src/view/xwayland.cpp +++ b/src/view/xwayland.cpp @@ -3,6 +3,7 @@ #include "wayfire/core.hpp" #include "../core/core-impl.hpp" #include "../core/seat/cursor.hpp" +#include #include "xwayland/xwayland-helpers.hpp" #include "xwayland/xwayland-view-base.hpp" @@ -38,7 +39,7 @@ void wayfire_xwayland_view_base::recreate_view() * at some point of this function. */ auto xw_surf = this->xw; - bool was_mapped = is_mapped(); + bool was_mapped = xw->mapped; // destroy the view (unmap + destroy) if (was_mapped) @@ -66,7 +67,8 @@ void wayfire_xwayland_view_base::recreate_view() break; } - wf::get_core().add_view(std::unique_ptr(new_view)); + wf::get_core().add_view(std::unique_ptr( + dynamic_cast(new_view))); if (was_mapped) { new_view->map(xw_surf->surface); diff --git a/src/view/xwayland/xwayland-toplevel-view.hpp b/src/view/xwayland/xwayland-toplevel-view.hpp index c2da6f3aa..5ea6f4dba 100644 --- a/src/view/xwayland/xwayland-toplevel-view.hpp +++ b/src/view/xwayland/xwayland-toplevel-view.hpp @@ -1,18 +1,35 @@ #pragma once #include "config.h" +#include "view/view-impl.hpp" +#include "wayfire/core.hpp" +#include "wayfire/debug.hpp" #include "wayfire/geometry.hpp" +#include "wayfire/scene-render.hpp" +#include "wayfire/signal-provider.hpp" +#include "wayfire/toplevel-view.hpp" +#include "wayfire/util.hpp" #include "xwayland-view-base.hpp" #include +#include +#include "xwayland-toplevel.hpp" +#include #if WF_HAS_XWAYLAND -class wayfire_xwayland_view : public wayfire_xwayland_view_base +class wayfire_xwayland_view : public wf::toplevel_view_interface_t, public wayfire_xwayland_view_base { wf::wl_listener_wrapper on_request_move, on_request_resize, on_request_maximize, on_request_minimize, on_request_activate, on_request_fullscreen, on_set_parent, on_set_hints; + wf::wl_listener_wrapper on_set_decorations; + + wf::wl_listener_wrapper on_map; + wf::wl_listener_wrapper on_unmap; + + std::shared_ptr toplevel; + /** * The bounding box of the view the last time it was rendered. * @@ -22,167 +39,206 @@ class wayfire_xwayland_view : public wayfire_xwayland_view_base */ wf::geometry_t last_bounding_box{0, 0, 0, 0}; - /** The output geometry of the view */ - wf::geometry_t geometry{100, 100, 0, 0}; - - wf::geometry_t get_output_geometry() override + wf::signal::connection_t output_geometry_changed = + [=] (wf::output_configuration_changed_signal *ev) { - return geometry; - } + toplevel->set_output_offset(wf::origin(ev->output->get_layout_geometry())); + }; - wf::geometry_t get_wm_geometry() override + void handle_client_configure(wlr_xwayland_surface_configure_event *ev) override { - if (priv->frame) - { - return priv->frame->expand_wm_geometry(geometry); - } else + wf::point_t output_origin = {0, 0}; + if (get_output()) { - return geometry; + output_origin = wf::origin(get_output()->get_layout_geometry()); } - } - void adjust_anchored_edge(wf::dimensions_t new_size) - { - if (priv->edges) + LOGC(XWL, "Client configure ", self(), " at ", ev->x, ",", ev->y, " ", ev->width, "x", ev->height); + if (!is_mapped()) { - auto wm = get_wm_geometry(); - if (priv->edges & WLR_EDGE_LEFT) - { - wm.x += geometry.width - new_size.width; - } - - if (priv->edges & WLR_EDGE_TOP) + /* If the view is not mapped yet, let it be configured as it + * wishes. We will position it properly in ::map() */ + wlr_xwayland_surface_configure(xw, ev->x, ev->y, ev->width, ev->height); + if ((ev->mask & XCB_CONFIG_WINDOW_X) && (ev->mask & XCB_CONFIG_WINDOW_Y)) { - wm.y += geometry.height - new_size.height; + this->self_positioned = true; + toplevel->pending().geometry.x = ev->x - output_origin.x; + toplevel->pending().geometry.y = ev->y - output_origin.y; } - set_position(wm.x, wm.y, get_wm_geometry(), false); + return; } + + /* Use old x/y values */ + ev->x = get_pending_geometry().x + output_origin.x; + ev->y = get_pending_geometry().y + output_origin.y; + configure_request(wlr_box{ev->x, ev->y, ev->width, ev->height}); } - void set_position(int x, int y, wf::geometry_t old_geometry, bool send_signal) + void update_decorated() { - auto obox = get_output_geometry(); - auto wm = get_wm_geometry(); - - wf::view_geometry_changed_signal data; - data.view = self(); - data.old_geometry = old_geometry; - - view_damage_raw(self(), last_bounding_box); - /* obox.x - wm.x is the current difference in the output and wm geometry */ - geometry.x = x + obox.x - wm.x; - geometry.y = y + obox.y - wm.y; - - /* Make sure that if we move the view while it is unmapped, its snapshot - * is still valid coordinates */ - priv->offscreen_buffer = priv->offscreen_buffer.translated({ - x - data.old_geometry.x, y - data.old_geometry.y, - }); - - damage(); - - if (send_signal) - { - emit(&data); - wf::get_core().emit(&data); - if (get_output()) - { - get_output()->emit(&data); - } - } - - last_bounding_box = get_bounding_box(); - wf::scene::update(this->get_surface_root_node(), wf::scene::update_flag::GEOMETRY); + uint32_t csd_flags = WLR_XWAYLAND_SURFACE_DECORATIONS_NO_TITLE | + WLR_XWAYLAND_SURFACE_DECORATIONS_NO_BORDER; + this->set_decoration_mode(xw->decorations & csd_flags); } - void update_size() + /* Translates geometry from X client configure requests to wayfire + * coordinate system. The X coordinate system treats all outputs + * as one big desktop, whereas wayfire treats the current workspace + * of an output as 0,0 and everything else relative to that. This + * means that we must take care when placing xwayland clients that + * request a configure after initial mapping, while not on the + * current workspace. + * + * @param output The view's output + * @param ws_offset The view's workspace minus the current workspace + * @param geometry The configure geometry as requested by the client + * + * @return Geometry with a position that is within the view's workarea. + * The workarea is the workspace where the view was initially mapped. + * Newly mapped views are placed on the current workspace. + */ + wf::geometry_t translate_geometry_to_output(wf::output_t *output, wf::point_t ws_offset, wf::geometry_t g) { - if (!is_mapped()) - { - return; - } - - wf::dimensions_t current_size{ - priv->wsurface->current.width, - priv->wsurface->current.height, - }; - - if ((current_size.width == geometry.width) && - (current_size.height == geometry.height)) + auto outputs = wf::get_core().output_layout->get_outputs(); + auto og = output->get_layout_geometry(); + auto from = wf::get_core().output_layout->get_output_at( + g.x + g.width / 2 + og.x, g.y + g.height / 2 + og.y); + if (!from) { - return; + return g; } - /* Damage current size */ - view_damage_raw(self(), last_bounding_box); - adjust_anchored_edge(current_size); - - wf::view_geometry_changed_signal data; - data.view = self(); - data.old_geometry = get_wm_geometry(); - - geometry.width = current_size.width; - geometry.height = current_size.height; - - /* Damage new size */ - last_bounding_box = get_bounding_box(); - view_damage_raw(self(), last_bounding_box); - emit(&data); - wf::get_core().emit(&data); - if (get_output()) + auto lg = from->get_layout_geometry(); + g.x += (og.x - lg.x) + ws_offset.x * og.width; + g.y += (og.y - lg.y) + ws_offset.y * og.height; + if (!this->is_mapped()) { - get_output()->emit(&data); + g.x *= (float)og.width / lg.width; + g.y *= (float)og.height / lg.height; } - wf::scene::update(this->get_surface_root_node(), wf::scene::update_flag::GEOMETRY); + return g; } - void handle_client_configure(wlr_xwayland_surface_configure_event *ev) override + void configure_request(wf::geometry_t configure_geometry) { - wf::point_t output_origin = {0, 0}; - if (get_output()) + /* Wayfire positions views relative to their output, but Xwayland + * windows have a global positioning. So, we need to make sure that we + * always transform between output-local coordinates and global + * coordinates. Additionally, when clients send a configure request + * after they have already been mapped, we keep the view on the + * workspace where its center point was from last configure, in + * case the current workspace is not where the view lives */ + wf::output_t *o = get_output(); + if (o) { - output_origin = wf::origin(get_output()->get_layout_geometry()); - } + auto view_workarea = (pending_fullscreen() ? + o->get_relative_geometry() : o->workarea->get_workarea()); + auto og = o->get_layout_geometry(); + configure_geometry.x -= og.x; + configure_geometry.y -= og.y; + + wayfire_toplevel_view view = {this}; + while (view->parent) + { + view = view->parent; + } - if (!is_mapped()) - { - /* If the view is not mapped yet, let it be configured as it - * wishes. We will position it properly in ::map() */ - wlr_xwayland_surface_configure(xw, ev->x, ev->y, ev->width, ev->height); - if ((ev->mask & XCB_CONFIG_WINDOW_X) && (ev->mask & XCB_CONFIG_WINDOW_Y)) + auto vg = view->get_pending_geometry(); + + // View workspace relative to current workspace + wf::point_t view_ws = {0, 0}; + if (view->is_mapped()) { - this->self_positioned = true; - this->geometry.x = ev->x - output_origin.x; - this->geometry.y = ev->y - output_origin.y; + view_ws = { + (int)std::floor((vg.x + vg.width / 2.0) / og.width), + (int)std::floor((vg.y + vg.height / 2.0) / og.height), + }; + + view_workarea.x += og.width * view_ws.x; + view_workarea.y += og.height * view_ws.y; } - return; + configure_geometry = translate_geometry_to_output( + o, view_ws, configure_geometry); + configure_geometry = wf::clamp(configure_geometry, view_workarea); } - /* Use old x/y values */ - ev->x = geometry.x + output_origin.x; - ev->y = geometry.y + output_origin.y; - configure_request(wlr_box{ev->x, ev->y, ev->width, ev->height}); + configure_geometry = wf::expand_geometry_by_margins(configure_geometry, toplevel->pending().margins); + set_geometry(configure_geometry); } + wf::signal::connection_t on_toplevel_applied = + [&] (wf::xw::xwayland_toplevel_applied_state_signal *ev) + { + this->handle_toplevel_state_changed(ev->old_state); + }; + + std::shared_ptr surface_root_node; + public: wayfire_xwayland_view(wlr_xwayland_surface *xww) : wayfire_xwayland_view_base(xww) - {} + { + surface_root_node = std::make_shared(this); + this->set_surface_root_node(surface_root_node); + } + + void set_activated(bool active) override + { + if (xw) + { + wlr_xwayland_surface_activate(xw, active); + } + + wf::toplevel_view_interface_t::set_activated(active); + } virtual void initialize() override { LOGE("new xwayland surface ", xw->title, " class: ", xw->class_t, " instance: ", xw->instance); - wayfire_xwayland_view_base::initialize(); - on_request_move.set_callback([&] (void*) { move_request(); }); + this->toplevel = std::make_shared(xw); + toplevel->connect(&on_toplevel_applied); + this->priv->toplevel = toplevel; + + _initialize(); + wf::view_interface_t::initialize(); + + // Set the output early, so that we can emit the signals on the output + if (!get_output()) + { + set_output(wf::get_core().get_active_output()); + } + + on_map.set_callback([&] (void*) + { + this->main_surface = std::make_shared(xw->surface, false); + priv->set_mapped_surface_contents(main_surface); + toplevel->set_main_surface(main_surface); + toplevel->pending().mapped = true; + toplevel->pending().geometry.width = xw->surface->current.width; + toplevel->pending().geometry.height = xw->surface->current.height; + wf::get_core().tx_manager->schedule_object(toplevel); + }); + + on_unmap.set_callback([&] (void*) + { + toplevel->set_main_surface(nullptr); + toplevel->pending().mapped = false; + wf::get_core().tx_manager->schedule_object(toplevel); + }); + + on_request_move.set_callback([&] (void*) + { + wf::get_core().default_wm->move_request({this}); + }); on_request_resize.set_callback([&] (auto data) { auto ev = static_cast(data); - resize_request(ev->edges); + wf::get_core().default_wm->resize_request({this}, ev->edges); }); on_request_activate.set_callback([&] (void*) { @@ -198,17 +254,17 @@ class wayfire_xwayland_view : public wayfire_xwayland_view_base on_request_maximize.set_callback([&] (void*) { - tile_request((xw->maximized_horz && xw->maximized_vert) ? - wf::TILED_EDGES_ALL : 0); + wf::get_core().default_wm->tile_request({this}, + (xw->maximized_horz && xw->maximized_vert) ? wf::TILED_EDGES_ALL : 0); }); on_request_fullscreen.set_callback([&] (void*) { - fullscreen_request(get_output(), xw->fullscreen); + wf::get_core().default_wm->fullscreen_request({this}, get_output(), xw->fullscreen); }); on_request_minimize.set_callback([&] (void *data) { auto ev = (wlr_xwayland_minimize_event*)data; - minimize_request(ev->minimize); + wf::get_core().default_wm->minimize_request({this}, ev->minimize); }); on_set_parent.set_callback([&] (void*) @@ -233,7 +289,7 @@ class wayfire_xwayland_view : public wayfire_xwayland_view_base } } - set_toplevel_parent(parent); + set_toplevel_parent(toplevel_cast(parent)); }); on_set_hints.set_callback([&] (void*) @@ -248,8 +304,17 @@ class wayfire_xwayland_view : public wayfire_xwayland_view_base wf::get_core().emit(&data); this->emit(&data); }); + on_set_decorations.set_callback([&] (void*) + { + update_decorated(); + }); + update_decorated(); + + on_map.connect(&xw->events.map); + on_unmap.connect(&xw->events.unmap); on_set_parent.connect(&xw->events.set_parent); on_set_hints.connect(&xw->events.set_hints); + on_set_decorations.connect(&xw->events.set_decorations); on_request_move.connect(&xw->events.request_move); on_request_resize.connect(&xw->events.request_resize); @@ -265,8 +330,11 @@ class wayfire_xwayland_view : public wayfire_xwayland_view_base virtual void destroy() override { + on_map.disconnect(); + on_unmap.disconnect(); on_set_parent.disconnect(); on_set_hints.disconnect(); + on_set_decorations.disconnect(); on_request_move.disconnect(); on_request_resize.disconnect(); on_request_activate.disconnect(); @@ -275,6 +343,14 @@ class wayfire_xwayland_view : public wayfire_xwayland_view_base on_request_fullscreen.disconnect(); wayfire_xwayland_view_base::destroy(); + + /* Drop the internal reference */ + unref(); + } + + bool is_mapped() const override + { + return priv->wsurface != nullptr; } void emit_view_map() override @@ -289,12 +365,7 @@ class wayfire_xwayland_view : public wayfire_xwayland_view_base void map(wlr_surface *surface) override { - priv->keyboard_focus_enabled = - wlr_xwayland_or_surface_wants_focus(xw); - - geometry.width = surface->current.width; - geometry.height = surface->current.height; - + priv->keyboard_focus_enabled = wlr_xwayland_or_surface_wants_focus(xw); if (xw->maximized_horz && xw->maximized_vert) { if ((xw->width > 0) && (xw->height > 0)) @@ -307,148 +378,165 @@ class wayfire_xwayland_view : public wayfire_xwayland_view_base /* Make sure geometry is properly visible on the view output */ save_geometry = wf::clamp(save_geometry, get_output()->workarea->get_workarea()); - priv->update_windowed_geometry(self(), save_geometry); + + toplevel->pending().geometry = save_geometry; + wf::get_core().default_wm->update_last_windowed_geometry({this}); } - tile_request(wf::TILED_EDGES_ALL); + wf::get_core().default_wm->tile_request({this}, wf::TILED_EDGES_ALL); } if (xw->fullscreen) { - fullscreen_request(get_output(), true); + wf::get_core().default_wm->fullscreen_request({this}, get_output(), true); } - if (!this->tiled_edges && !xw->fullscreen) + if (!this->pending_tiled_edges() && !xw->fullscreen) { configure_request({xw->x, xw->y, xw->width, xw->height}); } - wayfire_xwayland_view_base::map(surface); - } + priv->set_mapped(true); + on_surface_commit.connect(&surface->events.commit); - void commit() override - { - if (!xw->has_alpha) + if (!parent) { - pixman_region32_union_rect( - &priv->wsurface->opaque_region, &priv->wsurface->opaque_region, - 0, 0, priv->wsurface->current.width, priv->wsurface->current.height); + wf::scene::readd_front(get_output()->wset()->get_node(), get_root_node()); + get_output()->wset()->add_view({this}); } - wayfire_xwayland_view_base::commit(); + get_output()->focus_view(self(), true); - /* Avoid loops where the client wants to have a certain size but the - * compositor keeps trying to resize it */ - last_size_request = wf::dimensions(geometry); + damage(); + emit_view_map(); + /* Might trigger repositioning */ + set_toplevel_parent(this->parent); + } - update_size(); + void unmap() override + { + damage(); + emit_view_pre_unmap(); - /* Clear the resize edges. - * This is must be done here because if the user(or plugin) resizes too fast, - * the shell client might still haven't configured the surface, and in this - * case the next commit(here) needs to still have access to the gravity */ - if (!priv->in_continuous_resize) + main_surface = nullptr; + priv->unset_mapped_surface_contents(); + on_surface_commit.disconnect(); + + emit_view_unmap(); + priv->set_mapped(false); + } + + void commit() + { + if (!xw->has_alpha) { - priv->edges = 0; + pixman_region32_union_rect( + &priv->wsurface->opaque_region, &priv->wsurface->opaque_region, + 0, 0, priv->wsurface->current.width, priv->wsurface->current.height); } this->last_bounding_box = get_bounding_box(); } - void set_moving(bool moving) override + virtual void request_native_size() override { - wayfire_xwayland_view_base::set_moving(moving); - - /* We don't send updates while in continuous move, because that means - * too much configure requests. Instead, we set it at the end */ - if (!priv->in_continuous_move) - { - send_configure(); - } + toplevel->request_native_size(); } - void move(int x, int y) override + void set_minimized(bool minimized) override { - set_position(x, y, get_wm_geometry(), true); - if (!priv->in_continuous_move) + wf::toplevel_view_interface_t::set_minimized(minimized); + if (xw) { - send_configure(); + wlr_xwayland_surface_set_minimized(xw, minimized); } } - void set_geometry(wf::geometry_t geometry) override + void set_output(wf::output_t *wo) override { - move(geometry.x, geometry.y); - resize(geometry.width, geometry.height); + output_geometry_changed.disconnect(); + wf::toplevel_view_interface_t::set_output(wo); + + if (wo) + { + wo->connect(&output_geometry_changed); + toplevel->set_output_offset(wf::origin(wo->get_layout_geometry())); + } else + { + toplevel->set_output_offset({0, 0}); + } } - void resize(int w, int h) override + void handle_toplevel_state_changed(wf::toplevel_state_t old_state) { - if (priv->frame) + surface_root_node->set_offset(wf::origin(toplevel->calculate_base_geometry())); + if (xw && !old_state.mapped && toplevel->current().mapped) { - priv->frame->calculate_resize_size(w, h); + map(xw->surface); } - wf::dimensions_t current_size = { - get_output_geometry().width, - get_output_geometry().height - }; - if (!should_resize_client({w, h}, current_size)) + if (old_state.mapped && !toplevel->current().mapped) { - return; + unmap(); } - this->last_size_request = {w, h}; - send_configure(w, h); + wf::scene::damage_node(get_root_node(), last_bounding_box); + wf::emit_toplevel_state_change_signals({this}, old_state); + + damage(); + last_bounding_box = this->get_surface_root_node()->get_bounding_box(); + wf::scene::update(this->get_surface_root_node(), wf::scene::update_flag::GEOMETRY); } - virtual void request_native_size() override + std::string get_app_id() override { - if (!is_mapped() || !xw->size_hints) - { - return; - } + return this->app_id; + } - if ((xw->size_hints->base_width > 0) && (xw->size_hints->base_height > 0)) - { - this->last_size_request = { - xw->size_hints->base_width, - xw->size_hints->base_height - }; - send_configure(); - } + std::string get_title() override + { + return this->title; + } + + wf::xw::view_type get_current_impl_type() const override + { + return wf::xw::view_type::NORMAL; } - void set_tiled(uint32_t edges) override + wlr_surface *get_keyboard_focus_surface() override { - wayfire_xwayland_view_base::set_tiled(edges); - if (xw) - { - wlr_xwayland_surface_set_maximized(xw, !!edges); - } + return priv->keyboard_focus_enabled ? priv->wsurface : nullptr; } - void set_fullscreen(bool full) override + bool has_client_decoration = true; + void set_decoration_mode(bool use_csd) { - wayfire_xwayland_view_base::set_fullscreen(full); - if (xw) + bool was_decorated = should_be_decorated(); + this->has_client_decoration = use_csd; + if ((was_decorated != should_be_decorated()) && is_mapped()) { - wlr_xwayland_surface_set_fullscreen(xw, full); + wf::view_decoration_state_updated_signal data; + data.view = {this}; + + this->emit(&data); + wf::get_core().emit(&data); } } - void set_minimized(bool minimized) override + bool should_be_decorated() override { - wayfire_xwayland_view_base::set_minimized(minimized); - if (xw) - { - wlr_xwayland_surface_set_minimized(xw, minimized); - } + return role == wf::VIEW_ROLE_TOPLEVEL && !has_client_decoration && + !has_type(wf::xw::_NET_WM_WINDOW_TYPE_SPLASH); } - wf::xw::view_type get_current_impl_type() const override + void ping() override { - return wf::xw::view_type::NORMAL; + wayfire_xwayland_view_base::_ping(); + } + + void close() override + { + wayfire_xwayland_view_base::_close(); } }; diff --git a/src/view/xwayland/xwayland-toplevel.cpp b/src/view/xwayland/xwayland-toplevel.cpp new file mode 100644 index 000000000..24142877a --- /dev/null +++ b/src/view/xwayland/xwayland-toplevel.cpp @@ -0,0 +1,272 @@ +#include "xwayland-toplevel.hpp" +#include "wayfire/core.hpp" +#include "wayfire/debug.hpp" +#include "wayfire/geometry.hpp" +#include +#include "../view-impl.hpp" +#include "wayfire/toplevel.hpp" + +#if WF_HAS_XWAYLAND + +wf::xw::xwayland_toplevel_t::xwayland_toplevel_t(wlr_xwayland_surface *xw) +{ + this->xw = xw; + + on_surface_commit.set_callback([&] (void*) { handle_surface_commit(); }); + on_xw_destroy.set_callback([&] (void*) + { + this->xw = NULL; + on_xw_destroy.disconnect(); + on_surface_commit.disconnect(); + emit_ready(); + }); + + on_xw_destroy.connect(&xw->events.destroy); +} + +void wf::xw::xwayland_toplevel_t::set_main_surface( + std::shared_ptr main_surface) +{ + this->main_surface = main_surface; + on_surface_commit.disconnect(); + + if (main_surface) + { + if (main_surface->get_surface()) + { + on_surface_commit.connect(&main_surface->get_surface()->events.commit); + } else + { + LOGW("Setting xwayland toplevel's main surface to a surface without wlr_surface!"); + return; + } + + auto size = expand_dimensions_by_margins(get_current_xw_size(), _current.margins); + _pending.geometry.width = size.width; + _current.geometry.width = size.width; + _committed.geometry.width = size.width; + _pending.geometry.height = size.height; + _current.geometry.height = size.height; + _committed.geometry.height = size.height; + } +} + +void wf::xw::xwayland_toplevel_t::set_output_offset(wf::point_t output_offset) +{ + this->output_offset = output_offset; + if (pending().mapped) + { + // We want to reconfigure xwayland surfaces with output changes only if they are mapped. + // Otherwise, there is no need to generate x11 events, not to mention that perhaps we do not know the + // position of the view yet (e.g. if it had never been mapped so far). + reconfigure_xwayland_surface(); + } +} + +void wf::xw::xwayland_toplevel_t::request_native_size() +{ + if (!xw || !xw->size_hints) + { + return; + } + + if ((xw->size_hints->base_width > 0) && (xw->size_hints->base_height > 0)) + { + this->pending().geometry.width = xw->size_hints->base_width; + this->pending().geometry.height = xw->size_hints->base_height; + wf::get_core().tx_manager->schedule_object(this->shared_from_this()); + } +} + +void wf::xw::xwayland_toplevel_t::commit() +{ + this->pending_ready = true; + _committed = _pending; + LOGC(TXNI, this, ": committing xwayland state mapped=", _pending.mapped, " geometry=", _pending.geometry, + " tiled=", _pending.tiled_edges, " fs=", _pending.fullscreen, + " margins=", _pending.margins.left, ",", _pending.margins.right, ",", + _pending.margins.top, ",", _pending.margins.bottom); + + if (!this->xw) + { + // No longer mapped => we can do whatever + emit_ready(); + return; + } + + wf::dimensions_t current_size = + shrink_dimensions_by_margins(wf::dimensions(_current.geometry), _current.margins); + if (_pending.mapped && !_current.mapped) + { + // We are trying to map the toplevel => check whether we should wait until it sets the proper + // geometry, or whether we are 'only' mapping without resizing. + current_size = get_current_xw_size(); + } + + const wf::dimensions_t desired_size = wf::shrink_dimensions_by_margins( + wf::dimensions(_pending.geometry), _pending.margins); + + bool wait_for_client = false; + if (desired_size != current_size) + { + wait_for_client = true; + reconfigure_xwayland_surface(); + } + + if (_pending.tiled_edges != _current.tiled_edges) + { + wait_for_client = true; + wlr_xwayland_surface_set_maximized(xw, !!_pending.tiled_edges); + } + + if (_pending.fullscreen != _current.fullscreen) + { + wait_for_client = true; + wlr_xwayland_surface_set_fullscreen(xw, _pending.fullscreen); + } + + if (wait_for_client && main_surface) + { + // Send frame done to let the client know it can resize + main_surface->send_frame_done(); + } else + { + emit_ready(); + } +} + +void wf::xw::xwayland_toplevel_t::reconfigure_xwayland_surface() +{ + if (!xw) + { + return; + } + + const wf::geometry_t configure = + shrink_geometry_by_margins(_pending.geometry, _pending.margins) + output_offset; + + if ((configure.width <= 0) || (configure.height <= 0)) + { + /* such a configure request would freeze xwayland. + * This is most probably a bug somewhere in the compositor. */ + LOGE("Configuring a xwayland surface with width/height <0"); + return; + } + + LOGC(XWL, "Configuring xwayland surface ", nonull(xw->title), " ", nonull(xw->class_t), " ", configure); + wlr_xwayland_surface_configure(xw, configure.x, configure.y, configure.width, configure.height); +} + +void wf::xw::xwayland_toplevel_t::apply() +{ + xwayland_toplevel_applied_state_signal event_applied; + event_applied.old_state = current(); + + if (!xw) + { + // If toplevel does no longer exist, we can't change the size anymore. + _committed.geometry.width = _current.geometry.width; + _committed.geometry.height = _current.geometry.height; + } + + wf::adjust_geometry_for_gravity(_committed, + expand_dimensions_by_margins(this->get_current_xw_size(), _committed.margins)); + this->_current = committed(); + + const bool is_pending = wf::get_core().tx_manager->is_object_pending(shared_from_this()); + if (!is_pending) + { + // Adjust for potential moves due to gravity + _pending = committed(); + reconfigure_xwayland_surface(); + } + + apply_pending_state(); + emit(&event_applied); +} + +void wf::xw::xwayland_toplevel_t::handle_surface_commit() +{ + pending_state.merge_state(main_surface->get_surface()); + + const bool is_committed = wf::get_core().tx_manager->is_object_committed(shared_from_this()); + if (is_committed) + { + const wf::dimensions_t desired_size = + shrink_dimensions_by_margins(wf::dimensions(_committed.geometry), _committed.margins); + + if (get_current_xw_size() != desired_size) + { + // Desired state not reached => wait for the desired state to be reached. In the meantime, send a + // frame done so that the client can redraw faster. + main_surface->send_frame_done(); + return; + } + + adjust_geometry_for_gravity(_committed, this->get_current_xw_size()); + emit_ready(); + return; + } + + const bool is_pending = wf::get_core().tx_manager->is_object_pending(shared_from_this()); + if (is_pending) + { + return; + } + + auto toplevel_size = expand_dimensions_by_margins(get_current_xw_size(), current().margins); + if (toplevel_size == wf::dimensions(current().geometry)) + { + // Size did not change, there are no transactions going on - apply the new texture directly + apply_pending_state(); + return; + } + + adjust_geometry_for_gravity(_pending, toplevel_size); + LOGC(VIEWS, "Client-initiated resize to geometry ", pending().geometry); + auto tx = wf::txn::transaction_t::create(); + tx->add_object(shared_from_this()); + wf::get_core().tx_manager->schedule_transaction(std::move(tx)); +} + +wf::geometry_t wf::xw::xwayland_toplevel_t::calculate_base_geometry() +{ + auto geometry = current().geometry; + return shrink_geometry_by_margins(geometry, _current.margins); +} + +void wf::xw::xwayland_toplevel_t::apply_pending_state() +{ + if (xw && xw->surface) + { + pending_state.merge_state(xw->surface); + } + + if (main_surface) + { + main_surface->apply_state(std::move(pending_state)); + } +} + +void wf::xw::xwayland_toplevel_t::emit_ready() +{ + if (pending_ready) + { + pending_ready = false; + emit_object_ready(this); + } +} + +wf::dimensions_t wf::xw::xwayland_toplevel_t::get_current_xw_size() +{ + if (!main_surface || !main_surface->get_surface()) + { + return {0, 0}; + } + + auto surf = main_surface->get_surface(); + wf::dimensions_t size = wf::dimensions_t{surf->current.width, surf->current.height}; + return size; +} + +#endif diff --git a/src/view/xwayland/xwayland-toplevel.hpp b/src/view/xwayland/xwayland-toplevel.hpp new file mode 100644 index 000000000..d27843417 --- /dev/null +++ b/src/view/xwayland/xwayland-toplevel.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include "config.h" +#include "wayfire/geometry.hpp" +#include +#include +#include +#include + +#if WF_HAS_XWAYLAND + +namespace wf +{ +namespace xw +{ +/** + * A signal emitted on the xwayland_toplevel after the committed state is applied. + */ +struct xwayland_toplevel_applied_state_signal +{ + toplevel_state_t old_state; +}; + +class xwayland_toplevel_t : public wf::toplevel_t, public std::enable_shared_from_this +{ + public: + xwayland_toplevel_t(wlr_xwayland_surface *xw); + void commit() override; + void apply() override; + + void set_main_surface(std::shared_ptr main_surface); + void set_output_offset(wf::point_t output_offset); + + wf::geometry_t calculate_base_geometry(); + + void request_native_size(); + + private: + std::shared_ptr main_surface; + scene::surface_state_t pending_state; + + void apply_pending_state(); + wf::dimensions_t get_current_xw_size(); + + wf::wl_listener_wrapper on_surface_commit; + wf::wl_listener_wrapper on_xw_destroy; + + wlr_xwayland_surface *xw; + wf::point_t output_offset = {0, 0}; + void handle_surface_commit(); + + void reconfigure_xwayland_surface(); + void emit_ready(); + bool pending_ready = false; +}; +} +} + +#endif diff --git a/src/view/xwayland/xwayland-unmanaged-view.hpp b/src/view/xwayland/xwayland-unmanaged-view.hpp index 3dec37110..a5755718a 100644 --- a/src/view/xwayland/xwayland-unmanaged-view.hpp +++ b/src/view/xwayland/xwayland-unmanaged-view.hpp @@ -1,16 +1,85 @@ #pragma once #include "config.h" +#include "wayfire/output.hpp" +#include "wayfire/unstable/translation-node.hpp" +#include "wayfire/util.hpp" +#include "wayfire/view.hpp" #include "xwayland-view-base.hpp" #include #include +#include "../core/core-impl.hpp" +#include "../core/seat/seat-impl.hpp" +#include "../view-keyboard-interaction.hpp" #if WF_HAS_XWAYLAND -class wayfire_unmanaged_xwayland_view : public wayfire_xwayland_view_base +namespace wf +{ +class xwayland_unmanaged_view_node_t : public wf::scene::translation_node_t, public view_node_tag_t +{ + public: + xwayland_unmanaged_view_node_t(wayfire_view view) : view_node_tag_t(view) + { + this->kb_interaction = std::make_unique(view); + on_view_destroy = [=] (view_destruct_signal *ev) + { + this->view = nullptr; + this->kb_interaction = std::make_unique(); + }; + + view->connect(&on_view_destroy); + } + + wf::keyboard_focus_node_t keyboard_refocus(wf::output_t *output) override + { + if (!view || !view->get_keyboard_focus_surface()) + { + return wf::keyboard_focus_node_t{}; + } + + if (output != view->get_output()) + { + return wf::keyboard_focus_node_t{}; + } + + const uint64_t output_last_ts = view->get_output()->get_last_focus_timestamp(); + const uint64_t our_ts = keyboard_interaction().last_focus_timestamp; + auto cur_focus = wf::get_core_impl().seat->priv->keyboard_focus.get(); + bool has_focus = (cur_focus == this) || (our_ts == output_last_ts); + if (has_focus) + { + return wf::keyboard_focus_node_t{this, focus_importance::REGULAR}; + } + + return wf::keyboard_focus_node_t{}; + } + + keyboard_interaction_t& keyboard_interaction() override + { + return *kb_interaction; + } + + std::string stringify() const override + { + std::ostringstream out; + out << this->view; + return "unmanaged " + out.str() + " " + stringify_flags(); + } + + protected: + wayfire_toplevel_view view; + std::unique_ptr kb_interaction; + wf::signal::connection_t on_view_destroy; +}; +} + +class wayfire_unmanaged_xwayland_view : public wf::view_interface_t, public wayfire_xwayland_view_base { protected: wf::wl_listener_wrapper on_set_geometry; + wf::wl_listener_wrapper on_map; + wf::wl_listener_wrapper on_unmap; /** * The bounding box of the view the last time it was rendered. @@ -21,131 +90,53 @@ class wayfire_unmanaged_xwayland_view : public wayfire_xwayland_view_base */ wf::geometry_t last_bounding_box{0, 0, 0, 0}; - /** The output geometry of the view */ + // The geometry of the view in output-layout coordinates (same as Xwayland). wf::geometry_t geometry{100, 100, 0, 0}; - wf::geometry_t get_output_geometry() override - { - return geometry; - } - wf::geometry_t get_wm_geometry() override + void handle_client_configure(wlr_xwayland_surface_configure_event *ev) override { - if (priv->frame) - { - return priv->frame->expand_wm_geometry(geometry); - } else - { - return geometry; - } + // We accept the client requests without any modification when it comes to unmanaged views. + wlr_xwayland_surface_configure(xw, ev->x, ev->y, ev->width, ev->height); + update_geometry_from_xsurface(); } - void set_position(int x, int y, wf::geometry_t old_geometry, bool send_signal) + void update_geometry_from_xsurface() { - wf::view_geometry_changed_signal data; - data.view = self(); - data.old_geometry = old_geometry; + wf::scene::damage_node(get_root_node(), last_bounding_box); - view_damage_raw(self(), last_bounding_box); - geometry.x = x; - geometry.y = y; + geometry.x = xw->x; + geometry.y = xw->y; + geometry.width = xw->width; + geometry.height = xw->height; - /* Make sure that if we move the view while it is unmapped, its snapshot - * is still valid coordinates */ - priv->offscreen_buffer = priv->offscreen_buffer.translated({ - x - data.old_geometry.x, y - data.old_geometry.y, - }); + wf::point_t new_position = wf::origin(geometry); - damage(); - - if (send_signal) + // Move to the correct output, if the xsurface has changed geometry + wf::pointf_t midpoint = {geometry.x + geometry.width / 2.0, geometry.y + geometry.height / 2.0}; + wf::output_t *wo = wf::get_core().output_layout->get_output_coords_at(midpoint, midpoint); + if (wo != get_output()) { - emit(&data); - wf::get_core().emit(&data); - if (get_output()) + set_output(wo); + if (wo && (get_current_impl_type() != wf::xw::view_type::DND)) { - get_output()->emit(&data); + new_position = new_position - wf::origin(wo->get_layout_geometry()); + wf::scene::readd_front(wo->node_for_layer(wf::scene::layer::UNMANAGED), get_root_node()); } } + surface_root_node->set_offset(new_position); last_bounding_box = get_bounding_box(); - wf::scene::update(this->get_surface_root_node(), wf::scene::update_flag::GEOMETRY); + wf::scene::damage_node(get_root_node(), last_bounding_box); + wf::scene::update(surface_root_node, wf::scene::update_flag::GEOMETRY); } - void handle_client_configure(wlr_xwayland_surface_configure_event *ev) override + bool is_mapped() const override { - wf::point_t output_origin = {0, 0}; - if (get_output()) - { - output_origin = wf::origin(get_output()->get_layout_geometry()); - } - - if ((ev->mask & XCB_CONFIG_WINDOW_X) && (ev->mask & XCB_CONFIG_WINDOW_Y)) - { - this->self_positioned = true; - } else - { - /* Use old x/y values */ - ev->x = geometry.x + output_origin.x; - ev->y = geometry.y + output_origin.y; - } - - if (!is_mapped()) - { - /* If the view is not mapped yet, let it be configured as it - * wishes. We will position it properly in ::map() */ - wlr_xwayland_surface_configure(xw, ev->x, ev->y, ev->width, ev->height); - if ((ev->mask & XCB_CONFIG_WINDOW_X) && (ev->mask & XCB_CONFIG_WINDOW_Y)) - { - this->geometry.x = ev->x - output_origin.x; - this->geometry.y = ev->y - output_origin.y; - } - } else - { - configure_request({ev->x, ev->y, ev->width, ev->height}); - } + return priv->wsurface != nullptr; } - void update_size() - { - if (!is_mapped()) - { - return; - } - - wf::dimensions_t current_size{ - priv->wsurface->current.width, - priv->wsurface->current.height, - }; - - if ((current_size.width == geometry.width) && - (current_size.height == geometry.height)) - { - return; - } - - /* Damage current size */ - view_damage_raw(self(), last_bounding_box); - - wf::view_geometry_changed_signal data; - data.view = self(); - data.old_geometry = get_wm_geometry(); - - geometry.width = current_size.width; - geometry.height = current_size.height; - - /* Damage new size */ - last_bounding_box = get_bounding_box(); - view_damage_raw(self(), last_bounding_box); - emit(&data); - wf::get_core().emit(&data); - if (get_output()) - { - get_output()->emit(&data); - } - - wf::scene::update(this->get_surface_root_node(), wf::scene::update_flag::GEOMETRY); - } + std::shared_ptr surface_root_node; public: wayfire_unmanaged_xwayland_view(wlr_xwayland_surface *xww) : wayfire_xwayland_view_base(xww) @@ -153,70 +144,34 @@ class wayfire_unmanaged_xwayland_view : public wayfire_xwayland_view_base LOGE("new unmanaged xwayland surface ", xw->title, " class: ", xw->class_t, " instance: ", xw->instance); + surface_root_node = std::make_shared(this); + this->set_surface_root_node(surface_root_node); + xw->data = this; role = wf::VIEW_ROLE_UNMANAGED; - on_set_geometry.set_callback([&] (void*) - { - /* Xwayland O-R views manage their position on their own. So we need to - * update their position on each commit, if the position changed. */ - if ((global_x != xw->x) || (global_y != xw->y)) - { - geometry.x = global_x = xw->x; - geometry.y = global_y = xw->y; - - if (get_output()) - { - auto real_output = get_output()->get_layout_geometry(); - geometry.x -= real_output.x; - geometry.y -= real_output.y; - } - - move(geometry.x, geometry.y); - } - }); + on_set_geometry.set_callback([&] (void*) { update_geometry_from_xsurface(); }); + on_map.set_callback([&] (void*) { map(xw->surface); }); + on_unmap.set_callback([&] (void*) { unmap(); }); + on_map.connect(&xw->events.map); + on_unmap.connect(&xw->events.unmap); on_set_geometry.connect(&xw->events.set_geometry); } - int global_x, global_y; - void map(wlr_surface *surface) override + virtual void initialize() override { - /* move to the output where our center is - * FIXME: this is a bad idea, because a dropdown menu might get sent to - * an incorrect output. However, no matter how we calculate the real - * output, we just can't be 100% compatible because in X all windows are - * positioned in a global coordinate space */ - auto wo = wf::get_core().output_layout->get_output_at( - xw->x + surface->current.width / 2, xw->y + surface->current.height / 2); - - if (!wo) - { - /* if surface center is outside of anything, try to check the output - * where the pointer is */ - auto gc = wf::get_core().get_cursor_position(); - wo = wf::get_core().output_layout->get_output_at(gc.x, gc.y); - } - - if (!wo) - { - wo = wf::get_core().get_active_output(); - } - - assert(wo); - - auto real_output_geometry = wo->get_layout_geometry(); - - global_x = xw->x; - global_y = xw->y; - move(xw->x - real_output_geometry.x, xw->y - real_output_geometry.y); + _initialize(); + wf::view_interface_t::initialize(); + } - if (wo != get_output()) - { - set_output(wo); - } + void map(wlr_surface *surface) override + { + update_geometry_from_xsurface(); - damage(); + priv->set_mapped(true); + this->main_surface = std::make_shared(surface, true); + priv->set_mapped_surface_contents(main_surface); /* We update the keyboard focus before emitting the map event, so that * plugins can detect that this view can have keyboard focus. @@ -226,85 +181,77 @@ class wayfire_unmanaged_xwayland_view : public wayfire_xwayland_view_base wlr_xwayland_or_surface_wants_focus(xw)); wf::scene::readd_front(get_output()->node_for_layer(wf::scene::layer::UNMANAGED), get_root_node()); - wayfire_xwayland_view_base::map(surface); if (priv->keyboard_focus_enabled) { get_output()->focus_view(self(), true); } + + damage(); + emit_view_map(); } - void commit() override + void unmap() override { - wayfire_xwayland_view_base::commit(); - update_size(); - - /* Clear the resize edges. - * This is must be done here because if the user(or plugin) resizes too fast, - * the shell client might still haven't configured the surface, and in this - * case the next commit(here) needs to still have access to the gravity */ - if (!priv->in_continuous_resize) - { - priv->edges = 0; - } + damage(); + emit_view_pre_unmap(); + + main_surface = nullptr; + priv->unset_mapped_surface_contents(); + on_surface_commit.disconnect(); - this->last_bounding_box = get_bounding_box(); + emit_view_unmap(); + priv->set_mapped(false); } void destroy() override { + on_map.disconnect(); + on_unmap.disconnect(); on_set_geometry.disconnect(); wayfire_xwayland_view_base::destroy(); + /* Drop the internal reference */ + unref(); } - bool should_be_decorated() override + wf::xw::view_type get_current_impl_type() const override { - return (!xw->override_redirect && !this->has_client_decoration); + return wf::xw::view_type::UNMANAGED; } - void move(int x, int y) override + std::string get_app_id() override { - set_position(x, y, get_wm_geometry(), true); + return this->app_id; } - void resize(int w, int h) override + std::string get_title() override { - if (priv->frame) - { - priv->frame->calculate_resize_size(w, h); - } - - wf::dimensions_t current_size = { - get_output_geometry().width, - get_output_geometry().height - }; + return this->title; + } - if (!should_resize_client({w, h}, current_size)) + wlr_surface *get_keyboard_focus_surface() override + { + if (is_mapped() && priv->keyboard_focus_enabled) { - return; + return priv->wsurface; } - this->last_size_request = {w, h}; - send_configure(w, h); + return NULL; } - void set_geometry(wf::geometry_t geometry) override + void ping() override { - move(geometry.x, geometry.y); - resize(geometry.width, geometry.height); + wayfire_xwayland_view_base::_ping(); } - wf::xw::view_type get_current_impl_type() const override + void close() override { - return wf::xw::view_type::UNMANAGED; + wayfire_xwayland_view_base::_close(); } }; class wayfire_dnd_xwayland_view : public wayfire_unmanaged_xwayland_view { - protected: - wf::wl_listener_wrapper on_set_geometry; - public: using wayfire_unmanaged_xwayland_view::wayfire_unmanaged_xwayland_view; @@ -324,42 +271,16 @@ class wayfire_dnd_xwayland_view : public wayfire_unmanaged_xwayland_view wayfire_unmanaged_xwayland_view::deinitialize(); } - wf::geometry_t last_global_bbox = {0, 0, 0, 0}; - - void damage() override - { - if (!get_output()) - { - return; - } - - auto bbox = get_bounding_box() + - wf::origin(this->get_output()->get_layout_geometry()); - - for (auto& output : wf::get_core().output_layout->get_outputs()) - { - auto local_bbox = bbox + -wf::origin(output->get_layout_geometry()); - output->render->damage(local_bbox); - local_bbox = last_global_bbox + - -wf::origin(output->get_layout_geometry()); - output->render->damage(local_bbox); - } - - last_global_bbox = bbox; - } - void map(wlr_surface *surface) override { LOGD("Mapping a Xwayland drag icon"); - this->set_output(wf::get_core().get_active_output()); - wayfire_xwayland_view_base::map(surface); - this->damage(); - + wayfire_unmanaged_xwayland_view::map(surface); wf::scene::readd_front(wf::get_core().scene(), this->get_root_node()); } void unmap() override { + wayfire_unmanaged_xwayland_view::unmap(); wf::scene::remove_child(this->get_root_node()); } }; diff --git a/src/view/xwayland/xwayland-view-base.hpp b/src/view/xwayland/xwayland-view-base.hpp index d3bdd05f7..26897c20a 100644 --- a/src/view/xwayland/xwayland-view-base.hpp +++ b/src/view/xwayland/xwayland-view-base.hpp @@ -7,6 +7,8 @@ #include #include "../view-impl.hpp" +#include "../toplevel-node.hpp" +#include "wayfire/core.hpp" #include "wayfire/geometry.hpp" #include "wayfire/view.hpp" #include "xwayland-helpers.hpp" @@ -14,16 +16,14 @@ #if WF_HAS_XWAYLAND -class wayfire_xwayland_view_base : public wf::view_interface_t +class wayfire_xwayland_view_base { protected: - wf::wl_listener_wrapper on_destroy, on_unmap, on_map, on_configure, - on_set_title, on_set_app_id, on_or_changed, on_set_decorations, - on_ping_timeout, on_set_window_type; + wf::wl_listener_wrapper on_destroy, on_configure, + on_set_title, on_set_app_id, on_or_changed, on_ping_timeout, on_set_window_type; wf::wl_listener_wrapper on_surface_commit; std::shared_ptr main_surface; - std::unique_ptr surface_controller; wlr_xwayland_surface *xw; /** The geometry requested by the client */ @@ -34,50 +34,16 @@ class wayfire_xwayland_view_base : public wf::view_interface_t void handle_app_id_changed(std::string new_app_id) { this->app_id = new_app_id; - wf::view_app_id_changed_signal data; - data.view = self(); - emit(&data); - } - - std::string get_app_id() override - { - return this->app_id; + wf::emit_app_id_changed_signal(dynamic_cast(this)); } /** Used by view implementations when the title changes */ void handle_title_changed(std::string new_title) { this->title = new_title; - wf::view_title_changed_signal data; - data.view = self(); - emit(&data); + wf::emit_title_changed_signal(dynamic_cast(this)); } - std::string get_title() override - { - return this->title; - } - - wlr_surface *get_keyboard_focus_surface() override - { - if (is_mapped() && priv->keyboard_focus_enabled) - { - return priv->wsurface; - } - - return NULL; - } - - wf::signal::connection_t output_geometry_changed = - [=] (wf::output_configuration_changed_signal *ev) - { - if (is_mapped()) - { - auto wm_geometry = get_wm_geometry(); - move(wm_geometry.x, wm_geometry.y); - } - }; - bool has_type(xcb_atom_t type) { for (size_t i = 0; i < xw->window_type_len; i++) @@ -136,92 +102,14 @@ class wayfire_xwayland_view_base : public wf::view_interface_t */ virtual wf::xw::view_type get_current_impl_type() const = 0; - bool has_client_decoration = true; - void set_decoration_mode(bool use_csd) - { - bool was_decorated = should_be_decorated(); - this->has_client_decoration = use_csd; - if ((was_decorated != should_be_decorated()) && is_mapped()) - { - wf::view_decoration_state_updated_signal data; - data.view = self(); - - this->emit(&data); - wf::get_core().emit(&data); - } - } - public: - wayfire_xwayland_view_base(wlr_xwayland_surface *xww) : - view_interface_t(), xw(xww) - { - on_surface_commit.set_callback([&] (void*) { commit(); }); - } - - virtual void commit() - { - // TODO: this shoudl be done by the main node, likely unnecessary to do it here as well. - wf::region_t dmg; - wlr_surface_get_effective_damage(priv->wsurface, dmg.to_pixman()); - wf::scene::damage_node(this->get_surface_root_node(), dmg); - } - - virtual void map(wlr_surface *surface) - { - wf::scene::set_node_enabled(priv->root_node, true); - priv->wsurface = surface; - - this->main_surface = std::make_shared(surface, true); - priv->surface_root_node->set_children_list({main_surface}); - wf::scene::update(priv->surface_root_node, wf::scene::update_flag::CHILDREN_LIST); - surface_controller = std::make_unique(surface, priv->surface_root_node); - on_surface_commit.connect(&surface->events.commit); - if (role == wf::VIEW_ROLE_TOPLEVEL) - { - if (!parent) - { - wf::scene::readd_front(get_output()->wset()->get_node(), get_root_node()); - get_output()->wset()->add_view(self()); - } - - get_output()->focus_view(self(), true); - } - - damage(); - emit_view_map(); - /* Might trigger repositioning */ - set_toplevel_parent(this->parent); - } - - virtual void unmap() - { - damage(); - emit_view_pre_unmap(); - set_decoration(nullptr); - - main_surface = nullptr; - priv->wsurface = nullptr; - on_surface_commit.disconnect(); - surface_controller = nullptr; - priv->surface_root_node->set_children_list({}); - wf::scene::update(priv->surface_root_node, wf::scene::update_flag::CHILDREN_LIST); + wayfire_xwayland_view_base(wlr_xwayland_surface *xww) : xw(xww) + {} - emit_view_unmap(); - wf::scene::set_node_enabled(priv->root_node, false); - } + virtual ~wayfire_xwayland_view_base() = default; - virtual void initialize() override + void _initialize() { - wf::view_interface_t::initialize(); - - // Set the output early, so that we can emit the signals on the output - if (!get_output()) - { - set_output(wf::get_core().get_active_output()); - } - - on_map.set_callback([&] (void*) { map(xw->surface); }); - on_unmap.set_callback([&] (void*) { unmap(); }); on_destroy.set_callback([&] (void*) { destroy(); }); on_configure.set_callback([&] (void *data) { @@ -239,13 +127,9 @@ class wayfire_xwayland_view_base : public wf::view_interface_t { recreate_view(); }); - on_set_decorations.set_callback([&] (void*) - { - update_decorated(); - }); on_ping_timeout.set_callback([&] (void*) { - wf::emit_ping_timeout_signal(self()); + wf::emit_ping_timeout_signal(dynamic_cast(this)); }); on_set_window_type.set_callback([&] (void*) { @@ -254,17 +138,13 @@ class wayfire_xwayland_view_base : public wf::view_interface_t handle_title_changed(nonull(xw->title)); handle_app_id_changed(nonull(xw->class_t)); - update_decorated(); - on_map.connect(&xw->events.map); - on_unmap.connect(&xw->events.unmap); on_destroy.connect(&xw->events.destroy); on_configure.connect(&xw->events.request_configure); on_set_title.connect(&xw->events.set_title); on_set_app_id.connect(&xw->events.set_class); on_or_changed.connect(&xw->events.set_override_redirect); on_ping_timeout.connect(&xw->events.ping_timeout); - on_set_decorations.connect(&xw->events.set_decorations); on_set_window_type.connect(&xw->events.set_window_type); } @@ -282,24 +162,17 @@ class wayfire_xwayland_view_base : public wf::view_interface_t virtual void destroy() { this->xw = nullptr; - output_geometry_changed.disconnect(); - on_map.disconnect(); - on_unmap.disconnect(); on_destroy.disconnect(); on_configure.disconnect(); on_set_title.disconnect(); on_set_app_id.disconnect(); on_or_changed.disconnect(); on_ping_timeout.disconnect(); - on_set_decorations.disconnect(); on_set_window_type.disconnect(); - - /* Drop the internal reference */ - unref(); } - virtual void ping() override + void _ping() { if (xw) { @@ -307,211 +180,16 @@ class wayfire_xwayland_view_base : public wf::view_interface_t } } - virtual bool should_be_decorated() override - { - return role == wf::VIEW_ROLE_TOPLEVEL && !has_client_decoration && - !has_type(wf::xw::_NET_WM_WINDOW_TYPE_SPLASH); - } - - bool should_resize_client(wf::dimensions_t request, wf::dimensions_t current_geometry) - { - /* - * Do not send a configure if the client will retain its size. - * This is needed if a client starts with one size and immediately resizes - * again. - * - * If we do configure it with the given size, then it will think that we - * are requesting the given size, and won't resize itself again. - */ - if (this->last_size_request == wf::dimensions_t{0, 0}) - { - return request != current_geometry; - } else - { - return request != last_size_request; - } - } - - bool is_mapped() const override - { - return priv->wsurface != nullptr; - } - - /* Translates geometry from X client configure requests to wayfire - * coordinate system. The X coordinate system treats all outputs - * as one big desktop, whereas wayfire treats the current workspace - * of an output as 0,0 and everything else relative to that. This - * means that we must take care when placing xwayland clients that - * request a configure after initial mapping, while not on the - * current workspace. - * - * @param output The view's output - * @param ws_offset The view's workspace minus the current workspace - * @param geometry The configure geometry as requested by the client - * - * @return Geometry with a position that is within the view's workarea. - * The workarea is the workspace where the view was initially mapped. - * Newly mapped views are placed on the current workspace. - */ - wf::geometry_t translate_geometry_to_output(wf::output_t *output, - wf::point_t ws_offset, - wf::geometry_t g) - { - auto outputs = wf::get_core().output_layout->get_outputs(); - auto og = output->get_layout_geometry(); - auto from = wf::get_core().output_layout->get_output_at( - g.x + g.width / 2 + og.x, g.y + g.height / 2 + og.y); - if (!from) - { - return g; - } - - auto lg = from->get_layout_geometry(); - g.x += (og.x - lg.x) + ws_offset.x * og.width; - g.y += (og.y - lg.y) + ws_offset.y * og.height; - if (!this->is_mapped()) - { - g.x *= (float)og.width / lg.width; - g.y *= (float)og.height / lg.height; - } - - return g; - } - - virtual void configure_request(wf::geometry_t configure_geometry) - { - /* Wayfire positions views relative to their output, but Xwayland - * windows have a global positioning. So, we need to make sure that we - * always transform between output-local coordinates and global - * coordinates. Additionally, when clients send a configure request - * after they have already been mapped, we keep the view on the - * workspace where its center point was from last configure, in - * case the current workspace is not where the view lives */ - auto o = get_output(); - if (o) - { - auto view_workarea = (fullscreen ? - o->get_relative_geometry() : o->workarea->get_workarea()); - auto og = o->get_layout_geometry(); - configure_geometry.x -= og.x; - configure_geometry.y -= og.y; - - auto view = this->self(); - while (view->parent) - { - view = view->parent; - } - - auto vg = view->get_wm_geometry(); - - // View workspace relative to current workspace - wf::point_t view_ws = {0, 0}; - if (view->is_mapped()) - { - view_ws = { - (int)std::floor((vg.x + vg.width / 2.0) / og.width), - (int)std::floor((vg.y + vg.height / 2.0) / og.height), - }; - - view_workarea.x += og.width * view_ws.x; - view_workarea.y += og.height * view_ws.y; - } - - configure_geometry = translate_geometry_to_output( - o, view_ws, configure_geometry); - configure_geometry = wf::clamp(configure_geometry, view_workarea); - } - - if (priv->frame) - { - configure_geometry = - priv->frame->expand_wm_geometry(configure_geometry); - } - - set_geometry(configure_geometry); - } - - void update_decorated() - { - uint32_t csd_flags = WLR_XWAYLAND_SURFACE_DECORATIONS_NO_TITLE | - WLR_XWAYLAND_SURFACE_DECORATIONS_NO_BORDER; - this->set_decoration_mode(xw->decorations & csd_flags); - } - - virtual void close() override + void _close() { if (xw) { wlr_xwayland_surface_close(xw); } - - wf::view_interface_t::close(); - } - - void set_activated(bool active) override - { - if (xw) - { - wlr_xwayland_surface_activate(xw, active); - } - - wf::view_interface_t::set_activated(active); - } - - void send_configure(int width, int height) - { - if (!xw) - { - return; - } - - if ((width < 0) || (height < 0)) - { - /* such a configure request would freeze xwayland. - * This is most probably a bug somewhere in the compositor. */ - LOGE("Configuring a xwayland surface with width/height <0"); - - return; - } - - auto output_geometry = get_output_geometry(); - - int configure_x = output_geometry.x; - int configure_y = output_geometry.y; - - if (get_output()) - { - auto real_output = get_output()->get_layout_geometry(); - configure_x += real_output.x; - configure_y += real_output.y; - } - - wlr_xwayland_surface_configure(xw, - configure_x, configure_y, width, height); - } - - wf::dimensions_t last_size_request = {0, 0}; - void send_configure() - { - send_configure(last_size_request.width, last_size_request.height); } - virtual void set_output(wf::output_t *wo) override - { - output_geometry_changed.disconnect(); - wf::view_interface_t::set_output(wo); - - if (wo) - { - wo->connect(&output_geometry_changed); - } - - /* Update the real position */ - if (is_mapped()) - { - send_configure(); - } - } + virtual void map(wlr_surface *surface) = 0; + virtual void unmap() = 0; }; #endif