2020-10-09 10:23:32 -04:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2020 Red Hat
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
|
|
* License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
* 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* Author: Carlos Garnacho <carlosg@gnome.org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "wayland/meta-wayland-activation.h"
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
#include <wayland-server.h>
|
|
|
|
|
|
|
|
#include "wayland/meta-wayland-private.h"
|
|
|
|
#include "wayland/meta-wayland-versions.h"
|
|
|
|
|
|
|
|
#include "xdg-activation-v1-server-protocol.h"
|
|
|
|
|
|
|
|
typedef struct _MetaXdgActivationToken MetaXdgActivationToken;
|
|
|
|
|
|
|
|
struct _MetaWaylandActivation
|
|
|
|
{
|
|
|
|
MetaWaylandCompositor *compositor;
|
|
|
|
struct wl_list resource_list;
|
|
|
|
struct wl_list token_list;
|
|
|
|
GHashTable *tokens;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _MetaXdgActivationToken
|
|
|
|
{
|
|
|
|
MetaWaylandSurface *surface;
|
|
|
|
MetaWaylandSeat *seat;
|
|
|
|
MetaWaylandActivation *activation;
|
|
|
|
MetaStartupSequence *sequence;
|
|
|
|
char *app_id;
|
|
|
|
char *token;
|
|
|
|
uint32_t serial;
|
|
|
|
gulong sequence_complete_id;
|
wayland/activation: Remove token from hash table on timeout
When an activation times out, we'll be signalled two signals on the
startup sequence object: "timeout", and "complete".
Normally, the "complete" signal is emitted when a startup sequence is
completed succesfully by it being used for activation, and in this case,
the xdg_activation implementation should remove the sequence from the
startup notification machinery.
However, in the timeout case, we should not remove it, as the startup
notification machinery itself will deal with this. If we would, we'd end
up with use-after-free issues, as the sequence would be finalized when
removed the first time.
To avoid this, just clean up the Wayland side in the "timeout" signal
handler, leaving the "complete" signal handler early out if it was
already handled by it.
This avoids crashes like:
0) g_type_check_instance (type_instance=type_instance@entry=0xdd6740)
1) g_signal_handlers_disconnect_matched (instance=0xdd6740, ...)
2) meta_startup_notification_remove_sequence (sn=0x4cc890,
seq=0xdd6740) at
../src/core/startup-notification.c:544
3) startup_sequence_timeout (data=0x4cc890, ...) at
../src/core/startup-notification.c:504
4) g_timeout_dispatch (...) at ../glib/gmain.c:4933
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2081>
2021-11-08 04:44:17 -05:00
|
|
|
gulong sequence_timeout_id;
|
2020-10-09 10:23:32 -04:00
|
|
|
gboolean committed;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
unbind_resource (struct wl_resource *resource)
|
|
|
|
{
|
|
|
|
wl_list_remove (wl_resource_get_link (resource));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
token_set_serial (struct wl_client *client,
|
|
|
|
struct wl_resource *resource,
|
|
|
|
uint32_t serial,
|
|
|
|
struct wl_resource *seat_resource)
|
|
|
|
{
|
|
|
|
MetaXdgActivationToken *token = wl_resource_get_user_data (resource);
|
|
|
|
MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
|
|
|
|
|
|
|
|
token->serial = serial;
|
|
|
|
token->seat = seat;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
token_set_app_id (struct wl_client *client,
|
|
|
|
struct wl_resource *resource,
|
|
|
|
const char *app_id)
|
|
|
|
{
|
|
|
|
MetaXdgActivationToken *token = wl_resource_get_user_data (resource);
|
|
|
|
|
|
|
|
g_clear_pointer (&token->app_id, g_free);
|
|
|
|
token->app_id = g_strdup (app_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
token_set_surface (struct wl_client *client,
|
|
|
|
struct wl_resource *resource,
|
|
|
|
struct wl_resource *surface_resource)
|
|
|
|
{
|
|
|
|
MetaXdgActivationToken *token = wl_resource_get_user_data (resource);
|
|
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
|
|
|
|
|
|
token->surface = surface;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sequence_complete_cb (MetaStartupSequence *sequence,
|
|
|
|
MetaXdgActivationToken *token)
|
|
|
|
{
|
|
|
|
MetaWaylandActivation *activation = token->activation;
|
|
|
|
MetaDisplay *display = meta_get_display ();
|
|
|
|
|
wayland/activation: Remove token from hash table on timeout
When an activation times out, we'll be signalled two signals on the
startup sequence object: "timeout", and "complete".
Normally, the "complete" signal is emitted when a startup sequence is
completed succesfully by it being used for activation, and in this case,
the xdg_activation implementation should remove the sequence from the
startup notification machinery.
However, in the timeout case, we should not remove it, as the startup
notification machinery itself will deal with this. If we would, we'd end
up with use-after-free issues, as the sequence would be finalized when
removed the first time.
To avoid this, just clean up the Wayland side in the "timeout" signal
handler, leaving the "complete" signal handler early out if it was
already handled by it.
This avoids crashes like:
0) g_type_check_instance (type_instance=type_instance@entry=0xdd6740)
1) g_signal_handlers_disconnect_matched (instance=0xdd6740, ...)
2) meta_startup_notification_remove_sequence (sn=0x4cc890,
seq=0xdd6740) at
../src/core/startup-notification.c:544
3) startup_sequence_timeout (data=0x4cc890, ...) at
../src/core/startup-notification.c:504
4) g_timeout_dispatch (...) at ../glib/gmain.c:4933
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2081>
2021-11-08 04:44:17 -05:00
|
|
|
if (!g_hash_table_contains (activation->tokens, token->token))
|
|
|
|
return;
|
|
|
|
|
2020-10-09 10:23:32 -04:00
|
|
|
meta_startup_notification_remove_sequence (display->startup_notification,
|
|
|
|
sequence);
|
|
|
|
g_hash_table_remove (activation->tokens, token->token);
|
|
|
|
}
|
|
|
|
|
wayland/activation: Remove token from hash table on timeout
When an activation times out, we'll be signalled two signals on the
startup sequence object: "timeout", and "complete".
Normally, the "complete" signal is emitted when a startup sequence is
completed succesfully by it being used for activation, and in this case,
the xdg_activation implementation should remove the sequence from the
startup notification machinery.
However, in the timeout case, we should not remove it, as the startup
notification machinery itself will deal with this. If we would, we'd end
up with use-after-free issues, as the sequence would be finalized when
removed the first time.
To avoid this, just clean up the Wayland side in the "timeout" signal
handler, leaving the "complete" signal handler early out if it was
already handled by it.
This avoids crashes like:
0) g_type_check_instance (type_instance=type_instance@entry=0xdd6740)
1) g_signal_handlers_disconnect_matched (instance=0xdd6740, ...)
2) meta_startup_notification_remove_sequence (sn=0x4cc890,
seq=0xdd6740) at
../src/core/startup-notification.c:544
3) startup_sequence_timeout (data=0x4cc890, ...) at
../src/core/startup-notification.c:504
4) g_timeout_dispatch (...) at ../glib/gmain.c:4933
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2081>
2021-11-08 04:44:17 -05:00
|
|
|
static void
|
|
|
|
sequence_timeout_cb (MetaStartupSequence *sequence,
|
|
|
|
MetaXdgActivationToken *token)
|
|
|
|
{
|
|
|
|
MetaWaylandActivation *activation = token->activation;
|
|
|
|
|
|
|
|
g_hash_table_remove (activation->tokens, token->token);
|
|
|
|
}
|
|
|
|
|
2020-10-09 10:23:32 -04:00
|
|
|
static char *
|
|
|
|
create_startup_token (MetaWaylandActivation *activation,
|
|
|
|
MetaDisplay *display)
|
|
|
|
{
|
|
|
|
g_autofree char *uuid = NULL, *token = NULL;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
g_clear_pointer (&uuid, g_free);
|
|
|
|
g_clear_pointer (&token, g_free);
|
|
|
|
uuid = g_uuid_string_random ();
|
|
|
|
token = g_strdup_printf ("%s_TIME%d", uuid,
|
|
|
|
meta_display_get_current_time (display));
|
|
|
|
}
|
|
|
|
while (g_hash_table_contains (activation->tokens, token));
|
|
|
|
|
|
|
|
return g_steal_pointer (&token);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
token_commit (struct wl_client *client,
|
|
|
|
struct wl_resource *resource)
|
|
|
|
{
|
|
|
|
MetaXdgActivationToken *token = wl_resource_get_user_data (resource);
|
|
|
|
MetaWaylandActivation *activation = token->activation;
|
|
|
|
MetaDisplay *display = meta_get_display ();
|
|
|
|
uint32_t timestamp;
|
|
|
|
|
|
|
|
if (token->committed)
|
|
|
|
{
|
|
|
|
wl_resource_post_error (resource,
|
|
|
|
XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED,
|
|
|
|
"Activation token was already used");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
timestamp = meta_display_get_current_time_roundtrip (display);
|
|
|
|
|
|
|
|
token->committed = TRUE;
|
|
|
|
token->token = create_startup_token (activation, display);
|
|
|
|
token->sequence = g_object_new (META_TYPE_STARTUP_SEQUENCE,
|
|
|
|
"id", token->token,
|
|
|
|
"application-id", token->app_id,
|
|
|
|
"timestamp", timestamp,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
token->sequence_complete_id =
|
|
|
|
g_signal_connect (token->sequence,
|
|
|
|
"complete",
|
|
|
|
G_CALLBACK (sequence_complete_cb),
|
|
|
|
token);
|
wayland/activation: Remove token from hash table on timeout
When an activation times out, we'll be signalled two signals on the
startup sequence object: "timeout", and "complete".
Normally, the "complete" signal is emitted when a startup sequence is
completed succesfully by it being used for activation, and in this case,
the xdg_activation implementation should remove the sequence from the
startup notification machinery.
However, in the timeout case, we should not remove it, as the startup
notification machinery itself will deal with this. If we would, we'd end
up with use-after-free issues, as the sequence would be finalized when
removed the first time.
To avoid this, just clean up the Wayland side in the "timeout" signal
handler, leaving the "complete" signal handler early out if it was
already handled by it.
This avoids crashes like:
0) g_type_check_instance (type_instance=type_instance@entry=0xdd6740)
1) g_signal_handlers_disconnect_matched (instance=0xdd6740, ...)
2) meta_startup_notification_remove_sequence (sn=0x4cc890,
seq=0xdd6740) at
../src/core/startup-notification.c:544
3) startup_sequence_timeout (data=0x4cc890, ...) at
../src/core/startup-notification.c:504
4) g_timeout_dispatch (...) at ../glib/gmain.c:4933
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2081>
2021-11-08 04:44:17 -05:00
|
|
|
token->sequence_timeout_id =
|
|
|
|
g_signal_connect (token->sequence,
|
|
|
|
"timeout",
|
|
|
|
G_CALLBACK (sequence_timeout_cb),
|
|
|
|
token);
|
2020-10-09 10:23:32 -04:00
|
|
|
|
|
|
|
meta_startup_notification_add_sequence (display->startup_notification,
|
|
|
|
token->sequence);
|
|
|
|
|
|
|
|
xdg_activation_token_v1_send_done (resource, token->token);
|
|
|
|
g_hash_table_insert (activation->tokens, token->token, token);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
token_destroy (struct wl_client *client,
|
|
|
|
struct wl_resource *resource)
|
|
|
|
{
|
|
|
|
wl_resource_destroy (resource);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct xdg_activation_token_v1_interface token_interface = {
|
|
|
|
token_set_serial,
|
|
|
|
token_set_app_id,
|
|
|
|
token_set_surface,
|
|
|
|
token_commit,
|
|
|
|
token_destroy,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_xdg_activation_token_free (MetaXdgActivationToken *token)
|
|
|
|
{
|
|
|
|
if (token->sequence)
|
|
|
|
{
|
|
|
|
g_clear_signal_handler (&token->sequence_complete_id,
|
|
|
|
token->sequence);
|
wayland/activation: Remove token from hash table on timeout
When an activation times out, we'll be signalled two signals on the
startup sequence object: "timeout", and "complete".
Normally, the "complete" signal is emitted when a startup sequence is
completed succesfully by it being used for activation, and in this case,
the xdg_activation implementation should remove the sequence from the
startup notification machinery.
However, in the timeout case, we should not remove it, as the startup
notification machinery itself will deal with this. If we would, we'd end
up with use-after-free issues, as the sequence would be finalized when
removed the first time.
To avoid this, just clean up the Wayland side in the "timeout" signal
handler, leaving the "complete" signal handler early out if it was
already handled by it.
This avoids crashes like:
0) g_type_check_instance (type_instance=type_instance@entry=0xdd6740)
1) g_signal_handlers_disconnect_matched (instance=0xdd6740, ...)
2) meta_startup_notification_remove_sequence (sn=0x4cc890,
seq=0xdd6740) at
../src/core/startup-notification.c:544
3) startup_sequence_timeout (data=0x4cc890, ...) at
../src/core/startup-notification.c:504
4) g_timeout_dispatch (...) at ../glib/gmain.c:4933
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2081>
2021-11-08 04:44:17 -05:00
|
|
|
g_clear_signal_handler (&token->sequence_timeout_id,
|
|
|
|
token->sequence);
|
2020-10-09 10:23:32 -04:00
|
|
|
g_clear_object (&token->sequence);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (token->app_id);
|
|
|
|
g_free (token->token);
|
|
|
|
g_free (token);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_wayland_activation_token_create_new_resource (MetaWaylandActivation *activation,
|
|
|
|
struct wl_client *client,
|
|
|
|
struct wl_resource *activation_resource,
|
|
|
|
uint32_t id)
|
|
|
|
{
|
|
|
|
MetaXdgActivationToken *token;
|
|
|
|
struct wl_resource *token_resource;
|
|
|
|
|
|
|
|
token = g_new0 (MetaXdgActivationToken, 1);
|
|
|
|
token->activation = activation;
|
|
|
|
|
|
|
|
token_resource =
|
|
|
|
wl_resource_create (client, &xdg_activation_token_v1_interface,
|
|
|
|
wl_resource_get_version (activation_resource),
|
|
|
|
id);
|
|
|
|
wl_resource_set_implementation (token_resource, &token_interface,
|
|
|
|
token, unbind_resource);
|
|
|
|
wl_resource_set_user_data (token_resource, token);
|
|
|
|
wl_list_insert (&activation->token_list,
|
|
|
|
wl_resource_get_link (token_resource));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
activation_destroy (struct wl_client *client,
|
|
|
|
struct wl_resource *resource)
|
|
|
|
{
|
|
|
|
wl_resource_destroy (resource);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
activation_get_activation_token (struct wl_client *client,
|
|
|
|
struct wl_resource *resource,
|
|
|
|
uint32_t id)
|
|
|
|
{
|
|
|
|
MetaWaylandActivation *activation = wl_resource_get_user_data (resource);
|
|
|
|
|
|
|
|
meta_wayland_activation_token_create_new_resource (activation,
|
|
|
|
client,
|
|
|
|
resource,
|
|
|
|
id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
activation_activate (struct wl_client *client,
|
|
|
|
struct wl_resource *resource,
|
|
|
|
const char *token_str,
|
|
|
|
struct wl_resource *surface_resource)
|
|
|
|
{
|
|
|
|
MetaWaylandActivation *activation = wl_resource_get_user_data (resource);
|
|
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
|
|
MetaXdgActivationToken *token;
|
|
|
|
MetaWindow *window;
|
|
|
|
|
|
|
|
window = meta_wayland_surface_get_window (surface);
|
|
|
|
if (!window)
|
|
|
|
return;
|
|
|
|
|
|
|
|
token = g_hash_table_lookup (activation->tokens, token_str);
|
|
|
|
if (!token)
|
|
|
|
return;
|
|
|
|
|
wayland/activation: Don't grab if no serial or surface was provided
A client can create a token without any seat, serial, or surface. In
this case, we'd still try to grab, which would run into some unforseen
code paths, potentially resulting in the following crash:
0) meta_wayland_tablet_seat_device_added (tablet_seat=0x55dff4271c90,
device=0x7f87b80655b0) at
../src/wayland/meta-wayland-tablet-seat.c:200
1) meta_wayland_tablet_seat_new (seat=0x0, manager=0x55dff3ec7b40) at
../src/wayland/meta-wayland-tablet-seat.c:283
2) meta_wayland_tablet_manager_ensure_seat (manager=manager@entry=0x55dff3ec7b40,
seat=seat@entry=0x0) at
../src/wayland/meta-wayland-tablet-manager.c:239
3) meta_wayland_tablet_manager_ensure_seat (seat=0x0, manager=0x55dff3ec7b40) at
../src/wayland/meta-wayland-touch.c:595
4) meta_wayland_seat_get_grab_info (seat=0x0, surface=0x55dff43ff5b0,
serial=0, require_pressed=0, x=0x0, y=0x0) at
../src/wayland/meta-wayland-seat.c:479
5) activation_activate (...) at
../src/wayland/meta-wayland-activation.c:261
Fix this by not trying to grab if not enough parameters was passed when
creating the token. Also add a test case that reproduces the above
crash.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2081>
2021-11-08 05:13:06 -05:00
|
|
|
if (token->seat &&
|
|
|
|
token->surface &&
|
|
|
|
meta_wayland_seat_get_grab_info (token->seat,
|
2020-10-09 10:23:32 -04:00
|
|
|
token->surface,
|
|
|
|
token->serial,
|
|
|
|
FALSE, NULL, NULL))
|
|
|
|
{
|
|
|
|
uint32_t timestamp;
|
|
|
|
int32_t workspace_idx;
|
|
|
|
|
|
|
|
workspace_idx = meta_startup_sequence_get_workspace (token->sequence);
|
|
|
|
timestamp = meta_startup_sequence_get_timestamp (token->sequence);
|
|
|
|
|
|
|
|
if (workspace_idx >= 0)
|
|
|
|
meta_window_change_workspace_by_index (window, workspace_idx, TRUE);
|
|
|
|
|
|
|
|
meta_window_activate_full (window, timestamp,
|
|
|
|
META_CLIENT_TYPE_APPLICATION, NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
meta_window_set_demands_attention (window);
|
|
|
|
}
|
|
|
|
|
|
|
|
meta_startup_sequence_complete (token->sequence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct xdg_activation_v1_interface activation_interface = {
|
|
|
|
activation_destroy,
|
|
|
|
activation_get_activation_token,
|
|
|
|
activation_activate,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
bind_activation (struct wl_client *client,
|
|
|
|
void *data,
|
|
|
|
uint32_t version,
|
|
|
|
uint32_t id)
|
|
|
|
{
|
|
|
|
MetaWaylandCompositor *compositor = data;
|
|
|
|
MetaWaylandActivation *activation = compositor->activation;
|
|
|
|
struct wl_resource *resource;
|
|
|
|
|
|
|
|
resource = wl_resource_create (client, &xdg_activation_v1_interface,
|
|
|
|
MIN (version, META_XDG_ACTIVATION_V1_VERSION),
|
|
|
|
id);
|
|
|
|
wl_resource_set_implementation (resource, &activation_interface,
|
|
|
|
activation, unbind_resource);
|
|
|
|
wl_resource_set_user_data (resource, activation);
|
|
|
|
wl_list_insert (&activation->resource_list,
|
|
|
|
wl_resource_get_link (resource));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_wayland_activation_init (MetaWaylandCompositor *compositor)
|
|
|
|
{
|
|
|
|
MetaWaylandActivation *activation;
|
|
|
|
|
|
|
|
activation = g_new0 (MetaWaylandActivation, 1);
|
|
|
|
activation->compositor = compositor;
|
|
|
|
wl_list_init (&activation->resource_list);
|
|
|
|
wl_list_init (&activation->token_list);
|
|
|
|
|
|
|
|
activation->tokens =
|
|
|
|
g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
|
|
NULL,
|
|
|
|
(GDestroyNotify) meta_xdg_activation_token_free);
|
|
|
|
|
|
|
|
wl_global_create (compositor->wayland_display,
|
|
|
|
&xdg_activation_v1_interface,
|
|
|
|
META_XDG_ACTIVATION_V1_VERSION,
|
|
|
|
compositor, bind_activation);
|
|
|
|
|
|
|
|
compositor->activation = activation;
|
|
|
|
}
|