shell: Move tray implementation away from GtkSocket

Reimplement tray icon support on top of NaXembed. NaTrayIcon now
becomes a subclass of this type, replacing GtkSocket. The
ShellTrayIcon wrapper now embeds directly a NaTrayIcon, the
ShellEmbeddedWindow GTK widget and ShellGtkEmbed ClutterClone
objects are no longer necessary to wrap a NaXembed socket window.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2590>
This commit is contained in:
Carlos Garnacho 2022-12-22 20:02:28 +01:00 committed by Marge Bot
parent f9519f4a55
commit ab60902058
15 changed files with 552 additions and 1690 deletions

View File

@ -98,9 +98,7 @@ libshell_public_headers = [
'shell-app-system.h', 'shell-app-system.h',
'shell-app-usage.h', 'shell-app-usage.h',
'shell-blur-effect.h', 'shell-blur-effect.h',
'shell-embedded-window.h',
'shell-glsl-effect.h', 'shell-glsl-effect.h',
'shell-gtk-embed.h',
'shell-global.h', 'shell-global.h',
'shell-invert-lightness-effect.h', 'shell-invert-lightness-effect.h',
'shell-action-modes.h', 'shell-action-modes.h',
@ -128,6 +126,7 @@ libshell_private_headers = [
'shell-app-cache-private.h', 'shell-app-cache-private.h',
'shell-app-system-private.h', 'shell-app-system-private.h',
'shell-global-private.h', 'shell-global-private.h',
'shell-tray-icon-private.h',
'shell-window-tracker-private.h', 'shell-window-tracker-private.h',
'shell-wm-private.h' 'shell-wm-private.h'
] ]
@ -138,11 +137,8 @@ libshell_sources = [
'shell-app-system.c', 'shell-app-system.c',
'shell-app-usage.c', 'shell-app-usage.c',
'shell-blur-effect.c', 'shell-blur-effect.c',
'shell-embedded-window.c',
'shell-embedded-window-private.h',
'shell-global.c', 'shell-global.c',
'shell-glsl-effect.c', 'shell-glsl-effect.c',
'shell-gtk-embed.c',
'shell-invert-lightness-effect.c', 'shell-invert-lightness-effect.c',
'shell-keyring-prompt.c', 'shell-keyring-prompt.c',
'shell-keyring-prompt.h', 'shell-keyring-prompt.h',

View File

@ -1,20 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_EMBEDDED_WINDOW_PRIVATE_H__
#define __SHELL_EMBEDDED_WINDOW_PRIVATE_H__
#include "shell-embedded-window.h"
#include "shell-gtk-embed.h"
void _shell_embedded_window_set_actor (ShellEmbeddedWindow *window,
ShellGtkEmbed *embed);
void _shell_embedded_window_allocate (ShellEmbeddedWindow *window,
int x,
int y,
int width,
int height);
void _shell_embedded_window_map (ShellEmbeddedWindow *window);
void _shell_embedded_window_unmap (ShellEmbeddedWindow *window);
#endif /* __SHELL_EMBEDDED_WINDOW_PRIVATE_H__ */

View File

@ -1,247 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include <gdk/gdkx.h>
#include "shell-embedded-window-private.h"
/* This type is a subclass of GtkWindow that ties the window to a
* ShellGtkEmbed; the resizing logic is bound to the clutter logic.
*
* The typical usage we might expect is
*
* - ShellEmbeddedWindow is created and filled with content
* - ShellEmbeddedWindow is shown with gtk_widget_show_all()
* - ShellGtkEmbed is created for the ShellEmbeddedWindow
* - actor is added to a stage
*
* The way it works is that the GtkWindow is mapped if and only if both:
*
* - gtk_widget_visible (window) [widget has been shown]
* - Actor is mapped [actor and all parents visible, actor in stage]
*/
enum {
PROP_0
};
typedef struct _ShellEmbeddedWindowPrivate ShellEmbeddedWindowPrivate;
struct _ShellEmbeddedWindowPrivate {
ShellGtkEmbed *actor;
GdkRectangle position;
};
G_DEFINE_TYPE_WITH_PRIVATE (ShellEmbeddedWindow,
shell_embedded_window,
GTK_TYPE_WINDOW);
/*
* The normal gtk_window_show() starts all of the complicated asynchronous
* window resizing code running; we don't want or need any of that.
* Bypassing the normal code does mean that the extra geometry management
* available on GtkWindow: gridding, maximum sizes, etc, is ignored; we
* don't really want that anyways - we just want a way of embedding a
* GtkWidget into a Clutter stage.
*/
static void
shell_embedded_window_show (GtkWidget *widget)
{
ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
ShellEmbeddedWindowPrivate *priv;
GtkWidgetClass *widget_class;
priv = shell_embedded_window_get_instance_private (window);
/* Skip GtkWindow, but run the default GtkWidget handling which
* marks the widget visible */
widget_class = g_type_class_peek (GTK_TYPE_WIDGET);
widget_class->show (widget);
if (priv->actor)
{
/* Size is 0x0 if the GtkWindow is not shown */
clutter_actor_queue_relayout (CLUTTER_ACTOR (priv->actor));
if (clutter_actor_is_realized (CLUTTER_ACTOR (priv->actor)))
gtk_widget_map (widget);
}
}
static void
shell_embedded_window_hide (GtkWidget *widget)
{
ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
ShellEmbeddedWindowPrivate *priv;
priv = shell_embedded_window_get_instance_private (window);
if (priv->actor)
clutter_actor_queue_relayout (CLUTTER_ACTOR (priv->actor));
GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->hide (widget);
}
static gboolean
shell_embedded_window_configure_event (GtkWidget *widget,
GdkEventConfigure *event)
{
/* Normally a configure event coming back from X triggers the
* resizing logic inside GtkWindow; we just ignore them
* since we are handling the resizing logic separately.
*/
return FALSE;
}
static void
shell_embedded_window_check_resize (GtkContainer *container)
{
ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (container);
ShellEmbeddedWindowPrivate *priv;
priv = shell_embedded_window_get_instance_private (window);
/* Check resize is called when a resize is queued on something
* inside the GtkWindow; we need to make sure that in response
* to this gtk_widget_size_request() and then
* gtk_widget_size_allocate() are called; we defer to the Clutter
* logic and assume it will do the right thing.
*/
if (priv->actor)
clutter_actor_queue_relayout (CLUTTER_ACTOR (priv->actor));
}
static GObject *
shell_embedded_window_constructor (GType gtype,
guint n_properties,
GObjectConstructParam *properties)
{
GObject *object;
GObjectClass *parent_class;
parent_class = G_OBJECT_CLASS (shell_embedded_window_parent_class);
object = parent_class->constructor (gtype, n_properties, properties);
/* Setting the resize mode to immediate means that calling queue_resize()
* on a widget within the window will immediately call check_resize()
* to be called, instead of having it queued to an idle. From our perspective,
* this is ideal since we just are going to queue a resize to Clutter's
* idle resize anyways.
*/
g_object_set (object,
"app-paintable", TRUE,
"resize-mode", GTK_RESIZE_IMMEDIATE,
"type", GTK_WINDOW_POPUP,
NULL);
return object;
}
static void
shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
object_class->constructor = shell_embedded_window_constructor;
widget_class->show = shell_embedded_window_show;
widget_class->hide = shell_embedded_window_hide;
widget_class->configure_event = shell_embedded_window_configure_event;
container_class->check_resize = shell_embedded_window_check_resize;
}
static void
shell_embedded_window_init (ShellEmbeddedWindow *window)
{
}
/*
* Private routines called by ShellGtkEmbed
*/
void
_shell_embedded_window_set_actor (ShellEmbeddedWindow *window,
ShellGtkEmbed *actor)
{
ShellEmbeddedWindowPrivate *priv;
g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
priv = shell_embedded_window_get_instance_private (window);
priv->actor = actor;
if (actor &&
clutter_actor_is_mapped (CLUTTER_ACTOR (actor)) &&
gtk_widget_get_visible (GTK_WIDGET (window)))
gtk_widget_map (GTK_WIDGET (window));
}
void
_shell_embedded_window_allocate (ShellEmbeddedWindow *window,
int x,
int y,
int width,
int height)
{
ShellEmbeddedWindowPrivate *priv;
GtkAllocation allocation;
g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
priv = shell_embedded_window_get_instance_private (window);
if (priv->position.x == x &&
priv->position.y == y &&
priv->position.width == width &&
priv->position.height == height)
return;
priv->position.x = x;
priv->position.y = y;
priv->position.width = width;
priv->position.height = height;
if (gtk_widget_get_realized (GTK_WIDGET (window)))
gdk_window_move_resize (gtk_widget_get_window (GTK_WIDGET (window)),
x, y, width, height);
allocation.x = 0;
allocation.y = 0;
allocation.width = width;
allocation.height = height;
gtk_widget_size_allocate (GTK_WIDGET (window), &allocation);
}
void
_shell_embedded_window_map (ShellEmbeddedWindow *window)
{
g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
if (gtk_widget_get_visible (GTK_WIDGET (window)))
gtk_widget_map (GTK_WIDGET (window));
}
void
_shell_embedded_window_unmap (ShellEmbeddedWindow *window)
{
g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
gtk_widget_unmap (GTK_WIDGET (window));
}
/*
* Public API
*/
GtkWidget *
shell_embedded_window_new (void)
{
return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW,
NULL);
}

View File

@ -1,19 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_EMBEDDED_WINDOW_H__
#define __SHELL_EMBEDDED_WINDOW_H__
#include <gtk/gtk.h>
#include <clutter/clutter.h>
#define SHELL_TYPE_EMBEDDED_WINDOW (shell_embedded_window_get_type ())
G_DECLARE_DERIVABLE_TYPE (ShellEmbeddedWindow, shell_embedded_window,
SHELL, EMBEDDED_WINDOW, GtkWindow)
struct _ShellEmbeddedWindowClass
{
GtkWindowClass parent_class;
};
GtkWidget *shell_embedded_window_new (void);
#endif /* __SHELL_EMBEDDED_WINDOW_H__ */

View File

@ -1,364 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include "shell-embedded-window-private.h"
#include "shell-global.h"
#include "shell-util.h"
#include <gdk/gdkx.h>
#include <meta/display.h>
#include <meta/window.h>
enum {
PROP_0,
PROP_WINDOW
};
typedef struct _ShellGtkEmbedPrivate ShellGtkEmbedPrivate;
struct _ShellGtkEmbedPrivate
{
ShellEmbeddedWindow *window;
ClutterActor *window_actor;
gulong window_actor_destroyed_handler;
gulong window_created_handler;
};
G_DEFINE_TYPE_WITH_PRIVATE (ShellGtkEmbed, shell_gtk_embed, CLUTTER_TYPE_CLONE);
static void shell_gtk_embed_set_window (ShellGtkEmbed *embed,
ShellEmbeddedWindow *window);
static void
shell_gtk_embed_on_window_destroy (GtkWidget *object,
ShellGtkEmbed *embed)
{
shell_gtk_embed_set_window (embed, NULL);
}
static void
shell_gtk_embed_remove_window_actor (ShellGtkEmbed *embed)
{
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
if (priv->window_actor)
{
g_clear_signal_handler (&priv->window_actor_destroyed_handler,
priv->window_actor);
g_object_unref (priv->window_actor);
priv->window_actor = NULL;
}
clutter_clone_set_source (CLUTTER_CLONE (embed), NULL);
}
static void
shell_gtk_embed_window_created_cb (MetaDisplay *display,
MetaWindow *window,
ShellGtkEmbed *embed)
{
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
Window xwindow = meta_window_get_xwindow (window);
GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (priv->window));
if (gdk_window && xwindow == gdk_x11_window_get_xid (gdk_window))
{
ClutterActor *window_actor =
CLUTTER_ACTOR (meta_window_get_compositor_private (window));
GCallback remove_cb = G_CALLBACK (shell_gtk_embed_remove_window_actor);
cairo_region_t *empty_region;
clutter_clone_set_source (CLUTTER_CLONE (embed), window_actor);
/* We want to explicitly clear the clone source when the window
actor is destroyed because otherwise we might end up keeping
it alive after it has been disposed. Otherwise this can cause
a crash if there is a paint after mutter notices that the top
level window has been destroyed, which causes it to dispose
the window, and before the tray manager notices that the
window is gone which would otherwise reset the window and
unref the clone */
priv->window_actor = g_object_ref (window_actor);
priv->window_actor_destroyed_handler =
g_signal_connect_swapped (window_actor,
"destroy",
remove_cb,
embed);
/* Hide the original actor otherwise it will appear in the scene
as a normal window */
clutter_actor_set_opacity (window_actor, 0);
/* Also make sure it (or any of its children) doesn't block
events on wayland */
shell_util_set_hidden_from_pick (window_actor, TRUE);
/* Set an empty input shape on the window so that it can't get
any input. This probably isn't the ideal way to achieve this.
It would probably be better to force the window to go behind
Mutter's guard window, but this is quite difficult to do as
Mutter doesn't manage the stacking for override redirect
windows and the guard window is repeatedly lowered to the
bottom of the stack. */
empty_region = cairo_region_create ();
gdk_window_input_shape_combine_region (gdk_window,
empty_region,
0, 0 /* offset x/y */);
cairo_region_destroy (empty_region);
gdk_window_lower (gdk_window);
/* Now that we've found the window we don't need to listen for
new windows anymore */
g_clear_signal_handler (&priv->window_created_handler,
display);
}
}
static void
shell_gtk_embed_on_window_mapped (GtkWidget *object,
ShellGtkEmbed *embed)
{
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
MetaDisplay *display = shell_global_get_display (shell_global_get ());
if (priv->window_created_handler == 0 && priv->window_actor == NULL)
/* Listen for new windows so we can detect when Mutter has
created a MutterWindow for this window */
priv->window_created_handler =
g_signal_connect (display,
"window-created",
G_CALLBACK (shell_gtk_embed_window_created_cb),
embed);
}
static void
shell_gtk_embed_set_window (ShellGtkEmbed *embed,
ShellEmbeddedWindow *window)
{
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
MetaDisplay *display = shell_global_get_display (shell_global_get ());
if (priv->window)
{
g_clear_signal_handler (&priv->window_created_handler, display);
shell_gtk_embed_remove_window_actor (embed);
_shell_embedded_window_set_actor (priv->window, NULL);
g_object_unref (priv->window);
g_signal_handlers_disconnect_by_func (priv->window,
(gpointer)shell_gtk_embed_on_window_destroy,
embed);
g_signal_handlers_disconnect_by_func (priv->window,
(gpointer)shell_gtk_embed_on_window_mapped,
embed);
}
priv->window = window;
if (priv->window)
{
g_object_ref (priv->window);
_shell_embedded_window_set_actor (priv->window, embed);
g_signal_connect (priv->window, "destroy",
G_CALLBACK (shell_gtk_embed_on_window_destroy), embed);
g_signal_connect (priv->window, "map",
G_CALLBACK (shell_gtk_embed_on_window_mapped), embed);
}
clutter_actor_queue_relayout (CLUTTER_ACTOR (embed));
}
static void
shell_gtk_embed_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ShellGtkEmbed *embed = SHELL_GTK_EMBED (object);
switch (prop_id)
{
case PROP_WINDOW:
shell_gtk_embed_set_window (embed, (ShellEmbeddedWindow *)g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_gtk_embed_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ShellGtkEmbed *embed = SHELL_GTK_EMBED (object);
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
switch (prop_id)
{
case PROP_WINDOW:
g_value_set_object (value, priv->window);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_gtk_embed_get_preferred_width (ClutterActor *actor,
float for_height,
float *min_width_p,
float *natural_width_p)
{
ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
if (priv->window
&& gtk_widget_get_visible (GTK_WIDGET (priv->window)))
{
GtkRequisition min_req, natural_req;
gtk_widget_get_preferred_size (GTK_WIDGET (priv->window), &min_req, &natural_req);
*min_width_p = min_req.width;
*natural_width_p = natural_req.width;
}
else
*min_width_p = *natural_width_p = 0;
}
static void
shell_gtk_embed_get_preferred_height (ClutterActor *actor,
float for_width,
float *min_height_p,
float *natural_height_p)
{
ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
if (priv->window
&& gtk_widget_get_visible (GTK_WIDGET (priv->window)))
{
GtkRequisition min_req, natural_req;
gtk_widget_get_preferred_size (GTK_WIDGET (priv->window), &min_req, &natural_req);
*min_height_p = min_req.height;
*natural_height_p = natural_req.height;
}
else
*min_height_p = *natural_height_p = 0;
}
static void
shell_gtk_embed_allocate (ClutterActor *actor,
const ClutterActorBox *box)
{
ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
float wx, wy;
CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->
allocate (actor, box);
/* Find the actor's new coordinates in terms of the stage (which is
* priv->window's parent window.
*/
clutter_actor_get_transformed_position (actor, &wx, &wy);
_shell_embedded_window_allocate (priv->window,
(int)(0.5 + wx), (int)(0.5 + wy),
box->x2 - box->x1,
box->y2 - box->y1);
}
static void
shell_gtk_embed_map (ClutterActor *actor)
{
ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
_shell_embedded_window_map (priv->window);
CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->map (actor);
}
static void
shell_gtk_embed_unmap (ClutterActor *actor)
{
ShellGtkEmbed *embed = SHELL_GTK_EMBED (actor);
ShellGtkEmbedPrivate *priv = shell_gtk_embed_get_instance_private (embed);
_shell_embedded_window_unmap (priv->window);
CLUTTER_ACTOR_CLASS (shell_gtk_embed_parent_class)->unmap (actor);
}
static void
shell_gtk_embed_dispose (GObject *object)
{
ShellGtkEmbed *embed = SHELL_GTK_EMBED (object);
G_OBJECT_CLASS (shell_gtk_embed_parent_class)->dispose (object);
shell_gtk_embed_set_window (embed, NULL);
}
static void
shell_gtk_embed_class_init (ShellGtkEmbedClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
object_class->get_property = shell_gtk_embed_get_property;
object_class->set_property = shell_gtk_embed_set_property;
object_class->dispose = shell_gtk_embed_dispose;
actor_class->get_preferred_width = shell_gtk_embed_get_preferred_width;
actor_class->get_preferred_height = shell_gtk_embed_get_preferred_height;
actor_class->allocate = shell_gtk_embed_allocate;
actor_class->map = shell_gtk_embed_map;
actor_class->unmap = shell_gtk_embed_unmap;
g_object_class_install_property (object_class,
PROP_WINDOW,
g_param_spec_object ("window",
"Window",
"ShellEmbeddedWindow to embed",
SHELL_TYPE_EMBEDDED_WINDOW,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
static void
shell_gtk_embed_init (ShellGtkEmbed *embed)
{
}
/*
* Public API
*/
ClutterActor *
shell_gtk_embed_new (ShellEmbeddedWindow *window)
{
g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL);
return g_object_new (SHELL_TYPE_GTK_EMBED,
"window", window,
NULL);
}

View File

@ -1,20 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_GTK_EMBED_H__
#define __SHELL_GTK_EMBED_H__
#include <clutter/clutter.h>
#include "shell-embedded-window.h"
#define SHELL_TYPE_GTK_EMBED (shell_gtk_embed_get_type ())
G_DECLARE_DERIVABLE_TYPE (ShellGtkEmbed, shell_gtk_embed,
SHELL, GTK_EMBED, ClutterClone)
struct _ShellGtkEmbedClass
{
ClutterCloneClass parent_class;
};
ClutterActor *shell_gtk_embed_new (ShellEmbeddedWindow *window);
#endif /* __SHELL_GTK_EMBED_H__ */

View File

@ -0,0 +1,11 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef SHELL_TRAY_ICON_PRIVATE_H
#define SHELL_TRAY_ICON_PRIVATE_H
#include "shell-tray-icon.h"
#include "na-tray-child.h"
ClutterActor * shell_tray_icon_new (NaTrayChild *tray_child);
#endif /* SHELL_TRAY_ICON_PRIVATE_H */

View File

@ -2,13 +2,14 @@
#include "config.h" #include "config.h"
#include "shell-tray-icon.h" #include "shell-global.h"
#include "shell-gtk-embed.h" #include "shell-tray-icon-private.h"
#include "shell-util.h"
#include "tray/na-tray-child.h" #include "tray/na-tray-child.h"
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
#include "st.h" #include "st.h"
#include <X11/Xatom.h>
enum { enum {
PROP_0, PROP_0,
@ -21,76 +22,86 @@ typedef struct _ShellTrayIconPrivate ShellTrayIconPrivate;
struct _ShellTrayIcon struct _ShellTrayIcon
{ {
ShellGtkEmbed parent; ClutterClone parent;
NaTrayChild *tray_child;
ShellTrayIconPrivate *priv; ClutterActor *window_actor;
};
struct _ShellTrayIconPrivate
{
NaTrayChild *socket;
gulong window_actor_destroyed_handler;
gulong window_created_handler;
pid_t pid; pid_t pid;
char *title, *wm_class; char *title;
char *wm_class;
}; };
G_DEFINE_TYPE_WITH_PRIVATE (ShellTrayIcon, shell_tray_icon, SHELL_TYPE_GTK_EMBED); G_DEFINE_TYPE (ShellTrayIcon, shell_tray_icon, CLUTTER_TYPE_CLONE);
static void static void
shell_tray_icon_finalize (GObject *object) shell_tray_icon_finalize (GObject *object)
{ {
ShellTrayIcon *icon = SHELL_TRAY_ICON (object); ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
g_free (icon->priv->title); g_free (icon->title);
g_free (icon->priv->wm_class); g_free (icon->wm_class);
G_OBJECT_CLASS (shell_tray_icon_parent_class)->finalize (object); G_OBJECT_CLASS (shell_tray_icon_parent_class)->finalize (object);
} }
static void static void
shell_tray_icon_constructed (GObject *object) shell_tray_icon_remove_window_actor (ShellTrayIcon *tray_icon)
{ {
GdkWindow *icon_app_window; if (tray_icon->window_actor)
ShellTrayIcon *icon = SHELL_TRAY_ICON (object); {
ShellEmbeddedWindow *window; g_clear_signal_handler (&tray_icon->window_actor_destroyed_handler,
GdkDisplay *display; tray_icon->window_actor);
Window plug_xid; g_clear_object (&tray_icon->window_actor);
Atom _NET_WM_PID, type; }
int result, format;
gulong nitems, bytes_after, *val = NULL;
/* We do all this now rather than computing it on the fly later, clutter_clone_set_source (CLUTTER_CLONE (tray_icon), NULL);
* because the shell may want to see their values from a }
* tray-icon-removed signal handler, at which point the plug has
* already been removed from the socket.
*/
g_object_get (object, "window", &window, NULL); static void
g_return_if_fail (window != NULL); shell_tray_icon_window_created_cb (MetaDisplay *display,
icon->priv->socket = NA_TRAY_CHILD (gtk_bin_get_child (GTK_BIN (window))); MetaWindow *window,
g_object_unref (window); ShellTrayIcon *tray_icon)
{
Window xwindow = meta_window_get_xwindow (window);
icon->priv->title = na_tray_child_get_title (icon->priv->socket); if (tray_icon->tray_child &&
na_tray_child_get_wm_class (icon->priv->socket, NULL, &icon->priv->wm_class); xwindow == na_xembed_get_socket_window (NA_XEMBED (tray_icon->tray_child)))
{
ClutterActor *window_actor =
CLUTTER_ACTOR (meta_window_get_compositor_private (window));
icon_app_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket)); clutter_clone_set_source (CLUTTER_CLONE (tray_icon), window_actor);
plug_xid = GDK_WINDOW_XID (icon_app_window);
display = gtk_widget_get_display (GTK_WIDGET (icon->priv->socket)); /* We want to explicitly clear the clone source when the window
gdk_x11_display_error_trap_push (display); actor is destroyed because otherwise we might end up keeping
_NET_WM_PID = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PID"); it alive after it has been disposed. Otherwise this can cause
result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), plug_xid, a crash if there is a paint after mutter notices that the top
_NET_WM_PID, 0, G_MAXLONG, False, XA_CARDINAL, level window has been destroyed, which causes it to dispose
&type, &format, &nitems, the window, and before the tray manager notices that the
&bytes_after, (guchar **)&val); window is gone which would otherwise reset the window and
if (!gdk_x11_display_error_trap_pop (display) && unref the clone */
result == Success && tray_icon->window_actor = g_object_ref (window_actor);
type == XA_CARDINAL && tray_icon->window_actor_destroyed_handler =
nitems == 1) g_signal_connect_swapped (window_actor,
icon->priv->pid = *val; "destroy",
G_CALLBACK (shell_tray_icon_remove_window_actor),
tray_icon);
if (val) /* Hide the original actor otherwise it will appear in the scene
XFree (val); as a normal window */
clutter_actor_set_opacity (window_actor, 0);
/* Also make sure it (or any of its children) doesn't block
events on wayland */
shell_util_set_hidden_from_pick (window_actor, TRUE);
/* Now that we've found the window we don't need to listen for
new windows anymore */
g_clear_signal_handler (&tray_icon->window_created_handler,
display);
}
} }
static void static void
@ -104,15 +115,15 @@ shell_tray_icon_get_property (GObject *object,
switch (prop_id) switch (prop_id)
{ {
case PROP_PID: case PROP_PID:
g_value_set_uint (value, icon->priv->pid); g_value_set_uint (value, icon->pid);
break; break;
case PROP_TITLE: case PROP_TITLE:
g_value_set_string (value, icon->priv->title); g_value_set_string (value, icon->title);
break; break;
case PROP_WM_CLASS: case PROP_WM_CLASS:
g_value_set_string (value, icon->priv->wm_class); g_value_set_string (value, icon->wm_class);
break; break;
default: default:
@ -121,14 +132,78 @@ shell_tray_icon_get_property (GObject *object,
} }
} }
static void
shell_tray_icon_dispose (GObject *object)
{
ShellTrayIcon *tray_icon = SHELL_TRAY_ICON (object);
MetaDisplay *display = shell_global_get_display (shell_global_get ());
g_clear_signal_handler (&tray_icon->window_created_handler,
display);
shell_tray_icon_remove_window_actor (tray_icon);
G_OBJECT_CLASS (shell_tray_icon_parent_class)->dispose (object);
}
static void
shell_tray_icon_get_preferred_width (ClutterActor *actor,
float for_height,
float *min_width_p,
float *natural_width_p)
{
ShellTrayIcon *tray_icon = SHELL_TRAY_ICON (actor);
int width;
na_xembed_get_size (NA_XEMBED (tray_icon->tray_child), &width, NULL);
*min_width_p = width;
*natural_width_p = width;
}
static void
shell_tray_icon_get_preferred_height (ClutterActor *actor,
float for_width,
float *min_height_p,
float *natural_height_p)
{
ShellTrayIcon *tray_icon = SHELL_TRAY_ICON (actor);
int height;
na_xembed_get_size (NA_XEMBED (tray_icon->tray_child), NULL, &height);
*min_height_p = height;
*natural_height_p = height;
}
static void
shell_tray_icon_allocate (ClutterActor *actor,
const ClutterActorBox *box)
{
ShellTrayIcon *tray_icon = SHELL_TRAY_ICON (actor);
float wx, wy;
CLUTTER_ACTOR_CLASS (shell_tray_icon_parent_class)->allocate (actor, box);
/* Find the actor's new coordinates in terms of the stage.
*/
clutter_actor_get_transformed_position (actor, &wx, &wy);
na_xembed_set_root_position (NA_XEMBED (tray_icon->tray_child),
(int)(0.5 + wx), (int)(0.5 + wy));
}
static void static void
shell_tray_icon_class_init (ShellTrayIconClass *klass) shell_tray_icon_class_init (ShellTrayIconClass *klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
object_class->get_property = shell_tray_icon_get_property; object_class->get_property = shell_tray_icon_get_property;
object_class->constructed = shell_tray_icon_constructed; object_class->finalize = shell_tray_icon_finalize;
object_class->finalize = shell_tray_icon_finalize; object_class->dispose = shell_tray_icon_dispose;
actor_class->get_preferred_width = shell_tray_icon_get_preferred_width;
actor_class->get_preferred_height = shell_tray_icon_get_preferred_height;
actor_class->allocate = shell_tray_icon_allocate;
g_object_class_install_property (object_class, g_object_class_install_property (object_class,
PROP_PID, PROP_PID,
@ -156,20 +231,77 @@ shell_tray_icon_class_init (ShellTrayIconClass *klass)
static void static void
shell_tray_icon_init (ShellTrayIcon *icon) shell_tray_icon_init (ShellTrayIcon *icon)
{ {
icon->priv = shell_tray_icon_get_instance_private (icon); }
static void
shell_tray_icon_set_child (ShellTrayIcon *tray_icon,
NaTrayChild *tray_child)
{
MetaDisplay *display = shell_global_get_display (shell_global_get ());
MetaX11Display *x11_display;
Display *xdisplay;
Window plug_xid;
Atom type;
int result, format;
gulong nitems, bytes_after, *val = NULL;
g_return_if_fail (tray_icon != NULL);
g_return_if_fail (tray_child != NULL);
x11_display = meta_display_get_x11_display (display);
/* We do all this now rather than computing it on the fly later,
* because the shell may want to see their values from a
* tray-icon-removed signal handler, at which point the plug has
* already been removed from the socket.
*/
tray_icon->tray_child = tray_child;
tray_icon->title = na_tray_child_get_title (tray_icon->tray_child);
na_tray_child_get_wm_class (tray_icon->tray_child,
NULL, &tray_icon->wm_class);
plug_xid = na_xembed_get_plug_window (NA_XEMBED (tray_icon->tray_child));
xdisplay = meta_x11_display_get_xdisplay (x11_display);
meta_x11_error_trap_push (x11_display);
result = XGetWindowProperty (xdisplay, plug_xid,
XInternAtom (xdisplay, "_NET_WM_PID", False),
0, G_MAXLONG, False, XA_CARDINAL,
&type, &format, &nitems,
&bytes_after, (guchar **)&val);
if (!meta_x11_error_trap_pop_with_return (x11_display) &&
result == Success &&
type == XA_CARDINAL &&
nitems == 1)
tray_icon->pid = *val;
if (val)
XFree (val);
tray_icon->window_created_handler =
g_signal_connect (display,
"window-created",
G_CALLBACK (shell_tray_icon_window_created_cb),
tray_icon);
} }
/* /*
* Public API * Public API
*/ */
ClutterActor * ClutterActor *
shell_tray_icon_new (ShellEmbeddedWindow *window) shell_tray_icon_new (NaTrayChild *tray_child)
{ {
g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL); ShellTrayIcon *tray_icon;
return g_object_new (SHELL_TYPE_TRAY_ICON, g_return_val_if_fail (NA_IS_TRAY_CHILD (tray_child), NULL);
"window", window,
NULL); tray_icon = g_object_new (SHELL_TYPE_TRAY_ICON, NULL);
shell_tray_icon_set_child (tray_icon, tray_child);
return CLUTTER_ACTOR (tray_icon);
} }
/** /**
@ -187,37 +319,37 @@ void
shell_tray_icon_click (ShellTrayIcon *icon, shell_tray_icon_click (ShellTrayIcon *icon,
ClutterEvent *event) ClutterEvent *event)
{ {
MetaDisplay *display = shell_global_get_display (shell_global_get ());
MetaX11Display *x11_display;
XKeyEvent xkevent; XKeyEvent xkevent;
XButtonEvent xbevent; XButtonEvent xbevent;
XCrossingEvent xcevent; XCrossingEvent xcevent;
GdkDisplay *display;
GdkWindow *remote_window;
GdkScreen *screen;
int x_root, y_root;
Display *xdisplay; Display *xdisplay;
Window xwindow, xrootwindow; Window xwindow, xrootwindow;
ClutterEventType event_type = clutter_event_type (event); ClutterEventType event_type = clutter_event_type (event);
int width, height;
g_return_if_fail (event_type == CLUTTER_BUTTON_RELEASE || g_return_if_fail (event_type == CLUTTER_BUTTON_RELEASE ||
event_type == CLUTTER_KEY_PRESS || event_type == CLUTTER_KEY_PRESS ||
event_type == CLUTTER_KEY_RELEASE); event_type == CLUTTER_KEY_RELEASE);
remote_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket)); x11_display = meta_display_get_x11_display (display);
if (remote_window == NULL) if (!x11_display)
return;
xwindow = na_xembed_get_plug_window (NA_XEMBED (icon->tray_child));
if (xwindow == None)
{ {
g_warning ("shell tray: plug window is gone"); g_warning ("shell tray: plug window is gone");
return; return;
} }
xdisplay = GDK_WINDOW_XDISPLAY (remote_window);
display = gdk_x11_lookup_xdisplay (xdisplay); na_xembed_get_size (NA_XEMBED (icon->tray_child), &width, &height);
gdk_x11_display_error_trap_push (display);
xwindow = GDK_WINDOW_XID (remote_window); meta_x11_error_trap_push (x11_display);
screen = gdk_window_get_screen (remote_window);
xrootwindow = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
gdk_window_get_origin (remote_window, &x_root, &y_root);
xdisplay = meta_x11_display_get_xdisplay (x11_display);
xrootwindow = XDefaultRootWindow (xdisplay);
/* First make the icon believe the pointer is inside it */ /* First make the icon believe the pointer is inside it */
xcevent.type = EnterNotify; xcevent.type = EnterNotify;
@ -225,10 +357,10 @@ shell_tray_icon_click (ShellTrayIcon *icon,
xcevent.root = xrootwindow; xcevent.root = xrootwindow;
xcevent.subwindow = None; xcevent.subwindow = None;
xcevent.time = clutter_event_get_time (event); xcevent.time = clutter_event_get_time (event);
xcevent.x = gdk_window_get_width (remote_window) / 2; xcevent.x = width / 2;
xcevent.y = gdk_window_get_height (remote_window) / 2; xcevent.y = height / 2;
xcevent.x_root = x_root + xcevent.x; xcevent.x_root = xcevent.x;
xcevent.y_root = y_root + xcevent.y; xcevent.y_root = xcevent.y;
xcevent.mode = NotifyNormal; xcevent.mode = NotifyNormal;
xcevent.detail = NotifyNonlinear; xcevent.detail = NotifyNonlinear;
xcevent.same_screen = True; xcevent.same_screen = True;
@ -290,5 +422,5 @@ shell_tray_icon_click (ShellTrayIcon *icon,
xcevent.type = LeaveNotify; xcevent.type = LeaveNotify;
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent); XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
gdk_x11_display_error_trap_pop_ignored (display); meta_x11_error_trap_pop (x11_display);
} }

View File

@ -2,15 +2,13 @@
#ifndef __SHELL_TRAY_ICON_H__ #ifndef __SHELL_TRAY_ICON_H__
#define __SHELL_TRAY_ICON_H__ #define __SHELL_TRAY_ICON_H__
#include "shell-gtk-embed.h" #include <clutter/clutter.h>
#define SHELL_TYPE_TRAY_ICON (shell_tray_icon_get_type ()) #define SHELL_TYPE_TRAY_ICON (shell_tray_icon_get_type ())
G_DECLARE_FINAL_TYPE (ShellTrayIcon, shell_tray_icon, G_DECLARE_FINAL_TYPE (ShellTrayIcon, shell_tray_icon,
SHELL, TRAY_ICON, ShellGtkEmbed) SHELL, TRAY_ICON, ClutterClone)
ClutterActor *shell_tray_icon_new (ShellEmbeddedWindow *window); void shell_tray_icon_click (ShellTrayIcon *icon,
ClutterEvent *event);
void shell_tray_icon_click (ShellTrayIcon *icon,
ClutterEvent *event);
#endif /* __SHELL_TRAY_ICON_H__ */ #endif /* __SHELL_TRAY_ICON_H__ */

View File

@ -4,14 +4,12 @@
#include <clutter/clutter.h> #include <clutter/clutter.h>
#include <girepository.h> #include <girepository.h>
#include <gtk/gtk.h>
#include <meta/display.h> #include <meta/display.h>
#include "shell-tray-manager.h" #include "shell-tray-manager.h"
#include "na-tray-manager.h" #include "na-tray-manager.h"
#include "shell-tray-icon.h" #include "shell-tray-icon-private.h"
#include "shell-embedded-window.h"
#include "shell-global.h" #include "shell-global.h"
typedef struct _ShellTrayManagerPrivate ShellTrayManagerPrivate; typedef struct _ShellTrayManagerPrivate ShellTrayManagerPrivate;
@ -33,8 +31,7 @@ struct _ShellTrayManagerPrivate {
typedef struct { typedef struct {
ShellTrayManager *manager; ShellTrayManager *manager;
GtkWidget *socket; NaTrayChild *tray_child;
GtkWidget *window;
ClutterActor *actor; ClutterActor *actor;
} ShellTrayManagerChild; } ShellTrayManagerChild;
@ -60,15 +57,19 @@ static const ClutterColor default_color = { 0x00, 0x00, 0x00, 0xff };
static void shell_tray_manager_release_resources (ShellTrayManager *manager); static void shell_tray_manager_release_resources (ShellTrayManager *manager);
static void na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *child, gpointer manager); static void na_tray_icon_added (NaTrayManager *na_manager,
static void na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *child, gpointer manager); NaTrayChild *child,
gpointer manager);
static void na_tray_icon_removed (NaTrayManager *na_manager,
NaTrayChild *child,
gpointer manager);
static void static void
free_tray_icon (gpointer data) free_tray_icon (gpointer data)
{ {
ShellTrayManagerChild *child = data; ShellTrayManagerChild *child = data;
gtk_widget_destroy (child->window);
if (child->actor) if (child->actor)
{ {
g_signal_handlers_disconnect_matched (child->actor, G_SIGNAL_MATCH_DATA, g_signal_handlers_disconnect_matched (child->actor, G_SIGNAL_MATCH_DATA,
@ -188,13 +189,19 @@ shell_tray_manager_new (void)
static void static void
shell_tray_manager_ensure_resources (ShellTrayManager *manager) shell_tray_manager_ensure_resources (ShellTrayManager *manager)
{ {
MetaDisplay *display;
MetaX11Display *x11_display;
if (manager->priv->na_manager != NULL) if (manager->priv->na_manager != NULL)
return; return;
manager->priv->icons = g_hash_table_new_full (NULL, NULL, manager->priv->icons = g_hash_table_new_full (NULL, NULL,
NULL, free_tray_icon); NULL, free_tray_icon);
manager->priv->na_manager = na_tray_manager_new (); display = shell_global_get_display (shell_global_get ());
x11_display = meta_display_get_x11_display (display);
manager->priv->na_manager = na_tray_manager_new (x11_display);
g_signal_connect (manager->priv->na_manager, "tray-icon-added", g_signal_connect (manager->priv->na_manager, "tray-icon-added",
G_CALLBACK (na_tray_icon_added), manager); G_CALLBACK (na_tray_icon_added), manager);
@ -231,7 +238,7 @@ static void
shell_tray_manager_manage_screen_internal (ShellTrayManager *manager) shell_tray_manager_manage_screen_internal (ShellTrayManager *manager)
{ {
shell_tray_manager_ensure_resources (manager); shell_tray_manager_ensure_resources (manager);
na_tray_manager_manage_screen (manager->priv->na_manager); na_tray_manager_manage (manager->priv->na_manager);
} }
void void
@ -277,98 +284,63 @@ shell_tray_manager_unmanage_screen (ShellTrayManager *manager)
} }
static void static void
shell_tray_manager_child_on_realize (GtkWidget *widget, on_plug_added (NaTrayChild *tray_child,
ShellTrayManagerChild *child)
{
/* If the tray child is using an RGBA colormap (and so we have real
* transparency), we don't need to worry about the background. If
* not, we obey the bg-color property by creating a cairo pattern of
* that color and setting it as our background. Then "parent-relative"
* background on the socket and the plug within that will cause
* the icons contents to appear on top of our background color.
*/
if (!na_tray_child_has_alpha (NA_TRAY_CHILD (child->socket)))
{
ClutterColor color = child->manager->priv->bg_color;
cairo_pattern_t *bg_pattern;
bg_pattern = cairo_pattern_create_rgb (color.red / 255.,
color.green / 255.,
color.blue / 255.);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gdk_window_set_background_pattern (gtk_widget_get_window (widget),
bg_pattern);
G_GNUC_END_IGNORE_DEPRECATIONS
cairo_pattern_destroy (bg_pattern);
}
}
static void
on_plug_added (GtkSocket *socket,
ShellTrayManager *manager) ShellTrayManager *manager)
{ {
ShellTrayManagerChild *child; ShellTrayManagerChild *child;
g_signal_handlers_disconnect_by_func (socket, on_plug_added, manager); g_signal_handlers_disconnect_by_func (tray_child, on_plug_added, manager);
child = g_hash_table_lookup (manager->priv->icons, socket); child = g_hash_table_lookup (manager->priv->icons, tray_child);
child->actor = shell_tray_icon_new (SHELL_EMBEDDED_WINDOW (child->window)); child->actor = shell_tray_icon_new (tray_child);
g_object_ref_sink (child->actor); g_object_ref_sink (child->actor);
na_xembed_set_background_color (NA_XEMBED (tray_child),
&manager->priv->bg_color);
g_signal_emit (manager, shell_tray_manager_signals[TRAY_ICON_ADDED], 0, g_signal_emit (manager, shell_tray_manager_signals[TRAY_ICON_ADDED], 0,
child->actor); child->actor);
} }
static void static void
na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket, na_tray_icon_added (NaTrayManager *na_manager,
gpointer user_data) NaTrayChild *tray_child,
gpointer user_data)
{ {
ShellTrayManager *manager = user_data; ShellTrayManager *manager = user_data;
GtkWidget *win;
ShellTrayManagerChild *child; ShellTrayManagerChild *child;
win = shell_embedded_window_new ();
gtk_container_add (GTK_CONTAINER (win), socket);
/* The visual of the socket matches that of its contents; make
* the window we put it in match that as well */
gtk_widget_set_visual (win, gtk_widget_get_visual (socket));
child = g_new0 (ShellTrayManagerChild, 1); child = g_new0 (ShellTrayManagerChild, 1);
child->manager = manager; child->manager = manager;
child->window = win; child->tray_child = tray_child;
child->socket = socket;
g_signal_connect (win, "realize", g_hash_table_insert (manager->priv->icons, tray_child, child);
G_CALLBACK (shell_tray_manager_child_on_realize), child);
gtk_widget_show_all (win); g_signal_connect (tray_child, "plug-added",
G_CALLBACK (on_plug_added), manager);
g_hash_table_insert (manager->priv->icons, socket, child);
g_signal_connect (socket, "plug-added", G_CALLBACK (on_plug_added), manager);
} }
static void static void
na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *socket, na_tray_icon_removed (NaTrayManager *na_manager,
gpointer user_data) NaTrayChild *tray_child,
gpointer user_data)
{ {
ShellTrayManager *manager = user_data; ShellTrayManager *manager = user_data;
ShellTrayManagerChild *child; ShellTrayManagerChild *child;
child = g_hash_table_lookup (manager->priv->icons, socket); child = g_hash_table_lookup (manager->priv->icons, tray_child);
g_return_if_fail (child != NULL); g_return_if_fail (child != NULL);
if (child->actor != NULL) if (child->actor != NULL)
{ {
/* Only emit signal if a corresponding tray-icon-added signal was emitted, /* Only emit signal if a corresponding tray-icon-added signal was emitted,
that is, if embedding did not fail and we got a plug-added * that is, if embedding did not fail and we got a plug-added
*/ */
g_signal_emit (manager, g_signal_emit (manager,
shell_tray_manager_signals[TRAY_ICON_REMOVED], 0, shell_tray_manager_signals[TRAY_ICON_REMOVED], 0,
child->actor); child->actor);
} }
g_hash_table_remove (manager->priv->icons, socket);
g_hash_table_remove (manager->priv->icons, tray_child);
} }

View File

@ -9,6 +9,6 @@ tray_sources = [
libtray = static_library('tray', tray_sources, libtray = static_library('tray', tray_sources,
c_args: ['-DG_LOG_DOMAIN="notification_area"'], c_args: ['-DG_LOG_DOMAIN="notification_area"'],
dependencies: [mutter_dep, gtk_dep], dependencies: [mutter_dep],
include_directories: conf_inc include_directories: conf_inc
) )

View File

@ -22,203 +22,16 @@
#include "na-tray-child.h" #include "na-tray-child.h"
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xutil.h>
G_DEFINE_TYPE (NaTrayChild, na_tray_child, GTK_TYPE_SOCKET) struct _NaTrayChild
static void
na_tray_child_finalize (GObject *object)
{ {
G_OBJECT_CLASS (na_tray_child_parent_class)->finalize (object); NaXembed parent_instance;
} guint parent_relative_bg : 1;
};
static void G_DEFINE_TYPE (NaTrayChild, na_tray_child, NA_TYPE_XEMBED)
na_tray_child_realize (GtkWidget *widget)
{
NaTrayChild *child = NA_TRAY_CHILD (widget);
GdkVisual *visual = gtk_widget_get_visual (widget);
GdkWindow *window;
GTK_WIDGET_CLASS (na_tray_child_parent_class)->realize (widget);
window = gtk_widget_get_window (widget);
if (child->has_alpha)
{
/* We have real transparency with an ARGB visual and the Composite
* extension. */
/* Set a transparent background */
cairo_pattern_t *transparent = cairo_pattern_create_rgba (0, 0, 0, 0);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gdk_window_set_background_pattern (window, transparent);
G_GNUC_END_IGNORE_DEPRECATIONS
cairo_pattern_destroy (transparent);
child->parent_relative_bg = FALSE;
}
else if (visual == gdk_window_get_visual (gdk_window_get_parent (window)))
{
/* Otherwise, if the visual matches the visual of the parent window, we
* can use a parent-relative background and fake transparency. */
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gdk_window_set_background_pattern (window, NULL);
G_GNUC_END_IGNORE_DEPRECATIONS
child->parent_relative_bg = TRUE;
}
else
{
/* Nothing to do; the icon will sit on top of an ugly gray box */
child->parent_relative_bg = FALSE;
}
gtk_widget_set_app_paintable (GTK_WIDGET (child),
child->parent_relative_bg || child->has_alpha);
/* Double-buffering will interfere with the parent-relative-background fake
* transparency, since the double-buffer code doesn't know how to fill in the
* background of the double-buffer correctly.
* The function is deprecated because it is only meaningful on X11 - the
* same is true for XEmbed of course, so just ignore the warning.
*/
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gtk_widget_set_double_buffered (GTK_WIDGET (child),
child->parent_relative_bg);
G_GNUC_END_IGNORE_DEPRECATIONS
}
static void
na_tray_child_style_set (GtkWidget *widget,
GtkStyle *previous_style)
{
/* The default handler resets the background according to the new style.
* We either use a transparent background or a parent-relative background
* and ignore the style background. So, just don't chain up.
*/
}
#if 0
/* This is adapted from code that was commented out in na-tray-manager.c; the
* code in na-tray-manager.c wouldn't have worked reliably, this will. So maybe
* it can be re-enabled. On other hand, things seem to be working fine without
* it.
*
* If reenabling, you need to hook it up in na_tray_child_class_init().
*/
static void
na_tray_child_size_request (GtkWidget *widget,
GtkRequisition *request)
{
GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_request (widget, request);
/*
* Make sure the icons have a meaningful size ..
*/
if ((request->width < 16) || (request->height < 16))
{
gint nw = MAX (24, request->width);
gint nh = MAX (24, request->height);
g_warning ("Tray icon has requested a size of (%ix%i), resizing to (%ix%i)",
req.width, req.height, nw, nh);
request->width = nw;
request->height = nh;
}
}
#endif
static void
na_tray_child_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
NaTrayChild *child = NA_TRAY_CHILD (widget);
GtkAllocation widget_allocation;
gboolean moved, resized;
gtk_widget_get_allocation (widget, &widget_allocation);
moved = (allocation->x != widget_allocation.x ||
allocation->y != widget_allocation.y);
resized = (allocation->width != widget_allocation.width ||
allocation->height != widget_allocation.height);
/* When we are allocating the widget while mapped we need special handling
* for both real and fake transparency.
*
* Real transparency: we need to invalidate and trigger a redraw of the old
* and new areas. (GDK really should handle this for us, but doesn't as of
* GTK+-2.14)
*
* Fake transparency: if the widget moved, we need to force the contents to
* be redrawn with the new offset for the parent-relative background.
*/
if ((moved || resized) && gtk_widget_get_mapped (widget))
{
if (na_tray_child_has_alpha (child))
gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
&widget_allocation, FALSE);
}
GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_allocate (widget,
allocation);
if ((moved || resized) && gtk_widget_get_mapped (widget))
{
if (na_tray_child_has_alpha (NA_TRAY_CHILD (widget)))
gdk_window_invalidate_rect (gdk_window_get_parent (gtk_widget_get_window (widget)),
&widget_allocation, FALSE);
else if (moved && child->parent_relative_bg)
na_tray_child_force_redraw (child);
}
}
/* The plug window should completely occupy the area of the child, so we won't
* get a draw event. But in case we do (the plug unmaps itself, say), this
* draw handler draws with real or fake transparency.
*/
static gboolean
na_tray_child_draw (GtkWidget *widget,
cairo_t *cr)
{
NaTrayChild *child = NA_TRAY_CHILD (widget);
if (na_tray_child_has_alpha (child))
{
/* Clear to transparent */
cairo_set_source_rgba (cr, 0, 0, 0, 0);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
}
else if (child->parent_relative_bg)
{
GdkWindow *window;
cairo_surface_t *target;
GdkRectangle clip_rect;
window = gtk_widget_get_window (widget);
target = cairo_get_group_target (cr);
gdk_cairo_get_clip_rectangle (cr, &clip_rect);
/* Clear to parent-relative pixmap
* We need to use direct X access here because GDK doesn't know about
* the parent relative pixmap. */
cairo_surface_flush (target);
XClearArea (GDK_WINDOW_XDISPLAY (window),
GDK_WINDOW_XID (window),
clip_rect.x, clip_rect.y,
clip_rect.width, clip_rect.height,
False);
cairo_surface_mark_dirty_rectangle (target,
clip_rect.x, clip_rect.y,
clip_rect.width, clip_rect.height);
}
return FALSE;
}
static void static void
na_tray_child_init (NaTrayChild *child) na_tray_child_init (NaTrayChild *child)
@ -228,78 +41,47 @@ na_tray_child_init (NaTrayChild *child)
static void static void
na_tray_child_class_init (NaTrayChildClass *klass) na_tray_child_class_init (NaTrayChildClass *klass)
{ {
GObjectClass *gobject_class;
GtkWidgetClass *widget_class;
gobject_class = (GObjectClass *)klass;
widget_class = (GtkWidgetClass *)klass;
gobject_class->finalize = na_tray_child_finalize;
widget_class->style_set = na_tray_child_style_set;
widget_class->realize = na_tray_child_realize;
widget_class->size_allocate = na_tray_child_size_allocate;
widget_class->draw = na_tray_child_draw;
} }
GtkWidget * NaTrayChild *
na_tray_child_new (GdkScreen *screen, na_tray_child_new (MetaX11Display *x11_display,
Window icon_window) Window icon_window)
{ {
XWindowAttributes window_attributes; XWindowAttributes window_attributes;
GdkDisplay *display;
Display *xdisplay; Display *xdisplay;
NaTrayChild *child; NaTrayChild *child;
GdkVisual *visual;
gboolean visual_has_alpha;
int red_prec, green_prec, blue_prec, depth;
int result; int result;
g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); g_return_val_if_fail (META_IS_X11_DISPLAY (x11_display), NULL);
g_return_val_if_fail (icon_window != None, NULL); g_return_val_if_fail (icon_window != None, NULL);
xdisplay = GDK_SCREEN_XDISPLAY (screen); xdisplay = meta_x11_display_get_xdisplay (x11_display);
display = gdk_x11_lookup_xdisplay (xdisplay);
/* We need to determine the visual of the window we are embedding and create /* We need to determine the visual of the window we are embedding and create
* the socket in the same visual. * the socket in the same visual.
*/ */
gdk_x11_display_error_trap_push (display); meta_x11_error_trap_push (x11_display);
result = XGetWindowAttributes (xdisplay, icon_window, result = XGetWindowAttributes (xdisplay, icon_window,
&window_attributes); &window_attributes);
gdk_x11_display_error_trap_pop_ignored (display); meta_x11_error_trap_pop (x11_display);
if (!result) /* Window already gone */ if (!result) /* Window already gone */
return NULL; return NULL;
visual = gdk_x11_screen_lookup_visual (screen, child = g_object_new (NA_TYPE_TRAY_CHILD,
window_attributes.visual->visualid); "x11-display", x11_display,
if (!visual) /* Icon window is on another screen? */ NULL);
return NULL;
child = g_object_new (NA_TYPE_TRAY_CHILD, NULL); return child;
child->icon_window = icon_window;
gtk_widget_set_visual (GTK_WIDGET (child), visual);
/* We have alpha if the visual has something other than red, green,
* and blue */
gdk_visual_get_red_pixel_details (visual, NULL, NULL, &red_prec);
gdk_visual_get_green_pixel_details (visual, NULL, NULL, &green_prec);
gdk_visual_get_blue_pixel_details (visual, NULL, NULL, &blue_prec);
depth = gdk_visual_get_depth (visual);
visual_has_alpha = red_prec + blue_prec + green_prec < depth;
child->has_alpha = visual_has_alpha;
return GTK_WIDGET (child);
} }
char * char *
na_tray_child_get_title (NaTrayChild *child) na_tray_child_get_title (NaTrayChild *child)
{ {
char *retval = NULL; char *retval = NULL;
GdkDisplay *display; MetaX11Display *x11_display;
Display *xdisplay;
Atom utf8_string, atom, type; Atom utf8_string, atom, type;
int result; int result;
int format; int format;
@ -309,22 +91,23 @@ na_tray_child_get_title (NaTrayChild *child)
g_return_val_if_fail (NA_IS_TRAY_CHILD (child), NULL); g_return_val_if_fail (NA_IS_TRAY_CHILD (child), NULL);
display = gtk_widget_get_display (GTK_WIDGET (child)); x11_display = na_xembed_get_x11_display (NA_XEMBED (child));
xdisplay = meta_x11_display_get_xdisplay (x11_display);
utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"); utf8_string = XInternAtom (xdisplay, "UTF8_STRING", False);
atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME"); atom = XInternAtom (xdisplay, "_NET_WM_NAME", False);
gdk_x11_display_error_trap_push (display); meta_x11_error_trap_push (x11_display);
result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), result = XGetWindowProperty (xdisplay,
child->icon_window, na_xembed_get_plug_window (NA_XEMBED (child)),
atom, atom,
0, G_MAXLONG, 0, G_MAXLONG,
False, utf8_string, False, utf8_string,
&type, &format, &nitems, &type, &format, &nitems,
&bytes_after, (guchar **)&val); &bytes_after, (guchar **)&val);
if (gdk_x11_display_error_trap_pop (display) || result != Success) if (meta_x11_error_trap_pop_with_return (x11_display) || result != Success)
return NULL; return NULL;
if (type != utf8_string || if (type != utf8_string ||
@ -349,73 +132,6 @@ na_tray_child_get_title (NaTrayChild *child)
return retval; return retval;
} }
/**
* na_tray_child_has_alpha;
* @child: a #NaTrayChild
*
* Checks if the child has an ARGB visual and real alpha transparence.
* (as opposed to faked alpha transparency with an parent-relative
* background)
*
* Return value: %TRUE if the child has an alpha transparency
*/
gboolean
na_tray_child_has_alpha (NaTrayChild *child)
{
g_return_val_if_fail (NA_IS_TRAY_CHILD (child), FALSE);
return child->has_alpha;
}
/* If we are faking transparency with a window-relative background, force a
* redraw of the icon. This should be called if the background changes or if
* the child is shifted with respect to the background.
*/
void
na_tray_child_force_redraw (NaTrayChild *child)
{
GtkWidget *widget = GTK_WIDGET (child);
if (gtk_widget_get_mapped (widget) && child->parent_relative_bg)
{
#if 1
/* Sending an ExposeEvent might cause redraw problems if the
* icon is expecting the server to clear-to-background before
* the redraw. It should be ok for GtkStatusIcon or EggTrayIcon.
*/
GdkDisplay *display = gtk_widget_get_display (widget);
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
XEvent xev;
GdkWindow *plug_window;
GtkAllocation allocation;
plug_window = gtk_socket_get_plug_window (GTK_SOCKET (child));
gtk_widget_get_allocation (widget, &allocation);
xev.xexpose.type = Expose;
xev.xexpose.window = GDK_WINDOW_XID (plug_window);
xev.xexpose.x = 0;
xev.xexpose.y = 0;
xev.xexpose.width = allocation.width;
xev.xexpose.height = allocation.height;
xev.xexpose.count = 0;
gdk_x11_display_error_trap_push (display);
XSendEvent (xdisplay,
xev.xexpose.window,
False, ExposureMask,
&xev);
gdk_x11_display_error_trap_pop_ignored (display);
#else
/* Hiding and showing is the safe way to do it, but can result in more
* flickering.
*/
gdk_window_hide (widget->window);
gdk_window_show (widget->window);
#endif
}
}
/* from libwnck/xutils.c, comes as LGPLv2+ */ /* from libwnck/xutils.c, comes as LGPLv2+ */
static char * static char *
latin1_to_utf8 (const char *latin1) latin1_to_utf8 (const char *latin1)
@ -437,21 +153,22 @@ latin1_to_utf8 (const char *latin1)
/* derived from libwnck/xutils.c, comes as LGPLv2+ */ /* derived from libwnck/xutils.c, comes as LGPLv2+ */
static void static void
_get_wmclass (Display *xdisplay, _get_wmclass (MetaX11Display *x11_display,
Window xwindow, Window xwindow,
char **res_class, char **res_class,
char **res_name) char **res_name)
{ {
GdkDisplay *display;
XClassHint ch; XClassHint ch;
Display *xdisplay;
ch.res_name = NULL; ch.res_name = NULL;
ch.res_class = NULL; ch.res_class = NULL;
display = gdk_x11_lookup_xdisplay (xdisplay); xdisplay = meta_x11_display_get_xdisplay (x11_display);
gdk_x11_display_error_trap_push (display);
meta_x11_error_trap_push (x11_display);
XGetClassHint (xdisplay, xwindow, &ch); XGetClassHint (xdisplay, xwindow, &ch);
gdk_x11_display_error_trap_pop_ignored (display); meta_x11_error_trap_pop (x11_display);
if (res_class) if (res_class)
*res_class = NULL; *res_class = NULL;
@ -491,14 +208,14 @@ na_tray_child_get_wm_class (NaTrayChild *child,
char **res_name, char **res_name,
char **res_class) char **res_class)
{ {
GdkDisplay *display; MetaX11Display *x11_display;
g_return_if_fail (NA_IS_TRAY_CHILD (child)); g_return_if_fail (NA_IS_TRAY_CHILD (child));
display = gtk_widget_get_display (GTK_WIDGET (child)); x11_display = na_xembed_get_x11_display (NA_XEMBED (child));
_get_wmclass (GDK_DISPLAY_XDISPLAY (display), _get_wmclass (x11_display,
child->icon_window, na_xembed_get_plug_window (NA_XEMBED (child)),
res_class, res_class,
res_name); res_name);
} }

View File

@ -21,42 +21,18 @@
#ifndef __NA_TRAY_CHILD_H__ #ifndef __NA_TRAY_CHILD_H__
#define __NA_TRAY_CHILD_H__ #define __NA_TRAY_CHILD_H__
#include <gtk/gtk.h> #include "na-xembed.h"
#include <gtk/gtkx.h>
#include <meta/meta-x11-errors.h>
G_BEGIN_DECLS G_BEGIN_DECLS
#define NA_TYPE_TRAY_CHILD (na_tray_child_get_type ()) #define NA_TYPE_TRAY_CHILD (na_tray_child_get_type ())
#define NA_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_CHILD, NaTrayChild)) G_DECLARE_FINAL_TYPE (NaTrayChild, na_tray_child, NA, TRAY_CHILD, NaXembed)
#define NA_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_CHILD, NaTrayChildClass))
#define NA_IS_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_CHILD))
#define NA_IS_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_CHILD))
#define NA_TRAY_CHILD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_CHILD, NaTrayChildClass))
typedef struct _NaTrayChild NaTrayChild; NaTrayChild *na_tray_child_new (MetaX11Display *x11_display,
typedef struct _NaTrayChildClass NaTrayChildClass; Window icon_window);
typedef struct _NaTrayChildChild NaTrayChildChild;
struct _NaTrayChild
{
GtkSocket parent_instance;
Window icon_window;
guint has_alpha : 1;
guint parent_relative_bg : 1;
};
struct _NaTrayChildClass
{
GtkSocketClass parent_class;
};
GType na_tray_child_get_type (void);
GtkWidget *na_tray_child_new (GdkScreen *screen,
Window icon_window);
char *na_tray_child_get_title (NaTrayChild *child); char *na_tray_child_get_title (NaTrayChild *child);
gboolean na_tray_child_has_alpha (NaTrayChild *child);
void na_tray_child_force_redraw (NaTrayChild *child);
void na_tray_child_get_wm_class (NaTrayChild *child, void na_tray_child_get_wm_class (NaTrayChild *child,
char **res_name, char **res_name,
char **res_class); char **res_class);

View File

@ -24,13 +24,8 @@
#include "na-tray-manager.h" #include "na-tray-manager.h"
#if defined (GDK_WINDOWING_X11)
#include <gdk/gdkx.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
#elif defined (GDK_WINDOWING_WIN32) #include <X11/Xutil.h>
#include <gdk/gdkwin32.h>
#endif
#include <gtk/gtk.h>
/* Signals */ /* Signals */
enum enum
@ -43,11 +38,17 @@ enum
LAST_SIGNAL LAST_SIGNAL
}; };
enum { static guint manager_signals[LAST_SIGNAL];
enum
{
PROP_0, PROP_0,
PROP_ORIENTATION PROP_X11_DISPLAY,
N_PROPS,
}; };
static GParamSpec *props[N_PROPS];
typedef struct typedef struct
{ {
long id, len; long id, len;
@ -55,24 +56,35 @@ typedef struct
long timeout; long timeout;
char *str; char *str;
#ifdef GDK_WINDOWING_X11
Window window; Window window;
#endif
} PendingMessage; } PendingMessage;
static guint manager_signals[LAST_SIGNAL]; struct _NaTrayManager
{
GObject parent_instance;
MetaX11Display *x11_display;
Atom selection_atom;
Atom opcode_atom;
Atom message_data_atom;
Window window;
ClutterColor fg;
ClutterColor error;
ClutterColor warning;
ClutterColor success;
unsigned int event_func_id;
GList *messages;
GHashTable *children;
};
#define SYSTEM_TRAY_REQUEST_DOCK 0 #define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
#define SYSTEM_TRAY_ORIENTATION_HORZ 0
#define SYSTEM_TRAY_ORIENTATION_VERT 1
#ifdef GDK_WINDOWING_X11
static gboolean na_tray_manager_check_running_screen_x11 (void);
#endif
static void na_tray_manager_finalize (GObject *object); static void na_tray_manager_finalize (GObject *object);
static void na_tray_manager_set_property (GObject *object, static void na_tray_manager_set_property (GObject *object,
guint prop_id, guint prop_id,
@ -90,8 +102,8 @@ G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT)
static void static void
na_tray_manager_init (NaTrayManager *manager) na_tray_manager_init (NaTrayManager *manager)
{ {
manager->invisible = NULL; manager->window = None;
manager->socket_table = g_hash_table_new (NULL, NULL); manager->children = g_hash_table_new (NULL, NULL);
manager->fg.red = 0; manager->fg.red = 0;
manager->fg.green = 0; manager->fg.green = 0;
@ -121,69 +133,55 @@ na_tray_manager_class_init (NaTrayManagerClass *klass)
gobject_class->set_property = na_tray_manager_set_property; gobject_class->set_property = na_tray_manager_set_property;
gobject_class->get_property = na_tray_manager_get_property; gobject_class->get_property = na_tray_manager_get_property;
g_object_class_install_property (gobject_class,
PROP_ORIENTATION,
g_param_spec_enum ("orientation",
"orientation",
"orientation",
GTK_TYPE_ORIENTATION,
GTK_ORIENTATION_HORIZONTAL,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT));
manager_signals[TRAY_ICON_ADDED] = manager_signals[TRAY_ICON_ADDED] =
g_signal_new ("tray_icon_added", g_signal_new ("tray_icon_added",
G_OBJECT_CLASS_TYPE (klass), G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST, 0,
G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added),
NULL, NULL, NULL, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_NONE, 1,
GTK_TYPE_SOCKET); NA_TYPE_TRAY_CHILD);
manager_signals[TRAY_ICON_REMOVED] = manager_signals[TRAY_ICON_REMOVED] =
g_signal_new ("tray_icon_removed", g_signal_new ("tray_icon_removed",
G_OBJECT_CLASS_TYPE (klass), G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST, 0,
G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed),
NULL, NULL, NULL, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_NONE, 1,
GTK_TYPE_SOCKET); NA_TYPE_TRAY_CHILD);
manager_signals[MESSAGE_SENT] = manager_signals[MESSAGE_SENT] =
g_signal_new ("message_sent", g_signal_new ("message_sent",
G_OBJECT_CLASS_TYPE (klass), G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST, 0,
G_STRUCT_OFFSET (NaTrayManagerClass, message_sent),
NULL, NULL, NULL, NULL, NULL, NULL,
G_TYPE_NONE, 4, G_TYPE_NONE, 4,
GTK_TYPE_SOCKET, NA_TYPE_TRAY_CHILD,
G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_LONG, G_TYPE_LONG,
G_TYPE_LONG); G_TYPE_LONG);
manager_signals[MESSAGE_CANCELLED] = manager_signals[MESSAGE_CANCELLED] =
g_signal_new ("message_cancelled", g_signal_new ("message_cancelled",
G_OBJECT_CLASS_TYPE (klass), G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST, 0,
G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled),
NULL, NULL, NULL, NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_NONE, 2,
GTK_TYPE_SOCKET, NA_TYPE_TRAY_CHILD,
G_TYPE_LONG); G_TYPE_LONG);
manager_signals[LOST_SELECTION] = manager_signals[LOST_SELECTION] =
g_signal_new ("lost_selection", g_signal_new ("lost_selection",
G_OBJECT_CLASS_TYPE (klass), G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST, 0,
G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection),
NULL, NULL, NULL, NULL, NULL, NULL,
G_TYPE_NONE, 0); G_TYPE_NONE, 0);
#if defined (GDK_WINDOWING_X11) props[PROP_X11_DISPLAY] =
/* Nothing */ g_param_spec_object ("x11-display",
#elif defined (GDK_WINDOWING_WIN32) "x11-display",
g_warning ("Port NaTrayManager to Win32"); "x11-display",
#else META_TYPE_X11_DISPLAY,
g_warning ("Port NaTrayManager to this GTK+ backend"); G_PARAM_READWRITE |
#endif G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (gobject_class, N_PROPS, props);
} }
static void static void
@ -196,7 +194,7 @@ na_tray_manager_finalize (GObject *object)
na_tray_manager_unmanage (manager); na_tray_manager_unmanage (manager);
g_list_free (manager->messages); g_list_free (manager->messages);
g_hash_table_destroy (manager->socket_table); g_hash_table_destroy (manager->children);
G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object); G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object);
} }
@ -211,8 +209,8 @@ na_tray_manager_set_property (GObject *object,
switch (prop_id) switch (prop_id)
{ {
case PROP_ORIENTATION: case PROP_X11_DISPLAY:
na_tray_manager_set_orientation (manager, g_value_get_enum (value)); manager->x11_display = g_value_get_object (value);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -230,8 +228,8 @@ na_tray_manager_get_property (GObject *object,
switch (prop_id) switch (prop_id)
{ {
case PROP_ORIENTATION: case PROP_X11_DISPLAY:
g_value_set_enum (value, manager->orientation); g_value_set_object (value, manager->x11_display);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -240,26 +238,24 @@ na_tray_manager_get_property (GObject *object,
} }
NaTrayManager * NaTrayManager *
na_tray_manager_new (void) na_tray_manager_new (MetaX11Display *x11_display)
{ {
NaTrayManager *manager; return g_object_new (NA_TYPE_TRAY_MANAGER,
"x11-display", x11_display,
manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL); NULL);
return manager;
} }
#ifdef GDK_WINDOWING_X11
static gboolean static gboolean
na_tray_manager_plug_removed (GtkSocket *socket, na_tray_manager_plug_removed (NaTrayChild *tray_child,
NaTrayManager *manager) NaTrayManager *manager)
{ {
NaTrayChild *child = NA_TRAY_CHILD (socket); Window icon_window;
g_hash_table_remove (manager->socket_table, icon_window = na_xembed_get_plug_window (NA_XEMBED (tray_child));
GINT_TO_POINTER (child->icon_window));
g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child); g_hash_table_remove (manager->children,
GINT_TO_POINTER (icon_window));
g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, tray_child);
/* This destroys the socket. */ /* This destroys the socket. */
return FALSE; return FALSE;
@ -267,49 +263,40 @@ na_tray_manager_plug_removed (GtkSocket *socket,
static void static void
na_tray_manager_handle_dock_request (NaTrayManager *manager, na_tray_manager_handle_dock_request (NaTrayManager *manager,
XClientMessageEvent *xevent) XClientMessageEvent *xevent)
{ {
Window icon_window = xevent->data.l[2]; Window icon_window = xevent->data.l[2];
GtkWidget *child; NaTrayChild *child;
if (g_hash_table_lookup (manager->socket_table, if (g_hash_table_lookup (manager->children,
GINT_TO_POINTER (icon_window))) GINT_TO_POINTER (icon_window)))
{ {
/* We already got this notification earlier, ignore this one */ /* We already got this notification earlier, ignore this one */
return; return;
} }
child = na_tray_child_new (manager->screen, icon_window); child = na_tray_child_new (manager->x11_display, icon_window);
if (child == NULL) /* already gone or other error */ if (child == NULL) /* already gone or other error */
return; return;
g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0, g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0,
child); child);
/* If the child wasn't attached, then destroy it */ g_signal_connect (child, "plug-removed",
G_CALLBACK (na_tray_manager_plug_removed), manager);
if (!GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (child)))) na_xembed_add_id (NA_XEMBED (child), icon_window);
{
gtk_widget_destroy (child);
return;
}
g_signal_connect (child, "plug_removed", if (!na_xembed_get_plug_window (NA_XEMBED (child)))
G_CALLBACK (na_tray_manager_plug_removed), manager);
gtk_socket_add_id (GTK_SOCKET (child), icon_window);
if (!gtk_socket_get_plug_window (GTK_SOCKET (child)))
{ {
/* Embedding failed, we won't get a plug-removed signal */ /* Embedding failed, we won't get a plug-removed signal */
/* This signal destroys the socket */ /* This signal destroys the tray child */
g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child); g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
return; return;
} }
g_hash_table_insert (manager->socket_table, g_hash_table_insert (manager->children,
GINT_TO_POINTER (icon_window), child); GINT_TO_POINTER (icon_window), child);
gtk_widget_show (child);
} }
static void static void
@ -321,10 +308,10 @@ pending_message_free (PendingMessage *message)
static void static void
na_tray_manager_handle_message_data (NaTrayManager *manager, na_tray_manager_handle_message_data (NaTrayManager *manager,
XClientMessageEvent *xevent) XClientMessageEvent *xevent)
{ {
GList *p; GList *p;
int len; int len;
/* Try to see if we can find the pending message in the list */ /* Try to see if we can find the pending message in the list */
for (p = manager->messages; p; p = p->next) for (p = manager->messages; p; p = p->next)
@ -340,21 +327,23 @@ na_tray_manager_handle_message_data (NaTrayManager *manager,
&xevent->data, len); &xevent->data, len);
msg->remaining_len -= len; msg->remaining_len -= len;
if (msg->remaining_len == 0) if (msg->remaining_len == 0)
{ {
GtkSocket *socket; NaTrayChild *child;
socket = g_hash_table_lookup (manager->socket_table, child = g_hash_table_lookup (manager->children,
GINT_TO_POINTER (msg->window)); GINT_TO_POINTER (msg->window));
if (socket) if (child)
g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, {
socket, msg->str, msg->id, msg->timeout); g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
child, msg->str, msg->id, msg->timeout);
}
pending_message_free (msg); pending_message_free (msg);
manager->messages = g_list_remove_link (manager->messages, p); manager->messages = g_list_remove_link (manager->messages, p);
g_list_free_1 (p); g_list_free_1 (p);
} }
break; break;
} }
@ -363,24 +352,22 @@ na_tray_manager_handle_message_data (NaTrayManager *manager,
static void static void
na_tray_manager_handle_begin_message (NaTrayManager *manager, na_tray_manager_handle_begin_message (NaTrayManager *manager,
XClientMessageEvent *xevent) XClientMessageEvent *xevent)
{ {
GtkSocket *socket; NaTrayChild *child;
GList *p; GList *p;
PendingMessage *msg; PendingMessage *msg;
long timeout; long timeout, len, id;
long len;
long id;
socket = g_hash_table_lookup (manager->socket_table, child = g_hash_table_lookup (manager->children,
GINT_TO_POINTER (xevent->window)); GINT_TO_POINTER (xevent->window));
/* we don't know about this tray icon, so ignore the message */ /* we don't know about this tray icon, so ignore the message */
if (!socket) if (!child)
return; return;
timeout = xevent->data.l[2]; timeout = xevent->data.l[2];
len = xevent->data.l[3]; len = xevent->data.l[3];
id = xevent->data.l[4]; id = xevent->data.l[4];
/* Check if the same message is already in the queue and remove it if so */ /* Check if the same message is already in the queue and remove it if so */
for (p = manager->messages; p; p = p->next) for (p = manager->messages; p; p = p->next)
@ -401,7 +388,7 @@ na_tray_manager_handle_begin_message (NaTrayManager *manager,
if (len == 0) if (len == 0)
{ {
g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
socket, "", id, timeout); child, "", id, timeout);
} }
else else
{ {
@ -420,11 +407,11 @@ na_tray_manager_handle_begin_message (NaTrayManager *manager,
static void static void
na_tray_manager_handle_cancel_message (NaTrayManager *manager, na_tray_manager_handle_cancel_message (NaTrayManager *manager,
XClientMessageEvent *xevent) XClientMessageEvent *xevent)
{ {
GList *p; NaTrayChild *child;
GtkSocket *socket; GList *p;
long id; long id;
id = xevent->data.l[2]; id = xevent->data.l[2];
@ -443,166 +430,98 @@ na_tray_manager_handle_cancel_message (NaTrayManager *manager,
} }
} }
socket = g_hash_table_lookup (manager->socket_table, child = g_hash_table_lookup (manager->children,
GINT_TO_POINTER (xevent->window)); GINT_TO_POINTER (xevent->window));
if (socket) if (child)
{ {
g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0, g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0,
socket, xevent->data.l[2]); child, xevent->data.l[2]);
} }
} }
static GdkFilterReturn static void
na_tray_manager_window_filter (GdkXEvent *xev, na_tray_manager_event_func (MetaX11Display *x11_display,
GdkEvent *event, XEvent *xevent,
gpointer data) gpointer data)
{ {
XEvent *xevent = (GdkXEvent *)xev;
NaTrayManager *manager = data; NaTrayManager *manager = data;
if (xevent->type == ClientMessage) if (xevent->type == ClientMessage &&
xevent->xany.window == manager->window)
{ {
/* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_REQUEST_DOCK */ /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_REQUEST_DOCK */
if (xevent->xclient.message_type == manager->opcode_atom && if (xevent->xclient.message_type == manager->opcode_atom &&
xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK)
{ {
na_tray_manager_handle_dock_request (manager, na_tray_manager_handle_dock_request (manager,
(XClientMessageEvent *) xevent); (XClientMessageEvent *) xevent);
return GDK_FILTER_REMOVE; }
}
/* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_BEGIN_MESSAGE */ /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_BEGIN_MESSAGE */
else if (xevent->xclient.message_type == manager->opcode_atom && else if (xevent->xclient.message_type == manager->opcode_atom &&
xevent->xclient.data.l[1] == SYSTEM_TRAY_BEGIN_MESSAGE) xevent->xclient.data.l[1] == SYSTEM_TRAY_BEGIN_MESSAGE)
{ {
na_tray_manager_handle_begin_message (manager, na_tray_manager_handle_begin_message (manager,
(XClientMessageEvent *) event); (XClientMessageEvent *) xevent);
return GDK_FILTER_REMOVE;
} }
/* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_CANCEL_MESSAGE */ /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_CANCEL_MESSAGE */
else if (xevent->xclient.message_type == manager->opcode_atom && else if (xevent->xclient.message_type == manager->opcode_atom &&
xevent->xclient.data.l[1] == SYSTEM_TRAY_CANCEL_MESSAGE) xevent->xclient.data.l[1] == SYSTEM_TRAY_CANCEL_MESSAGE)
{ {
na_tray_manager_handle_cancel_message (manager, na_tray_manager_handle_cancel_message (manager,
(XClientMessageEvent *) event); (XClientMessageEvent *) xevent);
return GDK_FILTER_REMOVE;
} }
/* _NET_SYSTEM_TRAY_MESSAGE_DATA */ /* _NET_SYSTEM_TRAY_MESSAGE_DATA */
else if (xevent->xclient.message_type == manager->message_data_atom) else if (xevent->xclient.message_type == manager->message_data_atom)
{ {
na_tray_manager_handle_message_data (manager, na_tray_manager_handle_message_data (manager,
(XClientMessageEvent *) event); (XClientMessageEvent *) xevent);
return GDK_FILTER_REMOVE;
} }
} }
else if (xevent->type == SelectionClear) else if (xevent->type == SelectionClear &&
xevent->xany.window == manager->window)
{ {
g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
na_tray_manager_unmanage (manager); na_tray_manager_unmanage (manager);
} }
return GDK_FILTER_CONTINUE;
} }
#if 0
//FIXME investigate why this doesn't work
static gboolean
na_tray_manager_selection_clear_event (GtkWidget *widget,
GdkEventSelection *event,
NaTrayManager *manager)
{
g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
na_tray_manager_unmanage (manager);
return FALSE;
}
#endif
#endif
static void static void
na_tray_manager_unmanage (NaTrayManager *manager) na_tray_manager_unmanage (NaTrayManager *manager)
{ {
#ifdef GDK_WINDOWING_X11 Display *xdisplay;
GdkDisplay *display;
guint32 timestamp;
GtkWidget *invisible;
GdkWindow *window;
if (manager->invisible == NULL) if (manager->window == None)
return; return;
invisible = manager->invisible; xdisplay = meta_x11_display_get_xdisplay (manager->x11_display);
window = gtk_widget_get_window (invisible);
g_assert (GTK_IS_INVISIBLE (invisible)); if (XGetSelectionOwner (xdisplay, manager->selection_atom) == manager->window)
g_assert (gtk_widget_get_realized (invisible));
g_assert (GDK_IS_WINDOW (window));
display = gtk_widget_get_display (invisible);
if (gdk_selection_owner_get_for_display (display, manager->selection_atom) ==
window)
{ {
timestamp = gdk_x11_get_server_time (window); XSetSelectionOwner (xdisplay,
gdk_selection_owner_set_for_display (display, manager->selection_atom,
NULL, None,
manager->selection_atom, CurrentTime);
timestamp,
TRUE);
} }
gdk_window_remove_filter (window, meta_x11_display_remove_event_func (manager->x11_display,
na_tray_manager_window_filter, manager); manager->event_func_id);
manager->event_func_id = 0;
manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */ XDestroyWindow (xdisplay, manager->window);
gtk_widget_destroy (invisible); manager->window = None;
g_object_unref (G_OBJECT (invisible));
#endif
}
static void
na_tray_manager_set_orientation_property (NaTrayManager *manager)
{
#ifdef GDK_WINDOWING_X11
GdkWindow *window;
GdkDisplay *display;
Atom orientation_atom;
gulong data[1];
g_return_if_fail (manager->invisible != NULL);
window = gtk_widget_get_window (manager->invisible);
g_return_if_fail (window != NULL);
display = gtk_widget_get_display (manager->invisible);
orientation_atom = gdk_x11_get_xatom_by_name_for_display (display,
"_NET_SYSTEM_TRAY_ORIENTATION");
data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ?
SYSTEM_TRAY_ORIENTATION_HORZ :
SYSTEM_TRAY_ORIENTATION_VERT;
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
GDK_WINDOW_XID (window),
orientation_atom,
XA_CARDINAL, 32,
PropModeReplace,
(guchar *) &data, 1);
#endif
} }
static void static void
na_tray_manager_set_visual_property (NaTrayManager *manager) na_tray_manager_set_visual_property (NaTrayManager *manager)
{ {
#ifdef GDK_WINDOWING_X11 Display *xdisplay;
GdkWindow *window; Atom visual_atom;
GdkDisplay *display; XVisualInfo xvisual_info;
Visual *xvisual; gulong data[1];
Atom visual_atom; int result;
gulong data[1];
g_return_if_fail (manager->invisible != NULL); g_return_if_fail (manager->window != None);
window = gtk_widget_get_window (manager->invisible);
g_return_if_fail (window != NULL);
/* The visual property is a hint to the tray icons as to what visual they /* The visual property is a hint to the tray icons as to what visual they
* should use for their windows. If the X server has RGBA colormaps, then * should use for their windows. If the X server has RGBA colormaps, then
@ -611,49 +530,35 @@ na_tray_manager_set_visual_property (NaTrayManager *manager)
* the icon to use our colormap, and we'll do some hacks with parent * the icon to use our colormap, and we'll do some hacks with parent
* relative backgrounds to simulate transparency. * relative backgrounds to simulate transparency.
*/ */
xdisplay = meta_x11_display_get_xdisplay (manager->x11_display);
visual_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_VISUAL", False);
display = gtk_widget_get_display (manager->invisible); result = XMatchVisualInfo (xdisplay, DefaultScreen (xdisplay), 32, TrueColor, &xvisual_info);
visual_atom = gdk_x11_get_xatom_by_name_for_display (display,
"_NET_SYSTEM_TRAY_VISUAL");
if (gdk_screen_get_rgba_visual (manager->screen) != NULL) if (result == Success)
xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_rgba_visual (manager->screen)); data[0] = xvisual_info.visualid;
else else
{ data[0] = XVisualIDFromVisual (DefaultVisual (xdisplay, DefaultScreen (xdisplay)));
/* We actually want the visual of the tray where the icons will
* be embedded. In almost all cases, this will be the same as the visual
* of the screen.
*/
xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (manager->screen));
}
data[0] = XVisualIDFromVisual (xvisual); XChangeProperty (xdisplay,
manager->window,
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
GDK_WINDOW_XID (window),
visual_atom, visual_atom,
XA_VISUALID, 32, XA_VISUALID, 32,
PropModeReplace, PropModeReplace,
(guchar *) &data, 1); (guchar *) &data, 1);
#endif
} }
static void static void
na_tray_manager_set_colors_property (NaTrayManager *manager) na_tray_manager_set_colors_property (NaTrayManager *manager)
{ {
#ifdef GDK_WINDOWING_X11 Display *xdisplay;
GdkWindow *window; Atom atom;
GdkDisplay *display; gulong data[12];
Atom atom;
gulong data[12];
g_return_if_fail (manager->invisible != NULL); g_return_if_fail (manager->window != None);
window = gtk_widget_get_window (manager->invisible);
g_return_if_fail (window != NULL);
display = gtk_widget_get_display (manager->invisible); xdisplay = meta_x11_display_get_xdisplay (manager->x11_display);
atom = gdk_x11_get_xatom_by_name_for_display (display, atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_COLORS", False);
"_NET_SYSTEM_TRAY_COLORS");
data[0] = manager->fg.red * 0x101; data[0] = manager->fg.red * 0x101;
data[1] = manager->fg.green * 0x101; data[1] = manager->fg.green * 0x101;
@ -668,194 +573,87 @@ na_tray_manager_set_colors_property (NaTrayManager *manager)
data[10] = manager->success.green * 0x101; data[10] = manager->success.green * 0x101;
data[11] = manager->success.blue * 0x101; data[11] = manager->success.blue * 0x101;
XChangeProperty (GDK_DISPLAY_XDISPLAY (display), XChangeProperty (xdisplay,
GDK_WINDOW_XID (window), manager->window,
atom, atom,
XA_CARDINAL, 32, XA_CARDINAL, 32,
PropModeReplace, PropModeReplace,
(guchar *) &data, 12); (guchar *) &data, 12);
#endif
} }
#ifdef GDK_WINDOWING_X11 gboolean
na_tray_manager_manage (NaTrayManager *manager)
static gboolean
na_tray_manager_manage_screen_x11 (NaTrayManager *manager)
{ {
GdkDisplay *display; Display *xdisplay;
GdkScreen *screen;
Screen *xscreen;
GtkWidget *invisible;
GdkWindow *window;
char *selection_atom_name;
guint32 timestamp;
g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE); g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE);
g_return_val_if_fail (manager->screen == NULL, FALSE);
/* If there's already a manager running on the screen xdisplay = meta_x11_display_get_xdisplay (manager->x11_display);
* we can't create another one.
*/ meta_x11_error_trap_push (manager->x11_display);
#if 0 manager->window = XCreateSimpleWindow (xdisplay,
if (na_tray_manager_check_running_screen_x11 ()) XDefaultRootWindow (xdisplay),
0, 0, 1, 1,
0, 0, 0);
XSelectInput (xdisplay, manager->window,
StructureNotifyMask | PropertyChangeMask);
if (meta_x11_error_trap_pop_with_return (manager->x11_display) ||
!manager->window)
return FALSE; return FALSE;
#endif
screen = gdk_screen_get_default (); manager->selection_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_S0", False);
manager->screen = screen;
display = gdk_screen_get_display (screen);
xscreen = GDK_SCREEN_XSCREEN (screen);
invisible = gtk_invisible_new_for_screen (screen);
gtk_widget_realize (invisible);
gtk_widget_add_events (invisible,
GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
gdk_x11_get_default_screen ());
manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE);
g_free (selection_atom_name);
manager->invisible = invisible;
g_object_ref (G_OBJECT (manager->invisible));
na_tray_manager_set_orientation_property (manager);
na_tray_manager_set_visual_property (manager); na_tray_manager_set_visual_property (manager);
na_tray_manager_set_colors_property (manager); na_tray_manager_set_colors_property (manager);
window = gtk_widget_get_window (invisible); meta_x11_error_trap_push (manager->x11_display);
timestamp = gdk_x11_get_server_time (window); XSetSelectionOwner (xdisplay, manager->selection_atom,
manager->window, CurrentTime);
/* Check if we could set the selection owner successfully */ /* Check if we could set the selection owner successfully */
if (gdk_selection_owner_set_for_display (display, if (!meta_x11_error_trap_pop_with_return (manager->x11_display))
window,
manager->selection_atom,
timestamp,
TRUE))
{ {
XClientMessageEvent xev; XClientMessageEvent xev;
GdkAtom opcode_atom;
GdkAtom message_data_atom;
xev.type = ClientMessage; xev.type = ClientMessage;
xev.window = RootWindowOfScreen (xscreen); xev.window = XDefaultRootWindow (xdisplay);
xev.message_type = gdk_x11_get_xatom_by_name_for_display (display, xev.message_type = XInternAtom (xdisplay, "MANAGER", False);
"MANAGER");
xev.format = 32; xev.format = 32;
xev.data.l[0] = timestamp; xev.data.l[0] = CurrentTime;
xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display, xev.data.l[1] = manager->selection_atom;
manager->selection_atom); xev.data.l[2] = manager->window;
xev.data.l[2] = GDK_WINDOW_XID (window); xev.data.l[3] = 0; /* manager specific data */
xev.data.l[3] = 0; /* manager specific data */ xev.data.l[4] = 0; /* manager specific data */
xev.data.l[4] = 0; /* manager specific data */
XSendEvent (GDK_DISPLAY_XDISPLAY (display), XSendEvent (xdisplay,
RootWindowOfScreen (xscreen), XDefaultRootWindow (xdisplay),
False, StructureNotifyMask, (XEvent *)&xev); False, StructureNotifyMask, (XEvent *)&xev);
opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE); manager->opcode_atom =
manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display, XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_OPCODE", False);
opcode_atom); manager->message_data_atom =
XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA", /* Add an event filter */
FALSE); manager->event_func_id =
manager->message_data_atom = gdk_x11_atom_to_xatom_for_display (display, meta_x11_display_add_event_func (manager->x11_display,
message_data_atom); na_tray_manager_event_func,
manager,
/* Add a window filter */ NULL);
#if 0
/* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */
g_signal_connect (invisible, "selection-clear-event",
G_CALLBACK (na_tray_manager_selection_clear_event),
manager);
#endif
gdk_window_add_filter (window,
na_tray_manager_window_filter, manager);
return TRUE; return TRUE;
} }
else else
{ {
gtk_widget_destroy (invisible); XDestroyWindow (xdisplay, manager->window);
g_object_unref (invisible); manager->window = None;
manager->invisible = NULL;
manager->screen = NULL;
return FALSE; return FALSE;
} }
} }
#endif
gboolean
na_tray_manager_manage_screen (NaTrayManager *manager)
{
g_return_val_if_fail (manager->screen == NULL, FALSE);
#ifdef GDK_WINDOWING_X11
return na_tray_manager_manage_screen_x11 (manager);
#else
return FALSE;
#endif
}
#ifdef GDK_WINDOWING_X11
static gboolean
na_tray_manager_check_running_screen_x11 (void)
{
GdkDisplay *display;
GdkScreen *screen;
Atom selection_atom;
char *selection_atom_name;
screen = gdk_screen_get_default ();
display = gdk_screen_get_display (screen);
selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
gdk_x11_get_default_screen ());
selection_atom = gdk_x11_get_xatom_by_name_for_display (display,
selection_atom_name);
g_free (selection_atom_name);
if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
selection_atom) != None)
return TRUE;
else
return FALSE;
}
#endif
gboolean
na_tray_manager_check_running (void)
{
#ifdef GDK_WINDOWING_X11
return na_tray_manager_check_running_screen_x11 ();
#else
return FALSE;
#endif
}
void
na_tray_manager_set_orientation (NaTrayManager *manager,
GtkOrientation orientation)
{
g_return_if_fail (NA_IS_TRAY_MANAGER (manager));
if (manager->orientation != orientation)
{
manager->orientation = orientation;
na_tray_manager_set_orientation_property (manager);
g_object_notify (G_OBJECT (manager), "orientation");
}
}
void void
na_tray_manager_set_colors (NaTrayManager *manager, na_tray_manager_set_colors (NaTrayManager *manager,
ClutterColor *fg, ClutterColor *fg,
@ -878,11 +676,3 @@ na_tray_manager_set_colors (NaTrayManager *manager,
na_tray_manager_set_colors_property (manager); na_tray_manager_set_colors_property (manager);
} }
} }
GtkOrientation
na_tray_manager_get_orientation (NaTrayManager *manager)
{
g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL);
return manager->orientation;
}

View File

@ -22,84 +22,24 @@
#ifndef __NA_TRAY_MANAGER_H__ #ifndef __NA_TRAY_MANAGER_H__
#define __NA_TRAY_MANAGER_H__ #define __NA_TRAY_MANAGER_H__
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include <gtk/gtk.h>
#include <clutter/clutter.h> #include <clutter/clutter.h>
#include "na-tray-child.h" #include "na-tray-child.h"
G_BEGIN_DECLS G_BEGIN_DECLS
#define NA_TYPE_TRAY_MANAGER (na_tray_manager_get_type ()) #define NA_TYPE_TRAY_MANAGER (na_tray_manager_get_type ())
#define NA_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManager)) G_DECLARE_FINAL_TYPE (NaTrayManager, na_tray_manager, NA, TRAY_MANAGER, GObject)
#define NA_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass))
#define NA_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_MANAGER))
#define NA_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_MANAGER))
#define NA_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass))
typedef struct _NaTrayManager NaTrayManager; NaTrayManager *na_tray_manager_new (MetaX11Display *x11_display);
typedef struct _NaTrayManagerClass NaTrayManagerClass;
struct _NaTrayManager gboolean na_tray_manager_manage (NaTrayManager *manager);
{
GObject parent_instance;
#ifdef GDK_WINDOWING_X11
GdkAtom selection_atom;
Atom opcode_atom;
Atom message_data_atom;
#endif
GtkWidget *invisible;
GdkScreen *screen;
GtkOrientation orientation;
ClutterColor fg;
ClutterColor error;
ClutterColor warning;
ClutterColor success;
GList *messages;
GHashTable *socket_table;
};
struct _NaTrayManagerClass
{
GObjectClass parent_class;
void (* tray_icon_added) (NaTrayManager *manager,
NaTrayChild *child);
void (* tray_icon_removed) (NaTrayManager *manager,
NaTrayChild *child);
void (* message_sent) (NaTrayManager *manager,
NaTrayChild *child,
const gchar *message,
glong id,
glong timeout);
void (* message_cancelled) (NaTrayManager *manager,
NaTrayChild *child,
glong id);
void (* lost_selection) (NaTrayManager *manager);
};
GType na_tray_manager_get_type (void);
gboolean na_tray_manager_check_running (void);
NaTrayManager *na_tray_manager_new (void);
gboolean na_tray_manager_manage_screen (NaTrayManager *manager);
void na_tray_manager_set_orientation (NaTrayManager *manager,
GtkOrientation orientation);
GtkOrientation na_tray_manager_get_orientation (NaTrayManager *manager);
void na_tray_manager_set_colors (NaTrayManager *manager,
ClutterColor *fg,
ClutterColor *error,
ClutterColor *warning,
ClutterColor *success);
void na_tray_manager_set_colors (NaTrayManager *manager,
ClutterColor *fg,
ClutterColor *error,
ClutterColor *warning,
ClutterColor *success);
G_END_DECLS G_END_DECLS