diff --git a/src/Makefile.am b/src/Makefile.am index ca2483b8b..8dedc81f9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -198,6 +198,8 @@ libmutter_la_SOURCES = \ core/screen.c \ core/screen-private.h \ meta/screen.h \ + core/startup-notification.c \ + core/startup-notification-private.h \ meta/types.h \ core/restart.c \ core/stack.c \ diff --git a/src/core/display-private.h b/src/core/display-private.h index 29a3ccf10..531c6f70c 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -35,6 +35,7 @@ #include #include #include "keybindings-private.h" +#include "startup-notification-private.h" #include "meta-gesture-tracker-private.h" #include #include @@ -276,9 +277,8 @@ struct _MetaDisplay int xinput_event_base; int xinput_opcode; -#ifdef HAVE_STARTUP_NOTIFICATION - SnDisplay *sn_display; -#endif + MetaStartupNotification *startup_notification; + int xsync_event_base; int xsync_error_base; int shape_event_base; diff --git a/src/core/display.c b/src/core/display.c index dbfbc68f0..f1933598d 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -400,28 +400,6 @@ meta_display_remove_pending_pings_for_window (MetaDisplay *display, } -#ifdef HAVE_STARTUP_NOTIFICATION -static void -sn_error_trap_push (SnDisplay *sn_display, - Display *xdisplay) -{ - MetaDisplay *display; - display = meta_display_for_x_display (xdisplay); - if (display != NULL) - meta_error_trap_push (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_error_trap_pop (display); -} -#endif - static void enable_compositor (MetaDisplay *display) { @@ -527,6 +505,20 @@ gesture_tracker_state_changed (MetaGestureTracker *tracker, } } +static void +on_startup_notification_changed (MetaStartupNotification *sn, + gpointer sequence, + MetaDisplay *display) +{ + if (!display->screen) + return; + + g_slist_free (display->screen->startup_sequences); + display->screen->startup_sequences = + meta_startup_notification_get_sequences (display->startup_notification); + g_signal_emit_by_name (display->screen, "startup-sequence-changed", sequence); +} + /** * meta_display_open: * @@ -630,12 +622,6 @@ meta_display_open (void) display->screen = NULL; -#ifdef HAVE_STARTUP_NOTIFICATION - display->sn_display = sn_display_new (display->xdisplay, - sn_error_trap_push, - sn_error_trap_pop); -#endif - /* Get events */ meta_display_init_events (display); meta_display_init_events_x11 (display); @@ -916,6 +902,10 @@ meta_display_open (void) display->screen = screen; + display->startup_notification = meta_startup_notification_get (display); + g_signal_connect (display->startup_notification, "changed", + G_CALLBACK (on_startup_notification_changed), display); + meta_screen_init_workspaces (screen); enable_compositor (display); @@ -1100,6 +1090,7 @@ meta_display_close (MetaDisplay *display, meta_display_remove_autoraise_callback (display); + g_clear_object (&display->startup_notification); g_clear_object (&display->gesture_tracker); if (display->focus_timeout_id) @@ -1112,14 +1103,6 @@ meta_display_close (MetaDisplay *display, meta_screen_free (display->screen, timestamp); -#ifdef HAVE_STARTUP_NOTIFICATION - if (display->sn_display) - { - sn_display_unref (display->sn_display); - display->sn_display = NULL; - } -#endif - /* Must be after all calls to meta_window_unmanage() since they * unregister windows */ diff --git a/src/core/screen-private.h b/src/core/screen-private.h index 438d1f4cc..d0fc318a4 100644 --- a/src/core/screen-private.h +++ b/src/core/screen-private.h @@ -85,11 +85,7 @@ struct _MetaScreen /* Cache the current monitor */ int last_monitor_index; -#ifdef HAVE_STARTUP_NOTIFICATION - SnMonitorContext *sn_context; GSList *startup_sequences; - guint startup_sequence_timeout; -#endif Window wm_cm_selection_window; guint work_area_later; diff --git a/src/core/screen.c b/src/core/screen.c index 19f7db5e2..934b3af35 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -71,11 +71,6 @@ static void prefs_changed_callback (MetaPreference pref, static void set_desktop_geometry_hint (MetaScreen *screen); static void set_desktop_viewport_hint (MetaScreen *screen); -#ifdef HAVE_STARTUP_NOTIFICATION -static void meta_screen_sn_event (SnMonitorEvent *event, - void *user_data); -#endif - static void on_monitors_changed (MetaMonitorManager *manager, MetaScreen *screen); @@ -730,17 +725,6 @@ meta_screen_new (MetaDisplay *display, meta_prefs_add_listener (prefs_changed_callback, screen); -#ifdef HAVE_STARTUP_NOTIFICATION - screen->sn_context = - sn_monitor_context_new (screen->display->sn_display, - screen->number, - meta_screen_sn_event, - screen, - NULL); - screen->startup_sequences = NULL; - screen->startup_sequence_timeout = 0; -#endif - meta_verbose ("Added screen %d ('%s') root 0x%lx\n", screen->number, screen->screen_name, screen->xroot); @@ -800,24 +784,6 @@ meta_screen_free (MetaScreen *screen, meta_screen_ungrab_keys (screen); -#ifdef HAVE_STARTUP_NOTIFICATION - g_slist_foreach (screen->startup_sequences, - (GFunc) sn_startup_sequence_unref, NULL); - g_slist_free (screen->startup_sequences); - screen->startup_sequences = NULL; - - if (screen->startup_sequence_timeout != 0) - { - g_source_remove (screen->startup_sequence_timeout); - screen->startup_sequence_timeout = 0; - } - if (screen->sn_context) - { - sn_monitor_context_unref (screen->sn_context); - screen->sn_context = NULL; - } -#endif - meta_ui_free (screen->ui); meta_stack_free (screen->stack); @@ -2538,208 +2504,6 @@ meta_screen_unshow_desktop (MetaScreen *screen) meta_screen_update_showing_desktop_hint (screen); } - -#ifdef HAVE_STARTUP_NOTIFICATION -static gboolean startup_sequence_timeout (void *data); - -static void -update_startup_feedback (MetaScreen *screen) -{ - if (screen->startup_sequences != NULL) - { - meta_topic (META_DEBUG_STARTUP, - "Setting busy cursor\n"); - meta_screen_set_cursor (screen, META_CURSOR_BUSY); - } - else - { - meta_topic (META_DEBUG_STARTUP, - "Setting default cursor\n"); - meta_screen_set_cursor (screen, META_CURSOR_DEFAULT); - } -} - -static void -add_sequence (MetaScreen *screen, - SnStartupSequence *sequence) -{ - meta_topic (META_DEBUG_STARTUP, - "Adding sequence %s\n", - sn_startup_sequence_get_id (sequence)); - sn_startup_sequence_ref (sequence); - screen->startup_sequences = g_slist_prepend (screen->startup_sequences, - sequence); - - /* our timeout just polls every second, instead of bothering - * to compute exactly when we may next time out - */ - if (screen->startup_sequence_timeout == 0) - { - screen->startup_sequence_timeout = g_timeout_add_seconds (1, - startup_sequence_timeout, - screen); - g_source_set_name_by_id (screen->startup_sequence_timeout, - "[mutter] startup_sequence_timeout"); - } - - update_startup_feedback (screen); -} - -static void -remove_sequence (MetaScreen *screen, - SnStartupSequence *sequence) -{ - meta_topic (META_DEBUG_STARTUP, - "Removing sequence %s\n", - sn_startup_sequence_get_id (sequence)); - - screen->startup_sequences = g_slist_remove (screen->startup_sequences, - sequence); - - if (screen->startup_sequences == NULL && - screen->startup_sequence_timeout != 0) - { - g_source_remove (screen->startup_sequence_timeout); - screen->startup_sequence_timeout = 0; - } - - update_startup_feedback (screen); - - sn_startup_sequence_unref (sequence); -} - -typedef struct -{ - GSList *list; - GTimeVal now; -} CollectTimedOutData; - -/* This should be fairly long, as it should never be required unless - * apps or .desktop files are buggy, and it's confusing if - * OpenOffice or whatever seems to stop launching - people - * might decide they need to launch it again. - */ -#define STARTUP_TIMEOUT 15000 - -static void -collect_timed_out_foreach (void *element, - void *data) -{ - CollectTimedOutData *ctod = data; - SnStartupSequence *sequence = element; - long tv_sec, tv_usec; - double elapsed; - - sn_startup_sequence_get_last_active_time (sequence, &tv_sec, &tv_usec); - - elapsed = - ((((double)ctod->now.tv_sec - tv_sec) * G_USEC_PER_SEC + - (ctod->now.tv_usec - tv_usec))) / 1000.0; - - meta_topic (META_DEBUG_STARTUP, - "Sequence used %g seconds vs. %g max: %s\n", - elapsed, (double) STARTUP_TIMEOUT, - sn_startup_sequence_get_id (sequence)); - - if (elapsed > STARTUP_TIMEOUT) - ctod->list = g_slist_prepend (ctod->list, sequence); -} - -static gboolean -startup_sequence_timeout (void *data) -{ - MetaScreen *screen = data; - CollectTimedOutData ctod; - GSList *l; - - ctod.list = NULL; - g_get_current_time (&ctod.now); - g_slist_foreach (screen->startup_sequences, - collect_timed_out_foreach, - &ctod); - - for (l = ctod.list; l != NULL; l = l->next) - { - SnStartupSequence *sequence = l->data; - - meta_topic (META_DEBUG_STARTUP, - "Timed out sequence %s\n", - sn_startup_sequence_get_id (sequence)); - - sn_startup_sequence_complete (sequence); - } - - g_slist_free (ctod.list); - - if (screen->startup_sequences != NULL) - { - return TRUE; - } - else - { - /* remove */ - screen->startup_sequence_timeout = 0; - return FALSE; - } -} - -static void -meta_screen_sn_event (SnMonitorEvent *event, - void *user_data) -{ - MetaScreen *screen; - SnStartupSequence *sequence; - - screen = user_data; - - 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)"); - add_sequence (screen, sequence); - } - break; - - case SN_MONITOR_EVENT_COMPLETED: - { - meta_topic (META_DEBUG_STARTUP, - "Received startup completed for %s\n", - sn_startup_sequence_get_id (sequence)); - remove_sequence (screen, - sn_monitor_event_get_startup_sequence (event)); - } - 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; - } - - g_signal_emit (G_OBJECT (screen), screen_signals[STARTUP_SEQUENCE_CHANGED], 0, sequence); - - sn_startup_sequence_unref (sequence); -} - /** * meta_screen_get_startup_sequences: (skip) * @screen: @@ -2751,7 +2515,6 @@ meta_screen_get_startup_sequences (MetaScreen *screen) { return screen->startup_sequences; } -#endif /* Sets the initial_timestamp and initial_workspace properties * of a window according to information given us by the diff --git a/src/core/startup-notification-private.h b/src/core/startup-notification-private.h new file mode 100644 index 000000000..747153d5e --- /dev/null +++ b/src/core/startup-notification-private.h @@ -0,0 +1,48 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2001, 2002 Havoc Pennington + * Copyright (C) 2002, 2003 Red Hat Inc. + * Some ICCCM manager selection code derived from fvwm2, + * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team + * Copyright (C) 2003 Rob Adams + * Copyright (C) 2004-2006 Elijah Newren + * + * 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, see . + */ + +#ifndef META_STARTUP_NOTIFICATION_PRIVATE_H +#define META_STARTUP_NOTIFICATION_PRIVATE_H + +#include "display-private.h" + +#define META_TYPE_STARTUP_NOTIFICATION (meta_startup_notification_get_type ()) + +G_DECLARE_FINAL_TYPE (MetaStartupNotification, + meta_startup_notification, + META, STARTUP_NOTIFICATION, + GObject) + +MetaStartupNotification * + meta_startup_notification_get (MetaDisplay *display); + +gboolean meta_startup_notification_handle_xevent (MetaStartupNotification *sn, + XEvent *xevent); + +void meta_startup_notification_remove_sequence (MetaStartupNotification *sn, + const gchar *id); + +GSList * meta_startup_notification_get_sequences (MetaStartupNotification *sn); + +#endif /* META_STARTUP_NOTIFICATION_PRIVATE_H */ diff --git a/src/core/startup-notification.c b/src/core/startup-notification.c new file mode 100644 index 000000000..cfe0242ae --- /dev/null +++ b/src/core/startup-notification.c @@ -0,0 +1,735 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2001, 2002 Havoc Pennington + * Copyright (C) 2002, 2003 Red Hat Inc. + * Some ICCCM manager selection code derived from fvwm2, + * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team + * Copyright (C) 2003 Rob Adams + * Copyright (C) 2004-2006 Elijah Newren + * + * 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, see . + */ + +#include + +#include + +#include +#include "display-private.h" +#include "screen-private.h" +#include "startup-notification-private.h" + +/* This should be fairly long, as it should never be required unless + * apps or .desktop files are buggy, and it's confusing if + * OpenOffice or whatever seems to stop launching - people + * might decide they need to launch it again. + */ +#define STARTUP_TIMEOUT 15000000 + +typedef struct _MetaStartupNotification MetaStartupNotification; +typedef struct _MetaStartupNotificationSequence MetaStartupNotificationSequence; +typedef struct _MetaStartupNotificationSequenceClass MetaStartupNotificationSequenceClass; +typedef struct _MetaStartupNotificationSequenceX11 MetaStartupNotificationSequenceX11; +typedef struct _MetaStartupNotificationSequenceX11Class MetaStartupNotificationSequenceX11Class; + +enum { + PROP_SN_0, + PROP_SN_DISPLAY, + N_SN_PROPS +}; + +enum { + PROP_SEQ_0, + PROP_SEQ_ID, + PROP_SEQ_TIMESTAMP, + N_SEQ_PROPS +}; + +enum { + SN_CHANGED, + N_SN_SIGNALS +}; + +static guint sn_signals[N_SN_SIGNALS]; +static GParamSpec *sn_props[N_SN_PROPS]; +static GParamSpec *seq_props[N_SEQ_PROPS]; + +typedef struct +{ + GSList *list; + gint64 now; +} CollectTimedOutData; + +struct _MetaStartupNotification +{ + GObject parent_instance; + MetaDisplay *display; + +#ifdef HAVE_STARTUP_NOTIFICATION + SnDisplay *sn_display; + SnMonitorContext *sn_context; +#endif + + GSList *startup_sequences; + guint startup_sequence_timeout; +}; + +struct _MetaStartupNotificationSequence { + GObject parent_instance; + gchar *id; + time_t timestamp; +}; + +struct _MetaStartupNotificationSequenceClass { + GObjectClass parent_class; + + void (* complete) (MetaStartupNotificationSequence *sequence); +}; + +GType meta_startup_notification_get_type (void) G_GNUC_CONST; +GType meta_startup_notification_sequence_get_type (void) G_GNUC_CONST; + +#define META_TYPE_STARTUP_NOTIFICATION_SEQUENCE (meta_startup_notification_sequence_get_type ()) +#define META_STARTUP_NOTIFICATION_SEQUENCE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE, MetaStartupNotificationSequence)) +#define META_STARTUP_NOTIFICATION_SEQUENCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE, MetaStartupNotificationSequenceClass)) +#define META_IS_STARTUP_NOTIFICATION_SEQUENCE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE)) +#define META_IS_STARTUP_NOTIFICATION_SEQUENCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE)) +#define META_STARTUP_NOTIFICATION_SEQUENCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE, MetaStartupNotificationSequenceClass)) + +G_DEFINE_TYPE (MetaStartupNotification, + meta_startup_notification, + G_TYPE_OBJECT) +G_DEFINE_TYPE (MetaStartupNotificationSequence, + meta_startup_notification_sequence, + G_TYPE_OBJECT) + +#ifdef HAVE_STARTUP_NOTIFICATION + +enum { + PROP_SEQ_X11_0, + PROP_SEQ_X11_SEQ, + N_SEQ_X11_PROPS +}; + +struct _MetaStartupNotificationSequenceX11 { + MetaStartupNotificationSequence parent_instance; + SnStartupSequence *seq; +}; + +struct _MetaStartupNotificationSequenceX11Class { + MetaStartupNotificationSequenceClass parent_class; +}; + +static GParamSpec *seq_x11_props[N_SEQ_X11_PROPS]; + +GType meta_startup_notification_sequence_x11_get_type (void) G_GNUC_CONST; + +#define META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11 (meta_startup_notification_sequence_x11_get_type ()) +#define META_STARTUP_NOTIFICATION_SEQUENCE_X11(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11, MetaStartupNotificationSequenceX11)) +#define META_STARTUP_NOTIFICATION_SEQUENCE_X11_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11, MetaStartupNotificationSequenceX11Class)) +#define META_IS_STARTUP_NOTIFICATION_SEQUENCE_X11(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11)) +#define META_IS_STARTUP_NOTIFICATION_SEQUENCE_X11_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11)) +#define META_STARTUP_NOTIFICATION_SEQUENCE_X11_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11, MetaStartupNotificationSequenceX11Class)) + +G_DEFINE_TYPE (MetaStartupNotificationSequenceX11, + meta_startup_notification_sequence_x11, + META_TYPE_STARTUP_NOTIFICATION_SEQUENCE) + +static void meta_startup_notification_ensure_timeout (MetaStartupNotification *sn); + +#endif + +static void +meta_startup_notification_update_feedback (MetaStartupNotification *sn) +{ + MetaScreen *screen = sn->display->screen; + + if (sn->startup_sequences != NULL) + { + meta_topic (META_DEBUG_STARTUP, + "Setting busy cursor\n"); + meta_screen_set_cursor (screen, META_CURSOR_BUSY); + } + else + { + meta_topic (META_DEBUG_STARTUP, + "Setting default cursor\n"); + meta_screen_set_cursor (screen, META_CURSOR_DEFAULT); + } +} + +static void +meta_startup_notification_sequence_init (MetaStartupNotificationSequence *seq) +{ +} + +static void +meta_startup_notification_sequence_finalize (GObject *object) +{ + MetaStartupNotificationSequence *seq; + + seq = META_STARTUP_NOTIFICATION_SEQUENCE (object); + g_free (seq->id); + G_OBJECT_CLASS (meta_startup_notification_sequence_parent_class)->finalize (object); +} + +static void +meta_startup_notification_sequence_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaStartupNotificationSequence *seq; + + seq = META_STARTUP_NOTIFICATION_SEQUENCE (object); + + switch (prop_id) + { + case PROP_SEQ_ID: + seq->id = g_value_dup_string (value); + break; + case PROP_SEQ_TIMESTAMP: + seq->timestamp = g_value_get_int64 (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_startup_notification_sequence_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaStartupNotificationSequence *seq; + + seq = META_STARTUP_NOTIFICATION_SEQUENCE (object); + + switch (prop_id) + { + case PROP_SEQ_ID: + g_value_set_string (value, seq->id); + break; + case PROP_SEQ_TIMESTAMP: + g_value_set_int64 (value, seq->timestamp); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_startup_notification_sequence_class_init (MetaStartupNotificationSequenceClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = meta_startup_notification_sequence_finalize; + object_class->set_property = meta_startup_notification_sequence_set_property; + object_class->get_property = meta_startup_notification_sequence_get_property; + + seq_props[PROP_SEQ_ID] = + g_param_spec_string ("id", + "ID", + "ID", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + seq_props[PROP_SEQ_TIMESTAMP] = + g_param_spec_int64 ("timestamp", + "Timestamp", + "Timestamp", + G_MININT64, G_MAXINT64, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, N_SEQ_PROPS, seq_props); +} + +#ifdef HAVE_STARTUP_NOTIFICATION +static void +meta_startup_notification_sequence_complete (MetaStartupNotificationSequence *seq) +{ + MetaStartupNotificationSequenceClass *klass; + + klass = META_STARTUP_NOTIFICATION_SEQUENCE_GET_CLASS (seq); + + if (klass->complete) + klass->complete (seq); +} + +static void +meta_startup_notification_sequence_x11_complete (MetaStartupNotificationSequence *seq) +{ + MetaStartupNotificationSequenceX11 *seq_x11; + + seq_x11 = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (seq); + sn_startup_sequence_complete (seq_x11->seq); +} + +static void +meta_startup_notification_sequence_x11_finalize (GObject *object) +{ + MetaStartupNotificationSequenceX11 *seq; + + seq = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (object); + sn_startup_sequence_unref (seq->seq); + + G_OBJECT_CLASS (meta_startup_notification_sequence_x11_parent_class)->finalize (object); +} + +static void +meta_startup_notification_sequence_x11_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaStartupNotificationSequenceX11 *seq; + + seq = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (object); + + switch (prop_id) + { + case PROP_SEQ_X11_SEQ: + seq->seq = g_value_get_pointer (value); + sn_startup_sequence_ref (seq->seq); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_startup_notification_sequence_x11_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaStartupNotificationSequenceX11 *seq; + + seq = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (object); + + switch (prop_id) + { + case PROP_SEQ_X11_SEQ: + g_value_set_pointer (value, seq->seq); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_startup_notification_sequence_x11_init (MetaStartupNotificationSequenceX11 *seq) +{ +} + +static void +meta_startup_notification_sequence_x11_class_init (MetaStartupNotificationSequenceX11Class *klass) +{ + MetaStartupNotificationSequenceClass *seq_class; + GObjectClass *object_class; + + seq_class = META_STARTUP_NOTIFICATION_SEQUENCE_CLASS (klass); + seq_class->complete = meta_startup_notification_sequence_x11_complete; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = meta_startup_notification_sequence_x11_finalize; + object_class->set_property = meta_startup_notification_sequence_x11_set_property; + object_class->get_property = meta_startup_notification_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 MetaStartupNotificationSequence * +meta_startup_notification_sequence_x11_new (SnStartupSequence *seq) +{ + return g_object_new (meta_startup_notification_sequence_x11_get_type (), + "id", sn_startup_sequence_get_id (seq), + "timestamp", sn_startup_sequence_get_timestamp (seq) * 1000, + "seq", seq, + NULL); +} + +static void +meta_startup_notification_add_sequence_internal (MetaStartupNotification *sn, + MetaStartupNotificationSequence *seq) +{ + sn->startup_sequences = g_slist_prepend (sn->startup_sequences, + g_object_ref (seq)); + + meta_startup_notification_ensure_timeout (sn); + meta_startup_notification_update_feedback (sn); +} + +static void +collect_timed_out_foreach (void *element, + void *data) +{ + MetaStartupNotificationSequence *sequence = element; + CollectTimedOutData *ctod = data; + gint64 elapsed; + + elapsed = ctod->now - sequence->timestamp; + + meta_topic (META_DEBUG_STARTUP, + "Sequence used %ld ms vs. %d max: %s\n", + elapsed, STARTUP_TIMEOUT, sequence->id); + + if (elapsed > STARTUP_TIMEOUT) + ctod->list = g_slist_prepend (ctod->list, sequence); +} + +static gboolean +startup_sequence_timeout (void *data) +{ + MetaStartupNotification *sn = data; + CollectTimedOutData ctod; + GSList *l; + + ctod.list = NULL; + ctod.now = g_get_monotonic_time (); + g_slist_foreach (sn->startup_sequences, + collect_timed_out_foreach, + &ctod); + + for (l = ctod.list; l != NULL; l = l->next) + { + MetaStartupNotificationSequence *sequence = l->data; + + meta_topic (META_DEBUG_STARTUP, + "Timed out sequence %s\n", + sequence->id); + + meta_startup_notification_sequence_complete (sequence); + } + + g_slist_free (ctod.list); + + if (sn->startup_sequences != NULL) + { + return TRUE; + } + else + { + /* remove */ + sn->startup_sequence_timeout = 0; + return FALSE; + } +} + +static void +meta_startup_notification_ensure_timeout (MetaStartupNotification *sn) +{ + if (sn->startup_sequence_timeout != 0) + return; + + /* our timeout just polls every second, instead of bothering + * to compute exactly when we may next time out + */ + sn->startup_sequence_timeout = g_timeout_add_seconds (1, + startup_sequence_timeout, + sn); + g_source_set_name_by_id (sn->startup_sequence_timeout, + "[mutter] startup_sequence_timeout"); +} +#endif + +static void +meta_startup_notification_remove_sequence_internal (MetaStartupNotification *sn, + MetaStartupNotificationSequence *seq) +{ + sn->startup_sequences = g_slist_remove (sn->startup_sequences, seq); + meta_startup_notification_update_feedback (sn); + + if (sn->startup_sequences == NULL && + sn->startup_sequence_timeout != 0) + { + g_source_remove (sn->startup_sequence_timeout); + sn->startup_sequence_timeout = 0; + } + + g_object_unref (seq); +} + +static MetaStartupNotificationSequence * +meta_startup_notification_lookup_sequence (MetaStartupNotification *sn, + const gchar *id) +{ + MetaStartupNotificationSequence *seq; + GSList *l; + + for (l = sn->startup_sequences; l; l = l->next) + { + seq = l->data; + + if (g_str_equal (seq->id, id)) + return l->data; + } + + return NULL; +} + +static void +meta_startup_notification_init (MetaStartupNotification *sn) +{ +} + +static void +meta_startup_notification_finalize (GObject *object) +{ + MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); + + sn_monitor_context_unref (sn->sn_context); + G_OBJECT_CLASS (meta_startup_notification_parent_class)->finalize (object); +} + +static void +meta_startup_notification_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); + + switch (prop_id) + { + case PROP_SN_DISPLAY: + sn->display = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_startup_notification_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); + + switch (prop_id) + { + case PROP_SN_DISPLAY: + g_value_set_object (value, sn->display); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +#ifdef HAVE_STARTUP_NOTIFICATION +static void +sn_error_trap_push (SnDisplay *sn_display, + Display *xdisplay) +{ + MetaDisplay *display; + display = meta_display_for_x_display (xdisplay); + if (display != NULL) + meta_error_trap_push (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_error_trap_pop (display); +} + +static void +meta_startup_notification_sn_event (SnMonitorEvent *event, + void *user_data) +{ + MetaStartupNotification *sn = user_data; + MetaStartupNotificationSequence *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_notification_sequence_x11_new (sequence); + meta_startup_notification_add_sequence_internal (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)); + + meta_startup_notification_remove_sequence (sn, sn_startup_sequence_get_id (sequence)); + } + 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; + } + + g_signal_emit (sn, sn_signals[SN_CHANGED], 0, sequence); + + sn_startup_sequence_unref (sequence); +} +#endif + +static void +meta_startup_notification_constructed (GObject *object) +{ + MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); + + g_assert (sn->display != NULL); + +#ifdef HAVE_STARTUP_NOTIFICATION + sn->sn_display = sn_display_new (sn->display->xdisplay, + sn_error_trap_push, + sn_error_trap_pop); + sn->sn_context = + sn_monitor_context_new (sn->sn_display, + sn->display->screen->number, + meta_startup_notification_sn_event, + sn, + NULL); + sn->startup_sequences = NULL; + sn->startup_sequence_timeout = 0; +#endif +} + +static void +meta_startup_notification_class_init (MetaStartupNotificationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = meta_startup_notification_constructed; + object_class->finalize = meta_startup_notification_finalize; + object_class->set_property = meta_startup_notification_set_property; + object_class->get_property = meta_startup_notification_get_property; + + sn_props[PROP_SN_DISPLAY] = + g_param_spec_object ("display", + "Display", + "Display", + META_TYPE_DISPLAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + sn_signals[SN_CHANGED] = + g_signal_new ("changed", + META_TYPE_STARTUP_NOTIFICATION, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + g_object_class_install_properties (object_class, N_SN_PROPS, sn_props); +} + +MetaStartupNotification * +meta_startup_notification_get (MetaDisplay *display) +{ + static MetaStartupNotification *notification = NULL; + + if (!notification) + notification = g_object_new (META_TYPE_STARTUP_NOTIFICATION, + "display", display, + NULL); + + return notification; +} + +void +meta_startup_notification_remove_sequence (MetaStartupNotification *sn, + const gchar *id) +{ + MetaStartupNotificationSequence *seq; + + seq = meta_startup_notification_lookup_sequence (sn, id); + if (seq) + meta_startup_notification_remove_sequence_internal (sn, seq); +} + +gboolean +meta_startup_notification_handle_xevent (MetaStartupNotification *sn, + XEvent *xevent) +{ +#ifdef HAVE_STARTUP_NOTIFICATION + return sn_display_process_event (sn->sn_display, xevent); +#endif + return FALSE; +} + +GSList * +meta_startup_notification_get_sequences (MetaStartupNotification *sn) +{ + GSList *l, *sequences = NULL; + + /* We return a list of SnStartupSequences here */ + for (l = sn->startup_sequences; l; l = l->next) + { +#ifdef HAVE_STARTUP_NOTIFICATION + MetaStartupNotificationSequenceX11 *seq_x11; + + if (!META_IS_STARTUP_NOTIFICATION_SEQUENCE_X11 (l->data)) + continue; + + seq_x11 = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (l->data); + sequences = g_slist_prepend (sequences, seq_x11->seq); +#endif + } + + return sequences; +} diff --git a/src/x11/events.c b/src/x11/events.c index 11f5159d5..3270f81f5 100644 --- a/src/x11/events.c +++ b/src/x11/events.c @@ -1671,13 +1671,12 @@ meta_display_handle_xevent (MetaDisplay *display, meta_spew_event_print (display, event); #endif -#ifdef HAVE_STARTUP_NOTIFICATION - if (sn_display_process_event (display->sn_display, event)) + if (meta_startup_notification_handle_xevent (display->startup_notification, + event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } -#endif #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor () &&