[Galago-commits] r3012 - in trunk/notification-daemon: . src/daemon
galago-commits at freedesktop.org
galago-commits at freedesktop.org
Thu Sep 25 18:25:58 PDT 2008
Author: chipx86
Date: 2008-09-25 18:25:57 -0700 (Thu, 25 Sep 2008)
New Revision: 3012
Modified:
trunk/notification-daemon/ChangeLog
trunk/notification-daemon/NEWS
trunk/notification-daemon/src/daemon/daemon.c
Log:
Added better support for attaching context notifications to an icon on the system tray, even when it moves. Patch by Colin Walters.
Modified: trunk/notification-daemon/ChangeLog
===================================================================
--- trunk/notification-daemon/ChangeLog 2008-09-26 01:12:47 UTC (rev 3011)
+++ trunk/notification-daemon/ChangeLog 2008-09-26 01:25:57 UTC (rev 3012)
@@ -1,3 +1,10 @@
+Thu Sep 25 18:25:36 PDT 2008 Christian Hammond <chipx86 at chipx86.com>
+
+ * src/daemon/daemon.c:
+ * NEWS:
+ - Added better support for attaching context notifications to an icon on
+ the system tray, even when it moves. Patch by Colin Walters.
+
Tue Mar 18 21:39:53 PDT 2008 Christian Hammond <chipx86 at chipx86.com>
* src/daemon/daemon.c:
Modified: trunk/notification-daemon/NEWS
===================================================================
--- trunk/notification-daemon/NEWS 2008-09-26 01:12:47 UTC (rev 3011)
+++ trunk/notification-daemon/NEWS 2008-09-26 01:25:57 UTC (rev 3012)
@@ -15,6 +15,8 @@
* Added a control panel applet for controlling such things as the
notification theme and popup positions. Patch by John Wendell.
(Ticket #126)
+ * Added better support for attaching context notifications to an icon on
+ the system tray, even when it moves. Patch by Colin Walters.
* Added an Arabic translation. Patch by Djihed Afifi. (Ticket #131)
* Added an Italian translation. Patch by Luca Ferretti.
* Fixed a bug where notifications weren't displayed if a fullscreen
Modified: trunk/notification-daemon/src/daemon/daemon.c
===================================================================
--- trunk/notification-daemon/src/daemon/daemon.c 2008-09-26 01:12:47 UTC (rev 3011)
+++ trunk/notification-daemon/src/daemon/daemon.c 2008-09-26 01:25:57 UTC (rev 3012)
@@ -83,13 +83,15 @@
gboolean paused;
guint id;
GtkWindow *nw;
-
+ Window src_window_xid;
} NotifyTimeout;
struct _NotifyDaemonPrivate
{
guint next_id;
guint timeout_source;
+ GHashTable *idle_reposition_notify_ids;
+ GHashTable *monitored_window_hash;
GHashTable *notification_hash;
gboolean url_clicked_lock;
NotifyStack **stacks;
@@ -118,9 +120,17 @@
static void _close_notification(NotifyDaemon *daemon, guint id,
gboolean hide_notification,
NotifydClosedReason reason);
+static GdkFilterReturn _notify_x11_filter(GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer user_data);
static void _emit_closed_signal(GtkWindow *nw, NotifydClosedReason reason);
static void _action_invoked_cb(GtkWindow *nw, const char *key);
static NotifyStackLocation get_stack_location_from_string(const char *slocation);
+static void sync_notification_position(NotifyDaemon *daemon, GtkWindow *nw,
+ Window source);
+static void monitor_notification_source_windows(NotifyDaemon *daemon,
+ NotifyTimeout *nt,
+ Window source);
G_DEFINE_TYPE(NotifyDaemon, notify_daemon, G_TYPE_OBJECT);
@@ -167,6 +177,10 @@
daemon->priv->stacks_size = gdk_screen_get_n_monitors(screen);
daemon->priv->stacks = g_new0(NotifyStack *, daemon->priv->stacks_size);
+ daemon->priv->idle_reposition_notify_ids = g_hash_table_new(NULL, NULL);
+ daemon->priv->monitored_window_hash = g_hash_table_new(NULL, NULL);
+ gdk_window_add_filter(NULL, _notify_x11_filter, daemon);
+
for (i = 0; i < daemon->priv->stacks_size; i++)
{
daemon->priv->stacks[i] = notify_stack_new(daemon, screen,
@@ -184,6 +198,8 @@
NotifyDaemon *daemon = NOTIFY_DAEMON(object);
GObjectClass *parent_class = G_OBJECT_CLASS(notify_daemon_parent_class);
+ g_hash_table_destroy(daemon->priv->monitored_window_hash);
+ g_hash_table_destroy(daemon->priv->idle_reposition_notify_ids);
g_hash_table_destroy(daemon->priv->notification_hash);
g_free(daemon->priv);
@@ -295,7 +311,110 @@
NOTIFYD_CLOSED_EXPIRED);
}
+typedef struct
+{
+ NotifyDaemon *daemon;
+ gint id;
+} IdleRepositionData;
+
+static gboolean
+idle_reposition_notification(gpointer datap)
+{
+ IdleRepositionData *data = (IdleRepositionData *)datap;
+ NotifyDaemon *daemon = data->daemon;
+ NotifyTimeout *nt;
+ gint notify_id;
+
+ notify_id = data->id;
+
+ /* Look up the timeout, if it's completed we don't need to do anything */
+ nt = (NotifyTimeout *)g_hash_table_lookup(daemon->priv->notification_hash,
+ ¬ify_id);
+ if (nt != NULL) {
+ sync_notification_position(daemon, nt->nw, nt->src_window_xid);
+ }
+
+ g_hash_table_remove(daemon->priv->idle_reposition_notify_ids,
+ GINT_TO_POINTER(notify_id));
+ g_object_unref(daemon);
+ g_free(data);
+
+ return FALSE;
+}
+
static void
+_queue_idle_reposition_notification(NotifyDaemon *daemon, gint notify_id)
+{
+ IdleRepositionData *data;
+ gpointer orig_key;
+ gpointer value;
+ guint idle_id;
+
+ /* Do we already have an idle update pending? */
+ if (g_hash_table_lookup_extended(daemon->priv->idle_reposition_notify_ids,
+ GINT_TO_POINTER(notify_id), &orig_key, &value))
+ return;
+
+ data = g_new0(IdleRepositionData, 1);
+ data->daemon = g_object_ref(daemon);
+ data->id = notify_id;
+
+ /* We do this as a short timeout to avoid repositioning spam */
+ idle_id = g_timeout_add_full(G_PRIORITY_LOW, 50,
+ idle_reposition_notification, data, NULL);
+ g_hash_table_insert(daemon->priv->idle_reposition_notify_ids,
+ GINT_TO_POINTER(notify_id), GUINT_TO_POINTER(idle_id));
+}
+
+static GdkFilterReturn
+_notify_x11_filter(GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ NotifyDaemon *daemon = NOTIFY_DAEMON(user_data);
+ XEvent *xev = (XEvent *)xevent;
+ gpointer orig_key;
+ gpointer value;
+ gint notify_id;
+ NotifyTimeout *nt;
+
+ if (xev->xany.type == DestroyNotify)
+ {
+ g_hash_table_remove(daemon->priv->monitored_window_hash,
+ GUINT_TO_POINTER(xev->xany.window));
+ return GDK_FILTER_CONTINUE;
+ }
+
+ if (!g_hash_table_lookup_extended(daemon->priv->monitored_window_hash,
+ GUINT_TO_POINTER(xev->xany.window), &orig_key, &value))
+ return GDK_FILTER_CONTINUE;
+
+ notify_id = GPOINTER_TO_INT(value);
+
+ if (xev->xany.type == ConfigureNotify || xev->xany.type == MapNotify)
+ {
+ _queue_idle_reposition_notification(daemon, notify_id);
+ }
+ else if (xev->xany.type == ReparentNotify)
+ {
+ nt = (NotifyTimeout *)g_hash_table_lookup(
+ daemon->priv->notification_hash, ¬ify_id);
+
+ if (nt == NULL)
+ return GDK_FILTER_CONTINUE;
+
+ /*
+ * If the window got reparented, we need to start monitoring the
+ * new parents.
+ */
+ monitor_notification_source_windows(daemon, nt, nt->src_window_xid);
+ sync_notification_position(daemon, nt->nw, nt->src_window_xid);
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
_mouse_entered_cb(GtkWindow *nw, GdkEventCrossing *event, NotifyDaemon *daemon)
{
NotifyTimeout *nt;
@@ -436,7 +555,7 @@
}
}
-static guint
+static NotifyTimeout *
_store_notification(NotifyDaemon *daemon, GtkWindow *nw, int timeout)
{
NotifyDaemonPrivate *priv = daemon->priv;
@@ -466,7 +585,7 @@
g_hash_table_insert(priv->notification_hash,
g_memdup(&id, sizeof(guint)), nt);
- return id;
+ return nt;
}
static gboolean
@@ -634,7 +753,8 @@
if (expected_len != tmp_array->len)
{
g_warning("_notify_daemon_process_icon_data expected image "
- "data to be of length %i but got a length of %i",
+ "data to be of length %" G_GSIZE_FORMAT " but got a "
+ "length of %u",
expected_len, tmp_array->len);
return FALSE;
}
@@ -822,6 +942,102 @@
return FALSE;
}
+static Window
+get_window_parent(Display *display,
+ Window window,
+ Window *root)
+{
+ Window parent;
+ Window *children = NULL;
+ guint nchildren;
+ gboolean result;
+
+ gdk_error_trap_push();
+ result = XQueryTree(display, window, root, &parent, &children, &nchildren);
+ if (gdk_error_trap_pop() || !result)
+ return None;
+
+ if (children)
+ XFree(children);
+
+ return parent;
+}
+
+/*
+ * Recurse over X Window and parents, up to root, and start watching them
+ * for position changes.
+ */
+static void
+monitor_notification_source_windows(NotifyDaemon *daemon,
+ NotifyTimeout *nt,
+ Window source)
+{
+ Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ Window root = None;
+ Window parent;
+
+ /* Store the window in the timeout */
+ g_assert(nt != NULL);
+ nt->src_window_xid = source;
+
+ for (parent = get_window_parent(display, source, &root);
+ parent != None && root != parent;
+ parent = get_window_parent(display, parent, &root)) {
+
+ XSelectInput(display, parent, StructureNotifyMask);
+ g_hash_table_insert(daemon->priv->monitored_window_hash,
+ GUINT_TO_POINTER(parent), GINT_TO_POINTER(nt->id));
+ }
+}
+
+/* Use a source X Window ID to reposition a notification. */
+static void
+sync_notification_position(NotifyDaemon *daemon,
+ GtkWindow *nw,
+ Window source)
+{
+ Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ Status result;
+ Window root;
+ Window child;
+ int x, y;
+ unsigned int width, height;
+ unsigned int border_width, depth;
+
+ gdk_error_trap_push();
+
+ /* Get the root for this window */
+ result = XGetGeometry(display, source, &root, &x, &y,
+ &width, &height, &border_width, &depth);
+ if (gdk_error_trap_pop() || !result)
+ return;
+
+ /*
+ * Now calculate the offset coordinates for the source window from
+ * the root.
+ */
+ gdk_error_trap_push ();
+ result = XTranslateCoordinates(display, source, root, 0, 0,
+ &x, &y, &child);
+ if (gdk_error_trap_pop() || !result)
+ return;
+
+ x += width / 2;
+ y += height / 2;
+
+ theme_set_notification_arrow(nw, TRUE, x, y);
+ theme_move_notification(nw, x, y);
+ theme_show_notification(nw);
+
+ /*
+ * We need to manually queue a draw here as the default theme recalculates
+ * its position in the draw handler and moves the window (which seems
+ * fairly broken), so just calling move/show above isn't enough to cause
+ * its position to be calculated.
+ */
+ gtk_widget_queue_draw(GTK_WIDGET(nw));
+}
+
GQuark
notify_daemon_error_quark(void)
{
@@ -852,6 +1068,7 @@
gboolean new_notification = FALSE;
gint x = 0;
gint y = 0;
+ Window window_xid = None;
guint return_id;
gchar *sender;
gchar *sound_file = NULL;
@@ -897,8 +1114,13 @@
*XXX This needs to handle file URIs and all that.
*/
+
+ if ((data = (GValue *)g_hash_table_lookup(hints, "window-xid")) != NULL)
+ {
+ window_xid = (Window)g_value_get_uint(data);
+ }
/* deal with x, and y hints */
- if ((data = (GValue *)g_hash_table_lookup(hints, "x")) != NULL)
+ else if ((data = (GValue *)g_hash_table_lookup(hints, "x")) != NULL)
{
x = g_value_get_int(data);
@@ -1044,9 +1266,17 @@
}
}
- if (use_pos_data)
+
+ if (window_xid != None)
{
/*
+ * Do nothing here if we were passed an XID; we'll call
+ * sync_notification_position later.
+ */
+ }
+ else if (use_pos_data)
+ {
+ /*
* Typically, the theme engine will set its own position based on
* the arrow X, Y hints. However, in case, move the notification to
* that position.
@@ -1070,6 +1300,25 @@
notify_stack_add_window(priv->stacks[monitor], nw, new_notification);
}
+ if (id == 0)
+ {
+ nt = _store_notification(daemon, nw, timeout);
+ return_id = nt->id;
+ }
+ else
+ return_id = id;
+
+ /*
+ * If we have a source Window XID, start monitoring the tree
+ * for changes, and reposition the window based on the source
+ * window. We need to do this after return_id is calculated.
+ */
+ if (window_xid != None)
+ {
+ monitor_notification_source_windows(daemon, nt, window_xid);
+ sync_notification_position(daemon, nw, window_xid);
+ }
+
if (!screensaver_active(GTK_WIDGET(nw)) &&
!fullscreen_window_exists(GTK_WIDGET(nw)))
{
@@ -1080,8 +1329,6 @@
g_free(sound_file);
- return_id = (id == 0 ? _store_notification(daemon, nw, timeout) : id);
-
#if CHECK_DBUS_VERSION(0, 60)
sender = dbus_g_method_get_sender(context);
#else
More information about the galago-commits
mailing list