
We must be educated to X11 clients (which usually parse the timestamp from the DESKTOP_STARTUP_ID, and request focus with it) to make focus stealing prevention work across the board. To wayland clients the startup ID should be as opaque and meaningless as it was before. https://gitlab.gnome.org/GNOME/mutter/merge_requests/540
256 lines
7.5 KiB
C
256 lines
7.5 KiB
C
/*
|
|
* Copyright (C) 2018 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 <gio/gdesktopappinfo.h>
|
|
|
|
#include "core/display-private.h"
|
|
#include "meta/meta-launch-context.h"
|
|
#include "x11/meta-startup-notification-x11.h"
|
|
|
|
typedef struct _MetaLaunchContext MetaLaunchContext;
|
|
|
|
struct _MetaLaunchContext
|
|
{
|
|
GAppLaunchContext parent_instance;
|
|
MetaDisplay *display;
|
|
MetaWorkspace *workspace;
|
|
uint32_t timestamp;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaLaunchContext, meta_launch_context,
|
|
G_TYPE_APP_LAUNCH_CONTEXT)
|
|
|
|
enum
|
|
{
|
|
PROP_DISPLAY = 1,
|
|
PROP_WORKSPACE,
|
|
PROP_TIMESTAMP,
|
|
N_PROPS
|
|
};
|
|
|
|
static GParamSpec *props[N_PROPS] = { 0, };
|
|
|
|
static void
|
|
meta_launch_context_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaLaunchContext *context = META_LAUNCH_CONTEXT (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_DISPLAY:
|
|
context->display = g_value_get_object (value);
|
|
break;
|
|
case PROP_WORKSPACE:
|
|
meta_launch_context_set_workspace (context,
|
|
g_value_get_object (value));
|
|
break;
|
|
case PROP_TIMESTAMP:
|
|
meta_launch_context_set_timestamp (context,
|
|
g_value_get_uint (value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_launch_context_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaLaunchContext *context = META_LAUNCH_CONTEXT (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_DISPLAY:
|
|
g_value_set_object (value, context->display);
|
|
break;
|
|
case PROP_WORKSPACE:
|
|
g_value_set_object (value, context->workspace);
|
|
break;
|
|
case PROP_TIMESTAMP:
|
|
g_value_set_uint (value, context->timestamp);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_launch_context_finalize (GObject *object)
|
|
{
|
|
G_OBJECT_CLASS (meta_launch_context_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
meta_launch_context_constructed (GObject *object)
|
|
{
|
|
MetaLaunchContext *context = META_LAUNCH_CONTEXT (object);
|
|
|
|
G_OBJECT_CLASS (meta_launch_context_parent_class)->constructed (object);
|
|
|
|
g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context),
|
|
"DISPLAY", getenv ("DISPLAY"));
|
|
g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context),
|
|
"WAYLAND_DISPLAY", getenv ("WAYLAND_DISPLAY"));
|
|
}
|
|
|
|
static gchar *
|
|
create_startup_notification_id (uint32_t timestamp)
|
|
{
|
|
gchar *uuid, *id;
|
|
|
|
uuid = g_uuid_string_random ();
|
|
id = g_strdup_printf ("%s_TIME%u", uuid, timestamp);
|
|
g_free (uuid);
|
|
|
|
return id;
|
|
}
|
|
|
|
static gchar *
|
|
meta_launch_context_get_startup_notify_id (GAppLaunchContext *launch_context,
|
|
GAppInfo *info,
|
|
GList *files)
|
|
{
|
|
MetaLaunchContext *context = META_LAUNCH_CONTEXT (launch_context);
|
|
int workspace_idx = -1;
|
|
char *startup_id = NULL;
|
|
|
|
if (context->workspace)
|
|
workspace_idx = meta_workspace_index (context->workspace);
|
|
|
|
if (!startup_id)
|
|
{
|
|
const char *application_id = NULL;
|
|
MetaStartupNotification *sn;
|
|
MetaStartupSequence *seq;
|
|
|
|
startup_id = create_startup_notification_id (context->timestamp);
|
|
|
|
/* Fallback through inserting our own startup sequence, this
|
|
* will be enough for wayland clients.
|
|
*/
|
|
if (G_IS_DESKTOP_APP_INFO (info))
|
|
{
|
|
application_id =
|
|
g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (info));
|
|
}
|
|
|
|
sn = meta_display_get_startup_notification (context->display);
|
|
seq = g_object_new (META_TYPE_STARTUP_SEQUENCE,
|
|
"id", startup_id,
|
|
"application-id", application_id,
|
|
"name", g_app_info_get_name (info),
|
|
"workspace", workspace_idx,
|
|
"timestamp", context->timestamp,
|
|
NULL);
|
|
|
|
meta_startup_notification_add_sequence (sn, seq);
|
|
g_object_unref (seq);
|
|
}
|
|
|
|
return startup_id;
|
|
}
|
|
|
|
static void
|
|
meta_launch_context_launch_failed (GAppLaunchContext *launch_context,
|
|
const gchar *startup_notify_id)
|
|
{
|
|
MetaLaunchContext *context = META_LAUNCH_CONTEXT (launch_context);
|
|
MetaStartupNotification *sn;
|
|
MetaStartupSequence *seq;
|
|
|
|
sn = meta_display_get_startup_notification (context->display);
|
|
seq = meta_startup_notification_lookup_sequence (sn, startup_notify_id);
|
|
|
|
if (seq)
|
|
{
|
|
meta_startup_sequence_complete (seq);
|
|
meta_startup_notification_remove_sequence (sn, seq);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_launch_context_class_init (MetaLaunchContextClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GAppLaunchContextClass *ctx_class = G_APP_LAUNCH_CONTEXT_CLASS (klass);
|
|
|
|
object_class->finalize = meta_launch_context_finalize;
|
|
object_class->constructed = meta_launch_context_constructed;
|
|
object_class->set_property = meta_launch_context_set_property;
|
|
object_class->get_property = meta_launch_context_get_property;
|
|
|
|
ctx_class->get_startup_notify_id = meta_launch_context_get_startup_notify_id;
|
|
ctx_class->launch_failed = meta_launch_context_launch_failed;
|
|
|
|
props[PROP_DISPLAY] =
|
|
g_param_spec_object ("display",
|
|
"display",
|
|
"Display",
|
|
META_TYPE_DISPLAY,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
|
props[PROP_WORKSPACE] =
|
|
g_param_spec_object ("workspace",
|
|
"workspace",
|
|
"Workspace",
|
|
META_TYPE_WORKSPACE,
|
|
G_PARAM_READWRITE);
|
|
props[PROP_TIMESTAMP] =
|
|
g_param_spec_uint ("timestamp",
|
|
"timestamp",
|
|
"Timestamp",
|
|
0, G_MAXUINT32, 0,
|
|
G_PARAM_READWRITE);
|
|
|
|
g_object_class_install_properties (object_class, N_PROPS, props);
|
|
}
|
|
|
|
static void
|
|
meta_launch_context_init (MetaLaunchContext *context)
|
|
{
|
|
}
|
|
|
|
void
|
|
meta_launch_context_set_workspace (MetaLaunchContext *context,
|
|
MetaWorkspace *workspace)
|
|
{
|
|
g_return_if_fail (META_IS_LAUNCH_CONTEXT (context));
|
|
g_return_if_fail (META_IS_WORKSPACE (workspace));
|
|
|
|
g_set_object (&context->workspace, workspace);
|
|
}
|
|
|
|
void
|
|
meta_launch_context_set_timestamp (MetaLaunchContext *context,
|
|
uint32_t timestamp)
|
|
{
|
|
g_return_if_fail (META_IS_LAUNCH_CONTEXT (context));
|
|
|
|
context->timestamp = timestamp;
|
|
}
|