diff --git a/src/follow-focus.cpp b/src/follow-focus.cpp index ced6a92..b271452 100644 --- a/src/follow-focus.cpp +++ b/src/follow-focus.cpp @@ -28,10 +28,12 @@ #include #include #include +#include #include #include #include #include +#include namespace follow_focus { @@ -44,8 +46,10 @@ class wayfire_follow_focus : public wf::per_output_plugin_instance_t wayfire_view focus_view = nullptr; wf::wl_timer change_output_focus, change_view_focus; wf::pointf_t last_output_coords, last_view_coords; - wf::wl_idle_call workspace_focus_idle; + wf::wl_idle_call workspace_focus_idle, workspace_switch_idle; bool workspace_focus_pending = false; + bool workspace_switch_started = false; + uint64_t workspace_switch_focus_ts = 0; wf::option_wrapper_t focus_on_workspace_change{"follow-focus/focus_on_workspace_change"}; wf::option_wrapper_t should_change_view{"follow-focus/change_view"}; @@ -172,7 +176,8 @@ class wayfire_follow_focus : public wf::per_output_plugin_instance_t if (!should_change_view || !focus_on_workspace_change || output != wf::get_core().seat->get_active_output() || !output->can_activate_plugin(wf::CAPABILITY_MANAGE_COMPOSITOR) || - !view || view != focus_view || view->get_output() != output) + !view || view != focus_view || view->get_output() != output || + workspace_focus_overridden()) { return false; } @@ -183,6 +188,29 @@ class wayfire_follow_focus : public wf::per_output_plugin_instance_t }); } + // Remember the seat's focus timestamp when a workspace switch begins, so + // focus given explicitly as part of the switch (scale, switcher, + // activation requests, a window carried along) can be told apart from + // the core's refocus after the switch, which does not update timestamps. + // Requests are serviced synchronously, so the mark expires on idle. + void mark_workspace_switch_start() + { + if (!workspace_switch_started && !workspace_focus_pending) + { + workspace_switch_started = true; + workspace_switch_focus_ts = wf::get_core().seat->get_last_focus_timestamp(); + workspace_switch_idle.run_once([=] () { workspace_switch_started = false; }); + } + } + + bool workspace_focus_overridden() + { + auto active = wf::toplevel_cast(wf::get_core().seat->get_active_view()); + return active && (active->get_output() == output) && + (wf::get_focus_timestamp(active) > workspace_switch_focus_ts) && + output->wset()->view_visible_on(active, output->wset()->get_current_workspace()); + } + // A workspace switch can put a window under a stationary pointer. Wait // until the switcher's animation/grab is finished, then use the normal // follow-focus delay and raise policy without requiring pointer movement. @@ -195,8 +223,11 @@ class wayfire_follow_focus : public wf::per_output_plugin_instance_t return; } + // A lock (output inhibited) or an explicit focus since the switch + // makes the pending pointer focus stale; drop it. if (!should_change_view || !focus_on_workspace_change || - output != wf::get_core().seat->get_active_output()) + output != wf::get_core().seat->get_active_output() || + output->is_inhibited() || workspace_focus_overridden()) { workspace_focus_pending = false; return; @@ -221,12 +252,30 @@ class wayfire_follow_focus : public wf::per_output_plugin_instance_t { if (ev->old_viewport != ev->new_viewport) { + mark_workspace_switch_start(); + workspace_switch_started = false; change_view_focus.disconnect(); workspace_focus_pending = should_change_view && focus_on_workspace_change; schedule_workspace_focus(); } }; + wf::signal::connection_t workspace_change_request = + [=] (wf::workspace_change_request_signal *ev) + { + if (ev->old_viewport != ev->new_viewport) + { + mark_workspace_switch_start(); + } + }; + + // Emitted for views carried along with a switch, before they are focused. + wf::signal::connection_t view_change_workspace = + [=] (wf::view_change_workspace_signal *ev) + { + mark_workspace_switch_start(); + }; + wf::signal::connection_t plugin_changed = [=] (wf::output_plugin_activated_changed_signal *ev) { @@ -256,6 +305,8 @@ class wayfire_follow_focus : public wf::per_output_plugin_instance_t { wf::get_core().connect(&pointer_motion); wf::get_core().connect(&pointer_motion_absolute); + output->connect(&workspace_change_request); + output->connect(&view_change_workspace); output->connect(&workspace_changed); output->connect(&plugin_changed); } @@ -263,6 +314,7 @@ class wayfire_follow_focus : public wf::per_output_plugin_instance_t void fini() override { workspace_focus_idle.disconnect(); + workspace_switch_idle.disconnect(); change_output_focus.disconnect(); change_view_focus.disconnect(); }