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:
parent
f9519f4a55
commit
ab60902058
@ -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',
|
||||||
|
@ -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__ */
|
|
@ -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);
|
|
||||||
}
|
|
@ -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__ */
|
|
@ -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);
|
|
||||||
}
|
|
@ -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__ */
|
|
11
src/shell-tray-icon-private.h
Normal file
11
src/shell-tray-icon-private.h
Normal 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 */
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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__ */
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user