gnome-shell/src/shell-tray-manager.c

375 lines
11 KiB
C
Raw Normal View History

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#include <clutter/clutter.h>
#include <girepository.h>
#include <gtk/gtk.h>
#include <meta/display.h>
#include "shell-tray-manager.h"
#include "na-tray-manager.h"
#include "shell-tray-icon.h"
#include "shell-embedded-window.h"
#include "shell-global.h"
typedef struct _ShellTrayManagerPrivate ShellTrayManagerPrivate;
struct _ShellTrayManager
{
GObject parent_instance;
ShellTrayManagerPrivate *priv;
};
struct _ShellTrayManagerPrivate {
NaTrayManager *na_manager;
ClutterColor bg_color;
GHashTable *icons;
StWidget *theme_widget;
};
typedef struct {
ShellTrayManager *manager;
GtkWidget *socket;
GtkWidget *window;
ClutterActor *actor;
} ShellTrayManagerChild;
enum {
PROP_0,
PROP_BG_COLOR
};
/* Signals */
enum
{
TRAY_ICON_ADDED,
TRAY_ICON_REMOVED,
LAST_SIGNAL
};
G_DEFINE_TYPE_WITH_PRIVATE (ShellTrayManager, shell_tray_manager, G_TYPE_OBJECT);
static guint shell_tray_manager_signals [LAST_SIGNAL] = { 0 };
static const ClutterColor default_color = { 0x00, 0x00, 0x00, 0xff };
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_removed (NaTrayManager *na_manager, GtkWidget *child, gpointer manager);
static void
free_tray_icon (gpointer data)
{
ShellTrayManagerChild *child = data;
gtk_widget_destroy (child->window);
if (child->actor)
{
g_signal_handlers_disconnect_matched (child->actor, G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, child);
g_object_unref (child->actor);
}
g_free (child);
}
static void
shell_tray_manager_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
switch (prop_id)
{
case PROP_BG_COLOR:
{
ClutterColor *color = g_value_get_boxed (value);
if (color)
manager->priv->bg_color = *color;
else
manager->priv->bg_color = default_color;
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_tray_manager_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
switch (prop_id)
{
case PROP_BG_COLOR:
g_value_set_boxed (value, &manager->priv->bg_color);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_tray_manager_init (ShellTrayManager *manager)
{
manager->priv = shell_tray_manager_get_instance_private (manager);
manager->priv->bg_color = default_color;
}
static void
shell_tray_manager_finalize (GObject *object)
{
ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
shell_tray_manager_release_resources (manager);
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);
gobject_class->finalize = shell_tray_manager_finalize;
gobject_class->set_property = shell_tray_manager_set_property;
gobject_class->get_property = shell_tray_manager_get_property;
shell_tray_manager_signals[TRAY_ICON_ADDED] =
g_signal_new ("tray-icon-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
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,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1,
CLUTTER_TYPE_ACTOR);
/* Lifting the CONSTRUCT_ONLY here isn't hard; you just need to
* iterate through the icons, reset the background pixmap, and
* call na_tray_child_force_redraw()
*/
g_object_class_install_property (gobject_class,
PROP_BG_COLOR,
g_param_spec_boxed ("bg-color",
"BG Color",
"Background color (only if we don't have transparency)",
CLUTTER_TYPE_COLOR,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
ShellTrayManager *
shell_tray_manager_new (void)
{
return g_object_new (SHELL_TYPE_TRAY_MANAGER, NULL);
}
static void
shell_tray_manager_ensure_resources (ShellTrayManager *manager)
{
if (manager->priv->na_manager != NULL)
return;
manager->priv->icons = g_hash_table_new_full (NULL, NULL,
NULL, free_tray_icon);
manager->priv->na_manager = na_tray_manager_new ();
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_release_resources (ShellTrayManager *manager)
{
g_clear_object (&manager->priv->na_manager);
g_clear_pointer (&manager->priv->icons, g_hash_table_destroy);
}
static void
shell_tray_manager_style_changed (StWidget *theme_widget,
gpointer user_data)
{
ShellTrayManager *manager = user_data;
StThemeNode *theme_node;
StIconColors *icon_colors;
if (manager->priv->na_manager == NULL)
return;
theme_node = st_widget_get_theme_node (theme_widget);
icon_colors = st_theme_node_get_icon_colors (theme_node);
na_tray_manager_set_colors (manager->priv->na_manager,
&icon_colors->foreground, &icon_colors->warning,
&icon_colors->error, &icon_colors->success);
}
static void
shell_tray_manager_manage_screen_internal (ShellTrayManager *manager)
{
shell_tray_manager_ensure_resources (manager);
na_tray_manager_manage_screen (manager->priv->na_manager);
}
void
Don't use ClutterX11TexturePixmap directly to embed docked windows Previously when a client requests that a window should be docked the shell would reparent the socket window onto the stage's window and then use ClutterX11TexturePixmap to get a texture to represent the window. This will not work if Clutter is no longer using the X11 winsys for example if it becomes its own display server. Instead this patch leaves the socket window as a child of the root window and lets mutter create a MetaWindow out of it. If Mutter is acting as a display server then this mechanism will still work via the headless x server. The ShellGtkEmbed instance now registers for notification of the ‘window-created’ signal of the display so that it can find the MetaWindow that gets created to represent the socket window. When this window is found it is prevented from being displayed on the screen by setting the actor's opacity to 0. An input shape is then set on the window to prevent it receiving any input. Instead of being a subclass of ClutterX11TexturePixmap, ShellGtkEmbed is now a subclass of ClutterClone. When the MetaWindow is found for the socket window the clone's source is set to the invisible actor for the window so it can be displayed in the panel as before. The ShellEmbeddedWindow no longer needs to know what the stage is because it no longer reparents the socket window. Therefore the ShellTrayManager doesn't need to know the stage either so shell_tray_manager_manage_stage has been replaced with just shell_tray_manager_manage_screen. https://bugzilla.gnome.org/show_bug.cgi?id=693438
2013-02-12 22:31:38 +00:00
shell_tray_manager_manage_screen (ShellTrayManager *manager,
StWidget *theme_widget)
{
MetaDisplay *display = shell_global_get_display (shell_global_get ());
g_set_weak_pointer (&manager->priv->theme_widget, theme_widget);
if (meta_display_get_x11_display (display) != NULL)
shell_tray_manager_manage_screen_internal (manager);
g_signal_connect_object (display, "x11-display-setup",
G_CALLBACK (shell_tray_manager_manage_screen_internal),
manager, G_CONNECT_SWAPPED);
g_signal_connect_object (display, "x11-display-closing",
G_CALLBACK (shell_tray_manager_release_resources),
manager, G_CONNECT_SWAPPED);
g_signal_connect_object (theme_widget, "style-changed",
G_CALLBACK (shell_tray_manager_style_changed),
manager, 0);
shell_tray_manager_style_changed (theme_widget, manager);
}
void
shell_tray_manager_unmanage_screen (ShellTrayManager *manager)
{
MetaDisplay *display = shell_global_get_display (shell_global_get ());
g_signal_handlers_disconnect_by_data (display, manager);
if (manager->priv->theme_widget != NULL)
{
g_signal_handlers_disconnect_by_func (manager->priv->theme_widget,
G_CALLBACK (shell_tray_manager_style_changed),
manager);
}
g_set_weak_pointer (&manager->priv->theme_widget, NULL);
shell_tray_manager_release_resources (manager);
}
static void
shell_tray_manager_child_on_realize (GtkWidget *widget,
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)
{
ShellTrayManagerChild *child;
g_signal_handlers_disconnect_by_func (socket, on_plug_added, manager);
child = g_hash_table_lookup (manager->priv->icons, socket);
child->actor = shell_tray_icon_new (SHELL_EMBEDDED_WINDOW (child->window));
g_object_ref_sink (child->actor);
g_signal_emit (manager, shell_tray_manager_signals[TRAY_ICON_ADDED], 0,
child->actor);
}
static void
na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket,
gpointer user_data)
{
ShellTrayManager *manager = user_data;
GtkWidget *win;
ShellTrayManagerChild *child;
Don't use ClutterX11TexturePixmap directly to embed docked windows Previously when a client requests that a window should be docked the shell would reparent the socket window onto the stage's window and then use ClutterX11TexturePixmap to get a texture to represent the window. This will not work if Clutter is no longer using the X11 winsys for example if it becomes its own display server. Instead this patch leaves the socket window as a child of the root window and lets mutter create a MetaWindow out of it. If Mutter is acting as a display server then this mechanism will still work via the headless x server. The ShellGtkEmbed instance now registers for notification of the ‘window-created’ signal of the display so that it can find the MetaWindow that gets created to represent the socket window. When this window is found it is prevented from being displayed on the screen by setting the actor's opacity to 0. An input shape is then set on the window to prevent it receiving any input. Instead of being a subclass of ClutterX11TexturePixmap, ShellGtkEmbed is now a subclass of ClutterClone. When the MetaWindow is found for the socket window the clone's source is set to the invisible actor for the window so it can be displayed in the panel as before. The ShellEmbeddedWindow no longer needs to know what the stage is because it no longer reparents the socket window. Therefore the ShellTrayManager doesn't need to know the stage either so shell_tray_manager_manage_stage has been replaced with just shell_tray_manager_manage_screen. https://bugzilla.gnome.org/show_bug.cgi?id=693438
2013-02-12 22:31:38 +00:00
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->manager = manager;
child->window = win;
child->socket = socket;
g_signal_connect (win, "realize",
G_CALLBACK (shell_tray_manager_child_on_realize), child);
gtk_widget_show_all (win);
g_hash_table_insert (manager->priv->icons, socket, child);
g_signal_connect (socket, "plug-added", G_CALLBACK (on_plug_added), manager);
}
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);
if (child->actor != NULL)
{
/* 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
*/
g_signal_emit (manager,
shell_tray_manager_signals[TRAY_ICON_REMOVED], 0,
child->actor);
}
g_hash_table_remove (manager->priv->icons, socket);
}