Skip to content

Commit

Permalink
xdg-activation: add options to make it more strict (#2527)
Browse files Browse the repository at this point in the history
* xdg-activation: add options to make it more strict

These allow:
 - reject requests without a view supplied
 - only allow activating with the last issued token
Both are disabled by default.

Also added an option to control the timeout used by wlroots for invalidating tokens.
  • Loading branch information
dkondor authored Mar 2, 2025
1 parent aec7453 commit 179b604
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 24 deletions.
18 changes: 17 additions & 1 deletion metadata/xdg-activation.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,23 @@
<wayfire>
<plugin name="xdg-activation">
<_short>XDG Activation Protocol</_short>
<_long>An implementation of the xdg-activation-v1 protocol.</_long>
<_long>An implementation of the xdg-activation-v1 protocol. This allows the active app to pass the focus to a different view in the same or a different app.</_long>
<category>Utility</category>
<option name="check_surface" type="bool">
<_short>Restrict to valid view</_short>
<_long>Whether to reject creating activation requests if no source view exists. Without this option, any view can grab to focus at any time.</_long>
<default>false</default>
</option>
<option name="only_last_request" type="bool">
<_short>Restrict to the most recent activation request</_short>
<_long>Whether to reject activation requests if a newer request has arrived since their creation.</_long>
<default>false</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>
<default>30</default>
<min>0</min>
</option>
</plugin>
</wayfire>
121 changes: 98 additions & 23 deletions plugins/protocols/xdg-activation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,133 @@
#include <wayfire/toplevel-view.hpp>
#include <wayfire/nonstd/wlroots-full.hpp>
#include <wayfire/window-manager.hpp>
#include <wayfire/util.hpp>
#include "config.h"

class wayfire_xdg_activation_protocol_impl : public wf::plugin_interface_t
{
public:
wayfire_xdg_activation_protocol_impl()
{
set_callbacks();
}

void init() override
{
xdg_activation = wlr_xdg_activation_v1_create(wf::get_core().display);
xdg_activation_request_activate.notify = xdg_activation_handle_request_activate;
if (timeout >= 0)
{
xdg_activation->token_timeout_msec = 1000 * timeout;
}

wl_signal_add(&xdg_activation->events.request_activate, &xdg_activation_request_activate);
xdg_activation_request_activate.connect(&xdg_activation->events.request_activate);
xdg_activation_new_token.connect(&xdg_activation->events.new_token);
}

void fini() override
{}
{
xdg_activation_request_activate.disconnect();
xdg_activation_new_token.disconnect();
xdg_activation_token_destroy.disconnect();
last_token = nullptr;
}

bool is_unloadable() override
{
return false;
}

private:
static void xdg_activation_handle_request_activate(struct wl_listener *listener, void *data)
void set_callbacks()
{
auto event = static_cast<const struct wlr_xdg_activation_v1_request_activate_event*>(data);

wayfire_view view = wf::wl_surface_to_wayfire_view(event->surface->resource);
if (!view)
xdg_activation_request_activate.set_callback([this] (void *data)
{
LOGE("Could not get view");
return;
}
auto event = static_cast<const struct wlr_xdg_activation_v1_request_activate_event*>(data);

if (!event->token->seat)
{
LOGI("Denying focus request, token was rejected at creation");
return;
}

if (only_last_token && (event->token != last_token))
{
LOGI("Denying focus request, token is expired");
return;
}

last_token = nullptr; // avoid reusing the same token

wayfire_view view = wf::wl_surface_to_wayfire_view(event->surface->resource);
if (!view)
{
LOGE("Could not get view");
return;
}

auto toplevel = wf::toplevel_cast(view);
if (!toplevel)
auto toplevel = wf::toplevel_cast(view);
if (!toplevel)
{
LOGE("Could not get toplevel view");
return;
}

LOGD("Activating view");
wf::get_core().default_wm->focus_request(toplevel);
});

xdg_activation_new_token.set_callback([this] (void *data)
{
LOGE("Could not get toplevel view");
return;
}
auto token = static_cast<struct wlr_xdg_activation_token_v1*>(data);
if (!token->seat)
{
// note: for a valid seat, wlroots already checks that the serial is valid
LOGI("Not registering activation token, seat was not supplied");
return;
}

if (!event->token->seat)
if (check_surface && !token->surface)
{
// note: for a valid surface, wlroots already checks that this is the active surface
LOGI("Not registering activation token, surface was not supplied");
token->seat = nullptr; // this will ensure that this token will be rejected later
return;
}

// update our token and connect its destroy signal
last_token = token;
xdg_activation_token_destroy.disconnect();
xdg_activation_token_destroy.connect(&token->events.destroy);
});

xdg_activation_token_destroy.set_callback([this] (void *data)
{
LOGI("Denying focus request, seat wasn't supplied");
return;
}
last_token = nullptr;

LOGI("Activating view");
wf::get_core().default_wm->focus_request(toplevel);
xdg_activation_token_destroy.disconnect();
});

timeout.set_callback(timeout_changed);
}

wf::config::option_base_t::updated_callback_t timeout_changed =
[this] ()
{
if (xdg_activation && (timeout >= 0))
{
xdg_activation->token_timeout_msec = 1000 * timeout;
}
};

struct wlr_xdg_activation_v1 *xdg_activation;
struct wl_listener xdg_activation_request_activate;
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;

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<int> timeout{"xdg-activation/timeout"};
};

DECLARE_WAYFIRE_PLUGIN(wayfire_xdg_activation_protocol_impl);

0 comments on commit 179b604

Please sign in to comment.