Update version number to 0.4.991
authormartyj19 <martyj19@comcast.net>
Fri, 24 Jul 2009 15:37:14 +0000 (15:37 +0000)
committermartyj19 <martyj19@comcast.net>
Fri, 24 Jul 2009 15:37:14 +0000 (15:37 +0000)
Rework system tray to do event handling at the X protocol level
- Solves problem with lost "plug-removed" events
- Solves issue with non-removable event handler
- Simplifies to one event handler from three different ones
Remove extraneous holdover code from na-tray-manager
Remove all calls to gtk_main_iteration, breaks synchronization
Rework background draw to solve observed systray corruption
Rework 90 degree re-orientation to solve lost tray icons for Qt applications, conditional on 2.16 or later

configure.ac
src/configurator.c
src/icon-grid.c
src/misc.c
src/panel.c
src/panel.h
src/plugin.c
src/plugin.h
src/plugins/tray.c

index 44e42b6..288ac7a 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.53)
-AC_INIT(lxpanel, 0.4.990, http://lxde.org/)
+AC_INIT(lxpanel, 0.4.991, http://lxde.org/)
 AM_INIT_AUTOMAKE
 AC_CONFIG_SRCDIR([src/bg.c])
 AC_CONFIG_HEADER([config.h])
index 0ca58b1..f47b67b 100644 (file)
@@ -126,7 +126,6 @@ static void set_edge(Panel* p, int edge)
     p->edge = edge;
     update_panel_geometry(p);
     panel_set_panel_configuration_changed(p);
-    panel_update_background(p);
 }
 
 static void edge_bottom_toggle(GtkToggleButton *widget, Panel *p)
@@ -1118,9 +1117,6 @@ void restart(void)
     ENTER;
     is_restarting = TRUE;
 
-    /* processing any possible idle handlers before we restart */
-    while (gtk_events_pending ())
-        gtk_main_iteration ();
     gtk_main_quit();
     RET();
 }
index a1d8154..f9db7eb 100644 (file)
@@ -40,10 +40,7 @@ static gboolean icon_grid_placement(IconGrid * ig)
     /* Erase the window. */
     GdkWindow * window = ig->widget->window;
     if (window != NULL)
-    {
         panel_determine_background_pixmap(ig->panel, ig->widget, window);
-       gdk_window_clear(window);
-    }
 
     /* Get and save the desired container geometry. */
     ig->container_width = ig->container->allocation.width;
index c12ed11..f31aad8 100644 (file)
@@ -97,6 +97,7 @@ Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
 
 /* SYSTEM TRAY spec */
 Atom a_NET_SYSTEM_TRAY_OPCODE;
+Atom a_NET_SYSTEM_TRAY_MESSAGE_DATA;
 Atom a_NET_SYSTEM_TRAY_ORIENTATION;
 Atom a_MANAGER;
 
@@ -145,6 +146,7 @@ enum{
     I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR,
 
     I_NET_SYSTEM_TRAY_OPCODE,
+    I_NET_SYSTEM_TRAY_MESSAGE_DATA,
     I_NET_SYSTEM_TRAY_ORIENTATION,
     I_MANAGER,
 
@@ -390,6 +392,7 @@ void resolve_atoms()
     atom_names[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ] = "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR";
 
     atom_names[ I_NET_SYSTEM_TRAY_OPCODE ] = "_NET_SYSTEM_TRAY_OPCODE";
+    atom_names[ I_NET_SYSTEM_TRAY_MESSAGE_DATA ] = "_NET_SYSTEM_TRAY_MESSAGE_DATA";
     atom_names[ I_NET_SYSTEM_TRAY_ORIENTATION ] = "_NET_SYSTEM_TRAY_ORIENTATION";
     atom_names[ I_MANAGER ] = "MANAGER";
 
@@ -447,6 +450,7 @@ void resolve_atoms()
     a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR = atoms[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ];
 
     a_NET_SYSTEM_TRAY_OPCODE = atoms[ I_NET_SYSTEM_TRAY_OPCODE ];
+    a_NET_SYSTEM_TRAY_MESSAGE_DATA = atoms [ I_NET_SYSTEM_TRAY_MESSAGE_DATA ];
     a_NET_SYSTEM_TRAY_ORIENTATION = atoms[ I_NET_SYSTEM_TRAY_ORIENTATION ];
     a_MANAGER = atoms[ I_MANAGER ];
 
index e9efe58..da24ef3 100644 (file)
@@ -391,14 +391,14 @@ void panel_update_background(Panel * p)
     panel_determine_background_pixmap(p, p->topgwin, p->topgwin->window);
     gdk_window_clear(p->topgwin->window);
     gtk_widget_queue_draw(p->topgwin);
-    if (gtk_events_pending()) gtk_main_iteration();
 
     /* Loop over all plugins redrawing each plugin. */
     GList * l;
     for (l = p->plugins; l != NULL; l = l->next)
     {
         Plugin * pl = (Plugin *) l->data;
-        plugin_set_background(pl, p);
+        if (pl->pwid != NULL)
+            plugin_widget_set_background(pl->pwid, p);
     }
 
 }
@@ -1051,12 +1051,19 @@ void panel_set_panel_configuration_changed(Panel *p)
     }
 
     /* recreate the main layout box */
-    if( p->box ) {
-        GtkBox* newbox = GTK_BOX(recreate_box( GTK_BOX(p->box), p->orientation ));
-        if( GTK_WIDGET(newbox) != p->box ) {
+    if (p->box != NULL)
+    {
+#if GTK_CHECK_VERSION(2,16,0)
+        GtkOrientation bo = (p->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+        gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), bo);
+#else
+        GtkBox * newbox = GTK_BOX(recreate_box(GTK_BOX(p->box), p->orientation));
+        if (GTK_WIDGET(newbox) != p->box)
+        {
             p->box = GTK_WIDGET(newbox);
-            gtk_container_add( GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox) );
+            gtk_container_add(GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox));
         }
+#endif
     }
 
     /* NOTE: This loop won't be executed when panel started since
index 15a6c40..f159d83 100644 (file)
@@ -202,6 +202,7 @@ extern Atom a_NET_WM_ICON;
 extern Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
 
 extern Atom a_NET_SYSTEM_TRAY_OPCODE;
+extern Atom a_NET_SYSTEM_TRAY_MESSAGE_DATA;
 extern Atom a_NET_SYSTEM_TRAY_ORIENTATION;
 extern Atom a_MANAGER;
 
index 33b3a5f..4bbf432 100644 (file)
@@ -320,9 +320,11 @@ void plugin_widget_set_background(GtkWidget * w, Panel * p)
         {
             if ((p->background) || (p->transparent))
             {
-                gtk_widget_set_app_paintable(w, TRUE);
                 if (GTK_WIDGET_REALIZED(w))
-                    gdk_window_set_back_pixmap(w->window, NULL, TRUE);
+                {
+                    panel_determine_background_pixmap(p, w, w->window);
+                    gdk_window_invalidate_rect(w->window, NULL, TRUE);
+                }
             }
             else
             {
@@ -336,13 +338,13 @@ void plugin_widget_set_background(GtkWidget * w, Panel * p)
             }
         }
 
-        /* Special handling to get tray icons redrawn.  This is the only known working technique to date. */
+        /* Special handling to get tray icons redrawn. */
         if (GTK_IS_SOCKET(w))
         {
             gtk_widget_hide(w);
-            if (gtk_events_pending()) gtk_main_iteration();
+            gdk_window_process_all_updates();
             gtk_widget_show(w);
-            if (gtk_events_pending()) gtk_main_iteration();
+            gdk_window_process_all_updates();
         }
 
         /* Recursively process all children of a container. */
@@ -351,13 +353,6 @@ void plugin_widget_set_background(GtkWidget * w, Panel * p)
     }
 }
 
-/* Set the background of a plugin. */
-void plugin_set_background(Plugin * pl, Panel * p)
-{
-    if (pl->pwid != NULL)
-        plugin_widget_set_background(pl->pwid, p);
-}
-
 /* Handler for "button_press_event" signal with Plugin as parameter.
  * External so can be used from a plugin. */
 gboolean plugin_button_press_event(GtkWidget *widget, GdkEventButton *event, Plugin *plugin)
index 758c10c..05d8e95 100644 (file)
@@ -85,12 +85,11 @@ extern void plugin_delete(Plugin * pl);                     /* Delete an instance of a plugin */
 extern GList * plugin_get_available_classes(void);     /* Get a list of all plugin classes; free with plugin_class_list_free */
 extern void plugin_class_list_free(GList * list);      /* Free the list allocated by plugin_get_available_classes */
 extern void plugin_widget_set_background(GtkWidget * w, Panel * p);
-                                                       /* Recursively set the background of all widgets on a panel background configuration change. */
-extern void plugin_set_background( Plugin* pl, Panel* p );  /* Set the background of a plugin. */
+                                                       /* Recursively set the background of all widgets on a panel background configuration change */
 extern gboolean plugin_button_press_event(GtkWidget *widget, GdkEventButton *event, Plugin *plugin);
-                                                        /* Handler for "button_press_event" signal with Plugin as parameter. */
+                                                        /* Handler for "button_press_event" signal with Plugin as parameter */
 extern void plugin_popup_set_position_helper(Plugin * p, GtkWidget * near, GtkWidget * popup, GtkRequisition * popup_req, gint * px, gint * py);
-                                                       /* Helper for position-calculation callback for popup menus. */
+                                                       /* Helper for position-calculation callback for popup menus */
 
 /* FIXME: optional definitions */
 #define STATIC_SEPARATOR
index 10273af..a23781d 100644 (file)
@@ -70,7 +70,6 @@ typedef struct _tray_client {
     struct _tray_plugin * tr;                  /* Back pointer to tray plugin */
     Window window;                             /* X window ID */
     GtkWidget * socket;                                /* Socket */
-    gboolean request_dock_finished;            /* True if request dock finished */
 } TrayClient;
 
 /* Private context for system tray plugin. */
@@ -83,11 +82,12 @@ typedef struct _tray_plugin {
     GtkWidget * balloon_message_popup;         /* Popup showing balloon message */
     guint balloon_message_timer;               /* Timer controlling balloon message */
     GtkWidget * invisible;                     /* Invisible window that holds manager selection */
+    Window invisible_window;                   /* X window ID of invisible window */
     GdkAtom selection_atom;                    /* Atom for _NET_SYSTEM_TRAY_S%d */
 } TrayPlugin;
 
 static TrayClient * client_lookup(TrayPlugin * tr, Window win);
-static void client_delete(TrayPlugin * tr, TrayClient * tc);
+static void client_delete(TrayPlugin * tr, TrayClient * tc, gboolean unlink);
 static void balloon_message_free(BalloonMessage * message);
 static void balloon_message_advance(TrayPlugin * tr, gboolean destroy_timer, gboolean display_next);
 static gboolean balloon_message_activate_event(GtkWidget * widget, GdkEventButton * event, TrayPlugin * tr);
@@ -96,24 +96,16 @@ static void balloon_message_display(TrayPlugin * tr, BalloonMessage * msg);
 static void balloon_message_queue(TrayPlugin * tr, BalloonMessage * msg);
 static void balloon_incomplete_message_remove(TrayPlugin * tr, Window window, gboolean all_ids, long id);
 static void balloon_message_remove(TrayPlugin * tr, Window window, gboolean all_ids, long id);
-static GdkFilterReturn balloon_message_data_event(GdkXEvent * xev, GdkEvent * event, gpointer data);
 static void balloon_message_begin_event(TrayPlugin * tr, XClientMessageEvent * xevent);
 static void balloon_message_cancel_event(TrayPlugin * tr, XClientMessageEvent * xevent);
-static gboolean trayclient_plug_removed(GtkWidget * socket, TrayPlugin * tr);
-static void trayclient_realized(GtkWidget * widget, gpointer user_data);
-static gboolean trayclient_expose_event(GtkWidget * widget, GdkEventExpose * event, gpointer user_data);
+static void balloon_message_data_event(TrayPlugin * tr, XClientMessageEvent * xevent);
 static void trayclient_request_dock(TrayPlugin * tr, XClientMessageEvent * xevent);
+static GdkFilterReturn tray_event_filter(XEvent * xev, GdkEvent * event, TrayPlugin * tr);
 static void tray_unmanage_selection(TrayPlugin * tr);
 static int tray_constructor(Plugin * p, char ** fp);
 static void tray_destructor(Plugin * p);
 static void tray_panel_configuration_changed(Plugin * p);
 
-/* There is no way to remove the installed client message filters.
- * Thus they reference the tray context through this static variable, which is set
- * and cleared depending upon whether an instance of the system tray exists.
- * If later GTK provides an API to remove them, this should be fixed. */
-static TrayPlugin * tray_singleton = NULL;
-
 /* Look up a client in the client list. */
 static TrayClient * client_lookup(TrayPlugin * tr, Window window)
 {
@@ -129,23 +121,33 @@ static TrayClient * client_lookup(TrayPlugin * tr, Window window)
 }
 
 /* Delete a client. */
-static void client_delete(TrayPlugin * tr, TrayClient * tc)
+static void client_delete(TrayPlugin * tr, TrayClient * tc, gboolean unlink)
 {
-    if (tr->client_list == tc)
-        tr->client_list = tc->client_flink;
-    else
+    if (unlink)
     {
-        /* Locate the task and its predecessor in the list and then remove it.  For safety, ensure it is found. */
-        TrayClient * tc_pred = NULL;
-        TrayClient * tc_cursor;
-        for (
-          tc_cursor = tr->client_list;
-          ((tc_cursor != NULL) && (tc_cursor != tc));
-          tc_pred = tc_cursor, tc_cursor = tc_cursor->client_flink) ;
-          if (tc_cursor == tc)
-              tc_pred->client_flink = tc->client_flink;
+        if (tr->client_list == tc)
+            tr->client_list = tc->client_flink;
+        else
+        {
+            /* Locate the task and its predecessor in the list and then remove it.  For safety, ensure it is found. */
+            TrayClient * tc_pred = NULL;
+            TrayClient * tc_cursor;
+            for (
+              tc_cursor = tr->client_list;
+              ((tc_cursor != NULL) && (tc_cursor != tc));
+              tc_pred = tc_cursor, tc_cursor = tc_cursor->client_flink) ;
+            if (tc_cursor == tc)
+                tc_pred->client_flink = tc->client_flink;
+        }
     }
 
+    /* Clear out any balloon messages. */
+    balloon_incomplete_message_remove(tr, tc->window, TRUE, 0);
+    balloon_message_remove(tr, tc->window, TRUE, 0);
+
+    /* Remove the socket from the icon grid. */
+    icon_grid_remove(tr->icon_grid, tc->socket);
+
     /* Deallocate the client structure. */
     g_free(tc);
 }
@@ -205,7 +207,7 @@ static void balloon_message_display(TrayPlugin * tr, BalloonMessage * msg)
     /* Create a window and an item containing the text. */
     tr->balloon_message_popup = gtk_window_new(GTK_WINDOW_POPUP);
     GtkWidget * balloon_text = gtk_label_new(msg->string);
-    gtk_label_set_line_wrap(GTK_LABEL (balloon_text), TRUE);
+    gtk_label_set_line_wrap(GTK_LABEL(balloon_text), TRUE);
     gtk_misc_set_alignment(GTK_MISC(balloon_text), 0.5, 0.5);
     gtk_widget_show(balloon_text);
     gtk_container_add(GTK_CONTAINER(tr->balloon_message_popup), balloon_text);
@@ -336,49 +338,6 @@ static void balloon_message_remove(TrayPlugin * tr, Window window, gboolean all_
 
 /*** Event interfaces ***/
 
-/* Handle a balloon message _NET_SYSTEM_TRAY_MESSAGE_DATA event. */
-static GdkFilterReturn balloon_message_data_event(GdkXEvent * xev, GdkEvent * event, gpointer data)
-{
-    /* Locate tray context via a static variable.  If there is no active tray plugin, ignore the event. */
-    TrayPlugin * tr = tray_singleton;
-    if (tr == NULL)
-        return GDK_FILTER_CONTINUE;
-
-    /* Look up the pending message in the list. */
-    XClientMessageEvent * xevent  = (XClientMessageEvent *) xev;
-    BalloonMessage * msg_pred = NULL;
-    BalloonMessage * msg;
-    for (msg = tr->incomplete_messages; msg != NULL; msg_pred = msg, msg = msg->flink)
-    {
-        if (xevent->window == msg->window)
-        {
-            /* Append the message segment to the message. */
-            int length = MIN(msg->remaining_length, 20);
-            memcpy((msg->string + msg->length - msg->remaining_length), &xevent->data, length);
-            msg->remaining_length -= length;
-
-            /* If the message has been completely collected, display it. */
-            if (msg->remaining_length == 0)
-            {
-                /* Unlink the message from the structure. */
-                if (msg_pred == NULL)
-                    tr->incomplete_messages = msg->flink;
-                else
-                    msg_pred->flink = msg->flink;
-
-                /* If the client window is valid, queue the message.  Otherwise discard it. */
-                TrayClient * client = client_lookup(tr, msg->window);
-                if (client != NULL)
-                    balloon_message_queue(tr, msg);
-                else
-                    balloon_message_free(msg);
-            }
-            break;
-        }
-    }
-    return GDK_FILTER_REMOVE;
-}
-
 /* Handle a balloon message SYSTEM_TRAY_BEGIN_MESSAGE event. */
 static void balloon_message_begin_event(TrayPlugin * tr, XClientMessageEvent * xevent)
 {
@@ -421,42 +380,40 @@ static void balloon_message_cancel_event(TrayPlugin * tr, XClientMessageEvent *
         balloon_message_remove(tr, xevent->window, FALSE, xevent->data.l[2]);
 }
 
-/* Handler for "plug-removed" event on socket widget. */
-static gboolean trayclient_plug_removed(GtkWidget * socket, TrayPlugin * tr)
+/* Handle a balloon message _NET_SYSTEM_TRAY_MESSAGE_DATA event. */
+static void balloon_message_data_event(TrayPlugin * tr, XClientMessageEvent * xevent)
 {
-    /* Get the window. */
-    Window win = (Window) g_object_get_data(G_OBJECT(socket), "client-window");
-    g_object_set_data(G_OBJECT(socket), "client-window", NULL);
-
-    /* Look up the client structure.
-     * Delete any balloon messages owned by the client, and the client structure itself. */
-    TrayClient * tc = client_lookup(tr, win);
-    if (tc != NULL)
+    /* Look up the pending message in the list. */
+    BalloonMessage * msg_pred = NULL;
+    BalloonMessage * msg;
+    for (msg = tr->incomplete_messages; msg != NULL; msg_pred = msg, msg = msg->flink)
     {
-        balloon_incomplete_message_remove(tr, win, TRUE, 0);
-        balloon_message_remove(tr, win, TRUE, 0);
-        client_delete(tr, tc);
-    }
-
-    /* Remove the socket from the icon grid. */
-    icon_grid_remove(tr->icon_grid, socket);
-
-    /* This destroys the socket. */
-    return FALSE;
-}
+        if (xevent->window == msg->window)
+        {
+            /* Append the message segment to the message. */
+            int length = MIN(msg->remaining_length, 20);
+            memcpy((msg->string + msg->length - msg->remaining_length), &xevent->data, length);
+            msg->remaining_length -= length;
 
-/* Handler for "realized" event on socket widget. */
-static void trayclient_realized(GtkWidget * widget, gpointer user_data)
-{
-   if ( ! GTK_WIDGET_NO_WINDOW(widget))
-        gdk_window_set_back_pixmap(widget->window, NULL, TRUE);
-}
+            /* If the message has been completely collected, display it. */
+            if (msg->remaining_length == 0)
+            {
+                /* Unlink the message from the structure. */
+                if (msg_pred == NULL)
+                    tr->incomplete_messages = msg->flink;
+                else
+                    msg_pred->flink = msg->flink;
 
-/* Handler for "expose_event" event on socket widget. */
-static gboolean trayclient_expose_event(GtkWidget * widget, GdkEventExpose * event, gpointer user_data)
-{
-    gdk_window_clear_area(widget->window, event->area.x, event->area.y, event->area.width, event->area.height);
-    return FALSE;
+                /* If the client window is valid, queue the message.  Otherwise discard it. */
+                TrayClient * client = client_lookup(tr, msg->window);
+                if (client != NULL)
+                    balloon_message_queue(tr, msg);
+                else
+                    balloon_message_free(msg);
+            }
+            break;
+        }
+    }
 }
 
 /* Handler for request dock message. */
@@ -480,7 +437,6 @@ static void trayclient_request_dock(TrayPlugin * tr, XClientMessageEvent * xeven
 
     /* Allocate a socket.  This is the tray side of the Xembed connection. */
     tc->socket = gtk_socket_new();
-    g_object_set_data(G_OBJECT(tc->socket), "client-window", GINT_TO_POINTER(xevent->data.l[2]));
 
     /* Link the client structure into the client list. */
     if (tc_pred == NULL)
@@ -494,73 +450,74 @@ static void trayclient_request_dock(TrayPlugin * tr, XClientMessageEvent * xeven
         tc_pred->client_flink = tc;
     }
 
-    /* Set up widget parameters and connect signals. */
-    gtk_widget_set_app_paintable(tc->socket, TRUE);
-    gtk_widget_set_double_buffered(tc->socket, FALSE);
-    g_signal_connect(tc->socket, "realize", G_CALLBACK(trayclient_realized), NULL);
-    g_signal_connect(tc->socket, "expose_event", G_CALLBACK(trayclient_expose_event), NULL);
-    g_signal_connect(tc->socket, "plug_removed", G_CALLBACK(trayclient_plug_removed), tc->tr);
-
     /* Add the socket to the icon grid. */
     icon_grid_add(tr->icon_grid, tc->socket, TRUE);
 
-    /* Connect the socket to the plug. */
+    /* Connect the socket to the plug.  This can only be done after the socket is realized. */
     gtk_socket_add_id(GTK_SOCKET(tc->socket), tc->window);
 }
 
 /* GDK event filter. */
-static GdkFilterReturn tray_window_filter(GdkXEvent * xev, GdkEvent * event, gpointer data)
+static GdkFilterReturn tray_event_filter(XEvent * xev, GdkEvent * event, TrayPlugin * tr)
 {
-    XEvent * xevent = (GdkXEvent *) xev;
-    TrayPlugin * tr = data;
+    if (xev->type == DestroyNotify)
+    {
+        /* Look for DestroyNotify events on tray icon windows and update state.
+         * We do it this way rather than with a "plug_removed" event because delivery
+         * of plug_removed events is observed to be unreliable if the client
+         * disconnects within less than 10 ms. */
+        XDestroyWindowEvent * xev_destroy = (XDestroyWindowEvent *) xev;
+        TrayClient * tc = client_lookup(tr, xev_destroy->window);
+        if (tc != NULL)
+            client_delete(tr, tc, TRUE);
+    }
 
-    if (xevent->type == ClientMessage)
+    else if (xev->type == ClientMessage)
     {
-        /* We handle the REQUEST_DOCK client message here.
-         * See also tray_client_message_opcode_filter. */
-        if ((xevent->xclient.message_type == a_NET_SYSTEM_TRAY_OPCODE)
-        && (xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK))
+        if (xev->xclient.message_type == a_NET_SYSTEM_TRAY_OPCODE)
         {
-            trayclient_request_dock(tr, (XClientMessageEvent *) xevent);
+            /* Client message of type _NET_SYSTEM_TRAY_OPCODE.
+             * Dispatch on the request. */
+            switch (xev->xclient.data.l[1])
+            {
+                case SYSTEM_TRAY_REQUEST_DOCK:
+                    /* If a Request Dock event on the invisible window, which is holding the manager selection, execute it. */
+                    if (xev->xclient.window == tr->invisible_window)
+                    {
+                        trayclient_request_dock(tr, (XClientMessageEvent *) xev);
+                        return GDK_FILTER_REMOVE;
+                    }
+                    break;
+
+                case SYSTEM_TRAY_BEGIN_MESSAGE:
+                    /* If a Begin Message event. look up the tray icon and execute it. */
+                    balloon_message_begin_event(tr, (XClientMessageEvent *) xev);
+                    return GDK_FILTER_REMOVE;
+
+                case SYSTEM_TRAY_CANCEL_MESSAGE:
+                    /* If a Cancel Message event. look up the tray icon and execute it. */
+                    balloon_message_cancel_event(tr, (XClientMessageEvent *) xev);
+                    return GDK_FILTER_REMOVE;
+            }
+        }
+
+        else if (xev->xclient.message_type == a_NET_SYSTEM_TRAY_MESSAGE_DATA)
+        {
+            /* Client message of type _NET_SYSTEM_TRAY_MESSAGE_DATA.
+             * Look up the tray icon and execute it. */
+            balloon_message_data_event(tr, (XClientMessageEvent *) xev);
             return GDK_FILTER_REMOVE;
         }
     }
 
-    else if (xevent->type == SelectionClear)
+    else if ((xev->type == SelectionClear)
+    && (xev->xclient.window == tr->invisible_window))
     {
-        /* We lost the selection, which should not happen. */
+        /* Look for SelectionClear events on the invisible window, which is holding the manager selection.
+         * This should not happen. */
         tray_unmanage_selection(tr);
     }
 
-  return GDK_FILTER_CONTINUE;
-}
-
-/* Filter for _NET_SYSTEM_TRAY_OPCODE (SYSTEM_TRAY_BEGIN_MESSAGE and SYSTEM_TRAY_CANCEL_MESSAGE). */
-static GdkFilterReturn tray_client_message_opcode_filter(GdkXEvent * xev, GdkEvent * event, gpointer data)
-{
-    /* Locate tray context via a static variable.  If there is no active tray plugin, ignore the event. */
-    TrayPlugin * tr = tray_singleton;
-    if (tr == NULL)
-        return GDK_FILTER_CONTINUE;
-
-    /* Dispatch on the message. */
-    XClientMessageEvent * xevent  = (XClientMessageEvent *) xev;
-    switch (xevent->data.l[1])
-    {
-        case SYSTEM_TRAY_REQUEST_DOCK:
-            /* Ignore this one since we don't know on which window this was received
-             * and so we can't know for which screen this is.  It will be handled
-             * in tray_window_filter since we also receive it there. */
-            break;
-
-        case SYSTEM_TRAY_BEGIN_MESSAGE:
-            balloon_message_begin_event(tr, xevent);
-            return GDK_FILTER_REMOVE;
-
-        case SYSTEM_TRAY_CANCEL_MESSAGE:
-            balloon_message_cancel_event(tr, xevent);
-            return GDK_FILTER_REMOVE;
-    }
     return GDK_FILTER_CONTINUE;
 }
 
@@ -582,12 +539,9 @@ static void tray_unmanage_selection(TrayPlugin * tr)
                 TRUE);
         }
 
-        /* Remove the event filters.
-         * At this time, there is no way to remove the client message filters. */
-        gdk_window_remove_filter(invisible->window, tray_window_filter, tr);
-
         /* Destroy the invisible window. */
         tr->invisible = NULL;
+        tr->invisible_window = None;
         gtk_widget_destroy(invisible);
         g_object_unref(G_OBJECT(invisible));
     }
@@ -612,7 +566,6 @@ static int tray_constructor(Plugin * p, char ** fp)
     TrayPlugin * tr = g_new0(TrayPlugin, 1);
     p->priv = tr;
     tr->plugin = p;
-    tray_singleton = tr;
 
     /* Get the screen and display. */
     GdkScreen * screen = gtk_widget_get_screen(GTK_WIDGET(p->panel->topgwin));
@@ -670,19 +623,12 @@ static int tray_constructor(Plugin * p, char ** fp)
             PropModeReplace,
             (guchar *) &data, 1);
 
-        /* Add GDK window filter to handle SYSTEM_TRAY_REQUEST_DOCK and SelectionClear. */
-        gdk_window_add_filter(invisible->window, tray_window_filter, tr);
-
-        /* Add filter for _NET_SYSTEM_TRAY_OPCODE (SYSTEM_TRAY_BEGIN_MESSAGE and SYSTEM_TRAY_CANCEL_MESSAGE). */
-        GdkAtom opcode_atom = gdk_atom_intern("_NET_SYSTEM_TRAY_OPCODE", FALSE);
-        gdk_display_add_client_message_filter(display, opcode_atom, tray_client_message_opcode_filter, &tray_singleton);
-
-        /* Add filter for _NET_SYSTEM_TRAY_MESSAGE_DATA. */
-        GdkAtom message_data_atom = gdk_atom_intern("_NET_SYSTEM_TRAY_MESSAGE_DATA", FALSE);
-        gdk_display_add_client_message_filter(display, message_data_atom, balloon_message_data_event, &tray_singleton);
+        /* Add GDK event filter. */
+        gdk_window_add_filter(NULL, (GdkFilterFunc) tray_event_filter, tr);
 
         /* Reference the window since it is never added to a container. */
         tr->invisible = invisible;
+        tr->invisible_window = GDK_WINDOW_XWINDOW(invisible->window);
         g_object_ref(G_OBJECT(invisible));
     }
     else
@@ -709,6 +655,9 @@ static void tray_destructor(Plugin * p)
 {
     TrayPlugin * tr = (TrayPlugin *) p->priv;
 
+    /* Remove GDK event filter. */
+    gdk_window_remove_filter(NULL, (GdkFilterFunc) tray_event_filter, tr);
+
     /* Make sure we drop the manager selection. */
     tray_unmanage_selection(tr);
 
@@ -726,13 +675,12 @@ static void tray_destructor(Plugin * p)
 
     /* Deallocate client list. */
     while (tr->client_list != NULL)
-        client_delete(tr, tr->client_list);
+        client_delete(tr, tr->client_list, TRUE);
 
     /* Deallocate memory. */
-    icon_grid_free(tr->icon_grid);
+    if (tr->icon_grid != NULL)
+        icon_grid_free(tr->icon_grid);
 
-    /* Ensure that the client message filters will discard events. */
-    tray_singleton = NULL;
     g_free(tr);
 }
 
@@ -763,4 +711,5 @@ PluginClass tray_plugin_class = {
     config : NULL,
     save : NULL,
     panel_configuration_changed : tray_panel_configuration_changed
+
 };