Compare commits
2 Commits
3.32.2
...
wip/carlos
Author | SHA1 | Date | |
---|---|---|---|
![]() |
aea56b6f55 | ||
![]() |
217bd31531 |
41
NEWS
41
NEWS
@@ -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]
|
||||
|
@@ -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:
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
||||
|
10
meson.build
10
meson.build
@@ -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:',
|
||||
|
@@ -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 = {
|
||||
|
@@ -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]);
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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"
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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.
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
Reference in New Issue
Block a user