mutter/src/core/startup-notification.c
Jonas Ådahl 2f4a68c8c3 Clean up include macros mess
The order and way include macros were structured was chaotic, with no
real common thread between files. Try to tidy up the mess with some
common scheme, to make things look less messy.
2018-11-06 17:17:36 +01:00

802 lines
23 KiB
C

/* -*- 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib-object.h>
#include "core/display-private.h"
#include "core/startup-notification-private.h"
#include "meta/meta-x11-errors.h"
#include "x11/meta-x11-display-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 _MetaStartupNotificationSequence MetaStartupNotificationSequence;
typedef struct _MetaStartupNotificationSequenceClass MetaStartupNotificationSequenceClass;
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;
};
#define META_TYPE_STARTUP_NOTIFICATION_SEQUENCE \
(meta_startup_notification_sequence_get_type ())
G_DECLARE_DERIVABLE_TYPE (MetaStartupNotificationSequence,
meta_startup_notification_sequence,
META, STARTUP_NOTIFICATION_SEQUENCE,
GObject)
typedef struct {
gchar *id;
gint64 timestamp;
} MetaStartupNotificationSequencePrivate;
struct _MetaStartupNotificationSequenceClass {
GObjectClass parent_class;
void (* complete) (MetaStartupNotificationSequence *sequence);
};
G_DEFINE_TYPE (MetaStartupNotification,
meta_startup_notification,
G_TYPE_OBJECT)
G_DEFINE_TYPE_WITH_PRIVATE (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;
};
static GParamSpec *seq_x11_props[N_SEQ_X11_PROPS];
#define META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11 \
(meta_startup_notification_sequence_x11_get_type ())
G_DECLARE_FINAL_TYPE (MetaStartupNotificationSequenceX11,
meta_startup_notification_sequence_x11,
META, STARTUP_NOTIFICATION_SEQUENCE_X11,
MetaStartupNotificationSequence)
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)
{
MetaDisplay *display = sn->display;
if (sn->startup_sequences != NULL)
{
meta_topic (META_DEBUG_STARTUP,
"Setting busy cursor\n");
meta_display_set_cursor (display, META_CURSOR_BUSY);
}
else
{
meta_topic (META_DEBUG_STARTUP,
"Setting default cursor\n");
meta_display_set_cursor (display, META_CURSOR_DEFAULT);
}
}
static void
meta_startup_notification_sequence_init (MetaStartupNotificationSequence *seq)
{
}
static void
meta_startup_notification_sequence_finalize (GObject *object)
{
MetaStartupNotificationSequence *seq;
MetaStartupNotificationSequencePrivate *priv;
seq = META_STARTUP_NOTIFICATION_SEQUENCE (object);
priv = meta_startup_notification_sequence_get_instance_private (seq);
g_free (priv->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;
MetaStartupNotificationSequencePrivate *priv;
seq = META_STARTUP_NOTIFICATION_SEQUENCE (object);
priv = meta_startup_notification_sequence_get_instance_private (seq);
switch (prop_id)
{
case PROP_SEQ_ID:
priv->id = g_value_dup_string (value);
break;
case PROP_SEQ_TIMESTAMP:
priv->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;
MetaStartupNotificationSequencePrivate *priv;
seq = META_STARTUP_NOTIFICATION_SEQUENCE (object);
priv = meta_startup_notification_sequence_get_instance_private (seq);
switch (prop_id)
{
case PROP_SEQ_ID:
g_value_set_string (value, priv->id);
break;
case PROP_SEQ_TIMESTAMP:
g_value_set_int64 (value, priv->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);
}
static const gchar *
meta_startup_notification_sequence_get_id (MetaStartupNotificationSequence *seq)
{
MetaStartupNotificationSequencePrivate *priv;
priv = meta_startup_notification_sequence_get_instance_private (seq);
return priv->id;
}
#ifdef HAVE_STARTUP_NOTIFICATION
static gint64
meta_startup_notification_sequence_get_timestamp (MetaStartupNotificationSequence *seq)
{
MetaStartupNotificationSequencePrivate *priv;
priv = meta_startup_notification_sequence_get_instance_private (seq);
return priv->timestamp;
}
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);
}
#endif
#ifdef HAVE_STARTUP_NOTIFICATION
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)
{
gint64 timestamp;
timestamp = sn_startup_sequence_get_timestamp (seq) * 1000;
return g_object_new (META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11,
"id", sn_startup_sequence_get_id (seq),
"timestamp", timestamp,
"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, timestamp;
timestamp = meta_startup_notification_sequence_get_timestamp (sequence);
elapsed = ctod->now - timestamp;
meta_topic (META_DEBUG_STARTUP,
"Sequence used %" G_GINT64_FORMAT " ms vs. %d max: %s\n",
elapsed, STARTUP_TIMEOUT,
meta_startup_notification_sequence_get_id (sequence));
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",
meta_startup_notification_sequence_get_id (sequence));
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;
const gchar *seq_id;
GSList *l;
for (l = sn->startup_sequences; l; l = l->next)
{
seq = l->data;
seq_id = meta_startup_notification_sequence_get_id (seq);
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);
if (sn->startup_sequence_timeout)
g_source_remove (sn->startup_sequence_timeout);
g_slist_foreach (sn->startup_sequences, (GFunc) g_object_unref, NULL);
g_slist_free (sn->startup_sequences);
sn->startup_sequences = NULL;
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_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)
{
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);
}
static void
on_x11_display_opened (MetaStartupNotification *sn)
{
MetaX11Display *x11_display = sn->display->x11_display;
sn->sn_display = sn_display_new (x11_display->xdisplay,
sn_error_trap_push,
sn_error_trap_pop);
sn->sn_context =
sn_monitor_context_new (sn->sn_display,
meta_x11_display_get_screen_number (x11_display),
meta_startup_notification_sn_event,
sn,
NULL);
}
static void
on_x11_display_closing (MetaStartupNotification *sn)
{
sn_monitor_context_unref (sn->sn_context);
sn->sn_context = NULL;
sn_display_unref (sn->sn_display);
sn->sn_display = NULL;
}
#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 = NULL;
sn->sn_context = NULL;
g_signal_connect_object (sn->display,
"x11-display-opened",
G_CALLBACK (on_x11_display_opened),
sn,
G_CONNECT_SWAPPED);
g_signal_connect_object (sn->display,
"x11-display-closing",
G_CALLBACK (on_x11_display_closing),
sn,
G_CONNECT_SWAPPED);
#endif
sn->startup_sequences = NULL;
sn->startup_sequence_timeout = 0;
}
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 *sequences = NULL;
#ifdef HAVE_STARTUP_NOTIFICATION
GSList *l;
if (!sn->sn_display)
return sequences;
/* We return a list of SnStartupSequences here */
for (l = sn->startup_sequences; l; l = l->next)
{
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;
}