Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xdg-activation: add options to make it more strict #2527

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
124 changes: 101 additions & 23 deletions plugins/protocols/xdg-activation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,53 +12,131 @@
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;
}

LOGI("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;
}
auto token = static_cast<struct wlr_xdg_activation_token_v1*>(data);
if (token == last_token)
{
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);
Loading