diff --git a/src/api/wayfire/plugin.hpp b/src/api/wayfire/plugin.hpp index 8b61fb87f..0b12b07d3 100644 --- a/src/api/wayfire/plugin.hpp +++ b/src/api/wayfire/plugin.hpp @@ -105,7 +105,7 @@ class plugin_interface_t using wayfire_plugin_load_func = wf::plugin_interface_t * (*)(); /** The version of Wayfire's API/ABI */ -constexpr uint32_t WAYFIRE_API_ABI_VERSION = 2024'04'16; +constexpr uint32_t WAYFIRE_API_ABI_VERSION = 2024'06'18; /** * Each plugin must also provide a function which returns the Wayfire API/ABI diff --git a/src/api/wayfire/seat.hpp b/src/api/wayfire/seat.hpp index 2e96eea1e..d29f2a639 100644 --- a/src/api/wayfire/seat.hpp +++ b/src/api/wayfire/seat.hpp @@ -8,6 +8,14 @@ namespace wf { +enum class keyboard_focus_reason +{ + USER, + GRAB, + REFOCUS, + UNKNOWN, +}; + /** * A seat represents a group of input devices (mouse, keyboard, etc.) which logically belong together. * Each seat has its own keyboard, touch, pointer and tablet focus. @@ -52,7 +60,8 @@ class seat_t * * The new_focus' last focus timestamp will be updated. */ - void set_active_node(wf::scene::node_ptr node); + void set_active_node(wf::scene::node_ptr node, + wf::keyboard_focus_reason reason = keyboard_focus_reason::UNKNOWN); /** * Get the node which has current keyboard focus. diff --git a/src/api/wayfire/signal-definitions.hpp b/src/api/wayfire/signal-definitions.hpp index 5f7ad6e5c..46070397c 100644 --- a/src/api/wayfire/signal-definitions.hpp +++ b/src/api/wayfire/signal-definitions.hpp @@ -3,6 +3,7 @@ #include "wayfire/view.hpp" #include "wayfire/output.hpp" +#include "wayfire/seat.hpp" /** * Documentation of signals emitted from core components. @@ -201,6 +202,7 @@ struct reload_config_signal struct keyboard_focus_changed_signal { wf::scene::node_ptr new_focus; + keyboard_focus_reason reason = keyboard_focus_reason::UNKNOWN; }; /** diff --git a/src/core/seat/seat-impl.hpp b/src/core/seat/seat-impl.hpp index 1b6163b24..1d1b8207d 100644 --- a/src/core/seat/seat-impl.hpp +++ b/src/core/seat/seat-impl.hpp @@ -55,7 +55,7 @@ struct seat_t::impl uint32_t get_modifiers(); void break_mod_bindings(); - void set_keyboard_focus(wf::scene::node_ptr keyboard_focus); + void set_keyboard_focus(wf::scene::node_ptr keyboard_focus, wf::keyboard_focus_reason reason); wf::scene::node_ptr keyboard_focus; // Keys sent to the current keyboard focus std::multiset pressed_keys; diff --git a/src/core/seat/seat.cpp b/src/core/seat/seat.cpp index 40ee3ad64..ded0c97c1 100644 --- a/src/core/seat/seat.cpp +++ b/src/core/seat/seat.cpp @@ -25,7 +25,7 @@ #include "wayfire/view.hpp" wf::seat_t::~seat_t() = default; -void wf::seat_t::set_active_node(wf::scene::node_ptr node) +void wf::seat_t::set_active_node(wf::scene::node_ptr node, wf::keyboard_focus_reason reason) { if (node) { @@ -36,7 +36,7 @@ void wf::seat_t::set_active_node(wf::scene::node_ptr node) } auto focus = wf::get_core().scene()->keyboard_refocus(priv->active_output); - priv->set_keyboard_focus(focus.node ? focus.node->shared_from_this() : nullptr); + priv->set_keyboard_focus(focus.node ? focus.node->shared_from_this() : nullptr, reason); } wf::scene::node_ptr wf::seat_t::get_active_node() @@ -166,7 +166,7 @@ void wf::seat_t::focus_view(wayfire_view v) const auto& give_input_focus = [this] (wayfire_view view) { - set_active_node(view ? view->get_surface_root_node() : nullptr); + set_active_node(view ? view->get_surface_root_node() : nullptr, keyboard_focus_reason::USER); }; v = select_focus_view(v); @@ -198,7 +198,7 @@ void wf::seat_t::refocus() priv->update_active_view(focus_sptr); } - priv->set_keyboard_focus(focus_sptr); + priv->set_keyboard_focus(focus_sptr, keyboard_focus_reason::REFOCUS); } uint32_t wf::seat_t::get_keyboard_modifiers() @@ -504,10 +504,11 @@ void wf::seat_t::impl::transfer_grab(wf::scene::node_ptr grab_node) wf::keyboard_focus_changed_signal data; data.new_focus = grab_node; + data.reason = keyboard_focus_reason::GRAB; wf::get_core().emit(&data); } -void wf::seat_t::impl::set_keyboard_focus(wf::scene::node_ptr new_focus) +void wf::seat_t::impl::set_keyboard_focus(wf::scene::node_ptr new_focus, keyboard_focus_reason reason) { if (this->keyboard_focus == new_focus) { @@ -528,6 +529,7 @@ void wf::seat_t::impl::set_keyboard_focus(wf::scene::node_ptr new_focus) wf::keyboard_focus_changed_signal data; data.new_focus = new_focus; + data.reason = reason; wf::get_core().emit(&data); } diff --git a/src/view/xdg-shell.cpp b/src/view/xdg-shell.cpp index 239b56dc7..2be1ceac7 100644 --- a/src/view/xdg-shell.cpp +++ b/src/view/xdg-shell.cpp @@ -69,6 +69,37 @@ class wayfire_xdg_popup_node : public wf::scene::translation_node_t std::unique_ptr kb_interaction; }; +bool wayfire_xdg_popup::should_close_on_focus_change(wf::keyboard_focus_changed_signal *ev) +{ + if (!is_mapped()) + { + return false; + } + + auto view = wf::node_to_view(ev->new_focus); + const bool focus_client_changes = view && (view->get_client() != this->get_client()); + const bool has_grab = this->popup->seat != nullptr; + + if (has_grab) + { + return !view || focus_client_changes; + } else + { + if ((ev->reason == wf::keyboard_focus_reason::UNKNOWN) || + (ev->reason == wf::keyboard_focus_reason::REFOCUS)) + { + return false; + } + + if ((ev->new_focus && !view) || focus_client_changes) + { + return true; + } + } + + return false; +} + 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(); @@ -81,11 +112,9 @@ wayfire_xdg_popup::wayfire_xdg_popup(wlr_xdg_popup *popup) : wf::view_interface_ // Note: we shouldn't close nested popups manually, since the parent popups will destroy them as well. this->on_keyboard_focus_changed = [=] (wf::keyboard_focus_changed_signal *ev) { - auto view = wf::node_to_view(ev->new_focus); - if (!view || (view->get_client() != this->get_client())) + if (should_close_on_focus_change(ev)) { this->close(); - return; } }; } @@ -97,7 +126,6 @@ wayfire_xdg_popup::wayfire_xdg_popup(wlr_xdg_popup *popup) : wf::view_interface_ 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) { create_xdg_popup((wlr_xdg_popup*)data); @@ -113,7 +141,6 @@ wayfire_xdg_popup::wayfire_xdg_popup(wlr_xdg_popup *popup) : wf::view_interface_ on_map.connect(&popup->base->surface->events.map); on_unmap.connect(&popup->base->surface->events.unmap); - on_destroy.connect(&popup->base->events.destroy); on_new_popup.connect(&popup->base->events.new_popup); on_ping_timeout.connect(&popup->base->events.ping_timeout); on_reposition.connect(&popup->events.reposition); @@ -152,6 +179,7 @@ std::shared_ptr wayfire_xdg_popup::create(wlr_xdg_popup *popu void wayfire_xdg_popup::map() { + LOGC(VIEWS, "Trying to map xdg-popup ", self()); if (!get_output()) { close(); @@ -189,9 +217,12 @@ void wayfire_xdg_popup::unmap() { if (!is_mapped()) { + LOGC(VIEWS, "Denying unmap of unmapped xdg-popup ", self()); return; } + auto _self_ref = shared_from_this(); + LOGC(VIEWS, "Unmapping xdg-popup ", self()); on_keyboard_focus_changed.disconnect(); damage(); emit_view_pre_unmap(); @@ -266,7 +297,6 @@ void wayfire_xdg_popup::destroy() { on_map.disconnect(); on_unmap.disconnect(); - on_destroy.disconnect(); on_new_popup.disconnect(); on_ping_timeout.disconnect(); on_reposition.disconnect(); @@ -275,6 +305,7 @@ void wayfire_xdg_popup::destroy() void wayfire_xdg_popup::close() { + LOGC(VIEWS, "Closing xdg-popup ", self(), " ", is_mapped()); if (is_mapped()) { wlr_xdg_popup_destroy(popup); @@ -372,7 +403,11 @@ class xdg_popup_controller_t public: xdg_popup_controller_t(wlr_xdg_popup *popup) { - on_destroy.set_callback([=] (auto) { delete this; }); + on_destroy.set_callback([=] (auto) + { + view->destroy(); + delete this; + }); on_destroy.connect(&popup->base->events.destroy); view = wayfire_xdg_popup::create(popup); } diff --git a/src/view/xdg-shell.hpp b/src/view/xdg-shell.hpp index c6b64c784..832f9c976 100644 --- a/src/view/xdg-shell.hpp +++ b/src/view/xdg-shell.hpp @@ -15,8 +15,7 @@ class wayfire_xdg_popup_node; class wayfire_xdg_popup : public wf::view_interface_t { protected: - wf::wl_listener_wrapper on_destroy, on_new_popup, - on_map, on_unmap, on_ping_timeout, on_reposition; + wf::wl_listener_wrapper on_new_popup, on_map, on_unmap, on_ping_timeout, on_reposition; wf::signal::connection_t parent_geometry_changed; wf::signal::connection_t parent_title_changed; @@ -74,6 +73,8 @@ class wayfire_xdg_popup : public wf::view_interface_t void handle_app_id_changed(std::string new_app_id); void handle_title_changed(std::string new_title); void update_size(); + + bool should_close_on_focus_change(wf::keyboard_focus_changed_signal *ev); }; void create_xdg_popup(wlr_xdg_popup *popup);