Skip to content

Commit

Permalink
xdg-activation: add focus stealing prevention when using tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
dkondor committed Dec 21, 2024
1 parent 10afe15 commit dc19206
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 0 deletions.
5 changes: 5 additions & 0 deletions metadata/xdg-activation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
<_long>Whether to reject activation requests if a newer request has arrived since their creation.</_long>
<default>false</default>
</option>
<option name="focus_stealing_prevention" type="bool">
<_short>Prevent stealing focus by an activation request</_short>
<_long>Whether to reject an activation request if the user interacted with a different view since its creation.</_long>
<default>true</default>
</option>
<option name="timeout" type="int">
<_short>Timeout for activation (in seconds)</_short>
<_long>Focus requests will be ignored if at least this amount of time has elapsed between creating and using it.</_long>
Expand Down
65 changes: 65 additions & 0 deletions plugins/protocols/xdg-activation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ class wayfire_xdg_activation_protocol_impl : public wf::plugin_interface_t
xdg_activation_new_token.disconnect();
xdg_activation_token_destroy.disconnect();
last_token = nullptr;
if (last_view)
{
last_view->disconnect(&on_view_unmapped);
last_view->disconnect(&on_view_deactivated);
last_view = nullptr;
}
}

bool is_unloadable() override
Expand Down Expand Up @@ -63,6 +69,17 @@ class wayfire_xdg_activation_protocol_impl : public wf::plugin_interface_t

last_token = nullptr; // avoid reusing the same token

if (last_view)
{
last_view->disconnect(&on_view_unmapped);
last_view->disconnect(&on_view_deactivated);
last_view = nullptr;
} else if (prevent_focus_stealing)
{
LOGI("Denying focus request, requesting view has been deactivated");
return;
}

wayfire_view view = wf::wl_surface_to_wayfire_view(event->surface->resource);
if (!view)
{
Expand Down Expand Up @@ -99,6 +116,25 @@ class wayfire_xdg_activation_protocol_impl : public wf::plugin_interface_t
return;
}

// unset any previously saved view
if (last_view)
{
last_view->disconnect(&on_view_unmapped);
last_view->disconnect(&on_view_deactivated);
last_view = nullptr;
}

wayfire_view view = wf::wl_surface_to_wayfire_view(token->surface->resource);
if (view)
{
last_view = wf::toplevel_cast(view); // might return nullptr
if (last_view)
{
last_view->connect(&on_view_unmapped);
last_view->connect(&on_view_deactivated);
}
}

// update our token and connect its destroy signal
last_token = token;
xdg_activation_token_destroy.disconnect();
Expand Down Expand Up @@ -128,14 +164,43 @@ class wayfire_xdg_activation_protocol_impl : public wf::plugin_interface_t
}
};

wf::signal::connection_t<wf::view_unmapped_signal> on_view_unmapped = [this] (auto)
{
last_view->disconnect(&on_view_unmapped);
last_view->disconnect(&on_view_deactivated);
// handle the case when last_view was a dialog that is closed by user interaction
last_view = last_view->parent;
if (last_view)
{
last_view->connect(&on_view_unmapped);
last_view->connect(&on_view_deactivated);
}
};

wf::signal::connection_t<wf::view_activated_state_signal> on_view_deactivated = [this] (auto)
{
if (last_view->activated)
{
// could be a spurious event, e.g. activating the parent
// view after closing a dialog
return;
}

last_view->disconnect(&on_view_unmapped);
last_view->disconnect(&on_view_deactivated);
last_view = nullptr;
};

struct wlr_xdg_activation_v1 *xdg_activation;
wf::wl_listener_wrapper xdg_activation_request_activate;
wf::wl_listener_wrapper xdg_activation_new_token;
wf::wl_listener_wrapper xdg_activation_token_destroy;
struct wlr_xdg_activation_token_v1 *last_token = nullptr;
wayfire_toplevel_view last_view = nullptr; // view that created the token

wf::option_wrapper_t<bool> check_surface{"xdg-activation/check_surface"};
wf::option_wrapper_t<bool> only_last_token{"xdg-activation/only_last_request"};
wf::option_wrapper_t<bool> prevent_focus_stealing{"xdg-activation/focus_stealing_prevention"};
wf::option_wrapper_t<int> timeout{"xdg-activation/timeout"};
};

Expand Down

0 comments on commit dc19206

Please sign in to comment.