Compare commits

..

2 Commits

Author SHA1 Message Date
Carlos Garnacho
aea56b6f55 core: Set timestamp in our DESKTOP_STARTUP_ID tokens
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
2019-04-17 18:56:44 +02:00
Carlos Garnacho
217bd31531 x11: Drop usage of libstartup-notification
And replace with our own XClientMessageEvent handling. It has the advantage
that we can mix Wayland and X11 startup notifications without always going
through X11 so that libstartup-notification is able to get the full picture.
This will fare much better on no/intermittent X11 availability.

A second advantage is the removed dependency, and that it seems to result
in less code (yay abstractions!).

https://gitlab.gnome.org/GNOME/mutter/merge_requests/540
2019-04-17 18:56:44 +02:00
18 changed files with 279 additions and 475 deletions

41
NEWS
View File

@@ -1,44 +1,3 @@
3.32.2
======
* Disable mouse keys with Numlock on [Olivier; #530]
* Fix crash when restarting on X11 [Marco; #576]
* Fix mapping of touchscreens that don't report dimensions [Carlos; #581]
* Fix spurious idle signals that prevent session unblank [Jonas; !543]
* Misc. bug fixes and cleanups [Olivier, Marco, Carlos; !552, !557, #586]
Contributors:
Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Marco Trevisan (Treviño)
3.32.1
======
* Fix fallback app menu on wayland [Florian; #493]
* Fix elogind support [Tom; !491]
* Fix startup notifications not timing out [Carlos; #501]
* Fix keyboard accessibility toggle from keys
[Olivier, Carlos; !501, #529, !531]
* Fix touchscreen input on rotated displays [Carlos; #514]
* Work around hangul text input bug [Carlos; #1365]
* Fix blurry wallpaper scaling [Daniel; !505]
* Fix placement of window menu when using fractional scaling [Jan; #527]
* Fix repaint issues of offscreen effects on secondary monitors [Daniel; !511]
* Fix windows not getting focus after launch [Daniel; #505]
* Properly advertise support for 'underscan' property [Jonas; !507]
* Improve power-saving handling [Jonas; !506]
* Fix moving windows by super+touch [Jonas D.; !495]
* Misc. bug fixes and cleanups [Benjamin, Florian, Adam, Marco, Pablo,
Erik, Jonas, Heiher, Pekka, Daniel, Olivier, Carlos; !478, !475, !480,
!482, #490, !488, #491, #480, !477, !496, !492, !485, !515, !519, !521,
!216, !538, #541, #523]
Contributors:
Jonas Ådahl, Pablo Barciela, Benjamin Berg, Tom Briden, Jonas Dreßler,
Olivier Fourdan, Carlos Garnacho, Jan Alexander Steffens (heftig), Heiher,
Adam Jackson, Erik Kurzinger, Florian Müllner, Pekka Paalanen,
Marco Trevisan (Treviño), Daniel van Vugt
Translators:
Khaled Hosny [ar], Goran Vidović [hr], Daniel Mustieles [es]
3.32.0
======
* Fix deadlock when cancelling a theme sound [Andrea; !474]

View File

@@ -855,14 +855,6 @@ emulate_pointer_motion (ClutterInputDeviceEvdev *device,
clutter_virtual_input_device_notify_relative_motion (device->mousekeys_virtual_device,
time_us, dx_motion, dy_motion);
}
static gboolean
is_numlock_active (ClutterInputDeviceEvdev *device)
{
ClutterSeatEvdev *seat = device->seat;
return xkb_state_mod_name_is_active (seat->xkb,
"Mod2",
XKB_STATE_MODS_LOCKED);
}
static void
enable_mousekeys (ClutterInputDeviceEvdev *device)
@@ -1021,10 +1013,6 @@ handle_mousekeys_press (ClutterEvent *event,
if (!(event->key.flags & CLUTTER_EVENT_FLAG_SYNTHETIC))
stop_mousekeys_move (device);
/* Do not handle mousekeys if NumLock is ON */
if (is_numlock_active (device))
return FALSE;
/* Button selection */
switch (event->key.keyval)
{
@@ -1096,10 +1084,6 @@ static gboolean
handle_mousekeys_release (ClutterEvent *event,
ClutterInputDeviceEvdev *device)
{
/* Do not handle mousekeys if NumLock is ON */
if (is_numlock_active (device))
return FALSE;
switch (event->key.keyval)
{
case XKB_KEY_KP_0:

View File

@@ -54,7 +54,6 @@
#include "clutter-main.h"
#include "clutter-private.h"
#include "clutter-settings-private.h"
#include "clutter-xkb-a11y-x11.h"
G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND)
@@ -277,20 +276,6 @@ clutter_backend_x11_create_device_manager (ClutterBackendX11 *backend_x11)
_clutter_backend_add_event_translator (backend, translator);
}
static void
on_keymap_state_change (ClutterKeymapX11 *keymap_x11,
gpointer data)
{
ClutterDeviceManager *device_manager = CLUTTER_DEVICE_MANAGER (data);
ClutterKbdA11ySettings kbd_a11y_settings;
/* On keymaps state change, just reapply the current settings, it'll
* take care of enabling/disabling mousekeys based on NumLock state.
*/
clutter_device_manager_get_kbd_a11y_settings (device_manager, &kbd_a11y_settings);
clutter_device_manager_x11_apply_kbd_a11y_settings (device_manager, &kbd_a11y_settings);
}
static void
clutter_backend_x11_create_keymap (ClutterBackendX11 *backend_x11)
{
@@ -307,11 +292,6 @@ clutter_backend_x11_create_keymap (ClutterBackendX11 *backend_x11)
backend = CLUTTER_BACKEND (backend_x11);
translator = CLUTTER_EVENT_TRANSLATOR (backend_x11->keymap);
_clutter_backend_add_event_translator (backend, translator);
g_signal_connect (backend_x11->keymap,
"state-changed",
G_CALLBACK (on_keymap_state_change),
backend->device_manager);
}
}

View File

@@ -241,13 +241,8 @@ clutter_device_manager_x11_apply_kbd_a11y_settings (ClutterDeviceManager *devi
}
/* mouse keys */
if (clutter_keymap_get_num_lock_state (CLUTTER_KEYMAP (backend_x11->keymap)))
{
/* Disable mousekeys when NumLock is ON */
desc->ctrls->enabled_ctrls &= ~(XkbMouseKeysMask | XkbMouseKeysAccelMask);
}
else if (set_xkb_ctrl (desc, kbd_a11y_settings->controls,
CLUTTER_A11Y_MOUSE_KEYS_ENABLED, XkbMouseKeysMask | XkbMouseKeysAccelMask))
if (set_xkb_ctrl (desc, kbd_a11y_settings->controls,
CLUTTER_A11Y_MOUSE_KEYS_ENABLED, XkbMouseKeysMask | XkbMouseKeysAccelMask))
{
gint mk_max_speed;
gint mk_accel_time;

View File

@@ -46,9 +46,6 @@
/* Building with SM support */
#mesondefine HAVE_SM
/* Building with startup notification support */
#mesondefine HAVE_STARTUP_NOTIFICATION
/* Path to Xwayland executable */
#mesondefine XWAYLAND_PATH

View File

@@ -1,5 +1,5 @@
project('mutter', 'c',
version: '3.32.2',
version: '3.32.0',
meson_version: '>= 0.48.0',
license: 'GPLv2+'
)
@@ -225,12 +225,6 @@ if have_pango_ft2
pangoft2_dep = dependency('pangoft2')
endif
have_startup_notification = get_option('startup_notification')
if have_startup_notification
libstartup_notification_dep = dependency('libstartup-notification-1.0',
version: libstartup_notification_req)
endif
have_remote_desktop = get_option('remote_desktop')
if have_remote_desktop
libpipewire_dep = dependency('libpipewire-0.2', version: libpipewire_req)
@@ -334,7 +328,6 @@ cdata.set('HAVE_WAYLAND_EGLSTREAM', have_wayland_eglstream)
cdata.set('HAVE_LIBGUDEV', have_libgudev)
cdata.set('HAVE_LIBWACOM', have_libwacom)
cdata.set('HAVE_SM', have_sm)
cdata.set('HAVE_STARTUP_NOTIFICATION', have_startup_notification)
cdata.set('HAVE_INTROSPECTION', have_introspection)
xkb_base = xkeyboard_config_dep.get_pkgconfig_variable('xkb_base')
@@ -399,7 +392,6 @@ output = [
' gudev.................... ' + have_libgudev.to_string(),
' Wacom.................... ' + have_libwacom.to_string(),
' SM....................... ' + have_sm.to_string(),
' Startup notification..... ' + have_startup_notification.to_string(),
' Introspection............ ' + have_introspection.to_string(),
'',
' Tests:',

View File

@@ -316,18 +316,11 @@ idle_monitor_dispatch_timeout (GSource *source,
gpointer user_data)
{
MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) user_data;
int64_t now;
int64_t ready_time;
now = g_source_get_time (source);
ready_time = g_source_get_ready_time (source);
if (ready_time > now)
return G_SOURCE_CONTINUE;
_meta_idle_monitor_watch_fire (watch);
g_source_set_ready_time (watch->timeout_source, -1);
return G_SOURCE_CONTINUE;
return TRUE;
}
static GSourceFuncs idle_monitor_source_funcs = {

View File

@@ -381,7 +381,7 @@ guess_candidates (MetaInputMapper *mapper,
info->candidates[META_MATCH_SIZE] = matched_monitor;
}
if (input->builtin || best == N_OUTPUT_MATCHES)
if (input->builtin)
{
best = MIN (best, META_MATCH_IS_BUILTIN);
find_builtin_output (mapper, &info->candidates[META_MATCH_IS_BUILTIN]);

View File

@@ -1216,7 +1216,7 @@ load_keyboard_a11y_settings (MetaInputSettings *input_settings,
ClutterInputDevice *device)
{
MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings);
ClutterKbdA11ySettings kbd_a11y_settings = { 0 };
ClutterKbdA11ySettings kbd_a11y_settings;
ClutterInputDevice *core_keyboard;
guint i;

View File

@@ -18,9 +18,6 @@ struct _MetaCompositor
guint pre_paint_func_id;
guint post_paint_func_id;
guint stage_presented_id;
guint stage_after_paint_id;
gint64 server_time_query_time;
gint64 server_time_offset;

View File

@@ -92,10 +92,6 @@ on_presented (ClutterStage *stage,
ClutterFrameInfo *frame_info,
MetaCompositor *compositor);
static void
on_top_window_actor_destroyed (MetaWindowActor *window_actor,
MetaCompositor *compositor);
static gboolean
is_modal (MetaDisplay *display)
{
@@ -135,31 +131,9 @@ meta_switch_workspace_completed (MetaCompositor *compositor)
void
meta_compositor_destroy (MetaCompositor *compositor)
{
g_signal_handler_disconnect (compositor->stage,
compositor->stage_after_paint_id);
g_signal_handler_disconnect (compositor->stage,
compositor->stage_presented_id);
compositor->stage_after_paint_id = 0;
compositor->stage_presented_id = 0;
compositor->stage = NULL;
clutter_threads_remove_repaint_func (compositor->pre_paint_func_id);
clutter_threads_remove_repaint_func (compositor->post_paint_func_id);
if (compositor->top_window_actor)
{
g_signal_handlers_disconnect_by_func (compositor->top_window_actor,
on_top_window_actor_destroyed,
compositor);
compositor->top_window_actor = NULL;
}
g_clear_pointer (&compositor->window_group, clutter_actor_destroy);
g_clear_pointer (&compositor->top_window_group, clutter_actor_destroy);
g_clear_pointer (&compositor->feedback_group, clutter_actor_destroy);
g_clear_pointer (&compositor->windows, g_list_free);
if (compositor->have_x11_sync_object)
meta_sync_ring_destroy ();
}
@@ -529,10 +503,9 @@ meta_compositor_manage (MetaCompositor *compositor)
compositor->stage = meta_backend_get_stage (backend);
compositor->stage_presented_id =
g_signal_connect (compositor->stage, "presented",
G_CALLBACK (on_presented),
compositor);
g_signal_connect (compositor->stage, "presented",
G_CALLBACK (on_presented),
compositor);
/* We use connect_after() here to accomodate code in GNOME Shell that,
* when benchmarking drawing performance, connects to ::after-paint
@@ -542,9 +515,8 @@ meta_compositor_manage (MetaCompositor *compositor)
* connections to ::after-paint, connect() vs. connect_after() doesn't
* matter.
*/
compositor->stage_after_paint_id =
g_signal_connect_after (compositor->stage, "after-paint",
G_CALLBACK (after_stage_paint), compositor);
g_signal_connect_after (CLUTTER_STAGE (compositor->stage), "after-paint",
G_CALLBACK (after_stage_paint), compositor);
clutter_stage_set_sync_delay (CLUTTER_STAGE (compositor->stage), META_SYNC_DELAY);

View File

@@ -31,10 +31,6 @@
#include <X11/extensions/sync.h>
#include <X11/Xlib.h>
#ifdef HAVE_STARTUP_NOTIFICATION
#include <libsn/sn.h>
#endif
#include "clutter/clutter.h"
#include "core/keybindings-private.h"
#include "core/meta-gesture-tracker-private.h"

View File

@@ -2236,7 +2236,7 @@ meta_display_get_tab_list (MetaDisplay *display,
MetaWindow *l_window = w->data;
if (l_window->wm_state_demands_attention &&
!meta_window_located_on_workspace (l_window, workspace) &&
l_window->workspace != workspace &&
IN_TAB_CHAIN (l_window, type))
tab_list = g_list_prepend (tab_list, l_window);
}

View File

@@ -118,11 +118,6 @@ static void prefs_changed_callback (MetaPreference pref,
static void
meta_print_compilation_info (void)
{
#ifdef HAVE_STARTUP_NOTIFICATION
meta_verbose ("Compiled with startup notification\n");
#else
meta_verbose ("Compiled without startup notification\n");
#endif
}
/**

View File

@@ -110,24 +110,25 @@ static void
meta_launch_context_constructed (GObject *object)
{
MetaLaunchContext *context = META_LAUNCH_CONTEXT (object);
const char *x11_display, *wayland_display;
G_OBJECT_CLASS (meta_launch_context_parent_class)->constructed (object);
x11_display = getenv ("DISPLAY");
wayland_display = getenv ("WAYLAND_DISPLAY");
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"));
}
if (x11_display)
{
g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context),
"DISPLAY", x11_display);
}
static gchar *
create_startup_notification_id (uint32_t timestamp)
{
gchar *uuid, *id;
if (wayland_display)
{
g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context),
"WAYLAND_DISPLAY", wayland_display);
}
uuid = g_uuid_string_random ();
id = g_strdup_printf ("%s_TIME%u", uuid, timestamp);
g_free (uuid);
return id;
}
static gchar *
@@ -136,34 +137,19 @@ meta_launch_context_get_startup_notify_id (GAppLaunchContext *launch_context,
GList *files)
{
MetaLaunchContext *context = META_LAUNCH_CONTEXT (launch_context);
MetaDisplay *display = context->display;
int workspace_idx = -1;
char *startup_id = NULL;
if (context->workspace)
workspace_idx = meta_workspace_index (context->workspace);
if (display->x11_display)
{
/* If there is a X11 display, we prefer going entirely through
* libsn, as SnMonitor expects to keep a view of the full lifetime
* of the startup sequence. We can't avoid it when launching and
* expect that a "remove" message from a X11 client will be handled.
*/
startup_id =
meta_x11_startup_notification_launch (display->x11_display,
info,
context->timestamp,
workspace_idx);
}
if (!startup_id)
{
const char *application_id = NULL;
MetaStartupNotification *sn;
MetaStartupSequence *seq;
startup_id = g_uuid_string_random ();
startup_id = create_startup_notification_id (context->timestamp);
/* Fallback through inserting our own startup sequence, this
* will be enough for wayland clients.

View File

@@ -55,12 +55,6 @@ if have_libgudev
]
endif
if have_startup_notification
mutter_pkg_private_deps += [
libstartup_notification_dep,
]
endif
if have_libwacom
mutter_pkg_private_deps += [
libwacom_dep,

View File

@@ -26,266 +26,301 @@
#include "meta/meta-x11-errors.h"
#include "x11/meta-x11-display-private.h"
#ifdef HAVE_STARTUP_NOTIFICATION
#define MAX_MESSAGE_LENGTH 4096
#define CLIENT_MESSAGE_DATA_LENGTH 20
enum
{
PROP_SEQ_X11_0,
PROP_SEQ_X11_SEQ,
N_SEQ_X11_PROPS
};
struct _MetaStartupSequenceX11
{
MetaStartupSequence parent_instance;
SnStartupSequence *seq;
MESSAGE_TYPE_NEW,
MESSAGE_TYPE_REMOVE,
};
struct _MetaX11StartupNotification
{
SnDisplay *sn_display;
SnMonitorContext *sn_context;
Atom atom_net_startup_info_begin;
Atom atom_net_startup_info;
GHashTable *messages;
MetaX11Display *x11_display;
};
static GParamSpec *seq_x11_props[N_SEQ_X11_PROPS];
G_DEFINE_TYPE (MetaStartupSequenceX11,
meta_startup_sequence_x11,
META_TYPE_STARTUP_SEQUENCE)
static void
meta_startup_sequence_x11_complete (MetaStartupSequence *seq)
typedef struct
{
MetaStartupSequenceX11 *seq_x11;
Window xwindow;
GString *data;
} StartupMessage;
seq_x11 = META_STARTUP_SEQUENCE_X11 (seq);
sn_startup_sequence_complete (seq_x11->seq);
static StartupMessage *
startup_message_new (Window window)
{
StartupMessage *message;
message = g_new0 (StartupMessage, 1);
message->xwindow = window;
message->data = g_string_new (NULL);
return message;
}
static void
meta_startup_sequence_x11_finalize (GObject *object)
startup_message_free (StartupMessage *message)
{
MetaStartupSequenceX11 *seq_x11;
seq_x11 = META_STARTUP_SEQUENCE_X11 (object);
sn_startup_sequence_unref (seq_x11->seq);
G_OBJECT_CLASS (meta_startup_sequence_x11_parent_class)->finalize (object);
g_string_free (message->data, TRUE);
g_free (message);
}
static void
meta_startup_sequence_x11_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
skip_whitespace (const gchar **str)
{
MetaStartupSequenceX11 *seq_x11;
while ((*str)[0] == ' ')
(*str)++;
}
seq_x11 = META_STARTUP_SEQUENCE_X11 (object);
static gchar *
parse_key (const gchar **str)
{
const gchar *start = *str;
switch (prop_id)
while (*str[0] != '\0' && *str[0] != '=')
(*str)++;
if (start == *str)
return NULL;
return g_strndup (start, *str - start);
}
static gchar *
parse_value (const gchar **str)
{
const gchar *end;
gboolean escaped = FALSE, quoted = FALSE;
GString *value;
end = *str;
value = g_string_new (NULL);
while (end[0] != '\0')
{
case PROP_SEQ_X11_SEQ:
seq_x11->seq = g_value_get_pointer (value);
sn_startup_sequence_ref (seq_x11->seq);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
if (escaped)
{
g_string_append_c (value, end[0]);
escaped = FALSE;
}
else
{
if (!quoted && end[0] == ' ')
break;
else if (end[0] == '"')
quoted = !quoted;
else if (end[0] == '\\')
escaped = TRUE;
else
g_string_append_c (value, end[0]);
}
static void
meta_startup_sequence_x11_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaStartupSequenceX11 *seq_x11;
seq_x11 = META_STARTUP_SEQUENCE_X11 (object);
switch (prop_id)
{
case PROP_SEQ_X11_SEQ:
g_value_set_pointer (value, seq_x11->seq);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_startup_sequence_x11_init (MetaStartupSequenceX11 *seq)
{
}
static void
meta_startup_sequence_x11_class_init (MetaStartupSequenceX11Class *klass)
{
MetaStartupSequenceClass *seq_class;
GObjectClass *object_class;
seq_class = META_STARTUP_SEQUENCE_CLASS (klass);
seq_class->complete = meta_startup_sequence_x11_complete;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_startup_sequence_x11_finalize;
object_class->set_property = meta_startup_sequence_x11_set_property;
object_class->get_property = meta_startup_sequence_x11_get_property;
seq_x11_props[PROP_SEQ_X11_SEQ] =
g_param_spec_pointer ("seq",
"Sequence",
"Sequence",
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, N_SEQ_X11_PROPS,
seq_x11_props);
}
static MetaStartupSequence *
meta_startup_sequence_x11_new (SnStartupSequence *seq)
{
gint64 timestamp;
timestamp = sn_startup_sequence_get_timestamp (seq);
return g_object_new (META_TYPE_STARTUP_SEQUENCE_X11,
"id", sn_startup_sequence_get_id (seq),
"icon-name", sn_startup_sequence_get_icon_name (seq),
"application-id", sn_startup_sequence_get_application_id (seq),
"wmclass", sn_startup_sequence_get_wmclass (seq),
"name", sn_startup_sequence_get_name (seq),
"workspace", sn_startup_sequence_get_workspace (seq),
"timestamp", timestamp,
"seq", seq,
NULL);
}
static void
sn_error_trap_push (SnDisplay *sn_display,
Display *xdisplay)
{
MetaDisplay *display;
display = meta_display_for_x_display (xdisplay);
if (display != NULL)
meta_x11_error_trap_push (display->x11_display);
}
static void
sn_error_trap_pop (SnDisplay *sn_display,
Display *xdisplay)
{
MetaDisplay *display;
display = meta_display_for_x_display (xdisplay);
if (display != NULL)
meta_x11_error_trap_pop (display->x11_display);
}
static void
meta_startup_notification_sn_event (SnMonitorEvent *event,
void *user_data)
{
MetaX11Display *x11_display = user_data;
MetaStartupNotification *sn = x11_display->display->startup_notification;
MetaStartupSequence *seq;
SnStartupSequence *sequence;
sequence = sn_monitor_event_get_startup_sequence (event);
sn_startup_sequence_ref (sequence);
switch (sn_monitor_event_get_type (event))
{
case SN_MONITOR_EVENT_INITIATED:
{
const char *wmclass;
wmclass = sn_startup_sequence_get_wmclass (sequence);
meta_topic (META_DEBUG_STARTUP,
"Received startup initiated for %s wmclass %s\n",
sn_startup_sequence_get_id (sequence),
wmclass ? wmclass : "(unset)");
seq = meta_startup_sequence_x11_new (sequence);
meta_startup_notification_add_sequence (sn, seq);
g_object_unref (seq);
}
break;
case SN_MONITOR_EVENT_COMPLETED:
{
meta_topic (META_DEBUG_STARTUP,
"Received startup completed for %s\n",
sn_startup_sequence_get_id (sequence));
seq = meta_startup_notification_lookup_sequence (sn, sn_startup_sequence_get_id (sequence));
if (seq)
{
meta_startup_sequence_complete (seq);
meta_startup_notification_remove_sequence (sn, seq);
}
}
break;
case SN_MONITOR_EVENT_CHANGED:
meta_topic (META_DEBUG_STARTUP,
"Received startup changed for %s\n",
sn_startup_sequence_get_id (sequence));
break;
case SN_MONITOR_EVENT_CANCELED:
meta_topic (META_DEBUG_STARTUP,
"Received startup canceled for %s\n",
sn_startup_sequence_get_id (sequence));
break;
end++;
}
sn_startup_sequence_unref (sequence);
*str = end;
if (value->len == 0)
{
g_string_free (value, TRUE);
return NULL;
}
return g_string_free (value, FALSE);
}
static gboolean
startup_message_parse (StartupMessage *message,
int *type,
gchar **id,
GHashTable **data)
{
const gchar *str = message->data->str;
if (strncmp (str, "new:", 4) == 0)
{
*type = MESSAGE_TYPE_NEW;
str += 4;
}
else if (strncmp (str, "remove:", 7) == 0)
{
*type = MESSAGE_TYPE_REMOVE;
str += 7;
}
else
{
return FALSE;
}
*data = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
g_free);
while (str[0])
{
gchar *key, *value;
skip_whitespace (&str);
key = parse_key (&str);
if (!key)
break;
str++;
value = parse_value (&str);
if (!value)
{
g_free (key);
break;
}
g_hash_table_insert (*data, key, value);
}
*id = g_strdup (g_hash_table_lookup (*data, "ID"));
return TRUE;
}
static gboolean
startup_message_add_data (StartupMessage *message,
const gchar *data)
{
int len;
len = strnlen (data, CLIENT_MESSAGE_DATA_LENGTH);
g_string_append_len (message->data, data, len);
return (message->data->len > MAX_MESSAGE_LENGTH ||
len < CLIENT_MESSAGE_DATA_LENGTH);
}
#endif
void
meta_x11_startup_notification_init (MetaX11Display *x11_display)
{
#ifdef HAVE_STARTUP_NOTIFICATION
MetaX11StartupNotification *x11_sn;
x11_sn = g_new0 (MetaX11StartupNotification, 1);
x11_sn->sn_display = sn_display_new (x11_display->xdisplay,
sn_error_trap_push,
sn_error_trap_pop);
x11_sn->sn_context =
sn_monitor_context_new (x11_sn->sn_display,
meta_x11_display_get_screen_number (x11_display),
meta_startup_notification_sn_event,
x11_display,
NULL);
x11_sn->atom_net_startup_info_begin = XInternAtom (x11_display->xdisplay,
"_NET_STARTUP_INFO_BEGIN",
False);
x11_sn->atom_net_startup_info = XInternAtom (x11_display->xdisplay,
"_NET_STARTUP_INFO",
False);
x11_sn->messages = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) startup_message_free);
x11_sn->x11_display = x11_display;
x11_display->startup_notification = x11_sn;
#endif
}
void
meta_x11_startup_notification_release (MetaX11Display *x11_display)
{
#ifdef HAVE_STARTUP_NOTIFICATION
MetaX11StartupNotification *x11_sn = x11_display->startup_notification;
x11_display->startup_notification = NULL;
if (x11_sn)
{
sn_monitor_context_unref (x11_sn->sn_context);
sn_display_unref (x11_sn->sn_display);
g_hash_table_unref (x11_sn->messages);
g_free (x11_sn);
}
#endif
}
static void
handle_message (MetaX11StartupNotification *x11_sn,
StartupMessage *message)
{
MetaStartupNotification *sn = x11_sn->x11_display->display->startup_notification;
MetaStartupSequence *seq;
GHashTable *data;
char *id;
int type;
if (message->data->len <= MAX_MESSAGE_LENGTH &&
g_utf8_validate (message->data->str, -1, NULL) &&
startup_message_parse (message, &type, &id, &data))
{
if (type == MESSAGE_TYPE_NEW)
{
uint64_t timestamp = 0;
int workspace = -1;
if (g_hash_table_contains (data, "DESKTOP"))
workspace = atoi (g_hash_table_lookup (data, "DESKTOP"));
if (g_hash_table_contains (data, "TIMESTAMP"))
timestamp = g_ascii_strtoull (g_hash_table_lookup (data, "TIMESTAMP"), NULL, 10);
seq = g_object_new (META_TYPE_STARTUP_SEQUENCE,
"id", id,
"icon-name", g_hash_table_lookup (data, "ICON_NAME"),
"application-id", g_hash_table_lookup (data, "APPLICATION_ID"),
"wmclass", g_hash_table_lookup (data, "WMCLASS"),
"name", g_hash_table_lookup (data, "NAME"),
"workspace", workspace,
"timestamp", timestamp,
NULL);
meta_topic (META_DEBUG_STARTUP,
"Received startup initiated for %s wmclass %s\n",
id, (gchar*) g_hash_table_lookup (data, "WMCLASS"));
meta_startup_notification_add_sequence (sn, seq);
g_object_unref (seq);
}
else if (type == MESSAGE_TYPE_REMOVE)
{
meta_topic (META_DEBUG_STARTUP,
"Received startup completed for %s\n", id);
seq = meta_startup_notification_lookup_sequence (sn, id);
if (seq)
{
meta_startup_sequence_complete (seq);
meta_startup_notification_remove_sequence (sn, seq);
}
}
g_hash_table_unref (data);
g_free (id);
}
g_hash_table_remove (x11_sn->messages, GINT_TO_POINTER (message->xwindow));
}
static gboolean
handle_startup_notification_event (MetaX11StartupNotification *x11_sn,
XClientMessageEvent *client_event)
{
StartupMessage *message;
if (client_event->message_type == x11_sn->atom_net_startup_info_begin)
{
message = startup_message_new (client_event->window);
g_hash_table_insert (x11_sn->messages,
GINT_TO_POINTER (client_event->window),
message);
if (startup_message_add_data (message, client_event->data.b))
handle_message (x11_sn, message);
return TRUE;
}
else if (client_event->message_type == x11_sn->atom_net_startup_info)
{
message = g_hash_table_lookup (x11_sn->messages,
GINT_TO_POINTER (client_event->window));
if (message)
{
if (startup_message_add_data (message, client_event->data.b))
handle_message (x11_sn, message);
return TRUE;
}
}
return FALSE;
}
gboolean
@@ -297,74 +332,8 @@ meta_x11_startup_notification_handle_xevent (MetaX11Display *x11_display,
if (!x11_sn)
return FALSE;
return sn_display_process_event (x11_sn->sn_display, xevent);
}
typedef void (* SetAppIdFunc) (SnLauncherContext *context,
const char *app_id);
gchar *
meta_x11_startup_notification_launch (MetaX11Display *x11_display,
GAppInfo *app_info,
uint32_t timestamp,
int workspace)
{
gchar *startup_id = NULL;
#ifdef HAVE_STARTUP_NOTIFICATION
MetaX11StartupNotification *x11_sn = x11_display->startup_notification;
SnLauncherContext *sn_launcher;
int screen;
screen = meta_x11_display_get_screen_number (x11_display);
sn_launcher = sn_launcher_context_new (x11_sn->sn_display, screen);
sn_launcher_context_set_name (sn_launcher, g_app_info_get_name (app_info));
sn_launcher_context_set_workspace (sn_launcher, workspace);
sn_launcher_context_set_binary_name (sn_launcher,
g_app_info_get_executable (app_info));
if (G_IS_DESKTOP_APP_INFO (app_info))
{
const char *application_id;
SetAppIdFunc func = NULL;
GModule *self;
application_id =
g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (app_info));
self = g_module_open (NULL, G_MODULE_BIND_MASK);
/* This here is a terrible workaround to bypass a libsn bug that is not
* likely to get fixed at this point.
* sn_launcher_context_set_application_id is correctly defined in the
* sn-launcher.h file, but it's mistakenly called
* sn_launcher_set_application_id in the C file.
*
* We look up the symbol instead, but still prefer the correctly named
* function, if one were ever to be added.
*/
if (!g_module_symbol (self, "sn_launcher_context_set_application_id",
(gpointer *) &func))
{
g_module_symbol (self, "sn_launcher_set_application_id",
(gpointer *) &func);
}
if (func)
func (sn_launcher, application_id);
g_module_close (self);
}
sn_launcher_context_initiate (sn_launcher,
g_get_prgname (),
g_app_info_get_name (app_info),
timestamp);
startup_id = g_strdup (sn_launcher_context_get_startup_id (sn_launcher));
/* Fire and forget, we have a SnMonitor in addition */
sn_launcher_context_unref (sn_launcher);
#endif /* HAVE_STARTUP_NOTIFICATION */
return startup_id;
if (xevent->xany.type != ClientMessage)
return FALSE;
return handle_startup_notification_event (x11_sn, &xevent->xclient);
}

View File

@@ -36,9 +36,4 @@ void meta_x11_startup_notification_release (MetaX11Display *x11_display);
gboolean meta_x11_startup_notification_handle_xevent (MetaX11Display *x11_display,
XEvent *xevent);
gchar * meta_x11_startup_notification_launch (MetaX11Display *x11_display,
GAppInfo *app_info,
uint32_t timestamp,
int workspace);
#endif /* META_X11_STARTUP_NOTIFICATION_H */