Import part of the notification area applet, and use it to add a

notification are to the panel. A bit warty, but we don't know how we want
the final UI to look anyway. (The fact that transparency doesn't work is
a known bug.)

svn path=/trunk/; revision=44
This commit is contained in:
Dan Winship 2008-11-14 17:21:56 +00:00
parent d8f9d1a448
commit 8c61d46586
12 changed files with 1762 additions and 15 deletions

View File

@ -17,6 +17,10 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
[The prefix for our gettext translation domains.])
PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 metacity-plugins gjs-gi-1.0)
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
changequote(,)dnl
if test "x$GCC" = "xyes"; then

View File

@ -4,10 +4,12 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Clutter = imports.gi.Clutter;
const Tidy = imports.gi.Tidy;
const Main = imports.ui.main;
const PANEL_HEIGHT = 32;
const TRAY_HEIGHT = 24;
const PANEL_BACKGROUND_COLOR = new Clutter.Color();
PANEL_BACKGROUND_COLOR.from_pixel(0xeeddccff);
@ -26,7 +28,7 @@ Panel.prototype = {
width: global.screen_width+2,
height: PANEL_HEIGHT+1,
border_width: 1});
background.set_position(-1, -1)
background.set_position(-1, -1);
this._group.add_actor(background);
let message = new Clutter.Label({ font_name: "Sans Bold 16px",
@ -35,11 +37,35 @@ Panel.prototype = {
message.set_position(5, 5);
this._group.add_actor(message);
this._grid = new Tidy.Grid({ height: TRAY_HEIGHT,
valign: 0.5,
end_align: true,
column_gap: 2 })
this._group.add_actor(this._grid);
this._clock = new Clutter.Label({ font_name: "Sans Bold 16px",
text: "" });
this._clock.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
this._clock.set_position(global.screen_width - 5, 5);
this._group.add_actor(this._clock);
this._grid.add_actor(this._clock);
this._grid.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
this._grid.set_position(global.screen_width - 2, (PANEL_HEIGHT - TRAY_HEIGHT) / 2);
this._traymanager = new Shell.TrayManager();
let panel = this;
this._traymanager.connect('tray-icon-added',
function(o, icon) {
panel._grid.add_actor(icon);
/* bump the clock back to the end */
panel._grid.remove_actor(panel._clock);
panel._grid.add_actor(panel._clock);
panel._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
});
this._traymanager.connect('tray-icon-removed',
function(o, icon) {
panel._grid.remove_actor(icon);
panel._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
});
this._traymanager.manage_stage(global.stage);
message.connect('button-press-event',
function(o, event) {
@ -68,8 +94,6 @@ Panel.prototype = {
_updateClock: function() {
this._clock.set_text(new Date().toLocaleFormat("%H:%M"));
this._clock.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
return true;
}
};

View File

@ -1,8 +1,5 @@
NULL =
GLIB_GENMARSHAL = `pkg-config --variable=glib_genmarshal glib-2.0`
GLIB_MKENUMS = `pkg-config --variable=glib_mkenums glib-2.0`
tidy_cflags = \
-I$(top_srcdir)/src \
-DPREFIX=\""$(prefix)"\" \
@ -20,7 +17,7 @@ tidy_built_sources = \
BUILT_SOURCES += $(tidy_built_sources)
STAMP_FILES = stamp-tidy-marshal.h stamp-tidy-enum-types.h
TIDY_STAMP_FILES = stamp-tidy-marshal.h stamp-tidy-enum-types.h
# please, keep this sorted alphabetically
tidy_source_h = \
@ -100,7 +97,7 @@ libtidy_1_0_la_SOURCES = \
libtidy_1_0_la_CPPFLAGS = $(tidy_cflags)
libtidy_1_0_la_LDFLAGS = $(LDADD)
CLEANFILES += $(STAMP_FILES) $(BUILT_SOURCES)
CLEANFILES += $(TIDY_STAMP_FILES) $(BUILT_SOURCES)
EXTRA_DIST = \
tidy/tidy-enum-types.h.in \

57
src/Makefile-tray.am Normal file
View File

@ -0,0 +1,57 @@
tray_cflags = \
-I$(top_srcdir)/src \
-DG_DISABLE_DEPRECATED \
-DG_LOG_DOMAIN=\"notification_area\" \
$(TRAY_CFLAGS) \
$(NULL)
tray_built_sources = \
na-marshal.h \
na-marshal.c
BUILT_SOURCES += $(tray_built_sources)
TRAY_STAMP_FILES = stamp-na-marshal.h
# please, keep this sorted alphabetically
tray_source = \
tray/na-tray-child.c \
tray/na-tray-child.h \
tray/na-tray-manager.c \
tray/na-tray-manager.h \
$(NULL)
na-marshal.h: stamp-na-marshal.h
@true
stamp-na-marshal.h: Makefile tray/na-marshal.list
$(GLIB_GENMARSHAL) \
--prefix=_na_marshal \
--header \
$(srcdir)/tray/na-marshal.list > xgen-tmh && \
(cmp -s xgen-tmh na-marshal.h || cp -f xgen-tmh na-marshal.h) && \
rm -f xgen-tmh && \
echo timestamp > $(@F)
na-marshal.c: Makefile tray/na-marshal.list
(echo "#include \"na-marshal.h\"" ; \
$(GLIB_GENMARSHAL) \
--prefix=_na_marshal \
--body \
$(srcdir)/tray/na-marshal.list ) > xgen-tmc && \
cp -f xgen-tmc na-marshal.c && \
rm -f xgen-tmc
lib_LTLIBRARIES += libtray.la
libtray_la_LIBADD = $(TRAY_LIBS)
libtray_la_SOURCES = \
$(tray_source) \
$(tray_built_sources) \
$(NULL)
libtray_la_CPPFLAGS = $(tray_cflags)
libtray_la_LDFLAGS = $(LDADD)
CLEANFILES += $(TRAY_STAMP_FILES) $(BUILT_SOURCES)
EXTRA_DIST += \
tray/na-marshal.list

View File

@ -2,9 +2,11 @@ BUILT_SOURCES =
CLEANFILES =
include Makefile-tidy.am
include Makefile-tray.am
gnome_shell_cflags = \
$(MUTTER_PLUGIN_CFLAGS) \
-Itray \
-DGETTEXT_PACKAGE=gnome-shell \
-DJSDIR=\"$(pkgdatadir)/js\"
@ -16,10 +18,15 @@ libgnome_shell_la_SOURCES = \
shell-process.c \
shell-process.h \
shell-global.c \
shell-global.h
shell-global.h \
shell-tray-manager.c \
shell-tray-manager.h
libgnome_shell_la_LDFLAGS = -avoid-version -module
libgnome_shell_la_LIBADD = $(MUTTER_PLUGIN_LIBS) libtidy-1.0.la
libgnome_shell_la_LIBADD = \
$(MUTTER_PLUGIN_LIBS) \
libtidy-1.0.la \
libtray.la
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
# We can't have any undefined symbols when g-ir-scanner dlopens the library
@ -33,7 +40,10 @@ libgnome_shell_introspect_la_SOURCES = \
# The dummy -rpath here is needed to convince libtool to build a
# noinst_LTLIBRARY shared
libgnome_shell_introspect_la_LDFLAGS = -avoid-version -module -rpath $(libdir)
libgnome_shell_introspect_la_LIBADD = $(MUTTER_PLUGIN_LIBS) libtidy-1.0.la
libgnome_shell_introspect_la_LIBADD = \
$(MUTTER_PLUGIN_LIBS) \
libtidy-1.0.la \
libtray.la
libgnome_shell_introspect_la_CPPFLAGS = $(gnome_shell_cflags)
typelibdir = $(pkglibdir)/girepository
@ -49,7 +59,7 @@ Shell-0.1.gir: libgnome-shell-introspect.la $(libgnome_shell_la_SOURCES) Makefil
--include=Clutter-0.8 \
--include=Meta-2.25 \
--library=gnome-shell-introspect \
- $(libgnome_shell_la_SOURCES) \
$(libgnome_shell_la_SOURCES) \
$(libgnome_shell_la_CPPFLAGS) \
-o $@.tmp
sed 's/gnome-shell-introspect/gnome-shell/' < $@.tmp > $@ && rm $@.tmp

215
src/shell-tray-manager.c Normal file
View File

@ -0,0 +1,215 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include <clutter/clutter.h>
#include <clutter/glx/clutter-glx.h>
#include <clutter/x11/clutter-x11.h>
#include <gtk/gtk.h>
#include "shell-tray-manager.h"
#include "na-tray-manager.h"
struct _ShellTrayManagerPrivate {
NaTrayManager *na_manager;
ClutterStage *stage;
GdkWindow *stage_window;
GHashTable *icons;
};
typedef struct {
ShellTrayManager *manager;
GtkWidget *socket;
GtkWidget *window;
ClutterActor *actor;
} ShellTrayManagerChild;
/* Signals */
enum
{
TRAY_ICON_ADDED,
TRAY_ICON_REMOVED,
LAST_SIGNAL
};
G_DEFINE_TYPE (ShellTrayManager, shell_tray_manager, G_TYPE_OBJECT);
static guint shell_tray_manager_signals [LAST_SIGNAL] = { 0 };
static void na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *child, gpointer manager);
static void na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *child, gpointer manager);
static void
free_tray_icon (gpointer data)
{
ShellTrayManagerChild *child = data;
gtk_widget_hide (child->window);
gtk_widget_destroy (child->window);
g_signal_handlers_disconnect_matched (child->actor, G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, child);
g_object_unref (child->actor);
g_slice_free (ShellTrayManagerChild, child);
}
static void
shell_tray_manager_init (ShellTrayManager *manager)
{
manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, SHELL_TYPE_TRAY_MANAGER,
ShellTrayManagerPrivate);
manager->priv->na_manager = na_tray_manager_new ();
manager->priv->icons = g_hash_table_new_full (NULL, NULL,
NULL, free_tray_icon);
g_signal_connect (manager->priv->na_manager, "tray-icon-added",
G_CALLBACK (na_tray_icon_added), manager);
g_signal_connect (manager->priv->na_manager, "tray-icon-removed",
G_CALLBACK (na_tray_icon_removed), manager);
}
static void
shell_tray_manager_finalize (GObject *object)
{
ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
g_object_unref (manager->priv->na_manager);
g_object_unref (manager->priv->stage);
g_object_unref (manager->priv->stage_window);
g_hash_table_destroy (manager->priv->icons);
G_OBJECT_CLASS (shell_tray_manager_parent_class)->finalize (object);
}
static void
shell_tray_manager_class_init (ShellTrayManagerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (ShellTrayManagerPrivate));
gobject_class->finalize = shell_tray_manager_finalize;
shell_tray_manager_signals[TRAY_ICON_ADDED] =
g_signal_new ("tray-icon-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ShellTrayManagerClass, tray_icon_added),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
CLUTTER_TYPE_ACTOR);
shell_tray_manager_signals[TRAY_ICON_REMOVED] =
g_signal_new ("tray-icon-removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ShellTrayManagerClass, tray_icon_removed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
CLUTTER_TYPE_ACTOR);
}
ShellTrayManager *
shell_tray_manager_new (void)
{
return g_object_new (SHELL_TYPE_TRAY_MANAGER, NULL);
}
void
shell_tray_manager_manage_stage (ShellTrayManager *manager,
ClutterStage *stage)
{
Window stage_xwin;
g_return_if_fail (manager->priv->stage == NULL);
manager->priv->stage = g_object_ref (stage);
stage_xwin = clutter_x11_get_stage_window (stage);
manager->priv->stage_window = gdk_window_lookup (stage_xwin);
if (manager->priv->stage_window)
g_object_ref (manager->priv->stage_window);
else
manager->priv->stage_window = gdk_window_foreign_new (stage_xwin);
na_tray_manager_manage_screen (manager->priv->na_manager,
gdk_drawable_get_screen (GDK_DRAWABLE (manager->priv->stage_window)));
}
static void
actor_moved (GObject *object, GParamSpec *param, gpointer user_data)
{
ShellTrayManagerChild *child = user_data;
ClutterActor *actor = child->actor;
int wx = 0, wy = 0, x, y, ax, ay;
/* Find the actor's new coordinates in terms of the stage (which is
* child->window's parent window.
*/
while (actor)
{
clutter_actor_get_position (actor, &x, &y);
clutter_actor_get_anchor_point (actor, &ax, &ay);
wx += x - ax;
wy += y - ay;
actor = clutter_actor_get_parent (actor);
}
gtk_window_move (GTK_WINDOW (child->window), wx, wy);
}
static void
na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket,
gpointer user_data)
{
ShellTrayManager *manager = user_data;
GtkWidget *win;
ClutterActor *icon;
ShellTrayManagerChild *child;
win = gtk_window_new (GTK_WINDOW_POPUP);
gtk_container_add (GTK_CONTAINER (win), socket);
gtk_widget_set_size_request (win, 24, 24);
gtk_widget_realize (win);
gtk_widget_set_parent_window (win, manager->priv->stage_window);
gdk_window_reparent (win->window, manager->priv->stage_window, 0, 0);
gtk_widget_show_all (win);
icon = clutter_glx_texture_pixmap_new_with_window (GDK_WINDOW_XWINDOW (win->window));
clutter_x11_texture_pixmap_set_automatic (CLUTTER_X11_TEXTURE_PIXMAP (icon), TRUE);
clutter_actor_set_size (icon, 24, 24);
child = g_slice_new (ShellTrayManagerChild);
child->window = win;
child->socket = socket;
child->actor = g_object_ref (icon);
g_hash_table_insert (manager->priv->icons, socket, child);
g_signal_connect (child->actor, "notify::x",
G_CALLBACK (actor_moved), child);
g_signal_connect (child->actor, "notify::y",
G_CALLBACK (actor_moved), child);
g_signal_emit (manager,
shell_tray_manager_signals[TRAY_ICON_ADDED], 0,
icon);
}
static void
na_tray_icon_removed (NaTrayManager *na_manager, GtkWidget *socket,
gpointer user_data)
{
ShellTrayManager *manager = user_data;
ShellTrayManagerChild *child;
child = g_hash_table_lookup (manager->priv->icons, socket);
g_return_if_fail (child != NULL);
g_signal_emit (manager,
shell_tray_manager_signals[TRAY_ICON_REMOVED], 0,
child->actor);
g_hash_table_remove (manager->priv->icons, socket);
}

47
src/shell-tray-manager.h Normal file
View File

@ -0,0 +1,47 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_TRAY_MANAGER_H__
#define __SHELL_TRAY_MANAGER_H__
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define SHELL_TYPE_TRAY_MANAGER (shell_tray_manager_get_type ())
#define SHELL_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_TRAY_MANAGER, ShellTrayManager))
#define SHELL_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_TRAY_MANAGER, ShellTrayManagerClass))
#define SHELL_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_TRAY_MANAGER))
#define SHELL_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_TRAY_MANAGER))
#define SHELL_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_TRAY_MANAGER, ShellTrayManagerClass))
typedef struct _ShellTrayManager ShellTrayManager;
typedef struct _ShellTrayManagerPrivate ShellTrayManagerPrivate;
typedef struct _ShellTrayManagerClass ShellTrayManagerClass;
struct _ShellTrayManager
{
GObject parent_instance;
ShellTrayManagerPrivate *priv;
};
struct _ShellTrayManagerClass
{
GObjectClass parent_class;
void (* tray_icon_added) (ShellTrayManager *manager,
ClutterActor *icon);
void (* tray_icon_removed) (ShellTrayManager *manager,
ClutterActor *icon);
};
GType shell_tray_manager_get_type (void);
ShellTrayManager *shell_tray_manager_new (void);
void shell_tray_manager_manage_stage (ShellTrayManager *manager,
ClutterStage *stage);
G_END_DECLS
#endif /* __SHELL_TRAY_MANAGER_H__ */

3
src/tray/na-marshal.list Normal file
View File

@ -0,0 +1,3 @@
VOID:OBJECT,OBJECT
VOID:OBJECT,STRING,LONG,LONG
VOID:OBJECT,LONG

386
src/tray/na-tray-child.c Normal file
View File

@ -0,0 +1,386 @@
/* na-tray-child.c
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
* Copyright (C) 2003-2006 Vincent Untz
* Copyright (C) 2008 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include <string.h>
#include "na-tray-child.h"
#include <glib/gi18n.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
G_DEFINE_TYPE (NaTrayChild, na_tray_child, GTK_TYPE_SOCKET)
static void
na_tray_child_finalize (GObject *object)
{
G_OBJECT_CLASS (na_tray_child_parent_class)->finalize (object);
}
static void
na_tray_child_realize (GtkWidget *widget)
{
NaTrayChild *child = NA_TRAY_CHILD (widget);
GdkVisual *visual = gtk_widget_get_visual (widget);
gboolean visual_has_alpha;
GTK_WIDGET_CLASS (na_tray_child_parent_class)->realize (widget);
/* We have alpha if the visual has something other than red, green, and blue */
visual_has_alpha = visual->red_prec + visual->blue_prec + visual->green_prec < visual->depth;
if (visual_has_alpha && gdk_display_supports_composite (gtk_widget_get_display (widget)))
{
/* We have real transparency with an ARGB visual and the Composite extension.
*/
/* Set a transparent background */
GdkColor transparent = { 0, 0, 0, 0 }; /* only pixel=0 matters */
gdk_window_set_background(widget->window, &transparent);
gdk_window_set_composited (widget->window, TRUE);
child->is_composited = TRUE;
child->parent_relative_bg = FALSE;
}
else if (visual == gdk_window_get_visual (gdk_window_get_parent (widget->window)))
{
/* Otherwise, if the visual matches the visual of the parent window, we can
* use a parent-relative background and fake transparency.
*/
gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
child->is_composited = FALSE;
child->parent_relative_bg = TRUE;
}
else
{
/* Nothing to do; the icon will sit on top of an ugly gray box */
child->is_composited = FALSE;
child->parent_relative_bg = FALSE;
}
gtk_widget_set_app_paintable (GTK_WIDGET (child),
child->parent_relative_bg || child->is_composited);
/* 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.
*/
gtk_widget_set_double_buffered (GTK_WIDGET (child), child->parent_relative_bg);
}
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 reenabled. 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 (%i x %i), resizing to (%i x %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);
gboolean moved = allocation->x != widget->allocation.x || allocation->y != widget->allocation.y;
gboolean 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_MAPPED (widget))
{
if (na_tray_child_is_composited (child))
gdk_window_invalidate_rect (gdk_window_get_parent (widget->window),
&widget->allocation, FALSE);
}
GTK_WIDGET_CLASS (na_tray_child_parent_class)->size_allocate (widget, allocation);
if ((moved || resized) && GTK_WIDGET_MAPPED (widget))
{
if (na_tray_child_is_composited (NA_TRAY_CHILD (widget)))
gdk_window_invalidate_rect (gdk_window_get_parent (widget->window),
&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 an expose event. But in case we do (the plug unmaps itself, say), this
* expose handler draws with real or fake transparency.
*/
static gboolean
na_tray_child_expose_event (GtkWidget *widget,
GdkEventExpose *event)
{
NaTrayChild *child = NA_TRAY_CHILD (widget);
if (na_tray_child_is_composited (child))
{
/* Clear to transparent */
cairo_t *cr = gdk_cairo_create (widget->window);
cairo_set_source_rgba (cr, 0, 0, 0, 0);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
gdk_cairo_region (cr, event->region);
cairo_fill (cr);
cairo_destroy (cr);
}
else if (child->parent_relative_bg)
{
/* Clear to parent-relative pixmap */
gdk_window_clear_area (widget->window,
event->area.x, event->area.y,
event->area.width, event->area.height);
}
return FALSE;
}
static void
na_tray_child_init (NaTrayChild *child)
{
}
static void
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->expose_event = na_tray_child_expose_event;
}
GtkWidget *
na_tray_child_new (GdkScreen *screen,
Window icon_window)
{
XWindowAttributes window_attributes;
Display *xdisplay;
NaTrayChild *child;
GdkVisual *visual;
GdkColormap *colormap;
gboolean new_colormap;
int result;
g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
g_return_val_if_fail (icon_window != None, NULL);
xdisplay = GDK_SCREEN_XDISPLAY (screen);
/* We need to determine the visual of the window we are embedding and create
* the socket in the same visual.
*/
gdk_error_trap_push ();
result = XGetWindowAttributes (xdisplay, icon_window,
&window_attributes);
gdk_error_trap_pop ();
if (!result) /* Window already gone */
return NULL;
visual = gdk_x11_screen_lookup_visual (screen,
window_attributes.visual->visualid);
if (!visual) /* Icon window is on another screen? */
return NULL;
new_colormap = FALSE;
if (visual == gdk_screen_get_rgb_visual (screen))
colormap = gdk_screen_get_rgb_colormap (screen);
else if (visual == gdk_screen_get_rgba_visual (screen))
colormap = gdk_screen_get_rgba_colormap (screen);
else if (visual == gdk_screen_get_system_visual (screen))
colormap = gdk_screen_get_system_colormap (screen);
else
{
colormap = gdk_colormap_new (visual, FALSE);
new_colormap = TRUE;
}
child = g_object_new (NA_TYPE_TRAY_CHILD, NULL);
child->icon_window = icon_window;
gtk_widget_set_colormap (GTK_WIDGET (child), colormap);
if (new_colormap)
g_object_unref (colormap);
return GTK_WIDGET (child);
}
char *
na_tray_child_get_title (NaTrayChild *child)
{
char *retval = NULL;
GdkDisplay *display;
Atom utf8_string, atom, type;
int result;
int format;
gulong nitems;
gulong bytes_after;
gchar *val;
g_return_val_if_fail (NA_IS_TRAY_CHILD (child), NULL);
display = gtk_widget_get_display (GTK_WIDGET (child));
utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
gdk_error_trap_push ();
result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
child->icon_window,
atom,
0, G_MAXLONG,
False, utf8_string,
&type, &format, &nitems,
&bytes_after, (guchar **)&val);
if (gdk_error_trap_pop () || result != Success)
return NULL;
if (type != utf8_string ||
format != 8 ||
nitems == 0)
{
if (val)
XFree (val);
return NULL;
}
if (!g_utf8_validate (val, nitems, NULL))
{
XFree (val);
return NULL;
}
retval = g_strndup (val, nitems);
XFree (val);
return retval;
}
gboolean
na_tray_child_is_composited (NaTrayChild *child)
{
g_return_val_if_fail (NA_IS_TRAY_CHILD (child), FALSE);
return child->is_composited;
}
/* 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 shifed with respect to the background.
*/
void
na_tray_child_force_redraw (NaTrayChild *child)
{
GtkWidget *widget = GTK_WIDGET (child);
if (GTK_WIDGET_MAPPED (child) && 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.
*/
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget));
XEvent xev;
xev.xexpose.type = Expose;
xev.xexpose.window = GDK_WINDOW_XWINDOW (GTK_SOCKET (child)->plug_window);
xev.xexpose.x = 0;
xev.xexpose.y = 0;
xev.xexpose.width = widget->allocation.width;
xev.xexpose.height = widget->allocation.height;
xev.xexpose.count = 0;
gdk_error_trap_push ();
XSendEvent (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)),
xev.xexpose.window,
False, ExposureMask,
&xev);
/* We have to sync to reliably catch errors from the XSendEvent(),
* since that is asynchronous.
*/
XSync (xdisplay, False);
gdk_error_trap_pop ();
#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
}
}

65
src/tray/na-tray-child.h Normal file
View File

@ -0,0 +1,65 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* na-tray-child.h
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
* Copyright (C) 2003-2006 Vincent Untz
* Copyright (C) 2008 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __NA_TRAY_CHILD_H__
#define __NA_TRAY_CHILD_H__
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
G_BEGIN_DECLS
#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))
#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;
typedef struct _NaTrayChildClass NaTrayChildClass;
typedef struct _NaTrayChildChild NaTrayChildChild;
struct _NaTrayChild
{
GtkSocket parent_instance;
Window icon_window;
guint is_composited : 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);
gboolean na_tray_child_is_composited (NaTrayChild *child);
void na_tray_child_force_redraw (NaTrayChild *child);
G_END_DECLS
#endif /* __NA_TRAY_CHILD_H__ */

842
src/tray/na-tray-manager.c Normal file
View File

@ -0,0 +1,842 @@
/* na-tray-manager.c
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
* Copyright (C) 2003-2006 Vincent Untz
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Used to be: eggtraymanager.c
*/
#include <config.h>
#include <string.h>
#include <libintl.h>
#include "na-tray-manager.h"
#include <gdkconfig.h>
#include <glib/gi18n.h>
#if defined (GDK_WINDOWING_X11)
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
#elif defined (GDK_WINDOWING_WIN32)
#include <gdk/gdkwin32.h>
#endif
#include <gtk/gtkinvisible.h>
#include <gtk/gtksocket.h>
#include <gtk/gtkwindow.h>
#include "na-marshal.h"
/* Signals */
enum
{
TRAY_ICON_ADDED,
TRAY_ICON_REMOVED,
MESSAGE_SENT,
MESSAGE_CANCELLED,
LOST_SELECTION,
LAST_SIGNAL
};
enum {
PROP_0,
PROP_ORIENTATION
};
typedef struct
{
long id, len;
long remaining_len;
long timeout;
char *str;
#ifdef GDK_WINDOWING_X11
Window window;
#endif
} PendingMessage;
static guint manager_signals[LAST_SIGNAL];
#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#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 (GdkScreen *screen);
#endif
static void na_tray_manager_finalize (GObject *object);
static void na_tray_manager_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void na_tray_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void na_tray_manager_unmanage (NaTrayManager *manager);
G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT)
static void
na_tray_manager_init (NaTrayManager *manager)
{
manager->invisible = NULL;
manager->socket_table = g_hash_table_new (NULL, NULL);
}
static void
na_tray_manager_class_init (NaTrayManagerClass *klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *)klass;
gobject_class->finalize = na_tray_manager_finalize;
gobject_class->set_property = na_tray_manager_set_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_CONSTRUCT |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_NICK |
G_PARAM_STATIC_BLURB));
manager_signals[TRAY_ICON_ADDED] =
g_signal_new ("tray_icon_added",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
GTK_TYPE_SOCKET);
manager_signals[TRAY_ICON_REMOVED] =
g_signal_new ("tray_icon_removed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
GTK_TYPE_SOCKET);
manager_signals[MESSAGE_SENT] =
g_signal_new ("message_sent",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NaTrayManagerClass, message_sent),
NULL, NULL,
_na_marshal_VOID__OBJECT_STRING_LONG_LONG,
G_TYPE_NONE, 4,
GTK_TYPE_SOCKET,
G_TYPE_STRING,
G_TYPE_LONG,
G_TYPE_LONG);
manager_signals[MESSAGE_CANCELLED] =
g_signal_new ("message_cancelled",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled),
NULL, NULL,
_na_marshal_VOID__OBJECT_LONG,
G_TYPE_NONE, 2,
GTK_TYPE_SOCKET,
G_TYPE_LONG);
manager_signals[LOST_SELECTION] =
g_signal_new ("lost_selection",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
#if defined (GDK_WINDOWING_X11)
/* Nothing */
#elif defined (GDK_WINDOWING_WIN32)
g_warning ("Port NaTrayManager to Win32");
#else
g_warning ("Port NaTrayManager to this GTK+ backend");
#endif
}
static void
na_tray_manager_finalize (GObject *object)
{
NaTrayManager *manager;
manager = NA_TRAY_MANAGER (object);
na_tray_manager_unmanage (manager);
g_list_free (manager->messages);
g_hash_table_destroy (manager->socket_table);
G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object);
}
static void
na_tray_manager_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
NaTrayManager *manager = NA_TRAY_MANAGER (object);
switch (prop_id)
{
case PROP_ORIENTATION:
na_tray_manager_set_orientation (manager, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
na_tray_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
NaTrayManager *manager = NA_TRAY_MANAGER (object);
switch (prop_id)
{
case PROP_ORIENTATION:
g_value_set_enum (value, manager->orientation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
NaTrayManager *
na_tray_manager_new (void)
{
NaTrayManager *manager;
manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL);
return manager;
}
#ifdef GDK_WINDOWING_X11
static gboolean
na_tray_manager_plug_removed (GtkSocket *socket,
NaTrayManager *manager)
{
NaTrayChild *child = NA_TRAY_CHILD (socket);
g_hash_table_remove (manager->socket_table, GINT_TO_POINTER (child->icon_window));
g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
/* This destroys the socket. */
return FALSE;
}
static void
na_tray_manager_handle_dock_request (NaTrayManager *manager,
XClientMessageEvent *xevent)
{
Window icon_window = xevent->data.l[2];
GtkWidget *child;
if (g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (icon_window)))
{
/* We already got this notification earlier, ignore this one */
return;
}
child = na_tray_child_new (manager->screen, icon_window);
if (child == NULL) /* already gone or other error */
return;
g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0,
child);
/* If the child wasn't attached, then destroy it */
if (!GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (child))))
{
gtk_widget_destroy (child);
return;
}
g_signal_connect (child, "plug_removed",
G_CALLBACK (na_tray_manager_plug_removed), manager);
gtk_socket_add_id (GTK_SOCKET (child), icon_window);
if (!GTK_SOCKET (child)->plug_window)
{
/* Embedding failed, we won't get a plug-removed signal */
g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child);
gtk_widget_destroy (child);
return;
}
g_hash_table_insert (manager->socket_table, GINT_TO_POINTER (icon_window), child);
gtk_widget_show (child);
}
static void
pending_message_free (PendingMessage *message)
{
g_free (message->str);
g_free (message);
}
static GdkFilterReturn
na_tray_manager_handle_client_message_message_data (GdkXEvent *xev,
GdkEvent *event,
gpointer data)
{
XClientMessageEvent *xevent;
NaTrayManager *manager;
GList *p;
int len;
xevent = (XClientMessageEvent *) xev;
manager = data;
/* Try to see if we can find the pending message in the list */
for (p = manager->messages; p; p = p->next)
{
PendingMessage *msg = p->data;
if (xevent->window == msg->window)
{
/* Append the message */
len = MIN (msg->remaining_len, 20);
memcpy ((msg->str + msg->len - msg->remaining_len),
&xevent->data, len);
msg->remaining_len -= len;
if (msg->remaining_len == 0)
{
GtkSocket *socket;
socket = g_hash_table_lookup (manager->socket_table,
GINT_TO_POINTER (msg->window));
if (socket)
g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
socket, msg->str, msg->id, msg->timeout);
pending_message_free (msg);
manager->messages = g_list_remove_link (manager->messages, p);
g_list_free_1 (p);
}
break;
}
}
return GDK_FILTER_REMOVE;
}
static void
na_tray_manager_handle_begin_message (NaTrayManager *manager,
XClientMessageEvent *xevent)
{
GtkSocket *socket;
GList *p;
PendingMessage *msg;
long timeout;
long len;
long id;
socket = g_hash_table_lookup (manager->socket_table,
GINT_TO_POINTER (xevent->window));
/* we don't know about this tray icon, so ignore the message */
if (!socket)
return;
/* Check if the same message is already in the queue and remove it if so */
for (p = manager->messages; p; p = p->next)
{
PendingMessage *pmsg = p->data;
if (xevent->window == pmsg->window &&
xevent->data.l[4] == pmsg->id)
{
/* Hmm, we found it, now remove it */
pending_message_free (pmsg);
manager->messages = g_list_remove_link (manager->messages, p);
g_list_free_1 (p);
break;
}
}
timeout = xevent->data.l[2];
len = xevent->data.l[3];
id = xevent->data.l[4];
if (len == 0)
{
g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0,
socket, "", id, timeout);
}
else
{
/* Now add the new message to the queue */
msg = g_new0 (PendingMessage, 1);
msg->window = xevent->window;
msg->timeout = timeout;
msg->len = len;
msg->id = id;
msg->remaining_len = msg->len;
msg->str = g_malloc (msg->len + 1);
msg->str[msg->len] = '\0';
manager->messages = g_list_prepend (manager->messages, msg);
}
}
static void
na_tray_manager_handle_cancel_message (NaTrayManager *manager,
XClientMessageEvent *xevent)
{
GList *p;
GtkSocket *socket;
/* Check if the message is in the queue and remove it if so */
for (p = manager->messages; p; p = p->next)
{
PendingMessage *msg = p->data;
if (xevent->window == msg->window &&
xevent->data.l[4] == msg->id)
{
pending_message_free (msg);
manager->messages = g_list_remove_link (manager->messages, p);
g_list_free_1 (p);
break;
}
}
socket = g_hash_table_lookup (manager->socket_table,
GINT_TO_POINTER (xevent->window));
if (socket)
{
g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0,
socket, xevent->data.l[2]);
}
}
static GdkFilterReturn
na_tray_manager_handle_client_message_opcode (GdkXEvent *xev,
GdkEvent *event,
gpointer data)
{
XClientMessageEvent *xevent;
NaTrayManager *manager;
xevent = (XClientMessageEvent *) xev;
manager = data;
switch (xevent->data.l[1])
{
case SYSTEM_TRAY_REQUEST_DOCK:
/* Ignore this one since we don't know on which window this was received
* and so we can't know for which screen this is. It will be handled
* in na_tray_manager_window_filter() since we also receive it there */
break;
case SYSTEM_TRAY_BEGIN_MESSAGE:
na_tray_manager_handle_begin_message (manager, xevent);
return GDK_FILTER_REMOVE;
case SYSTEM_TRAY_CANCEL_MESSAGE:
na_tray_manager_handle_cancel_message (manager, xevent);
return GDK_FILTER_REMOVE;
default:
break;
}
return GDK_FILTER_CONTINUE;
}
static GdkFilterReturn
na_tray_manager_window_filter (GdkXEvent *xev,
GdkEvent *event,
gpointer data)
{
XEvent *xevent = (GdkXEvent *)xev;
NaTrayManager *manager = data;
if (xevent->type == ClientMessage)
{
/* We handle this client message here. See comment in
* na_tray_manager_handle_client_message_opcode() for details */
if (xevent->xclient.message_type == manager->opcode_atom &&
xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK)
{
na_tray_manager_handle_dock_request (manager,
(XClientMessageEvent *) xevent);
return GDK_FILTER_REMOVE;
}
}
else if (xevent->type == SelectionClear)
{
g_signal_emit (manager, manager_signals[LOST_SELECTION], 0);
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
na_tray_manager_unmanage (NaTrayManager *manager)
{
#ifdef GDK_WINDOWING_X11
GdkDisplay *display;
guint32 timestamp;
GtkWidget *invisible;
if (manager->invisible == NULL)
return;
invisible = manager->invisible;
g_assert (GTK_IS_INVISIBLE (invisible));
g_assert (GTK_WIDGET_REALIZED (invisible));
g_assert (GDK_IS_WINDOW (invisible->window));
display = gtk_widget_get_display (invisible);
if (gdk_selection_owner_get_for_display (display, manager->selection_atom) ==
invisible->window)
{
timestamp = gdk_x11_get_server_time (invisible->window);
gdk_selection_owner_set_for_display (display,
NULL,
manager->selection_atom,
timestamp,
TRUE);
}
//FIXME: we should also use gdk_remove_client_message_filter when it's
//available
// See bug #351254
gdk_window_remove_filter (invisible->window,
na_tray_manager_window_filter, manager);
manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */
gtk_widget_destroy (invisible);
g_object_unref (G_OBJECT (invisible));
#endif
}
static void
na_tray_manager_set_orientation_property (NaTrayManager *manager)
{
#ifdef GDK_WINDOWING_X11
GdkDisplay *display;
Atom orientation_atom;
gulong data[1];
if (!manager->invisible || !manager->invisible->window)
return;
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_XWINDOW (manager->invisible->window),
orientation_atom,
XA_CARDINAL, 32,
PropModeReplace,
(guchar *) &data, 1);
#endif
}
static void
na_tray_manager_set_visual_property (NaTrayManager *manager)
{
#ifdef GDK_WINDOWING_X11
GdkDisplay *display;
Visual *xvisual;
Atom visual_atom;
gulong data[1];
if (!manager->invisible || !manager->invisible->window)
return;
/* 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
* we tell the tray icons to use a RGBA colormap and we'll composite the
* icon onto its parents with real transparency. Otherwise, we just tell
* the icon to use our colormap, and we'll do some hacks with parent
* relative backgrounds to simulate transparency.
*/
display = gtk_widget_get_display (manager->invisible);
visual_atom = gdk_x11_get_xatom_by_name_for_display (display,
"_NET_SYSTEM_TRAY_VISUAL");
if (gdk_screen_get_rgba_visual (manager->screen) != NULL &&
gdk_display_supports_composite (display))
{
xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_rgba_visual (manager->screen));
}
else
{
/* 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
*/
GdkColormap *colormap = gdk_screen_get_default_colormap (manager->screen);
xvisual = GDK_VISUAL_XVISUAL (gdk_colormap_get_visual (colormap));
}
data[0] = XVisualIDFromVisual (xvisual);
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
GDK_WINDOW_XWINDOW (manager->invisible->window),
visual_atom,
XA_VISUALID, 32,
PropModeReplace,
(guchar *) &data, 1);
#endif
}
#ifdef GDK_WINDOWING_X11
static gboolean
na_tray_manager_manage_screen_x11 (NaTrayManager *manager,
GdkScreen *screen)
{
GdkDisplay *display;
Screen *xscreen;
GtkWidget *invisible;
char *selection_atom_name;
guint32 timestamp;
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
* we can't create another one.
*/
#if 0
if (na_tray_manager_check_running_screen_x11 (screen))
return FALSE;
#endif
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_screen_get_number (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);
timestamp = gdk_x11_get_server_time (invisible->window);
/* Check if we could set the selection owner successfully */
if (gdk_selection_owner_set_for_display (display,
invisible->window,
manager->selection_atom,
timestamp,
TRUE))
{
XClientMessageEvent xev;
GdkAtom opcode_atom;
GdkAtom message_data_atom;
xev.type = ClientMessage;
xev.window = RootWindowOfScreen (xscreen);
xev.message_type = gdk_x11_get_xatom_by_name_for_display (display,
"MANAGER");
xev.format = 32;
xev.data.l[0] = timestamp;
xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display,
manager->selection_atom);
xev.data.l[2] = GDK_WINDOW_XWINDOW (invisible->window);
xev.data.l[3] = 0; /* manager specific data */
xev.data.l[4] = 0; /* manager specific data */
XSendEvent (GDK_DISPLAY_XDISPLAY (display),
RootWindowOfScreen (xscreen),
False, StructureNotifyMask, (XEvent *)&xev);
opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE);
manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display,
opcode_atom);
message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA",
FALSE);
/* Add a window filter */
#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
/* This is for SYSTEM_TRAY_REQUEST_DOCK and SelectionClear */
gdk_window_add_filter (invisible->window,
na_tray_manager_window_filter, manager);
/* This is for SYSTEM_TRAY_BEGIN_MESSAGE and SYSTEM_TRAY_CANCEL_MESSAGE */
gdk_display_add_client_message_filter (display, opcode_atom,
na_tray_manager_handle_client_message_opcode,
manager);
/* This is for _NET_SYSTEM_TRAY_MESSAGE_DATA */
gdk_display_add_client_message_filter (display, message_data_atom,
na_tray_manager_handle_client_message_message_data,
manager);
return TRUE;
}
else
{
gtk_widget_destroy (invisible);
g_object_unref (invisible);
manager->invisible = NULL;
manager->screen = NULL;
return FALSE;
}
}
#endif
gboolean
na_tray_manager_manage_screen (NaTrayManager *manager,
GdkScreen *screen)
{
g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
g_return_val_if_fail (manager->screen == NULL, FALSE);
#ifdef GDK_WINDOWING_X11
return na_tray_manager_manage_screen_x11 (manager, screen);
#else
return FALSE;
#endif
}
#ifdef GDK_WINDOWING_X11
static gboolean
na_tray_manager_check_running_screen_x11 (GdkScreen *screen)
{
GdkDisplay *display;
Atom selection_atom;
char *selection_atom_name;
display = gdk_screen_get_display (screen);
selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
gdk_screen_get_number (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 (GdkScreen *screen)
{
g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
#ifdef GDK_WINDOWING_X11
return na_tray_manager_check_running_screen_x11 (screen);
#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");
}
}
GtkOrientation
na_tray_manager_get_orientation (NaTrayManager *manager)
{
g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL);
return manager->orientation;
}

View File

@ -0,0 +1,97 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* na-tray-manager.h
* Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
* Copyright (C) 2003-2006 Vincent Untz
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Used to be: eggtraymanager.h
*/
#ifndef __NA_TRAY_MANAGER_H__
#define __NA_TRAY_MANAGER_H__
#include <gtk/gtkwidget.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "na-tray-child.h"
G_BEGIN_DECLS
#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))
#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;
typedef struct _NaTrayManagerClass NaTrayManagerClass;
struct _NaTrayManager
{
GObject parent_instance;
#ifdef GDK_WINDOWING_X11
GdkAtom selection_atom;
Atom opcode_atom;
#endif
GtkWidget *invisible;
GdkScreen *screen;
GtkOrientation orientation;
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 (GdkScreen *screen);
NaTrayManager *na_tray_manager_new (void);
gboolean na_tray_manager_manage_screen (NaTrayManager *manager,
GdkScreen *screen);
void na_tray_manager_set_orientation (NaTrayManager *manager,
GtkOrientation orientation);
GtkOrientation na_tray_manager_get_orientation (NaTrayManager *manager);
G_END_DECLS
#endif /* __NA_TRAY_MANAGER_H__ */