├── .gitignore ├── README.md ├── lib ├── fixedtip.c ├── fixedtip.h ├── meson.build ├── na-grid.c ├── na-grid.h ├── na-host.c ├── na-host.h ├── na-item.c ├── na-item.h ├── na-marshal.c ├── na-marshal.h ├── na-tray-child.c ├── na-tray-child.h ├── na-tray-manager.c ├── na-tray-manager.h ├── na-tray.c └── na-tray.h ├── meson.build ├── src ├── Indicator.vala └── meson.build └── vapi └── gtk_x11tray.vapi /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | builddir -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wingpanel-indicator-na-tray 2 | 3 | meson builddir --prefix=/usr 4 | ninja -C builddir 5 | sudo ninja -C builddir install -------------------------------------------------------------------------------- /lib/fixedtip.c: -------------------------------------------------------------------------------- 1 | /* Marco fixed tooltip routine */ 2 | 3 | /* 4 | * Copyright (C) 2001 Havoc Pennington 5 | * Copyright (C) 2003-2006 Vincent Untz 6 | * 7 | * This program is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation; either version 2 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 | * 02110-1301, USA. 21 | */ 22 | 23 | #include 24 | #include "fixedtip.h" 25 | 26 | /* Signals */ 27 | enum 28 | { 29 | CLICKED, 30 | LAST_SIGNAL 31 | }; 32 | 33 | static guint fixedtip_signals[LAST_SIGNAL] = { 0 }; 34 | 35 | struct _NaFixedTipPrivate 36 | { 37 | GtkWidget *parent; 38 | GtkWidget *label; 39 | GtkOrientation orientation; 40 | }; 41 | 42 | G_DEFINE_TYPE_WITH_PRIVATE (NaFixedTip, na_fixed_tip, GTK_TYPE_WINDOW) 43 | 44 | static gboolean 45 | button_press_handler (GtkWidget *fixedtip, 46 | GdkEventButton *event, 47 | gpointer data) 48 | { 49 | if (event->button == 1 && event->type == GDK_BUTTON_PRESS) 50 | g_signal_emit (fixedtip, fixedtip_signals[CLICKED], 0); 51 | 52 | return FALSE; 53 | } 54 | 55 | static gboolean 56 | na_fixed_tip_draw (GtkWidget *widget, cairo_t *cr) 57 | { 58 | GtkStyleContext *context; 59 | GtkStateFlags state; 60 | int width, height; 61 | 62 | width = gtk_widget_get_allocated_width (widget); 63 | height = gtk_widget_get_allocated_height (widget); 64 | 65 | state = gtk_widget_get_state_flags (widget); 66 | context = gtk_widget_get_style_context (widget); 67 | gtk_style_context_save (context); 68 | gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOOLTIP); 69 | gtk_style_context_set_state (context, state); 70 | 71 | cairo_save (cr); 72 | gtk_render_background (context, cr, 73 | 0., 0., 74 | (gdouble)width, 75 | (gdouble)height); 76 | cairo_restore (cr); 77 | 78 | gtk_style_context_restore (context); 79 | 80 | return FALSE; 81 | } 82 | 83 | static void 84 | na_fixed_tip_class_init (NaFixedTipClass *class) 85 | { 86 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); 87 | widget_class->draw = na_fixed_tip_draw; 88 | 89 | fixedtip_signals[CLICKED] = 90 | g_signal_new ("clicked", 91 | G_OBJECT_CLASS_TYPE (class), 92 | G_SIGNAL_RUN_LAST, 93 | G_STRUCT_OFFSET (NaFixedTipClass, clicked), 94 | NULL, NULL, 95 | g_cclosure_marshal_VOID__VOID, 96 | G_TYPE_NONE, 0); 97 | } 98 | 99 | /* Did you already see this code? Yes, it's gtk_tooltips_ force_window() ;-) */ 100 | static void 101 | na_fixed_tip_init (NaFixedTip *fixedtip) 102 | { 103 | GtkWidget *label; 104 | 105 | fixedtip->priv = na_fixed_tip_get_instance_private (fixedtip); 106 | 107 | gtk_window_set_type_hint (GTK_WINDOW (fixedtip), 108 | GDK_WINDOW_TYPE_HINT_TOOLTIP); 109 | 110 | gtk_widget_set_app_paintable (GTK_WIDGET (fixedtip), TRUE); 111 | gtk_window_set_resizable (GTK_WINDOW (fixedtip), FALSE); 112 | gtk_widget_set_name (GTK_WIDGET (fixedtip), "gtk-tooltips"); 113 | gtk_container_set_border_width (GTK_CONTAINER (fixedtip), 4); 114 | 115 | label = gtk_label_new (NULL); 116 | gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); 117 | gtk_label_set_xalign (GTK_LABEL (label), 0.5); 118 | gtk_label_set_yalign (GTK_LABEL (label), 0.5); 119 | gtk_widget_show (label); 120 | gtk_container_add (GTK_CONTAINER (fixedtip), label); 121 | fixedtip->priv->label = label; 122 | 123 | gtk_widget_add_events (GTK_WIDGET (fixedtip), GDK_BUTTON_PRESS_MASK); 124 | 125 | g_signal_connect (fixedtip, "button_press_event", 126 | G_CALLBACK (button_press_handler), NULL); 127 | 128 | fixedtip->priv->orientation = GTK_ORIENTATION_HORIZONTAL; 129 | } 130 | 131 | static void 132 | na_fixed_tip_position (NaFixedTip *fixedtip) 133 | { 134 | GdkScreen *screen; 135 | GdkWindow *parent_window; 136 | GtkRequisition req; 137 | int root_x; 138 | int root_y; 139 | int parent_width; 140 | int parent_height; 141 | int screen_width; 142 | int screen_height; 143 | 144 | screen = gtk_widget_get_screen (fixedtip->priv->parent); 145 | parent_window = gtk_widget_get_window (fixedtip->priv->parent); 146 | 147 | gtk_window_set_screen (GTK_WINDOW (fixedtip), screen); 148 | 149 | gtk_widget_get_preferred_size (GTK_WIDGET (fixedtip), &req, NULL); 150 | 151 | gdk_window_get_origin (parent_window, &root_x, &root_y); 152 | parent_width = gdk_window_get_width(parent_window); 153 | parent_height = gdk_window_get_height(parent_window); 154 | 155 | screen_width = WidthOfScreen (gdk_x11_screen_get_xscreen (screen)); 156 | screen_height = HeightOfScreen (gdk_x11_screen_get_xscreen (screen)); 157 | 158 | /* pad between panel and message window */ 159 | #define PAD 5 160 | 161 | if (fixedtip->priv->orientation == GTK_ORIENTATION_VERTICAL) 162 | { 163 | if (root_x <= screen_width / 2) 164 | root_x += parent_width + PAD; 165 | else 166 | root_x -= req.width + PAD; 167 | } 168 | else 169 | { 170 | if (root_y <= screen_height / 2) 171 | root_y += parent_height + PAD; 172 | else 173 | root_y -= req.height + PAD; 174 | } 175 | 176 | /* Push onscreen */ 177 | if ((root_x + req.width) > screen_width) 178 | root_x = screen_width - req.width; 179 | 180 | if ((root_y + req.height) > screen_height) 181 | root_y = screen_height - req.height; 182 | 183 | gtk_window_move (GTK_WINDOW (fixedtip), root_x, root_y); 184 | } 185 | 186 | static void 187 | na_fixed_tip_parent_size_allocated (GtkWidget *parent, 188 | GtkAllocation *allocation, 189 | NaFixedTip *fixedtip) 190 | { 191 | na_fixed_tip_position (fixedtip); 192 | } 193 | 194 | static void 195 | na_fixed_tip_parent_screen_changed (GtkWidget *parent, 196 | GdkScreen *new_screen, 197 | NaFixedTip *fixedtip) 198 | { 199 | na_fixed_tip_position (fixedtip); 200 | } 201 | 202 | GtkWidget * 203 | na_fixed_tip_new (GtkWidget *parent, 204 | GtkOrientation orientation) 205 | { 206 | NaFixedTip *fixedtip; 207 | 208 | g_return_val_if_fail (parent != NULL, NULL); 209 | 210 | fixedtip = g_object_new (NA_TYPE_FIXED_TIP, 211 | "type", GTK_WINDOW_POPUP, 212 | NULL); 213 | 214 | fixedtip->priv->parent = parent; 215 | 216 | #if 0 217 | /* FIXME: would be nice to be able to get the toplevel for the tip, but this 218 | * doesn't work 219 | */ 220 | GtkWidget *toplevel; 221 | 222 | toplevel = gtk_widget_get_toplevel (parent); 223 | /* 224 | if (toplevel && gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel)) 225 | gtk_window_set_transient_for (GTK_WINDOW (fixedtip), GTK_WINDOW (toplevel)); 226 | */ 227 | #endif 228 | 229 | fixedtip->priv->orientation = orientation; 230 | 231 | /* FIXME: would be nice to move the tip when the notification area moves */ 232 | g_signal_connect_object (parent, "size-allocate", 233 | G_CALLBACK (na_fixed_tip_parent_size_allocated), 234 | fixedtip, 0); 235 | g_signal_connect_object (parent, "screen-changed", 236 | G_CALLBACK (na_fixed_tip_parent_screen_changed), 237 | fixedtip, 0); 238 | 239 | na_fixed_tip_position (fixedtip); 240 | 241 | return GTK_WIDGET (fixedtip); 242 | } 243 | 244 | void 245 | na_fixed_tip_set_markup (GtkWidget *widget, 246 | const char *markup_text) 247 | { 248 | NaFixedTip *fixedtip; 249 | 250 | g_return_if_fail (NA_IS_FIXED_TIP (widget)); 251 | 252 | fixedtip = NA_FIXED_TIP (widget); 253 | 254 | gtk_label_set_markup (GTK_LABEL (fixedtip->priv->label), 255 | markup_text); 256 | 257 | na_fixed_tip_position (fixedtip); 258 | } 259 | 260 | void 261 | na_fixed_tip_set_orientation (GtkWidget *widget, 262 | GtkOrientation orientation) 263 | { 264 | NaFixedTip *fixedtip; 265 | 266 | g_return_if_fail (NA_IS_FIXED_TIP (widget)); 267 | 268 | fixedtip = NA_FIXED_TIP (widget); 269 | 270 | if (orientation == fixedtip->priv->orientation) 271 | return; 272 | 273 | fixedtip->priv->orientation = orientation; 274 | 275 | na_fixed_tip_position (fixedtip); 276 | } -------------------------------------------------------------------------------- /lib/fixedtip.h: -------------------------------------------------------------------------------- 1 | /* Fixed tooltip routine */ 2 | 3 | /* 4 | * Copyright (C) 2001 Havoc Pennington, 2002 Red Hat Inc. 5 | * Copyright (C) 2003-2006 Vincent Untz 6 | * 7 | * This program is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation; either version 2 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 | * 02110-1301, USA. 21 | */ 22 | 23 | #ifndef FIXED_TIP_H 24 | #define FIXED_TIP_H 25 | 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | #define NA_TYPE_FIXED_TIP (na_fixed_tip_get_type ()) 33 | #define NA_FIXED_TIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_FIXED_TIP, NaFixedTip)) 34 | #define NA_FIXED_TIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_FIXED_TIP, NaFixedTipClass)) 35 | #define NA_IS_FIXED_TIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_FIXED_TIP)) 36 | #define NA_IS_FIXED_TIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_FIXED_TIP)) 37 | #define NA_FIXED_TIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_FIXED_TIP, NaFixedTipClass)) 38 | 39 | typedef struct _NaFixedTip NaFixedTip; 40 | typedef struct _NaFixedTipPrivate NaFixedTipPrivate; 41 | typedef struct _NaFixedTipClass NaFixedTipClass; 42 | 43 | struct _NaFixedTip 44 | { 45 | GtkWindow parent_instance; 46 | 47 | NaFixedTipPrivate *priv; 48 | }; 49 | 50 | struct _NaFixedTipClass 51 | { 52 | GtkWindowClass parent_class; 53 | 54 | void (* clicked) (NaFixedTip *fixedtip); 55 | }; 56 | 57 | GType na_fixed_tip_get_type (void); 58 | 59 | GtkWidget *na_fixed_tip_new (GtkWidget *parent, 60 | GtkOrientation orientation); 61 | 62 | void na_fixed_tip_set_markup (GtkWidget *widget, 63 | const char *markup_text); 64 | 65 | void na_fixed_tip_set_orientation (GtkWidget *widget, 66 | GtkOrientation orientation); 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif /* FIXED_TIP_H */ -------------------------------------------------------------------------------- /lib/meson.build: -------------------------------------------------------------------------------- 1 | lib_gtk_x11tray_sources = files( 2 | 'fixedtip.c', 3 | 'na-grid.c', 4 | 'na-host.c', 5 | 'na-item.c', 6 | 'na-marshal.c', 7 | 'na-tray-child.c', 8 | 'na-tray-manager.c', 9 | 'na-tray.c' 10 | ) 11 | 12 | lib_gtk_x11tray_dependencies = [ 13 | gtk_dep, 14 | gobject_dep, 15 | gdk_pixbuf_dep, 16 | x11_dep, 17 | m_dep 18 | ] 19 | 20 | lib_gtk_x11tray_static = static_library ('gtk_x11tray', 21 | lib_gtk_x11tray_sources, 22 | dependencies: lib_gtk_x11tray_dependencies 23 | ) 24 | 25 | lib_gtk_x11tray_dependency = declare_dependency ( 26 | link_with: lib_gtk_x11tray_static, 27 | include_directories: [ include_directories('.') ] 28 | ) -------------------------------------------------------------------------------- /lib/na-grid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002 Red Hat, Inc. 3 | * Copyright (C) 2003-2006 Vincent Untz 4 | * Copyright (C) 2007 Christian Persch 5 | * Copyright (C) 2017 Colomban Wendling 6 | * 7 | * This program is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation; either version 2 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 | * 02110-1301, USA. 21 | */ 22 | 23 | /* Well, actuall'y is the Tray itself, the container for the items. But 24 | * NaTray is already taken for the XEMBED part, so for now it's called NaGrid, 25 | * don't make a big deal out of it. */ 26 | 27 | #include 28 | 29 | #include "na-grid.h" 30 | 31 | #include "na-tray.h" 32 | //#include "status-notifier/sn-host-v0.h" 33 | 34 | #define MIN_ICON_SIZE_DEFAULT 24 35 | 36 | typedef struct 37 | { 38 | GtkOrientation orientation; 39 | gint index; 40 | NaGrid *grid; 41 | } SortData; 42 | 43 | struct _NaGrid 44 | { 45 | GtkGrid parent; 46 | 47 | gint icon_padding; 48 | gint icon_size; 49 | 50 | gint min_icon_size; 51 | gint cols; 52 | gint rows; 53 | gint length; 54 | 55 | GSList *hosts; 56 | GSList *items; 57 | }; 58 | 59 | enum 60 | { 61 | PROP_0, 62 | PROP_ICON_PADDING, 63 | PROP_ICON_SIZE 64 | }; 65 | 66 | G_DEFINE_TYPE (NaGrid, na_grid, GTK_TYPE_GRID) 67 | 68 | static gint 69 | compare_items (gconstpointer a, 70 | gconstpointer b) 71 | { 72 | NaItem *item1; 73 | NaItem *item2; 74 | NaItemCategory c1; 75 | NaItemCategory c2; 76 | const gchar *id1; 77 | const gchar *id2; 78 | 79 | item1 = (NaItem *) a; 80 | item2 = (NaItem *) b; 81 | 82 | c1 = na_item_get_category (item1); 83 | c2 = na_item_get_category (item2); 84 | 85 | if (c1 < c2) 86 | return -1; 87 | else if (c1 > c2) 88 | return 1; 89 | 90 | id1 = na_item_get_id (item1); 91 | id2 = na_item_get_id (item2); 92 | 93 | return g_strcmp0 (id1, id2); 94 | } 95 | 96 | static void 97 | sort_items (GtkWidget *item, 98 | SortData *data) 99 | { 100 | gint col, row, left_attach, top_attach; 101 | 102 | /* row / col number depends on whether we are horizontal or vertical */ 103 | if (data->orientation == GTK_ORIENTATION_HORIZONTAL) 104 | { 105 | col = data->index / data->grid->rows; 106 | row = data->index % data->grid->rows; 107 | } 108 | else 109 | { 110 | row = data->index / data->grid->cols; 111 | col = data->index % data->grid->cols; 112 | } 113 | 114 | /* only update item position if it has changed from current */ 115 | gtk_container_child_get (GTK_CONTAINER (data->grid), 116 | item, 117 | "left-attach", &left_attach, 118 | "top-attach", &top_attach, 119 | NULL); 120 | 121 | if (left_attach != col || top_attach != row) 122 | { 123 | gtk_container_child_set (GTK_CONTAINER (data->grid), 124 | item, 125 | "left-attach", col, 126 | "top-attach", row, 127 | NULL); 128 | } 129 | 130 | /* increment to index of next item */ 131 | data->index++; 132 | } 133 | 134 | static void 135 | refresh_grid (NaGrid *self) 136 | { 137 | GtkOrientation orientation; 138 | GtkAllocation allocation; 139 | gint rows, cols, length; 140 | 141 | orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (self)); 142 | gtk_widget_get_allocation (GTK_WIDGET (self), &allocation); 143 | length = g_slist_length (self->items); 144 | 145 | if (orientation == GTK_ORIENTATION_HORIZONTAL) 146 | { 147 | gtk_grid_set_row_homogeneous (GTK_GRID (self), TRUE); 148 | gtk_grid_set_column_homogeneous (GTK_GRID (self), FALSE); 149 | rows = MAX (1, allocation.height / self->min_icon_size); 150 | cols = MAX (1, length / rows); 151 | if (length % rows) 152 | cols++; 153 | } 154 | else 155 | { 156 | gtk_grid_set_row_homogeneous (GTK_GRID (self), FALSE); 157 | gtk_grid_set_column_homogeneous (GTK_GRID (self), TRUE); 158 | cols = MAX (1, allocation.width / self->min_icon_size); 159 | rows = MAX (1, length / cols); 160 | if (length % cols) 161 | rows++; 162 | } 163 | 164 | if (self->cols != cols || self->rows != rows || self->length != length) 165 | { 166 | self->cols = cols; 167 | self->rows = rows; 168 | self->length = length; 169 | 170 | SortData data; 171 | data.orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (self)); 172 | data.index = 0; 173 | data.grid = self; 174 | 175 | g_slist_foreach (self->items, 176 | (GFunc) sort_items, 177 | &data); 178 | } 179 | } 180 | 181 | void 182 | na_grid_set_min_icon_size (NaGrid *grid, 183 | gint min_icon_size) 184 | { 185 | g_return_if_fail (NA_IS_GRID (grid)); 186 | 187 | grid->min_icon_size = min_icon_size; 188 | 189 | refresh_grid (grid); 190 | } 191 | 192 | static void 193 | item_added_cb (NaHost *host, 194 | NaItem *item, 195 | NaGrid *self) 196 | { 197 | g_return_if_fail (NA_IS_HOST (host)); 198 | g_return_if_fail (NA_IS_ITEM (item)); 199 | g_return_if_fail (NA_IS_GRID (self)); 200 | 201 | g_object_bind_property (self, "orientation", 202 | item, "orientation", 203 | G_BINDING_SYNC_CREATE); 204 | 205 | self->items = g_slist_prepend (self->items, item); 206 | 207 | gtk_widget_set_hexpand (GTK_WIDGET (item), TRUE); 208 | gtk_widget_set_vexpand (GTK_WIDGET (item), TRUE); 209 | gtk_grid_attach (GTK_GRID (self), 210 | GTK_WIDGET (item), 211 | self->cols - 1, 212 | self->rows - 1, 213 | 1, 1); 214 | 215 | self->items = g_slist_sort (self->items, compare_items); 216 | refresh_grid (self); 217 | } 218 | 219 | static void 220 | item_removed_cb (NaHost *host, 221 | NaItem *item, 222 | NaGrid *self) 223 | { 224 | g_return_if_fail (NA_IS_HOST (host)); 225 | g_return_if_fail (NA_IS_ITEM (item)); 226 | g_return_if_fail (NA_IS_GRID (self)); 227 | 228 | gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (item)); 229 | self->items = g_slist_remove (self->items, item); 230 | refresh_grid (self); 231 | } 232 | 233 | static void 234 | na_grid_init (NaGrid *self) 235 | { 236 | self->icon_padding = 10; 237 | self->icon_size = 0; 238 | 239 | self->min_icon_size = MIN_ICON_SIZE_DEFAULT; 240 | self->cols = 1; 241 | self->rows = 1; 242 | self->length = 0; 243 | 244 | self->hosts = NULL; 245 | self->items = NULL; 246 | 247 | gtk_grid_set_row_homogeneous (GTK_GRID (self), TRUE); 248 | gtk_grid_set_column_homogeneous (GTK_GRID (self), TRUE); 249 | 250 | } 251 | 252 | static void 253 | add_host (NaGrid *self, 254 | NaHost *host) 255 | { 256 | self->hosts = g_slist_prepend (self->hosts, host); 257 | 258 | g_object_bind_property (self, "icon-padding", host, "icon-padding", 259 | G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); 260 | g_object_bind_property (self, "icon-size", host, "icon-size", 261 | G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); 262 | 263 | g_signal_connect_object (host, "item-added", 264 | G_CALLBACK (item_added_cb), self, 0); 265 | g_signal_connect_object (host, "item-removed", 266 | G_CALLBACK (item_removed_cb), self, 0); 267 | } 268 | 269 | static void 270 | na_grid_style_updated (GtkWidget *widget) 271 | { 272 | NaGrid *self = NA_GRID (widget); 273 | GtkStyleContext *context; 274 | GSList *node; 275 | 276 | if (GTK_WIDGET_CLASS (na_grid_parent_class)->style_updated) 277 | GTK_WIDGET_CLASS (na_grid_parent_class)->style_updated (widget); 278 | 279 | context = gtk_widget_get_style_context (widget); 280 | 281 | for (node = self->hosts; node; node = node->next) 282 | { 283 | gtk_style_context_save (context); 284 | na_host_style_updated (node->data, context); 285 | gtk_style_context_restore (context); 286 | } 287 | } 288 | 289 | /* Custom drawing because system-tray items need weird stuff. */ 290 | static gboolean 291 | na_grid_draw (GtkWidget *grid, 292 | cairo_t *cr) 293 | { 294 | GList *child; 295 | GList *children = gtk_container_get_children (GTK_CONTAINER (grid)); 296 | 297 | for (child = children; child; child = child->next) 298 | { 299 | if (! NA_IS_ITEM (child->data) || 300 | ! na_item_draw_on_parent (child->data, grid, cr)) 301 | { 302 | if (gtk_widget_is_drawable (child->data) && 303 | gtk_cairo_should_draw_window (cr, gtk_widget_get_window (child->data))) 304 | gtk_container_propagate_draw (GTK_CONTAINER (grid), child->data, cr); 305 | } 306 | } 307 | 308 | g_list_free (children); 309 | 310 | return TRUE; 311 | } 312 | 313 | static void 314 | na_grid_realize (GtkWidget *widget) 315 | { 316 | NaGrid *self = NA_GRID (widget); 317 | GdkScreen *screen; 318 | GtkOrientation orientation; 319 | NaHost *tray_host; 320 | 321 | GTK_WIDGET_CLASS (na_grid_parent_class)->realize (widget); 322 | 323 | /* Instantiate the hosts now we have a screen */ 324 | screen = gtk_widget_get_screen (GTK_WIDGET (self)); 325 | orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (self)); 326 | tray_host = na_tray_new_for_screen (screen, orientation); 327 | g_object_bind_property (self, "orientation", 328 | tray_host, "orientation", 329 | G_BINDING_DEFAULT); 330 | 331 | add_host (self, tray_host); 332 | //add_host (self, sn_host_v0_new ()); 333 | } 334 | 335 | static void 336 | na_grid_unrealize (GtkWidget *widget) 337 | { 338 | NaGrid *self = NA_GRID (widget); 339 | 340 | if (self->hosts != NULL) 341 | { 342 | g_slist_free_full (self->hosts, g_object_unref); 343 | self->hosts = NULL; 344 | } 345 | 346 | g_clear_pointer (&self->items, g_slist_free); 347 | 348 | GTK_WIDGET_CLASS (na_grid_parent_class)->unrealize (widget); 349 | } 350 | 351 | static void 352 | na_grid_size_allocate (GtkWidget *widget, 353 | GtkAllocation *allocation) 354 | { 355 | GTK_WIDGET_CLASS (na_grid_parent_class)->size_allocate (widget, allocation); 356 | refresh_grid (NA_GRID (widget)); 357 | } 358 | 359 | static void 360 | na_grid_get_property (GObject *object, 361 | guint property_id, 362 | GValue *value, 363 | GParamSpec *pspec) 364 | { 365 | NaGrid *self = NA_GRID (object); 366 | 367 | switch (property_id) 368 | { 369 | case PROP_ICON_PADDING: 370 | g_value_set_int (value, self->icon_padding); 371 | break; 372 | 373 | case PROP_ICON_SIZE: 374 | g_value_set_int (value, self->icon_size); 375 | break; 376 | 377 | default: 378 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 379 | break; 380 | } 381 | } 382 | 383 | static void 384 | na_grid_set_property (GObject *object, 385 | guint property_id, 386 | const GValue *value, 387 | GParamSpec *pspec) 388 | { 389 | NaGrid *self = NA_GRID (object); 390 | 391 | switch (property_id) 392 | { 393 | case PROP_ICON_PADDING: 394 | self->icon_padding = g_value_get_int (value); 395 | break; 396 | 397 | case PROP_ICON_SIZE: 398 | self->icon_size = g_value_get_int (value); 399 | break; 400 | 401 | default: 402 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 403 | break; 404 | } 405 | } 406 | 407 | static void 408 | na_grid_class_init (NaGridClass *klass) 409 | { 410 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 411 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); 412 | 413 | gobject_class->get_property = na_grid_get_property; 414 | gobject_class->set_property = na_grid_set_property; 415 | 416 | widget_class->draw = na_grid_draw; 417 | widget_class->realize = na_grid_realize; 418 | widget_class->unrealize = na_grid_unrealize; 419 | widget_class->style_updated = na_grid_style_updated; 420 | widget_class->size_allocate = na_grid_size_allocate; 421 | 422 | g_object_class_install_property (gobject_class, PROP_ICON_PADDING, 423 | g_param_spec_int ("icon-padding", 424 | "Padding around icons", 425 | "Padding that should be put around icons, in pixels", 426 | 0, G_MAXINT, 0, 427 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 428 | 429 | g_object_class_install_property (gobject_class, PROP_ICON_SIZE, 430 | g_param_spec_int ("icon-size", 431 | "Icon size", 432 | "If non-zero, hardcodes the size of the icons in pixels", 433 | 0, G_MAXINT, 0, 434 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 435 | } 436 | 437 | GtkWidget * 438 | na_grid_new (GtkOrientation orientation) 439 | { 440 | return g_object_new (NA_TYPE_GRID, 441 | "orientation", orientation, 442 | NULL); 443 | } 444 | 445 | void 446 | na_grid_force_redraw (NaGrid *grid) 447 | { 448 | GSList *node; 449 | 450 | for (node = grid->hosts; node; node = node->next) 451 | na_host_force_redraw (node->data); 452 | } -------------------------------------------------------------------------------- /lib/na-grid.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ 2 | /* na-tray-tray.h 3 | * Copyright (C) 2002 Anders Carlsson 4 | * Copyright (C) 2003-2006 Vincent Untz 5 | * Copyright (C) 2017 Colomban Wendling 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the 19 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 20 | * Boston, MA 02110-1301, USA. 21 | * 22 | * Used to be: eggtraytray.h 23 | */ 24 | 25 | #ifndef NA_GRID_H 26 | #define NA_GRID_H 27 | 28 | #include 29 | #include 30 | 31 | G_BEGIN_DECLS 32 | 33 | #define NA_TYPE_GRID (na_grid_get_type ()) 34 | G_DECLARE_FINAL_TYPE (NaGrid, na_grid, NA, GRID, GtkGrid) 35 | 36 | void na_grid_set_min_icon_size (NaGrid *grid, 37 | gint min_icon_size); 38 | GtkWidget *na_grid_new (GtkOrientation orientation); 39 | void na_grid_force_redraw (NaGrid *grid); 40 | 41 | G_END_DECLS 42 | 43 | #endif /* __NA_GRID_H__ */ -------------------------------------------------------------------------------- /lib/na-host.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Alberts Muktupāvels 3 | * Copyright (C) 2017 Colomban Wendling 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "na-host.h" 20 | #include "na-item.h" 21 | 22 | enum 23 | { 24 | SIGNAL_ITEM_ADDED, 25 | SIGNAL_ITEM_REMOVED, 26 | 27 | LAST_SIGNAL 28 | }; 29 | 30 | static guint signals[LAST_SIGNAL] = { 0 }; 31 | 32 | G_DEFINE_INTERFACE (NaHost, na_host, G_TYPE_OBJECT) 33 | 34 | static void 35 | na_host_default_init (NaHostInterface *iface) 36 | { 37 | signals[SIGNAL_ITEM_ADDED] = 38 | g_signal_new ("item-added", G_TYPE_FROM_INTERFACE (iface), 39 | G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, 40 | G_TYPE_NONE, 1, NA_TYPE_ITEM); 41 | 42 | signals[SIGNAL_ITEM_REMOVED] = 43 | g_signal_new ("item-removed", G_TYPE_FROM_INTERFACE (iface), 44 | G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, 45 | G_TYPE_NONE, 1, NA_TYPE_ITEM); 46 | 47 | g_object_interface_install_property (iface, 48 | g_param_spec_int ("icon-padding", 49 | "Padding around icons", 50 | "Padding that should be put around icons, in pixels", 51 | 0, G_MAXINT, 0, 52 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 53 | 54 | g_object_interface_install_property (iface, 55 | g_param_spec_int ("icon-size", 56 | "Icon size", 57 | "If non-zero, hardcodes the size of the icons in pixels", 58 | 0, G_MAXINT, 0, 59 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 60 | 61 | iface->style_updated = NULL; 62 | } 63 | 64 | void 65 | na_host_force_redraw (NaHost *host) 66 | { 67 | NaHostInterface *iface; 68 | 69 | g_return_if_fail (NA_IS_HOST (host)); 70 | 71 | iface = NA_HOST_GET_IFACE (host); 72 | 73 | if (iface->force_redraw != NULL) 74 | iface->force_redraw (host); 75 | } 76 | 77 | void 78 | na_host_style_updated (NaHost *host, 79 | GtkStyleContext *context) 80 | { 81 | NaHostInterface *iface; 82 | 83 | g_return_if_fail (NA_IS_HOST (host)); 84 | 85 | iface = NA_HOST_GET_IFACE (host); 86 | 87 | if (iface->style_updated != NULL) 88 | iface->style_updated (host, context); 89 | } 90 | 91 | void 92 | na_host_emit_item_added (NaHost *host, 93 | NaItem *item) 94 | { 95 | g_signal_emit (host, signals[SIGNAL_ITEM_ADDED], 0, item); 96 | } 97 | 98 | void 99 | na_host_emit_item_removed (NaHost *host, 100 | NaItem *item) 101 | { 102 | g_signal_emit (host, signals[SIGNAL_ITEM_REMOVED], 0, item); 103 | } -------------------------------------------------------------------------------- /lib/na-host.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Alberts Muktupāvels 3 | * Copyright (C) 2017 Colomban Wendling 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef NA_HOST_H 20 | #define NA_HOST_H 21 | 22 | #include "na-item.h" 23 | 24 | G_BEGIN_DECLS 25 | 26 | #define NA_TYPE_HOST (na_host_get_type ()) 27 | #define NA_HOST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_HOST, NaHost)) 28 | #define NA_IS_HOST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_HOST)) 29 | #define NA_HOST_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NA_TYPE_HOST, NaHostInterface)) 30 | 31 | typedef struct _NaHost NaHost; 32 | typedef struct _NaHostInterface NaHostInterface; 33 | 34 | struct _NaHostInterface 35 | { 36 | GTypeInterface parent; 37 | 38 | void (*force_redraw) (NaHost *host); 39 | void (*style_updated) (NaHost *host, 40 | GtkStyleContext *context); 41 | }; 42 | 43 | GType na_host_get_type (void); 44 | void na_host_force_redraw (NaHost *host); 45 | void na_host_style_updated (NaHost *host, 46 | GtkStyleContext *context); 47 | void na_host_emit_item_added (NaHost *host, 48 | NaItem *item); 49 | void na_host_emit_item_removed (NaHost *host, 50 | NaItem *item); 51 | 52 | G_END_DECLS 53 | 54 | #endif -------------------------------------------------------------------------------- /lib/na-item.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Alberts Muktupāvels 3 | * Copyright (C) 2017 Colomban Wendling 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "na-item.h" 20 | 21 | G_DEFINE_INTERFACE_WITH_CODE (NaItem, na_item, GTK_TYPE_WIDGET, 22 | g_type_interface_add_prerequisite (g_define_type_id, 23 | GTK_TYPE_ORIENTABLE);) 24 | 25 | static gboolean 26 | na_item_draw_on_parent_default (NaItem *item, 27 | GtkWidget *parent, 28 | cairo_t *parent_cr) 29 | { 30 | return FALSE; 31 | } 32 | 33 | static void 34 | na_item_default_init (NaItemInterface *iface) 35 | { 36 | iface->draw_on_parent = na_item_draw_on_parent_default; 37 | } 38 | 39 | const gchar * 40 | na_item_get_id (NaItem *item) 41 | { 42 | NaItemInterface *iface; 43 | 44 | g_return_val_if_fail (NA_IS_ITEM (item), NULL); 45 | 46 | iface = NA_ITEM_GET_IFACE (item); 47 | g_return_val_if_fail (iface->get_id != NULL, NULL); 48 | 49 | return iface->get_id (item); 50 | } 51 | 52 | NaItemCategory 53 | na_item_get_category (NaItem *item) 54 | { 55 | NaItemInterface *iface; 56 | 57 | g_return_val_if_fail (NA_IS_ITEM (item), 58 | NA_ITEM_CATEGORY_APPLICATION_STATUS); 59 | 60 | iface = NA_ITEM_GET_IFACE (item); 61 | g_return_val_if_fail (iface->get_category != NULL, 62 | NA_ITEM_CATEGORY_APPLICATION_STATUS); 63 | 64 | return iface->get_category (item); 65 | } 66 | 67 | /* 68 | * Fairly ugly hack because system-tray/NaTrayChild uses a weird hack for 69 | * drawing itself. I'm not sure it's still needed with the current GTK3 70 | * drawing where not all widgets have an own window, but well. 71 | * 72 | * Should return %TRUE if it handled itself, or %FALSE if the parent should 73 | * draw normally. Default is to draw normally. 74 | */ 75 | gboolean 76 | na_item_draw_on_parent (NaItem *item, 77 | GtkWidget *parent, 78 | cairo_t *parent_cr) 79 | { 80 | NaItemInterface *iface; 81 | 82 | g_return_val_if_fail (NA_IS_ITEM (item), FALSE); 83 | g_return_val_if_fail (GTK_IS_WIDGET (parent), FALSE); 84 | 85 | iface = NA_ITEM_GET_IFACE (item); 86 | g_return_val_if_fail (iface->draw_on_parent != NULL, FALSE); 87 | 88 | return iface->draw_on_parent (item, parent, parent_cr); 89 | } -------------------------------------------------------------------------------- /lib/na-item.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Alberts Muktupāvels 3 | * Copyright (C) 2017 Colomban Wendling 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef NA_ITEM_H 20 | #define NA_ITEM_H 21 | 22 | #include 23 | 24 | G_BEGIN_DECLS 25 | 26 | #define NA_TYPE_ITEM (na_item_get_type ()) 27 | #define NA_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_ITEM, NaItem)) 28 | #define NA_IS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_ITEM)) 29 | #define NA_ITEM_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NA_TYPE_ITEM, NaItemInterface)) 30 | 31 | typedef struct _NaItem NaItem; 32 | typedef struct _NaItemInterface NaItemInterface; 33 | 34 | typedef enum 35 | { 36 | NA_ITEM_CATEGORY_APPLICATION_STATUS, 37 | NA_ITEM_CATEGORY_COMMUNICATIONS, 38 | NA_ITEM_CATEGORY_SYSTEM_SERVICES, 39 | NA_ITEM_CATEGORY_HARDWARE, 40 | } NaItemCategory; 41 | 42 | struct _NaItemInterface 43 | { 44 | GTypeInterface g_iface; 45 | 46 | const gchar * (* get_id) (NaItem *item); 47 | NaItemCategory (* get_category) (NaItem *item); 48 | 49 | gboolean (* draw_on_parent) (NaItem *item, 50 | GtkWidget *parent, 51 | cairo_t *parent_cr); 52 | }; 53 | 54 | GType na_item_get_type (void); 55 | const gchar *na_item_get_id (NaItem *item); 56 | NaItemCategory na_item_get_category (NaItem *item); 57 | gboolean na_item_draw_on_parent (NaItem *item, 58 | GtkWidget *parent, 59 | cairo_t *parent_cr); 60 | 61 | G_END_DECLS 62 | 63 | #endif -------------------------------------------------------------------------------- /lib/na-marshal.c: -------------------------------------------------------------------------------- 1 | #include "na-marshal.h" 2 | /* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ 3 | #include 4 | 5 | #ifdef G_ENABLE_DEBUG 6 | #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) 7 | #define g_marshal_value_peek_char(v) g_value_get_schar (v) 8 | #define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) 9 | #define g_marshal_value_peek_int(v) g_value_get_int (v) 10 | #define g_marshal_value_peek_uint(v) g_value_get_uint (v) 11 | #define g_marshal_value_peek_long(v) g_value_get_long (v) 12 | #define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) 13 | #define g_marshal_value_peek_int64(v) g_value_get_int64 (v) 14 | #define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) 15 | #define g_marshal_value_peek_enum(v) g_value_get_enum (v) 16 | #define g_marshal_value_peek_flags(v) g_value_get_flags (v) 17 | #define g_marshal_value_peek_float(v) g_value_get_float (v) 18 | #define g_marshal_value_peek_double(v) g_value_get_double (v) 19 | #define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) 20 | #define g_marshal_value_peek_param(v) g_value_get_param (v) 21 | #define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) 22 | #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) 23 | #define g_marshal_value_peek_object(v) g_value_get_object (v) 24 | #define g_marshal_value_peek_variant(v) g_value_get_variant (v) 25 | #else /* !G_ENABLE_DEBUG */ 26 | /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. 27 | * Do not access GValues directly in your code. Instead, use the 28 | * g_value_get_*() functions 29 | */ 30 | #define g_marshal_value_peek_boolean(v) (v)->data[0].v_int 31 | #define g_marshal_value_peek_char(v) (v)->data[0].v_int 32 | #define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint 33 | #define g_marshal_value_peek_int(v) (v)->data[0].v_int 34 | #define g_marshal_value_peek_uint(v) (v)->data[0].v_uint 35 | #define g_marshal_value_peek_long(v) (v)->data[0].v_long 36 | #define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong 37 | #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 38 | #define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 39 | #define g_marshal_value_peek_enum(v) (v)->data[0].v_long 40 | #define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong 41 | #define g_marshal_value_peek_float(v) (v)->data[0].v_float 42 | #define g_marshal_value_peek_double(v) (v)->data[0].v_double 43 | #define g_marshal_value_peek_string(v) (v)->data[0].v_pointer 44 | #define g_marshal_value_peek_param(v) (v)->data[0].v_pointer 45 | #define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer 46 | #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer 47 | #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer 48 | #define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer 49 | #endif /* !G_ENABLE_DEBUG */ 50 | 51 | /* VOID:OBJECT,OBJECT (na-marshal.list:1) */ 52 | void 53 | _na_marshal_VOID__OBJECT_OBJECT (GClosure *closure, 54 | GValue *return_value G_GNUC_UNUSED, 55 | guint n_param_values, 56 | const GValue *param_values, 57 | gpointer invocation_hint G_GNUC_UNUSED, 58 | gpointer marshal_data) 59 | { 60 | typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT) (gpointer data1, 61 | gpointer arg1, 62 | gpointer arg2, 63 | gpointer data2); 64 | GCClosure *cc = (GCClosure *) closure; 65 | gpointer data1, data2; 66 | GMarshalFunc_VOID__OBJECT_OBJECT callback; 67 | 68 | g_return_if_fail (n_param_values == 3); 69 | 70 | if (G_CCLOSURE_SWAP_DATA (closure)) 71 | { 72 | data1 = closure->data; 73 | data2 = g_value_peek_pointer (param_values + 0); 74 | } 75 | else 76 | { 77 | data1 = g_value_peek_pointer (param_values + 0); 78 | data2 = closure->data; 79 | } 80 | callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback); 81 | 82 | callback (data1, 83 | g_marshal_value_peek_object (param_values + 1), 84 | g_marshal_value_peek_object (param_values + 2), 85 | data2); 86 | } 87 | 88 | /* VOID:OBJECT,STRING,LONG,LONG (na-marshal.list:2) */ 89 | void 90 | _na_marshal_VOID__OBJECT_STRING_LONG_LONG (GClosure *closure, 91 | GValue *return_value G_GNUC_UNUSED, 92 | guint n_param_values, 93 | const GValue *param_values, 94 | gpointer invocation_hint G_GNUC_UNUSED, 95 | gpointer marshal_data) 96 | { 97 | typedef void (*GMarshalFunc_VOID__OBJECT_STRING_LONG_LONG) (gpointer data1, 98 | gpointer arg1, 99 | gpointer arg2, 100 | glong arg3, 101 | glong arg4, 102 | gpointer data2); 103 | GCClosure *cc = (GCClosure *) closure; 104 | gpointer data1, data2; 105 | GMarshalFunc_VOID__OBJECT_STRING_LONG_LONG callback; 106 | 107 | g_return_if_fail (n_param_values == 5); 108 | 109 | if (G_CCLOSURE_SWAP_DATA (closure)) 110 | { 111 | data1 = closure->data; 112 | data2 = g_value_peek_pointer (param_values + 0); 113 | } 114 | else 115 | { 116 | data1 = g_value_peek_pointer (param_values + 0); 117 | data2 = closure->data; 118 | } 119 | callback = (GMarshalFunc_VOID__OBJECT_STRING_LONG_LONG) (marshal_data ? marshal_data : cc->callback); 120 | 121 | callback (data1, 122 | g_marshal_value_peek_object (param_values + 1), 123 | g_marshal_value_peek_string (param_values + 2), 124 | g_marshal_value_peek_long (param_values + 3), 125 | g_marshal_value_peek_long (param_values + 4), 126 | data2); 127 | } 128 | 129 | /* VOID:OBJECT,LONG (na-marshal.list:3) */ 130 | void 131 | _na_marshal_VOID__OBJECT_LONG (GClosure *closure, 132 | GValue *return_value G_GNUC_UNUSED, 133 | guint n_param_values, 134 | const GValue *param_values, 135 | gpointer invocation_hint G_GNUC_UNUSED, 136 | gpointer marshal_data) 137 | { 138 | typedef void (*GMarshalFunc_VOID__OBJECT_LONG) (gpointer data1, 139 | gpointer arg1, 140 | glong arg2, 141 | gpointer data2); 142 | GCClosure *cc = (GCClosure *) closure; 143 | gpointer data1, data2; 144 | GMarshalFunc_VOID__OBJECT_LONG callback; 145 | 146 | g_return_if_fail (n_param_values == 3); 147 | 148 | if (G_CCLOSURE_SWAP_DATA (closure)) 149 | { 150 | data1 = closure->data; 151 | data2 = g_value_peek_pointer (param_values + 0); 152 | } 153 | else 154 | { 155 | data1 = g_value_peek_pointer (param_values + 0); 156 | data2 = closure->data; 157 | } 158 | callback = (GMarshalFunc_VOID__OBJECT_LONG) (marshal_data ? marshal_data : cc->callback); 159 | 160 | callback (data1, 161 | g_marshal_value_peek_object (param_values + 1), 162 | g_marshal_value_peek_long (param_values + 2), 163 | data2); 164 | } 165 | 166 | -------------------------------------------------------------------------------- /lib/na-marshal.h: -------------------------------------------------------------------------------- 1 | /* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ 2 | #ifndef ___NA_MARSHAL_MARSHAL_H__ 3 | #define ___NA_MARSHAL_MARSHAL_H__ 4 | 5 | #include 6 | 7 | G_BEGIN_DECLS 8 | 9 | /* VOID:OBJECT,OBJECT (na-marshal.list:1) */ 10 | extern 11 | void _na_marshal_VOID__OBJECT_OBJECT (GClosure *closure, 12 | GValue *return_value, 13 | guint n_param_values, 14 | const GValue *param_values, 15 | gpointer invocation_hint, 16 | gpointer marshal_data); 17 | 18 | /* VOID:OBJECT,STRING,LONG,LONG (na-marshal.list:2) */ 19 | extern 20 | void _na_marshal_VOID__OBJECT_STRING_LONG_LONG (GClosure *closure, 21 | GValue *return_value, 22 | guint n_param_values, 23 | const GValue *param_values, 24 | gpointer invocation_hint, 25 | gpointer marshal_data); 26 | 27 | /* VOID:OBJECT,LONG (na-marshal.list:3) */ 28 | extern 29 | void _na_marshal_VOID__OBJECT_LONG (GClosure *closure, 30 | GValue *return_value, 31 | guint n_param_values, 32 | const GValue *param_values, 33 | gpointer invocation_hint, 34 | gpointer marshal_data); 35 | 36 | 37 | G_END_DECLS 38 | 39 | #endif /* ___NA_MARSHAL_MARSHAL_H__ */ 40 | -------------------------------------------------------------------------------- /lib/na-tray-child.c: -------------------------------------------------------------------------------- 1 | /* na-tray-child.c 2 | * Copyright (C) 2002 Anders Carlsson 3 | * Copyright (C) 2003-2006 Vincent Untz 4 | * Copyright (C) 2008 Red Hat, Inc. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the 18 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 19 | * Boston, MA 02110-1301, USA. 20 | */ 21 | 22 | #include 23 | 24 | #include "na-tray-child.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "na-item.h" 33 | 34 | enum 35 | { 36 | PROP_0, 37 | PROP_ORIENTATION 38 | }; 39 | 40 | static void na_item_init (NaItemInterface *iface); 41 | 42 | G_DEFINE_TYPE_WITH_CODE (NaTrayChild, na_tray_child, GTK_TYPE_SOCKET, 43 | G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL) 44 | G_IMPLEMENT_INTERFACE (NA_TYPE_ITEM, na_item_init)) 45 | 46 | static void 47 | na_tray_child_finalize (GObject *object) 48 | { 49 | NaTrayChild *child = NA_TRAY_CHILD (object); 50 | 51 | g_clear_pointer (&child->id, g_free); 52 | 53 | G_OBJECT_CLASS (na_tray_child_parent_class)->finalize (object); 54 | } 55 | 56 | static void 57 | na_tray_child_realize (GtkWidget *widget) 58 | { 59 | NaTrayChild *child = NA_TRAY_CHILD (widget); 60 | GdkVisual *visual = gtk_widget_get_visual (widget); 61 | GdkWindow *window; 62 | 63 | GTK_WIDGET_CLASS (na_tray_child_parent_class)->realize (widget); 64 | 65 | window = gtk_widget_get_window (widget); 66 | 67 | if (child->has_alpha) 68 | { 69 | /* We have real transparency with an ARGB visual and the Composite 70 | * extension. */ 71 | 72 | /* Set a transparent background */ 73 | cairo_pattern_t *transparent = cairo_pattern_create_rgba (0, 0, 0, 0); 74 | gdk_window_set_background_pattern (window, transparent); 75 | gdk_window_set_composited (window, TRUE); 76 | cairo_pattern_destroy (transparent); 77 | 78 | child->parent_relative_bg = FALSE; 79 | } 80 | else if (visual == gdk_window_get_visual(gdk_window_get_parent(window))) 81 | { 82 | /* Otherwise, if the visual matches the visual of the parent window, we 83 | * can use a parent-relative background and fake transparency. */ 84 | gdk_window_set_background_pattern (window, NULL); 85 | 86 | child->parent_relative_bg = TRUE; 87 | } 88 | else 89 | { 90 | /* Nothing to do; the icon will sit on top of an ugly gray box */ 91 | child->parent_relative_bg = FALSE; 92 | } 93 | 94 | gdk_window_set_composited (window, child->composited); 95 | 96 | gtk_widget_set_app_paintable (GTK_WIDGET (child), 97 | child->parent_relative_bg || child->has_alpha); 98 | } 99 | 100 | static void 101 | na_tray_child_style_set (GtkWidget *widget, 102 | GtkStyle *previous_style) 103 | { 104 | /* The default handler resets the background according to the new style. 105 | * We either use a transparent background or a parent-relative background 106 | * and ignore the style background. So, just don't chain up. 107 | */ 108 | } 109 | 110 | #if !GTK_CHECK_VERSION (3, 23, 0) 111 | static void 112 | na_tray_child_get_preferred_width (GtkWidget *widget, 113 | gint *minimal_width, 114 | gint *natural_width) 115 | { 116 | gint scale; 117 | scale = gtk_widget_get_scale_factor (widget); 118 | GTK_WIDGET_CLASS (na_tray_child_parent_class)->get_preferred_width (widget, 119 | minimal_width, 120 | natural_width); 121 | 122 | if (*minimal_width < 16) 123 | *minimal_width = 16; 124 | 125 | if (*natural_width < 16) 126 | *natural_width = 16; 127 | 128 | *minimal_width = *minimal_width / scale; 129 | *natural_width = *natural_width / scale; 130 | } 131 | 132 | static void 133 | na_tray_child_get_preferred_height (GtkWidget *widget, 134 | gint *minimal_height, 135 | gint *natural_height) 136 | { 137 | gint scale; 138 | scale = gtk_widget_get_scale_factor (widget); 139 | GTK_WIDGET_CLASS (na_tray_child_parent_class)->get_preferred_height (widget, 140 | minimal_height, 141 | natural_height); 142 | 143 | if (*minimal_height < 16) 144 | *minimal_height = 16; 145 | 146 | if (*natural_height < 16) 147 | *natural_height = 16; 148 | 149 | *minimal_height = *minimal_height / scale; 150 | *natural_height = *natural_height / scale; 151 | } 152 | #endif 153 | 154 | /* The plug window should completely occupy the area of the child, so we won't 155 | * get an expose event. But in case we do (the plug unmaps itself, say), this 156 | * expose handler draws with real or fake transparency. 157 | */ 158 | static gboolean 159 | na_tray_child_draw (GtkWidget *widget, 160 | cairo_t *cr) 161 | { 162 | NaTrayChild *child = NA_TRAY_CHILD (widget); 163 | 164 | if (na_tray_child_has_alpha (child)) 165 | { 166 | /* Clear to transparent */ 167 | cairo_set_source_rgba (cr, 0, 0, 0, 0); 168 | cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); 169 | cairo_paint (cr); 170 | } 171 | else if (child->parent_relative_bg) 172 | { 173 | /* Clear to parent-relative pixmap */ 174 | GdkWindow *window; 175 | cairo_surface_t *target; 176 | GdkRectangle clip_rect; 177 | 178 | window = gtk_widget_get_window (widget); 179 | target = cairo_get_group_target (cr); 180 | 181 | gdk_cairo_get_clip_rectangle (cr, &clip_rect); 182 | 183 | /* Clear to parent-relative pixmap 184 | * We need to use direct X access here because GDK doesn't know about 185 | * the parent relative pixmap. */ 186 | cairo_surface_flush (target); 187 | 188 | XClearArea (GDK_WINDOW_XDISPLAY (window), 189 | GDK_WINDOW_XID (window), 190 | clip_rect.x, clip_rect.y, 191 | clip_rect.width, clip_rect.height, 192 | False); 193 | cairo_surface_mark_dirty_rectangle (target, 194 | clip_rect.x, clip_rect.y, 195 | clip_rect.width, clip_rect.height); 196 | } 197 | 198 | return FALSE; 199 | } 200 | 201 | /* Children with alpha channels have been set to be composited by calling 202 | * gdk_window_set_composited(). We need to paint these children ourselves. 203 | * 204 | * FIXME: is that still needed on GTK3? Seems like it could be done in draw(). 205 | */ 206 | static gboolean 207 | na_tray_child_draw_on_parent (NaItem *item, 208 | GtkWidget *parent, 209 | cairo_t *parent_cr) 210 | { 211 | if (na_tray_child_has_alpha (NA_TRAY_CHILD (item))) 212 | { 213 | GtkWidget *widget = GTK_WIDGET (item); 214 | GtkAllocation parent_allocation = { 0 }; 215 | GtkAllocation allocation; 216 | 217 | /* if the parent doesn't have a window, our allocation is not relative to 218 | * the context coordinates but to the parent's allocation */ 219 | if (! gtk_widget_get_has_window (parent)) 220 | gtk_widget_get_allocation (parent, &parent_allocation); 221 | 222 | gtk_widget_get_allocation (widget, &allocation); 223 | allocation.x -= parent_allocation.x; 224 | allocation.y -= parent_allocation.y; 225 | 226 | cairo_save (parent_cr); 227 | gdk_cairo_set_source_window (parent_cr, 228 | gtk_widget_get_window (widget), 229 | allocation.x, 230 | allocation.y); 231 | cairo_rectangle (parent_cr, allocation.x, allocation.y, allocation.width, allocation.height); 232 | cairo_clip (parent_cr); 233 | cairo_paint (parent_cr); 234 | cairo_restore (parent_cr); 235 | } 236 | 237 | return TRUE; 238 | } 239 | 240 | static void 241 | na_tray_child_get_property (GObject *object, 242 | guint property_id, 243 | GValue *value, 244 | GParamSpec *pspec) 245 | { 246 | switch (property_id) 247 | { 248 | case PROP_ORIENTATION: 249 | /* whatever */ 250 | g_value_set_enum (value, GTK_ORIENTATION_HORIZONTAL); 251 | break; 252 | 253 | default: 254 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 255 | break; 256 | } 257 | } 258 | 259 | static void 260 | na_tray_child_set_property (GObject *object, 261 | guint property_id, 262 | const GValue *value, 263 | GParamSpec *pspec) 264 | { 265 | switch (property_id) 266 | { 267 | case PROP_ORIENTATION: 268 | /* we so don't care */ 269 | break; 270 | 271 | default: 272 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 273 | break; 274 | } 275 | } 276 | 277 | /* Hack to keep order of some known system-tray elements. For a wm_class 278 | * match, give it category @category and ID @id. 279 | * 280 | * TODO: improve this to play well if one of those elements were to start 281 | * using SNI instead */ 282 | static const struct 283 | { 284 | const gchar *const wm_class; 285 | const gchar *const id; 286 | NaItemCategory category; 287 | } wmclass_categories[] = { 288 | /* order is LTR, so higher category and higher ASCII ordering on the right */ 289 | { "keyboard", "~01-keyboard", NA_ITEM_CATEGORY_HARDWARE }, 290 | { "Mate-volume-control-applet", "~02-volume", NA_ITEM_CATEGORY_HARDWARE }, 291 | { "Bluetooth-applet", "~03-bluetooth", NA_ITEM_CATEGORY_HARDWARE }, 292 | { "Nm-applet", "~04-network", NA_ITEM_CATEGORY_HARDWARE }, 293 | { "Mate-power-manager", "~05-battery", NA_ITEM_CATEGORY_HARDWARE }, 294 | }; 295 | 296 | static const gchar * 297 | na_tray_child_get_id (NaItem *item) 298 | { 299 | NaTrayChild *child = NA_TRAY_CHILD (item); 300 | 301 | if (! child->id) 302 | { 303 | char *res_name = NULL; 304 | char *res_class = NULL; 305 | guint i; 306 | 307 | na_tray_child_get_wm_class (child, &res_name, &res_class); 308 | 309 | for (i = 0; i < G_N_ELEMENTS (wmclass_categories) && ! child->id; i++) 310 | { 311 | if (g_strcmp0 (res_class, wmclass_categories[i].wm_class) == 0) 312 | child->id = g_strdup (wmclass_categories[i].id); 313 | } 314 | 315 | if (! child->id) 316 | child->id = res_name; 317 | else 318 | g_free (res_name); 319 | 320 | g_free (res_class); 321 | } 322 | 323 | return child->id; 324 | } 325 | 326 | static NaItemCategory 327 | na_tray_child_get_category (NaItem *item) 328 | { 329 | guint i; 330 | NaItemCategory category = NA_ITEM_CATEGORY_APPLICATION_STATUS; 331 | char *res_class = NULL; 332 | 333 | na_tray_child_get_wm_class (NA_TRAY_CHILD (item), NULL, &res_class); 334 | 335 | for (i = 0; i < G_N_ELEMENTS (wmclass_categories); i++) 336 | { 337 | if (g_strcmp0 (res_class, wmclass_categories[i].wm_class) == 0) 338 | { 339 | category = wmclass_categories[i].category; 340 | break; 341 | } 342 | } 343 | 344 | g_free (res_class); 345 | 346 | return category; 347 | } 348 | 349 | static void 350 | na_item_init (NaItemInterface *iface) 351 | { 352 | iface->get_id = na_tray_child_get_id; 353 | iface->get_category = na_tray_child_get_category; 354 | 355 | iface->draw_on_parent = na_tray_child_draw_on_parent; 356 | } 357 | 358 | static void 359 | na_tray_child_init (NaTrayChild *child) 360 | { 361 | child->id = NULL; 362 | } 363 | 364 | static void 365 | na_tray_child_class_init (NaTrayChildClass *klass) 366 | { 367 | GObjectClass *gobject_class; 368 | GtkWidgetClass *widget_class; 369 | 370 | gobject_class = (GObjectClass *)klass; 371 | widget_class = (GtkWidgetClass *)klass; 372 | 373 | gobject_class->finalize = na_tray_child_finalize; 374 | gobject_class->get_property = na_tray_child_get_property; 375 | gobject_class->set_property = na_tray_child_set_property; 376 | 377 | widget_class->style_set = na_tray_child_style_set; 378 | widget_class->realize = na_tray_child_realize; 379 | #if !GTK_CHECK_VERSION (3, 23, 0) 380 | widget_class->get_preferred_width = na_tray_child_get_preferred_width; 381 | widget_class->get_preferred_height = na_tray_child_get_preferred_height; 382 | #endif 383 | widget_class->draw = na_tray_child_draw; 384 | 385 | /* we don't really care actually */ 386 | g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation"); 387 | } 388 | 389 | GtkWidget * 390 | na_tray_child_new (GdkScreen *screen, 391 | Window icon_window) 392 | { 393 | XWindowAttributes window_attributes; 394 | Display *xdisplay; 395 | GdkDisplay *display; 396 | NaTrayChild *child; 397 | GdkVisual *visual; 398 | gboolean visual_has_alpha; 399 | int red_prec, green_prec, blue_prec, depth; 400 | int result; 401 | 402 | g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); 403 | g_return_val_if_fail (icon_window != None, NULL); 404 | 405 | xdisplay = GDK_SCREEN_XDISPLAY (screen); 406 | 407 | /* We need to determine the visual of the window we are embedding and create 408 | * the socket in the same visual. 409 | */ 410 | 411 | display = gdk_screen_get_display (screen); 412 | if (!GDK_IS_X11_DISPLAY (display)) { 413 | g_warning ("na_tray only works on X11"); 414 | return NULL; 415 | } 416 | gdk_x11_display_error_trap_push (display); 417 | result = XGetWindowAttributes (xdisplay, icon_window, 418 | &window_attributes); 419 | gdk_x11_display_error_trap_pop_ignored (display); 420 | 421 | if (!result) /* Window already gone */ 422 | return NULL; 423 | 424 | visual = gdk_x11_screen_lookup_visual (screen, 425 | window_attributes.visual->visualid); 426 | if (!visual) /* Icon window is on another screen? */ 427 | return NULL; 428 | 429 | child = g_object_new (NA_TYPE_TRAY_CHILD, NULL); 430 | child->icon_window = icon_window; 431 | 432 | gtk_widget_set_visual (GTK_WIDGET (child), visual); 433 | 434 | /* We have alpha if the visual has something other than red, green, 435 | * and blue */ 436 | gdk_visual_get_red_pixel_details (visual, NULL, NULL, &red_prec); 437 | gdk_visual_get_green_pixel_details (visual, NULL, NULL, &green_prec); 438 | gdk_visual_get_blue_pixel_details (visual, NULL, NULL, &blue_prec); 439 | depth = gdk_visual_get_depth (visual); 440 | 441 | visual_has_alpha = red_prec + blue_prec + green_prec < depth; 442 | child->has_alpha = (visual_has_alpha && 443 | gdk_display_supports_composite (gdk_screen_get_display (screen))); 444 | 445 | child->composited = child->has_alpha; 446 | 447 | return GTK_WIDGET (child); 448 | } 449 | 450 | char * 451 | na_tray_child_get_title (NaTrayChild *child) 452 | { 453 | char *retval = NULL; 454 | GdkDisplay *display; 455 | Atom utf8_string, atom, type; 456 | int result; 457 | int format; 458 | gulong nitems; 459 | gulong bytes_after; 460 | gchar *val; 461 | 462 | g_return_val_if_fail (NA_IS_TRAY_CHILD (child), NULL); 463 | 464 | display = gtk_widget_get_display (GTK_WIDGET (child)); 465 | 466 | utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"); 467 | atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME"); 468 | 469 | gdk_x11_display_error_trap_push (display); 470 | 471 | result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), 472 | child->icon_window, 473 | atom, 474 | 0, G_MAXLONG, 475 | False, utf8_string, 476 | &type, &format, &nitems, 477 | &bytes_after, (guchar **)&val); 478 | 479 | if (gdk_x11_display_error_trap_pop (display) || result != Success) 480 | return NULL; 481 | 482 | if (type != utf8_string || 483 | format != 8 || 484 | nitems == 0) 485 | { 486 | if (val) 487 | XFree (val); 488 | return NULL; 489 | } 490 | 491 | if (!g_utf8_validate (val, nitems, NULL)) 492 | { 493 | XFree (val); 494 | return NULL; 495 | } 496 | 497 | retval = g_strndup (val, nitems); 498 | 499 | XFree (val); 500 | 501 | return retval; 502 | } 503 | 504 | /** 505 | * na_tray_child_has_alpha; 506 | * @child: a #NaTrayChild 507 | * 508 | * Checks if the child has an ARGB visual and real alpha transparence. 509 | * (as opposed to faked alpha transparency with an parent-relative 510 | * background) 511 | * 512 | * Return value: %TRUE if the child has an alpha transparency 513 | */ 514 | gboolean 515 | na_tray_child_has_alpha (NaTrayChild *child) 516 | { 517 | g_return_val_if_fail (NA_IS_TRAY_CHILD (child), FALSE); 518 | 519 | return child->has_alpha; 520 | } 521 | 522 | /** 523 | * na_tray_child_set_composited; 524 | * @child: a #NaTrayChild 525 | * @composited: %TRUE if the child's window should be redirected 526 | * 527 | * Sets whether the #GdkWindow of the child should be set redirected 528 | * using gdk_window_set_composited(). By default this is based off of 529 | * na_tray_child_has_alpha(), but it may be useful to override it in 530 | * certain circumstances; for example, if the #NaTrayChild is added 531 | * to a parent window and that parent window is composited against the 532 | * background. 533 | */ 534 | void 535 | na_tray_child_set_composited (NaTrayChild *child, 536 | gboolean composited) 537 | { 538 | g_return_if_fail (NA_IS_TRAY_CHILD (child)); 539 | 540 | if (child->composited == composited) 541 | return; 542 | 543 | child->composited = composited; 544 | if (gtk_widget_get_realized (GTK_WIDGET (child))) 545 | gdk_window_set_composited (gtk_widget_get_window (GTK_WIDGET (child)), 546 | composited); 547 | } 548 | 549 | /* If we are faking transparency with a window-relative background, force a 550 | * redraw of the icon. This should be called if the background changes or if 551 | * the child is shifted with respect to the background. 552 | */ 553 | void 554 | na_tray_child_force_redraw (gpointer key, 555 | gpointer value, 556 | gpointer user_data) 557 | { 558 | (void) value; 559 | (void) user_data; 560 | NaTrayChild *child = key; 561 | GtkWidget *widget = GTK_WIDGET (child); 562 | 563 | if (gtk_widget_get_mapped (widget)) 564 | { 565 | /* Hiding and showing is the safe way to do it, but can result in more 566 | * flickering. 567 | */ 568 | gtk_widget_hide(widget); 569 | gtk_widget_show_all(widget); 570 | } 571 | } 572 | 573 | /* from libwnck/xutils.c, comes as LGPLv2+ */ 574 | static char * 575 | latin1_to_utf8 (const char *latin1) 576 | { 577 | GString *str; 578 | const char *p; 579 | 580 | str = g_string_new (NULL); 581 | 582 | p = latin1; 583 | while (*p) 584 | { 585 | g_string_append_unichar (str, (gunichar) *p); 586 | ++p; 587 | } 588 | 589 | return g_string_free (str, FALSE); 590 | } 591 | 592 | /* derived from libwnck/xutils.c, comes as LGPLv2+ */ 593 | static void 594 | _get_wmclass (Display *xdisplay, 595 | Window xwindow, 596 | char **res_class, 597 | char **res_name) 598 | { 599 | GdkDisplay *display; 600 | XClassHint ch; 601 | 602 | ch.res_name = NULL; 603 | ch.res_class = NULL; 604 | 605 | display = gdk_display_get_default (); 606 | gdk_x11_display_error_trap_push (display); 607 | XGetClassHint (xdisplay, xwindow, &ch); 608 | gdk_x11_display_error_trap_pop_ignored (display); 609 | 610 | if (res_class) 611 | *res_class = NULL; 612 | 613 | if (res_name) 614 | *res_name = NULL; 615 | 616 | if (ch.res_name) 617 | { 618 | if (res_name) 619 | *res_name = latin1_to_utf8 (ch.res_name); 620 | 621 | XFree (ch.res_name); 622 | } 623 | 624 | if (ch.res_class) 625 | { 626 | if (res_class) 627 | *res_class = latin1_to_utf8 (ch.res_class); 628 | 629 | XFree (ch.res_class); 630 | } 631 | } 632 | 633 | /** 634 | * na_tray_child_get_wm_class; 635 | * @child: a #NaTrayChild 636 | * @res_name: return location for a string containing the application name of 637 | * @child, or %NULL 638 | * @res_class: return location for a string containing the application class of 639 | * @child, or %NULL 640 | * 641 | * Fetches the resource associated with @child. 642 | */ 643 | void 644 | na_tray_child_get_wm_class (NaTrayChild *child, 645 | char **res_name, 646 | char **res_class) 647 | { 648 | GdkDisplay *display; 649 | 650 | g_return_if_fail (NA_IS_TRAY_CHILD (child)); 651 | 652 | display = gtk_widget_get_display (GTK_WIDGET (child)); 653 | 654 | _get_wmclass (GDK_DISPLAY_XDISPLAY (display), 655 | child->icon_window, 656 | res_class, 657 | res_name); 658 | } -------------------------------------------------------------------------------- /lib/na-tray-child.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ 2 | /* na-tray-child.h 3 | * Copyright (C) 2002 Anders Carlsson 4 | * Copyright (C) 2003-2006 Vincent Untz 5 | * Copyright (C) 2008 Red Hat, Inc. 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the 19 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 20 | * Boston, MA 02110-1301, USA. 21 | */ 22 | 23 | #ifndef __NA_TRAY_CHILD_H__ 24 | #define __NA_TRAY_CHILD_H__ 25 | 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | #define NA_TYPE_TRAY_CHILD (na_tray_child_get_type ()) 37 | #define NA_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_CHILD, NaTrayChild)) 38 | #define NA_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_CHILD, NaTrayChildClass)) 39 | #define NA_IS_TRAY_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_CHILD)) 40 | #define NA_IS_TRAY_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_CHILD)) 41 | #define NA_TRAY_CHILD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_CHILD, NaTrayChildClass)) 42 | 43 | typedef struct _NaTrayChild NaTrayChild; 44 | typedef struct _NaTrayChildClass NaTrayChildClass; 45 | typedef struct _NaTrayChildChild NaTrayChildChild; 46 | 47 | struct _NaTrayChild 48 | { 49 | GtkSocket parent_instance; 50 | Window icon_window; 51 | guint has_alpha : 1; 52 | guint composited : 1; 53 | guint parent_relative_bg : 1; 54 | 55 | gchar *id; 56 | }; 57 | 58 | struct _NaTrayChildClass 59 | { 60 | GtkSocketClass parent_class; 61 | }; 62 | 63 | GType na_tray_child_get_type (void); 64 | 65 | GtkWidget *na_tray_child_new (GdkScreen *screen, 66 | Window icon_window); 67 | char *na_tray_child_get_title (NaTrayChild *child); 68 | gboolean na_tray_child_has_alpha (NaTrayChild *child); 69 | void na_tray_child_set_composited (NaTrayChild *child, 70 | gboolean composited); 71 | void na_tray_child_force_redraw (gpointer key, 72 | gpointer value, 73 | gpointer user_data); 74 | void na_tray_child_get_wm_class (NaTrayChild *child, 75 | char **res_name, 76 | char **res_class); 77 | 78 | #ifdef __cplusplus 79 | } 80 | #endif 81 | 82 | #endif /* __NA_TRAY_CHILD_H__ */ -------------------------------------------------------------------------------- /lib/na-tray-manager.c: -------------------------------------------------------------------------------- 1 | /* na-tray-manager.c 2 | * Copyright (C) 2002 Anders Carlsson 3 | * Copyright (C) 2003-2006 Vincent Untz 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 18 | * Boston, MA 02110-1301, USA. 19 | * 20 | * Used to be: eggtraymanager.c 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include "na-tray-manager.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "na-marshal.h" 34 | 35 | /* Signals */ 36 | enum 37 | { 38 | TRAY_ICON_ADDED, 39 | TRAY_ICON_REMOVED, 40 | MESSAGE_SENT, 41 | MESSAGE_CANCELLED, 42 | LOST_SELECTION, 43 | LAST_SIGNAL 44 | }; 45 | 46 | enum { 47 | PROP_0, 48 | PROP_ORIENTATION 49 | }; 50 | 51 | typedef struct 52 | { 53 | long id, len; 54 | long remaining_len; 55 | 56 | long timeout; 57 | char *str; 58 | #ifdef GDK_WINDOWING_X11 59 | Window window; 60 | #endif 61 | } PendingMessage; 62 | 63 | static guint manager_signals[LAST_SIGNAL] = { 0 }; 64 | 65 | #define SYSTEM_TRAY_REQUEST_DOCK 0 66 | #define SYSTEM_TRAY_BEGIN_MESSAGE 1 67 | #define SYSTEM_TRAY_CANCEL_MESSAGE 2 68 | 69 | #define SYSTEM_TRAY_ORIENTATION_HORZ 0 70 | #define SYSTEM_TRAY_ORIENTATION_VERT 1 71 | 72 | #ifdef GDK_WINDOWING_X11 73 | static gboolean na_tray_manager_check_running_screen_x11 (GdkScreen *screen); 74 | #endif 75 | 76 | static void na_tray_manager_finalize (GObject *object); 77 | static void na_tray_manager_set_property (GObject *object, 78 | guint prop_id, 79 | const GValue *value, 80 | GParamSpec *pspec); 81 | static void na_tray_manager_get_property (GObject *object, 82 | guint prop_id, 83 | GValue *value, 84 | GParamSpec *pspec); 85 | 86 | static void na_tray_manager_unmanage (NaTrayManager *manager); 87 | 88 | G_DEFINE_TYPE (NaTrayManager, na_tray_manager, G_TYPE_OBJECT) 89 | 90 | static void 91 | na_tray_manager_init (NaTrayManager *manager) 92 | { 93 | manager->invisible = NULL; 94 | manager->socket_table = g_hash_table_new (NULL, NULL); 95 | 96 | manager->padding = 0; 97 | manager->icon_size = 0; 98 | 99 | manager->fg.red = 0.0; 100 | manager->fg.green = 0.0; 101 | manager->fg.blue = 0.0; 102 | manager->fg.alpha = 1.0; 103 | 104 | manager->error.red = 1.0; 105 | manager->error.green = 0.0; 106 | manager->error.blue = 0.0; 107 | manager->error.alpha = 1.0; 108 | 109 | manager->warning.red = 1.0; 110 | manager->warning.green = 1.0; 111 | manager->warning.blue = 0.0; 112 | manager->warning.alpha = 1.0; 113 | 114 | manager->success.red = 0.0; 115 | manager->success.green = 1.0; 116 | manager->success.blue = 0.0; 117 | manager->success.alpha = 1.0; 118 | } 119 | 120 | static void 121 | na_tray_manager_class_init (NaTrayManagerClass *klass) 122 | { 123 | GObjectClass *gobject_class; 124 | 125 | gobject_class = (GObjectClass *)klass; 126 | 127 | gobject_class->finalize = na_tray_manager_finalize; 128 | gobject_class->set_property = na_tray_manager_set_property; 129 | gobject_class->get_property = na_tray_manager_get_property; 130 | 131 | g_object_class_install_property (gobject_class, 132 | PROP_ORIENTATION, 133 | g_param_spec_enum ("orientation", 134 | "orientation", 135 | "orientation", 136 | GTK_TYPE_ORIENTATION, 137 | GTK_ORIENTATION_HORIZONTAL, 138 | G_PARAM_READWRITE | 139 | G_PARAM_CONSTRUCT | 140 | G_PARAM_STATIC_NAME | 141 | G_PARAM_STATIC_NICK | 142 | G_PARAM_STATIC_BLURB)); 143 | 144 | manager_signals[TRAY_ICON_ADDED] = 145 | g_signal_new ("tray_icon_added", 146 | G_OBJECT_CLASS_TYPE (klass), 147 | G_SIGNAL_RUN_LAST, 148 | G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_added), 149 | NULL, NULL, 150 | g_cclosure_marshal_VOID__OBJECT, 151 | G_TYPE_NONE, 1, 152 | GTK_TYPE_SOCKET); 153 | 154 | manager_signals[TRAY_ICON_REMOVED] = 155 | g_signal_new ("tray_icon_removed", 156 | G_OBJECT_CLASS_TYPE (klass), 157 | G_SIGNAL_RUN_LAST, 158 | G_STRUCT_OFFSET (NaTrayManagerClass, tray_icon_removed), 159 | NULL, NULL, 160 | g_cclosure_marshal_VOID__OBJECT, 161 | G_TYPE_NONE, 1, 162 | GTK_TYPE_SOCKET); 163 | manager_signals[MESSAGE_SENT] = 164 | g_signal_new ("message_sent", 165 | G_OBJECT_CLASS_TYPE (klass), 166 | G_SIGNAL_RUN_LAST, 167 | G_STRUCT_OFFSET (NaTrayManagerClass, message_sent), 168 | NULL, NULL, 169 | _na_marshal_VOID__OBJECT_STRING_LONG_LONG, 170 | G_TYPE_NONE, 4, 171 | GTK_TYPE_SOCKET, 172 | G_TYPE_STRING, 173 | G_TYPE_LONG, 174 | G_TYPE_LONG); 175 | manager_signals[MESSAGE_CANCELLED] = 176 | g_signal_new ("message_cancelled", 177 | G_OBJECT_CLASS_TYPE (klass), 178 | G_SIGNAL_RUN_LAST, 179 | G_STRUCT_OFFSET (NaTrayManagerClass, message_cancelled), 180 | NULL, NULL, 181 | _na_marshal_VOID__OBJECT_LONG, 182 | G_TYPE_NONE, 2, 183 | GTK_TYPE_SOCKET, 184 | G_TYPE_LONG); 185 | manager_signals[LOST_SELECTION] = 186 | g_signal_new ("lost_selection", 187 | G_OBJECT_CLASS_TYPE (klass), 188 | G_SIGNAL_RUN_LAST, 189 | G_STRUCT_OFFSET (NaTrayManagerClass, lost_selection), 190 | NULL, NULL, 191 | g_cclosure_marshal_VOID__VOID, 192 | G_TYPE_NONE, 0); 193 | } 194 | 195 | static void 196 | na_tray_manager_finalize (GObject *object) 197 | { 198 | NaTrayManager *manager; 199 | 200 | manager = NA_TRAY_MANAGER (object); 201 | 202 | na_tray_manager_unmanage (manager); 203 | 204 | g_list_free (manager->messages); 205 | g_hash_table_destroy (manager->socket_table); 206 | 207 | G_OBJECT_CLASS (na_tray_manager_parent_class)->finalize (object); 208 | } 209 | 210 | static void 211 | na_tray_manager_set_property (GObject *object, 212 | guint prop_id, 213 | const GValue *value, 214 | GParamSpec *pspec) 215 | { 216 | NaTrayManager *manager = NA_TRAY_MANAGER (object); 217 | 218 | switch (prop_id) 219 | { 220 | case PROP_ORIENTATION: 221 | na_tray_manager_set_orientation (manager, g_value_get_enum (value)); 222 | break; 223 | default: 224 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 225 | break; 226 | } 227 | } 228 | 229 | static void 230 | na_tray_manager_get_property (GObject *object, 231 | guint prop_id, 232 | GValue *value, 233 | GParamSpec *pspec) 234 | { 235 | NaTrayManager *manager = NA_TRAY_MANAGER (object); 236 | 237 | switch (prop_id) 238 | { 239 | case PROP_ORIENTATION: 240 | g_value_set_enum (value, manager->orientation); 241 | break; 242 | default: 243 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 244 | break; 245 | } 246 | } 247 | 248 | NaTrayManager * 249 | na_tray_manager_new (void) 250 | { 251 | NaTrayManager *manager; 252 | 253 | manager = g_object_new (NA_TYPE_TRAY_MANAGER, NULL); 254 | 255 | return manager; 256 | } 257 | 258 | #ifdef GDK_WINDOWING_X11 259 | 260 | static gboolean 261 | na_tray_manager_plug_removed (GtkSocket *socket, 262 | NaTrayManager *manager) 263 | { 264 | NaTrayChild *child = NA_TRAY_CHILD (socket); 265 | 266 | g_hash_table_remove (manager->socket_table, 267 | GINT_TO_POINTER (child->icon_window)); 268 | g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child); 269 | 270 | /* This destroys the socket. */ 271 | return FALSE; 272 | } 273 | 274 | static void 275 | na_tray_manager_handle_dock_request (NaTrayManager *manager, 276 | XClientMessageEvent *xevent) 277 | { 278 | Window icon_window = xevent->data.l[2]; 279 | GtkWidget *child; 280 | 281 | if (g_hash_table_lookup (manager->socket_table, 282 | GINT_TO_POINTER (icon_window))) 283 | { 284 | /* We already got this notification earlier, ignore this one */ 285 | return; 286 | } 287 | 288 | child = na_tray_child_new (manager->screen, icon_window); 289 | if (child == NULL) /* already gone or other error */ 290 | return; 291 | 292 | g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0, 293 | child); 294 | 295 | /* If the child wasn't attached, then destroy it */ 296 | 297 | if (!GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (child)))) 298 | { 299 | gtk_widget_destroy (child); 300 | return; 301 | } 302 | 303 | g_signal_connect (child, "plug_removed", 304 | G_CALLBACK (na_tray_manager_plug_removed), manager); 305 | 306 | gtk_socket_add_id (GTK_SOCKET (child), icon_window); 307 | 308 | if (!gtk_socket_get_plug_window (GTK_SOCKET (child))) 309 | { 310 | /* Embedding failed, we won't get a plug-removed signal */ 311 | /* This signal destroys the socket */ 312 | g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, child); 313 | return; 314 | } 315 | 316 | g_hash_table_insert (manager->socket_table, 317 | GINT_TO_POINTER (icon_window), child); 318 | gtk_widget_show (child); 319 | } 320 | 321 | static void 322 | pending_message_free (PendingMessage *message) 323 | { 324 | g_free (message->str); 325 | g_free (message); 326 | } 327 | 328 | static void 329 | na_tray_manager_handle_message_data (NaTrayManager *manager, 330 | XClientMessageEvent *xevent) 331 | { 332 | GList *p; 333 | int len; 334 | 335 | /* Try to see if we can find the pending message in the list */ 336 | for (p = manager->messages; p; p = p->next) 337 | { 338 | PendingMessage *msg = p->data; 339 | 340 | if (xevent->window == msg->window) 341 | { 342 | /* Append the message */ 343 | len = MIN (msg->remaining_len, 20); 344 | 345 | memcpy ((msg->str + msg->len - msg->remaining_len), 346 | &xevent->data, len); 347 | msg->remaining_len -= len; 348 | 349 | if (msg->remaining_len == 0) 350 | { 351 | GtkSocket *socket; 352 | 353 | socket = g_hash_table_lookup (manager->socket_table, 354 | GINT_TO_POINTER (msg->window)); 355 | 356 | if (socket) 357 | g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, 358 | socket, msg->str, msg->id, msg->timeout); 359 | 360 | pending_message_free (msg); 361 | manager->messages = g_list_remove_link (manager->messages, p); 362 | g_list_free_1 (p); 363 | } 364 | 365 | break; 366 | } 367 | } 368 | } 369 | 370 | static void 371 | na_tray_manager_handle_begin_message (NaTrayManager *manager, 372 | XClientMessageEvent *xevent) 373 | { 374 | GtkSocket *socket; 375 | GList *p; 376 | PendingMessage *msg; 377 | long timeout; 378 | long len; 379 | long id; 380 | 381 | socket = g_hash_table_lookup (manager->socket_table, 382 | GINT_TO_POINTER (xevent->window)); 383 | /* we don't know about this tray icon, so ignore the message */ 384 | if (!socket) 385 | return; 386 | 387 | timeout = xevent->data.l[2]; 388 | len = xevent->data.l[3]; 389 | id = xevent->data.l[4]; 390 | 391 | /* Check if the same message is already in the queue and remove it if so */ 392 | for (p = manager->messages; p; p = p->next) 393 | { 394 | PendingMessage *pmsg = p->data; 395 | 396 | if (xevent->window == pmsg->window && 397 | id == pmsg->id) 398 | { 399 | /* Hmm, we found it, now remove it */ 400 | pending_message_free (pmsg); 401 | manager->messages = g_list_remove_link (manager->messages, p); 402 | g_list_free_1 (p); 403 | break; 404 | } 405 | } 406 | 407 | if (len == 0) 408 | { 409 | g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, 410 | socket, "", id, timeout); 411 | } 412 | else 413 | { 414 | /* Now add the new message to the queue */ 415 | msg = g_new0 (PendingMessage, 1); 416 | msg->window = xevent->window; 417 | msg->timeout = timeout; 418 | msg->len = len; 419 | msg->id = id; 420 | msg->remaining_len = msg->len; 421 | msg->str = g_malloc (msg->len + 1); 422 | msg->str[msg->len] = '\0'; 423 | manager->messages = g_list_prepend (manager->messages, msg); 424 | } 425 | } 426 | 427 | static void 428 | na_tray_manager_handle_cancel_message (NaTrayManager *manager, 429 | XClientMessageEvent *xevent) 430 | { 431 | GList *p; 432 | GtkSocket *socket; 433 | long id; 434 | 435 | id = xevent->data.l[2]; 436 | 437 | /* Check if the message is in the queue and remove it if so */ 438 | for (p = manager->messages; p; p = p->next) 439 | { 440 | PendingMessage *msg = p->data; 441 | 442 | if (xevent->window == msg->window && 443 | id == msg->id) 444 | { 445 | pending_message_free (msg); 446 | manager->messages = g_list_remove_link (manager->messages, p); 447 | g_list_free_1 (p); 448 | break; 449 | } 450 | } 451 | 452 | socket = g_hash_table_lookup (manager->socket_table, 453 | GINT_TO_POINTER (xevent->window)); 454 | 455 | if (socket) 456 | { 457 | g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0, 458 | socket, xevent->data.l[2]); 459 | } 460 | } 461 | 462 | static GdkFilterReturn 463 | na_tray_manager_window_filter (GdkXEvent *xev, 464 | GdkEvent *event, 465 | gpointer data) 466 | { 467 | XEvent *xevent = (GdkXEvent *)xev; 468 | NaTrayManager *manager = data; 469 | 470 | if (xevent->type == ClientMessage) 471 | { 472 | /* We handle this client message here. See comment in 473 | * na_tray_manager_handle_client_message_opcode() for details */ 474 | if (xevent->xclient.message_type == manager->opcode_atom && 475 | xevent->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) 476 | { 477 | na_tray_manager_handle_dock_request (manager, 478 | (XClientMessageEvent *) xevent); 479 | return GDK_FILTER_REMOVE; 480 | } 481 | /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_BEGIN_MESSAGE */ 482 | else if (xevent->xclient.message_type == manager->opcode_atom && 483 | xevent->xclient.data.l[1] == SYSTEM_TRAY_BEGIN_MESSAGE) 484 | { 485 | na_tray_manager_handle_begin_message (manager, 486 | (XClientMessageEvent *) event); 487 | return GDK_FILTER_REMOVE; 488 | } 489 | /* _NET_SYSTEM_TRAY_OPCODE: SYSTEM_TRAY_CANCEL_MESSAGE */ 490 | else if (xevent->xclient.message_type == manager->opcode_atom && 491 | xevent->xclient.data.l[1] == SYSTEM_TRAY_CANCEL_MESSAGE) 492 | { 493 | na_tray_manager_handle_cancel_message (manager, 494 | (XClientMessageEvent *) event); 495 | return GDK_FILTER_REMOVE; 496 | } 497 | /* _NET_SYSTEM_TRAY_MESSAGE_DATA */ 498 | else if (xevent->xclient.message_type == manager->message_data_atom) 499 | { 500 | na_tray_manager_handle_message_data (manager, 501 | (XClientMessageEvent *) event); 502 | return GDK_FILTER_REMOVE; 503 | } 504 | } 505 | else if (xevent->type == SelectionClear) 506 | { 507 | g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); 508 | na_tray_manager_unmanage (manager); 509 | } 510 | 511 | return GDK_FILTER_CONTINUE; 512 | } 513 | 514 | #if 0 515 | /* FIXME investigate why this doesn't work */ 516 | static gboolean 517 | na_tray_manager_selection_clear_event (GtkWidget *widget, 518 | GdkEventSelection *event, 519 | NaTrayManager *manager) 520 | { 521 | g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); 522 | na_tray_manager_unmanage (manager); 523 | 524 | return FALSE; 525 | } 526 | #endif 527 | #endif 528 | 529 | static void 530 | na_tray_manager_unmanage (NaTrayManager *manager) 531 | { 532 | #ifdef GDK_WINDOWING_X11 533 | GdkDisplay *display; 534 | guint32 timestamp; 535 | GtkWidget *invisible; 536 | GdkWindow *window; 537 | 538 | if (manager->invisible == NULL) 539 | return; 540 | 541 | invisible = manager->invisible; 542 | window = gtk_widget_get_window (invisible); 543 | 544 | g_assert (GTK_IS_INVISIBLE (invisible)); 545 | g_assert (gtk_widget_get_realized (invisible)); 546 | g_assert (GDK_IS_WINDOW (window)); 547 | 548 | display = gtk_widget_get_display (invisible); 549 | 550 | if (gdk_selection_owner_get_for_display (display, manager->selection_atom) == 551 | window) 552 | { 553 | timestamp = gdk_x11_get_server_time (window); 554 | gdk_selection_owner_set_for_display (display, 555 | NULL, 556 | manager->selection_atom, 557 | timestamp, 558 | TRUE); 559 | } 560 | 561 | gdk_window_remove_filter (window, 562 | na_tray_manager_window_filter, manager); 563 | 564 | manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */ 565 | gtk_widget_destroy (invisible); 566 | g_object_unref (G_OBJECT (invisible)); 567 | #endif 568 | } 569 | 570 | static void 571 | na_tray_manager_set_orientation_property (NaTrayManager *manager) 572 | { 573 | #ifdef GDK_WINDOWING_X11 574 | GdkWindow *window; 575 | GdkDisplay *display; 576 | Atom orientation_atom; 577 | gulong data[1]; 578 | 579 | g_return_if_fail (manager->invisible != NULL); 580 | window = gtk_widget_get_window (manager->invisible); 581 | g_return_if_fail (window != NULL); 582 | 583 | display = gtk_widget_get_display (manager->invisible); 584 | orientation_atom = gdk_x11_get_xatom_by_name_for_display (display, 585 | "_NET_SYSTEM_TRAY_ORIENTATION"); 586 | 587 | data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ? 588 | SYSTEM_TRAY_ORIENTATION_HORZ : 589 | SYSTEM_TRAY_ORIENTATION_VERT; 590 | 591 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), 592 | GDK_WINDOW_XID (window), 593 | orientation_atom, 594 | XA_CARDINAL, 32, 595 | PropModeReplace, 596 | (guchar *) &data, 1); 597 | #endif 598 | } 599 | 600 | static void 601 | na_tray_manager_set_visual_property (NaTrayManager *manager) 602 | { 603 | #ifdef GDK_WINDOWING_X11 604 | GdkWindow *window; 605 | GdkDisplay *display; 606 | Visual *xvisual; 607 | Atom visual_atom; 608 | gulong data[1]; 609 | 610 | g_return_if_fail (manager->invisible != NULL); 611 | window = gtk_widget_get_window (manager->invisible); 612 | g_return_if_fail (window != NULL); 613 | 614 | /* The visual property is a hint to the tray icons as to what visual they 615 | * should use for their windows. If the X server has RGBA colormaps, then 616 | * we tell the tray icons to use a RGBA colormap and we'll composite the 617 | * icon onto its parents with real transparency. Otherwise, we just tell 618 | * the icon to use our colormap, and we'll do some hacks with parent 619 | * relative backgrounds to simulate transparency. 620 | */ 621 | 622 | display = gtk_widget_get_display (manager->invisible); 623 | visual_atom = gdk_x11_get_xatom_by_name_for_display (display, 624 | "_NET_SYSTEM_TRAY_VISUAL"); 625 | 626 | if (gdk_screen_get_rgba_visual (manager->screen) != NULL && 627 | gdk_display_supports_composite (display)) 628 | { 629 | xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_rgba_visual (manager->screen)); 630 | } 631 | else 632 | { 633 | /* We actually want the visual of the tray where the icons will 634 | * be embedded. In almost all cases, this will be the same as the visual 635 | * of the screen. 636 | */ 637 | xvisual = GDK_VISUAL_XVISUAL (gdk_screen_get_system_visual (manager->screen)); 638 | } 639 | 640 | data[0] = XVisualIDFromVisual (xvisual); 641 | 642 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), 643 | GDK_WINDOW_XID (window), 644 | visual_atom, 645 | XA_VISUALID, 32, 646 | PropModeReplace, 647 | (guchar *) &data, 1); 648 | #endif 649 | } 650 | 651 | static void 652 | na_tray_manager_set_padding_property (NaTrayManager *manager) 653 | { 654 | #ifdef GDK_WINDOWING_X11 655 | GdkWindow *window; 656 | GdkDisplay *display; 657 | Atom atom; 658 | gulong data[1]; 659 | 660 | g_return_if_fail (manager->invisible != NULL); 661 | window = gtk_widget_get_window (manager->invisible); 662 | g_return_if_fail (window != NULL); 663 | 664 | display = gtk_widget_get_display (manager->invisible); 665 | atom = gdk_x11_get_xatom_by_name_for_display (display, 666 | "_NET_SYSTEM_TRAY_PADDING"); 667 | 668 | data[0] = manager->padding; 669 | 670 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), 671 | GDK_WINDOW_XID (window), 672 | atom, 673 | XA_CARDINAL, 32, 674 | PropModeReplace, 675 | (guchar *) &data, 1); 676 | #endif 677 | } 678 | 679 | static void 680 | na_tray_manager_set_icon_size_property (NaTrayManager *manager) 681 | { 682 | #ifdef GDK_WINDOWING_X11 683 | GdkWindow *window; 684 | GdkDisplay *display; 685 | Atom atom; 686 | gulong data[1]; 687 | 688 | g_return_if_fail (manager->invisible != NULL); 689 | window = gtk_widget_get_window (manager->invisible); 690 | g_return_if_fail (window != NULL); 691 | 692 | display = gtk_widget_get_display (manager->invisible); 693 | atom = gdk_x11_get_xatom_by_name_for_display (display, 694 | "_NET_SYSTEM_TRAY_ICON_SIZE"); 695 | 696 | data[0] = manager->icon_size; 697 | 698 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), 699 | GDK_WINDOW_XID (window), 700 | atom, 701 | XA_CARDINAL, 32, 702 | PropModeReplace, 703 | (guchar *) &data, 1); 704 | #endif 705 | } 706 | 707 | static void 708 | na_tray_manager_set_colors_property (NaTrayManager *manager) 709 | { 710 | #ifdef GDK_WINDOWING_X11 711 | GdkWindow *window; 712 | GdkDisplay *display; 713 | Atom atom; 714 | gulong data[12]; 715 | 716 | g_return_if_fail (manager->invisible != NULL); 717 | window = gtk_widget_get_window (manager->invisible); 718 | g_return_if_fail (window != NULL); 719 | 720 | display = gtk_widget_get_display (manager->invisible); 721 | atom = gdk_x11_get_xatom_by_name_for_display (display, 722 | "_NET_SYSTEM_TRAY_COLORS"); 723 | 724 | data[0] = manager->fg.red * 65535; 725 | data[1] = manager->fg.green * 65535; 726 | data[2] = manager->fg.blue * 65535; 727 | data[3] = manager->error.red * 65535; 728 | data[4] = manager->error.green * 65535; 729 | data[5] = manager->error.blue * 65535; 730 | data[6] = manager->warning.red * 65535; 731 | data[7] = manager->warning.green * 65535; 732 | data[8] = manager->warning.blue * 65535; 733 | data[9] = manager->success.red * 65535; 734 | data[10] = manager->success.green * 65535; 735 | data[11] = manager->success.blue * 65535; 736 | 737 | XChangeProperty (GDK_DISPLAY_XDISPLAY (display), 738 | GDK_WINDOW_XID (window), 739 | atom, 740 | XA_CARDINAL, 32, 741 | PropModeReplace, 742 | (guchar *) &data, 12); 743 | #endif 744 | } 745 | 746 | #ifdef GDK_WINDOWING_X11 747 | 748 | static gboolean 749 | na_tray_manager_manage_screen_x11 (NaTrayManager *manager, 750 | GdkScreen *screen) 751 | { 752 | GdkDisplay *display; 753 | Screen *xscreen; 754 | GtkWidget *invisible; 755 | GdkWindow *window; 756 | char *selection_atom_name; 757 | guint32 timestamp; 758 | 759 | g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), FALSE); 760 | g_return_val_if_fail (manager->screen == NULL, FALSE); 761 | 762 | /* If there's already a manager running on the screen 763 | * we can't create another one. 764 | */ 765 | #if 0 766 | if (na_tray_manager_check_running_screen_x11 (screen)) 767 | return FALSE; 768 | #endif 769 | 770 | manager->screen = screen; 771 | 772 | display = gdk_screen_get_display (screen); 773 | xscreen = GDK_SCREEN_XSCREEN (screen); 774 | 775 | invisible = gtk_invisible_new_for_screen (screen); 776 | gtk_widget_realize (invisible); 777 | 778 | gtk_widget_add_events (invisible, 779 | GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK); 780 | 781 | selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", 782 | gdk_x11_screen_get_screen_number (screen)); 783 | manager->selection_atom = gdk_atom_intern (selection_atom_name, FALSE); 784 | g_free (selection_atom_name); 785 | 786 | manager->invisible = invisible; 787 | g_object_ref (G_OBJECT (manager->invisible)); 788 | 789 | na_tray_manager_set_orientation_property (manager); 790 | na_tray_manager_set_visual_property (manager); 791 | na_tray_manager_set_padding_property (manager); 792 | na_tray_manager_set_icon_size_property (manager); 793 | na_tray_manager_set_colors_property (manager); 794 | 795 | window = gtk_widget_get_window (invisible); 796 | 797 | timestamp = gdk_x11_get_server_time (window); 798 | 799 | /* Check if we could set the selection owner successfully */ 800 | if (gdk_selection_owner_set_for_display (display, 801 | window, 802 | manager->selection_atom, 803 | timestamp, 804 | TRUE)) 805 | { 806 | XClientMessageEvent xev; 807 | GdkAtom opcode_atom; 808 | GdkAtom message_data_atom; 809 | 810 | xev.type = ClientMessage; 811 | xev.window = RootWindowOfScreen (xscreen); 812 | xev.message_type = gdk_x11_get_xatom_by_name_for_display (display, 813 | "MANAGER"); 814 | 815 | xev.format = 32; 816 | xev.data.l[0] = timestamp; 817 | xev.data.l[1] = gdk_x11_atom_to_xatom_for_display (display, 818 | manager->selection_atom); 819 | xev.data.l[2] = GDK_WINDOW_XID (window); 820 | xev.data.l[3] = 0; /* manager specific data */ 821 | xev.data.l[4] = 0; /* manager specific data */ 822 | 823 | XSendEvent (GDK_DISPLAY_XDISPLAY (display), 824 | RootWindowOfScreen (xscreen), 825 | False, StructureNotifyMask, (XEvent *)&xev); 826 | 827 | opcode_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_OPCODE", FALSE); 828 | manager->opcode_atom = gdk_x11_atom_to_xatom_for_display (display, 829 | opcode_atom); 830 | 831 | message_data_atom = gdk_atom_intern ("_NET_SYSTEM_TRAY_MESSAGE_DATA", 832 | FALSE); 833 | 834 | manager->message_data_atom = gdk_x11_atom_to_xatom_for_display (display, 835 | message_data_atom); 836 | 837 | /* Add a window filter */ 838 | #if 0 839 | /* This is for when we lose the selection of _NET_SYSTEM_TRAY_Sx */ 840 | g_signal_connect (invisible, "selection-clear-event", 841 | G_CALLBACK (na_tray_manager_selection_clear_event), 842 | manager); 843 | #endif 844 | /* This is for SYSTEM_TRAY_REQUEST_DOCK and SelectionClear */ 845 | gdk_window_add_filter (window, 846 | na_tray_manager_window_filter, manager); 847 | return TRUE; 848 | } 849 | else 850 | { 851 | gtk_widget_destroy (invisible); 852 | g_object_unref (invisible); 853 | manager->invisible = NULL; 854 | 855 | manager->screen = NULL; 856 | 857 | return FALSE; 858 | } 859 | } 860 | 861 | #endif 862 | 863 | gboolean 864 | na_tray_manager_manage_screen (NaTrayManager *manager, 865 | GdkScreen *screen) 866 | { 867 | g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); 868 | g_return_val_if_fail (manager->screen == NULL, FALSE); 869 | 870 | #ifdef GDK_WINDOWING_X11 871 | return na_tray_manager_manage_screen_x11 (manager, screen); 872 | #else 873 | return FALSE; 874 | #endif 875 | } 876 | 877 | #ifdef GDK_WINDOWING_X11 878 | 879 | static gboolean 880 | na_tray_manager_check_running_screen_x11 (GdkScreen *screen) 881 | { 882 | GdkDisplay *display; 883 | Atom selection_atom; 884 | char *selection_atom_name; 885 | 886 | display = gdk_screen_get_display (screen); 887 | selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", 888 | gdk_x11_screen_get_screen_number (screen)); 889 | selection_atom = gdk_x11_get_xatom_by_name_for_display (display, 890 | selection_atom_name); 891 | g_free (selection_atom_name); 892 | 893 | if (XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), 894 | selection_atom) != None) 895 | return TRUE; 896 | else 897 | return FALSE; 898 | } 899 | 900 | #endif 901 | 902 | gboolean 903 | na_tray_manager_check_running (GdkScreen *screen) 904 | { 905 | g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); 906 | 907 | #ifdef GDK_WINDOWING_X11 908 | return na_tray_manager_check_running_screen_x11 (screen); 909 | #else 910 | return FALSE; 911 | #endif 912 | } 913 | 914 | void 915 | na_tray_manager_set_orientation (NaTrayManager *manager, 916 | GtkOrientation orientation) 917 | { 918 | g_return_if_fail (NA_IS_TRAY_MANAGER (manager)); 919 | 920 | if (manager->orientation != orientation) 921 | { 922 | manager->orientation = orientation; 923 | 924 | na_tray_manager_set_orientation_property (manager); 925 | 926 | g_object_notify (G_OBJECT (manager), "orientation"); 927 | } 928 | } 929 | 930 | void 931 | na_tray_manager_set_padding (NaTrayManager *manager, 932 | gint padding) 933 | { 934 | g_return_if_fail (NA_IS_TRAY_MANAGER (manager)); 935 | 936 | if (manager->padding != padding) 937 | { 938 | manager->padding = padding; 939 | 940 | na_tray_manager_set_padding_property (manager); 941 | } 942 | } 943 | 944 | void 945 | na_tray_manager_set_icon_size (NaTrayManager *manager, 946 | gint icon_size) 947 | { 948 | g_return_if_fail (NA_IS_TRAY_MANAGER (manager)); 949 | 950 | if (manager->icon_size != icon_size) 951 | { 952 | manager->icon_size = icon_size; 953 | 954 | na_tray_manager_set_icon_size_property (manager); 955 | } 956 | } 957 | 958 | void 959 | na_tray_manager_set_colors (NaTrayManager *manager, 960 | GdkRGBA *fg, 961 | GdkRGBA *error, 962 | GdkRGBA *warning, 963 | GdkRGBA *success) 964 | { 965 | g_return_if_fail (NA_IS_TRAY_MANAGER (manager)); 966 | 967 | if (!gdk_rgba_equal (&manager->fg, fg) || 968 | !gdk_rgba_equal (&manager->error, error) || 969 | !gdk_rgba_equal (&manager->warning, warning) || 970 | !gdk_rgba_equal (&manager->success, success)) 971 | { 972 | manager->fg = *fg; 973 | manager->error = *error; 974 | manager->warning = *warning; 975 | manager->success = *success; 976 | 977 | na_tray_manager_set_colors_property (manager); 978 | } 979 | } 980 | 981 | GtkOrientation 982 | na_tray_manager_get_orientation (NaTrayManager *manager) 983 | { 984 | g_return_val_if_fail (NA_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL); 985 | 986 | return manager->orientation; 987 | } 988 | -------------------------------------------------------------------------------- /lib/na-tray-manager.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ 2 | /* na-tray-manager.h 3 | * Copyright (C) 2002 Anders Carlsson 4 | * Copyright (C) 2003-2006 Vincent Untz 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the 18 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 19 | * Boston, MA 02110-1301, USA. 20 | * 21 | * Used to be: eggtraymanager.h 22 | */ 23 | 24 | #ifndef __NA_TRAY_MANAGER_H__ 25 | #define __NA_TRAY_MANAGER_H__ 26 | 27 | #ifdef GDK_WINDOWING_X11 28 | #include 29 | #endif 30 | #include 31 | 32 | #include "na-tray-child.h" 33 | 34 | G_BEGIN_DECLS 35 | 36 | #define NA_TYPE_TRAY_MANAGER (na_tray_manager_get_type ()) 37 | #define NA_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManager)) 38 | #define NA_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass)) 39 | #define NA_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY_MANAGER)) 40 | #define NA_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY_MANAGER)) 41 | #define NA_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY_MANAGER, NaTrayManagerClass)) 42 | 43 | typedef struct _NaTrayManager NaTrayManager; 44 | typedef struct _NaTrayManagerClass NaTrayManagerClass; 45 | 46 | struct _NaTrayManager 47 | { 48 | GObject parent_instance; 49 | 50 | #ifdef GDK_WINDOWING_X11 51 | GdkAtom selection_atom; 52 | Atom opcode_atom; 53 | Atom message_data_atom; 54 | #endif 55 | 56 | GtkWidget *invisible; 57 | GdkScreen *screen; 58 | GtkOrientation orientation; 59 | gint padding; 60 | gint icon_size; 61 | GdkRGBA fg; 62 | GdkRGBA error; 63 | GdkRGBA warning; 64 | GdkRGBA success; 65 | 66 | GList *messages; 67 | GHashTable *socket_table; 68 | }; 69 | 70 | struct _NaTrayManagerClass 71 | { 72 | GObjectClass parent_class; 73 | 74 | void (* tray_icon_added) (NaTrayManager *manager, 75 | NaTrayChild *child); 76 | void (* tray_icon_removed) (NaTrayManager *manager, 77 | NaTrayChild *child); 78 | 79 | void (* message_sent) (NaTrayManager *manager, 80 | NaTrayChild *child, 81 | const gchar *message, 82 | glong id, 83 | glong timeout); 84 | 85 | void (* message_cancelled) (NaTrayManager *manager, 86 | NaTrayChild *child, 87 | glong id); 88 | 89 | void (* lost_selection) (NaTrayManager *manager); 90 | }; 91 | 92 | GType na_tray_manager_get_type (void); 93 | 94 | gboolean na_tray_manager_check_running (GdkScreen *screen); 95 | NaTrayManager *na_tray_manager_new (void); 96 | gboolean na_tray_manager_manage_screen (NaTrayManager *manager, 97 | GdkScreen *screen); 98 | void na_tray_manager_set_orientation (NaTrayManager *manager, 99 | GtkOrientation orientation); 100 | GtkOrientation na_tray_manager_get_orientation (NaTrayManager *manager); 101 | void na_tray_manager_set_padding (NaTrayManager *manager, 102 | gint padding); 103 | void na_tray_manager_set_icon_size (NaTrayManager *manager, 104 | gint padding); 105 | void na_tray_manager_set_colors (NaTrayManager *manager, 106 | GdkRGBA *fg, 107 | GdkRGBA *error, 108 | GdkRGBA *warning, 109 | GdkRGBA *success); 110 | 111 | G_END_DECLS 112 | 113 | #endif /* __NA_TRAY_MANAGER_H__ */ -------------------------------------------------------------------------------- /lib/na-tray.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002 Red Hat, Inc. 3 | * Copyright (C) 2003-2006 Vincent Untz 4 | * Copyright (C) 2007 Christian Persch 5 | * Copyright (C) 2017 Colomban Wendling 6 | * 7 | * This program is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation; either version 2 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, but 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 | * 02110-1301, USA. 21 | */ 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include "na-tray-manager.h" 28 | #include "fixedtip.h" 29 | 30 | #include "na-tray.h" 31 | 32 | typedef struct 33 | { 34 | NaTrayManager *tray_manager; 35 | GSList *all_trays; 36 | GHashTable *icon_table; 37 | GHashTable *tip_table; 38 | } TraysScreen; 39 | 40 | struct _NaTrayPrivate 41 | { 42 | GdkScreen *screen; 43 | TraysScreen *trays_screen; 44 | 45 | guint idle_redraw_id; 46 | 47 | GtkOrientation orientation; 48 | gint icon_padding; 49 | gint icon_size; 50 | }; 51 | 52 | typedef struct 53 | { 54 | char *text; 55 | glong id; 56 | glong timeout; 57 | } IconTipBuffer; 58 | 59 | typedef struct 60 | { 61 | NaTray *tray; /* tray containing the tray icon */ 62 | GtkWidget *icon; /* tray icon sending the message */ 63 | GtkWidget *fixedtip; 64 | guint source_id; 65 | glong id; /* id of the current message */ 66 | GSList *buffer; /* buffered messages */ 67 | } IconTip; 68 | 69 | enum 70 | { 71 | PROP_0, 72 | PROP_ORIENTATION, 73 | PROP_ICON_PADDING, 74 | PROP_ICON_SIZE, 75 | PROP_SCREEN 76 | }; 77 | 78 | static gboolean initialized = FALSE; 79 | static TraysScreen *trays_screens = NULL; 80 | 81 | static void icon_tip_show_next (IconTip *icontip); 82 | 83 | /* NaTray */ 84 | static void na_host_init (NaHostInterface *iface); 85 | static void na_tray_style_updated (NaHost *host, 86 | GtkStyleContext *context); 87 | static void na_tray_force_redraw (NaHost *host); 88 | 89 | G_DEFINE_TYPE_WITH_CODE (NaTray, na_tray, G_TYPE_OBJECT, 90 | G_ADD_PRIVATE (NaTray) 91 | G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL) 92 | G_IMPLEMENT_INTERFACE (NA_TYPE_HOST, na_host_init)) 93 | 94 | static void 95 | na_host_init (NaHostInterface *iface) 96 | { 97 | iface->force_redraw = na_tray_force_redraw; 98 | iface->style_updated = na_tray_style_updated; 99 | } 100 | 101 | static NaTray * 102 | get_tray (TraysScreen *trays_screen) 103 | { 104 | if (trays_screen->all_trays == NULL) 105 | return NULL; 106 | 107 | return trays_screen->all_trays->data; 108 | } 109 | 110 | static void 111 | tray_added (NaTrayManager *manager, 112 | NaTrayChild *icon, 113 | TraysScreen *trays_screen) 114 | { 115 | NaTray *tray; 116 | NaTrayPrivate *priv; 117 | 118 | tray = get_tray (trays_screen); 119 | if (tray == NULL) 120 | return; 121 | 122 | priv = tray->priv; 123 | 124 | g_assert (priv->trays_screen == trays_screen); 125 | 126 | g_hash_table_insert (trays_screen->icon_table, icon, tray); 127 | 128 | na_host_emit_item_added (NA_HOST (tray), NA_ITEM (icon)); 129 | 130 | /*Does not seem to be needed anymore and can cause a render issue with hidpi*/ 131 | /*gtk_widget_show (GTK_WIDGET (icon));*/ 132 | } 133 | 134 | static void 135 | tray_removed (NaTrayManager *manager, 136 | NaTrayChild *icon, 137 | TraysScreen *trays_screen) 138 | { 139 | NaTray *tray; 140 | 141 | tray = g_hash_table_lookup (trays_screen->icon_table, icon); 142 | if (tray == NULL) 143 | return; 144 | 145 | g_assert (tray->priv->trays_screen == trays_screen); 146 | 147 | na_host_emit_item_removed (NA_HOST (tray), NA_ITEM (icon)); 148 | 149 | g_hash_table_remove (trays_screen->icon_table, icon); 150 | /* this will also destroy the tip associated to this icon */ 151 | g_hash_table_remove (trays_screen->tip_table, icon); 152 | } 153 | 154 | static void 155 | icon_tip_buffer_free (gpointer data, 156 | gpointer userdata) 157 | { 158 | IconTipBuffer *buffer; 159 | 160 | buffer = data; 161 | 162 | g_free (buffer->text); 163 | buffer->text = NULL; 164 | 165 | g_free (buffer); 166 | } 167 | 168 | static void 169 | icon_tip_free (gpointer data) 170 | { 171 | IconTip *icontip; 172 | 173 | if (data == NULL) 174 | return; 175 | 176 | icontip = data; 177 | 178 | if (icontip->fixedtip != NULL) 179 | gtk_widget_destroy (GTK_WIDGET (icontip->fixedtip)); 180 | icontip->fixedtip = NULL; 181 | 182 | if (icontip->source_id != 0) 183 | g_source_remove (icontip->source_id); 184 | icontip->source_id = 0; 185 | 186 | if (icontip->buffer != NULL) 187 | { 188 | g_slist_foreach (icontip->buffer, icon_tip_buffer_free, NULL); 189 | g_slist_free (icontip->buffer); 190 | } 191 | icontip->buffer = NULL; 192 | 193 | g_free (icontip); 194 | } 195 | 196 | static int 197 | icon_tip_buffer_compare (gconstpointer a, 198 | gconstpointer b) 199 | { 200 | const IconTipBuffer *buffer_a = a; 201 | const IconTipBuffer *buffer_b = b; 202 | 203 | if (buffer_a == NULL || buffer_b == NULL) 204 | return !(buffer_a == buffer_b); 205 | 206 | return buffer_a->id - buffer_b->id; 207 | } 208 | 209 | static void 210 | icon_tip_show_next_clicked (GtkWidget *widget, 211 | gpointer data) 212 | { 213 | icon_tip_show_next ((IconTip *) data); 214 | } 215 | 216 | static gboolean 217 | icon_tip_show_next_timeout (gpointer data) 218 | { 219 | IconTip *icontip = (IconTip *) data; 220 | 221 | icon_tip_show_next (icontip); 222 | 223 | return FALSE; 224 | } 225 | 226 | static void 227 | icon_tip_show_next (IconTip *icontip) 228 | { 229 | IconTipBuffer *buffer; 230 | 231 | if (icontip->buffer == NULL) 232 | { 233 | /* this will also destroy the tip window */ 234 | g_hash_table_remove (icontip->tray->priv->trays_screen->tip_table, 235 | icontip->icon); 236 | return; 237 | } 238 | 239 | if (icontip->source_id != 0) 240 | g_source_remove (icontip->source_id); 241 | icontip->source_id = 0; 242 | 243 | buffer = icontip->buffer->data; 244 | icontip->buffer = g_slist_remove (icontip->buffer, buffer); 245 | 246 | if (icontip->fixedtip == NULL) 247 | { 248 | icontip->fixedtip = na_fixed_tip_new (icontip->icon, 249 | gtk_orientable_get_orientation (GTK_ORIENTABLE (icontip->tray))); 250 | 251 | g_signal_connect (icontip->fixedtip, "clicked", 252 | G_CALLBACK (icon_tip_show_next_clicked), icontip); 253 | } 254 | 255 | na_fixed_tip_set_markup (icontip->fixedtip, buffer->text); 256 | 257 | if (!gtk_widget_get_mapped (icontip->fixedtip)) 258 | gtk_widget_show (icontip->fixedtip); 259 | 260 | icontip->id = buffer->id; 261 | 262 | if (buffer->timeout > 0) 263 | icontip->source_id = g_timeout_add_seconds (buffer->timeout, 264 | icon_tip_show_next_timeout, 265 | icontip); 266 | 267 | icon_tip_buffer_free (buffer, NULL); 268 | } 269 | 270 | static void 271 | message_sent (NaTrayManager *manager, 272 | GtkWidget *icon, 273 | const char *text, 274 | glong id, 275 | glong timeout, 276 | TraysScreen *trays_screen) 277 | { 278 | IconTip *icontip; 279 | IconTipBuffer find_buffer; 280 | IconTipBuffer *buffer; 281 | gboolean show_now; 282 | 283 | icontip = g_hash_table_lookup (trays_screen->tip_table, icon); 284 | 285 | find_buffer.id = id; 286 | if (icontip && 287 | (icontip->id == id || 288 | g_slist_find_custom (icontip->buffer, &find_buffer, 289 | icon_tip_buffer_compare) != NULL)) 290 | /* we already have this message, so ignore it */ 291 | /* FIXME: in an ideal world, we'd remember all the past ids and ignore them 292 | * too */ 293 | return; 294 | 295 | show_now = FALSE; 296 | 297 | if (icontip == NULL) 298 | { 299 | NaTray *tray; 300 | 301 | tray = g_hash_table_lookup (trays_screen->icon_table, icon); 302 | if (tray == NULL) 303 | { 304 | /* We don't know about the icon sending the message, so ignore it. 305 | * But this should never happen since NaTrayManager shouldn't send 306 | * us the message if there's no socket for it. */ 307 | g_critical ("Ignoring a message sent by a tray icon " 308 | "we don't know: \"%s\".\n", text); 309 | return; 310 | } 311 | 312 | icontip = g_new0 (IconTip, 1); 313 | icontip->tray = tray; 314 | icontip->icon = icon; 315 | 316 | g_hash_table_insert (trays_screen->tip_table, icon, icontip); 317 | 318 | show_now = TRUE; 319 | } 320 | 321 | buffer = g_new0 (IconTipBuffer, 1); 322 | 323 | buffer->text = g_strdup (text); 324 | buffer->id = id; 325 | buffer->timeout = timeout; 326 | 327 | icontip->buffer = g_slist_append (icontip->buffer, buffer); 328 | 329 | if (show_now) 330 | icon_tip_show_next (icontip); 331 | } 332 | 333 | static void 334 | message_cancelled (NaTrayManager *manager, 335 | GtkWidget *icon, 336 | glong id, 337 | TraysScreen *trays_screen) 338 | { 339 | IconTip *icontip; 340 | IconTipBuffer find_buffer; 341 | GSList *cancel_buffer_l; 342 | IconTipBuffer *cancel_buffer; 343 | 344 | icontip = g_hash_table_lookup (trays_screen->tip_table, icon); 345 | if (icontip == NULL) 346 | return; 347 | 348 | if (icontip->id == id) 349 | { 350 | icon_tip_show_next (icontip); 351 | return; 352 | } 353 | 354 | find_buffer.id = id; 355 | cancel_buffer_l = g_slist_find_custom (icontip->buffer, &find_buffer, 356 | icon_tip_buffer_compare); 357 | if (cancel_buffer_l == NULL) 358 | return; 359 | 360 | cancel_buffer = cancel_buffer_l->data; 361 | icon_tip_buffer_free (cancel_buffer, NULL); 362 | 363 | icontip->buffer = g_slist_remove_link (icontip->buffer, cancel_buffer_l); 364 | g_slist_free_1 (cancel_buffer_l); 365 | } 366 | 367 | static void 368 | update_orientation_for_messages (gpointer key, 369 | gpointer value, 370 | gpointer data) 371 | { 372 | NaTray *tray; 373 | IconTip *icontip; 374 | 375 | if (value == NULL) 376 | return; 377 | 378 | icontip = value; 379 | tray = data; 380 | if (icontip->tray != tray) 381 | return; 382 | 383 | if (icontip->fixedtip) 384 | na_fixed_tip_set_orientation (icontip->fixedtip, tray->priv->orientation); 385 | } 386 | 387 | static void 388 | update_size_and_orientation (NaTray *tray) 389 | { 390 | NaTrayPrivate *priv = tray->priv; 391 | 392 | /* This only happens when setting the property during object construction */ 393 | if (!priv->trays_screen) 394 | return; 395 | 396 | g_hash_table_foreach (priv->trays_screen->tip_table, 397 | update_orientation_for_messages, tray); 398 | 399 | if (get_tray (priv->trays_screen) == tray) 400 | na_tray_manager_set_orientation (priv->trays_screen->tray_manager, 401 | priv->orientation); 402 | } 403 | 404 | static void 405 | na_tray_init (NaTray *tray) 406 | { 407 | NaTrayPrivate *priv; 408 | 409 | priv = tray->priv = na_tray_get_instance_private (tray); 410 | 411 | priv->screen = NULL; 412 | priv->orientation = GTK_ORIENTATION_HORIZONTAL; 413 | priv->icon_padding = 0; 414 | priv->icon_size = 0; 415 | } 416 | 417 | static GObject * 418 | na_tray_constructor (GType type, 419 | guint n_construct_properties, 420 | GObjectConstructParam *construct_params) 421 | { 422 | GObject *object; 423 | NaTray *tray; 424 | NaTrayPrivate *priv; 425 | int screen_number; 426 | 427 | object = G_OBJECT_CLASS (na_tray_parent_class)->constructor (type, 428 | n_construct_properties, 429 | construct_params); 430 | tray = NA_TRAY (object); 431 | priv = tray->priv; 432 | 433 | g_assert (priv->screen != NULL); 434 | 435 | if (!initialized) 436 | { 437 | trays_screens = g_new0 (TraysScreen, 1); 438 | initialized = TRUE; 439 | } 440 | 441 | screen_number = gdk_x11_screen_get_screen_number (priv->screen); 442 | 443 | if (trays_screens [screen_number].tray_manager == NULL) 444 | { 445 | NaTrayManager *tray_manager; 446 | 447 | tray_manager = na_tray_manager_new (); 448 | 449 | if (na_tray_manager_manage_screen (tray_manager, priv->screen)) 450 | { 451 | trays_screens [screen_number].tray_manager = tray_manager; 452 | 453 | g_signal_connect (tray_manager, "tray_icon_added", 454 | G_CALLBACK (tray_added), 455 | &trays_screens [screen_number]); 456 | g_signal_connect (tray_manager, "tray_icon_removed", 457 | G_CALLBACK (tray_removed), 458 | &trays_screens [screen_number]); 459 | g_signal_connect (tray_manager, "message_sent", 460 | G_CALLBACK (message_sent), 461 | &trays_screens [screen_number]); 462 | g_signal_connect (tray_manager, "message_cancelled", 463 | G_CALLBACK (message_cancelled), 464 | &trays_screens [screen_number]); 465 | 466 | trays_screens [screen_number].icon_table = g_hash_table_new (NULL, 467 | NULL); 468 | trays_screens [screen_number].tip_table = g_hash_table_new_full ( 469 | NULL, 470 | NULL, 471 | NULL, 472 | icon_tip_free); 473 | } 474 | else 475 | { 476 | g_printerr ("System tray didn't get the system tray manager selection for screen %d\n", 477 | screen_number); 478 | g_object_unref (tray_manager); 479 | } 480 | } 481 | 482 | priv->trays_screen = &trays_screens [screen_number]; 483 | trays_screens [screen_number].all_trays = g_slist_append (trays_screens [screen_number].all_trays, 484 | tray); 485 | 486 | update_size_and_orientation (tray); 487 | 488 | return object; 489 | } 490 | 491 | static void 492 | na_tray_dispose (GObject *object) 493 | { 494 | NaTray *tray = NA_TRAY (object); 495 | NaTrayPrivate *priv = tray->priv; 496 | TraysScreen *trays_screen = priv->trays_screen; 497 | 498 | if (trays_screen != NULL) 499 | { 500 | trays_screen->all_trays = g_slist_remove (trays_screen->all_trays, tray); 501 | 502 | if (trays_screen->all_trays == NULL) 503 | { 504 | /* Make sure we drop the manager selection */ 505 | g_object_unref (trays_screen->tray_manager); 506 | trays_screen->tray_manager = NULL; 507 | 508 | g_hash_table_destroy (trays_screen->icon_table); 509 | trays_screen->icon_table = NULL; 510 | 511 | g_hash_table_destroy (trays_screen->tip_table); 512 | trays_screen->tip_table = NULL; 513 | } 514 | else 515 | { 516 | NaTray *new_tray; 517 | 518 | new_tray = get_tray (trays_screen); 519 | if (new_tray != NULL) 520 | na_tray_manager_set_orientation (trays_screen->tray_manager, 521 | gtk_orientable_get_orientation (GTK_ORIENTABLE (new_tray))); 522 | } 523 | } 524 | 525 | priv->trays_screen = NULL; 526 | 527 | if (priv->idle_redraw_id != 0) 528 | { 529 | g_source_remove (priv->idle_redraw_id); 530 | priv->idle_redraw_id = 0; 531 | } 532 | 533 | G_OBJECT_CLASS (na_tray_parent_class)->dispose (object); 534 | } 535 | 536 | static void 537 | na_tray_set_orientation (NaTray *tray, 538 | GtkOrientation orientation) 539 | { 540 | NaTrayPrivate *priv = tray->priv; 541 | 542 | if (orientation == priv->orientation) 543 | return; 544 | 545 | priv->orientation = orientation; 546 | 547 | update_size_and_orientation (tray); 548 | } 549 | 550 | static void 551 | na_tray_set_property (GObject *object, 552 | guint prop_id, 553 | const GValue *value, 554 | GParamSpec *pspec) 555 | { 556 | NaTray *tray = NA_TRAY (object); 557 | NaTrayPrivate *priv = tray->priv; 558 | 559 | switch (prop_id) 560 | { 561 | case PROP_ORIENTATION: 562 | na_tray_set_orientation (tray, g_value_get_enum (value)); 563 | break; 564 | case PROP_ICON_PADDING: 565 | na_tray_set_padding (tray, g_value_get_int (value)); 566 | break; 567 | case PROP_ICON_SIZE: 568 | na_tray_set_icon_size (tray, g_value_get_int (value)); 569 | break; 570 | case PROP_SCREEN: 571 | priv->screen = g_value_get_object (value); 572 | break; 573 | default: 574 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 575 | break; 576 | } 577 | } 578 | 579 | static void 580 | na_tray_get_property (GObject *object, 581 | guint prop_id, 582 | GValue *value, 583 | GParamSpec *pspec) 584 | { 585 | NaTray *tray = NA_TRAY (object); 586 | NaTrayPrivate *priv = tray->priv; 587 | 588 | switch (prop_id) 589 | { 590 | case PROP_ORIENTATION: 591 | g_value_set_enum (value, tray->priv->orientation); 592 | break; 593 | case PROP_ICON_PADDING: 594 | g_value_set_int (value, tray->priv->icon_padding); 595 | break; 596 | case PROP_ICON_SIZE: 597 | g_value_set_int (value, tray->priv->icon_size); 598 | break; 599 | case PROP_SCREEN: 600 | g_value_set_object (value, priv->screen); 601 | break; 602 | default: 603 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 604 | break; 605 | } 606 | } 607 | 608 | static void 609 | na_tray_class_init (NaTrayClass *klass) 610 | { 611 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 612 | 613 | gobject_class->constructor = na_tray_constructor; 614 | gobject_class->set_property = na_tray_set_property; 615 | gobject_class->get_property = na_tray_get_property; 616 | gobject_class->dispose = na_tray_dispose; 617 | 618 | g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation"); 619 | 620 | g_object_class_override_property (gobject_class, PROP_ICON_PADDING, "icon-padding"); 621 | g_object_class_override_property (gobject_class, PROP_ICON_SIZE, "icon-size"); 622 | 623 | g_object_class_install_property 624 | (gobject_class, 625 | PROP_SCREEN, 626 | g_param_spec_object ("screen", "screen", "screen", 627 | GDK_TYPE_SCREEN, 628 | G_PARAM_WRITABLE | 629 | G_PARAM_CONSTRUCT_ONLY | 630 | G_PARAM_STATIC_NAME | 631 | G_PARAM_STATIC_NICK | 632 | G_PARAM_STATIC_BLURB)); 633 | } 634 | 635 | NaHost * 636 | na_tray_new_for_screen (GdkScreen *screen, 637 | GtkOrientation orientation) 638 | { 639 | return g_object_new (NA_TYPE_TRAY, 640 | "screen", screen, 641 | "orientation", orientation, 642 | NULL); 643 | } 644 | 645 | void 646 | na_tray_set_padding (NaTray *tray, 647 | gint padding) 648 | { 649 | NaTrayPrivate *priv = tray->priv; 650 | 651 | priv->icon_padding = padding; 652 | if (get_tray (priv->trays_screen) == tray) 653 | na_tray_manager_set_padding (priv->trays_screen->tray_manager, padding); 654 | } 655 | 656 | void 657 | na_tray_set_icon_size (NaTray *tray, 658 | gint size) 659 | { 660 | NaTrayPrivate *priv = tray->priv; 661 | 662 | priv->icon_size = size; 663 | if (get_tray (priv->trays_screen) == tray) 664 | na_tray_manager_set_icon_size (priv->trays_screen->tray_manager, size); 665 | } 666 | 667 | static void 668 | na_tray_set_colors (NaTray *tray, 669 | GdkRGBA *fg, 670 | GdkRGBA *error, 671 | GdkRGBA *warning, 672 | GdkRGBA *success) 673 | { 674 | NaTrayPrivate *priv = tray->priv; 675 | 676 | if (get_tray (priv->trays_screen) == tray) 677 | na_tray_manager_set_colors (priv->trays_screen->tray_manager, fg, error, warning, success); 678 | } 679 | 680 | static void 681 | na_tray_style_updated (NaHost *host, 682 | GtkStyleContext *context) 683 | { 684 | GdkRGBA fg; 685 | GdkRGBA error; 686 | GdkRGBA warning; 687 | GdkRGBA success; 688 | 689 | gtk_style_context_save (context); 690 | gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL); 691 | 692 | gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &fg); 693 | 694 | if (!gtk_style_context_lookup_color (context, "error_color", &error)) 695 | error = fg; 696 | if (!gtk_style_context_lookup_color (context, "warning_color", &warning)) 697 | warning = fg; 698 | if (!gtk_style_context_lookup_color (context, "success_color", &success)) 699 | success = fg; 700 | 701 | gtk_style_context_restore (context); 702 | 703 | na_tray_set_colors (NA_TRAY (host), &fg, &error, &warning, &success); 704 | } 705 | 706 | static gboolean 707 | idle_redraw_cb (NaTray *tray) 708 | { 709 | NaTrayPrivate *priv = tray->priv; 710 | 711 | g_hash_table_foreach (priv->trays_screen->icon_table, 712 | na_tray_child_force_redraw, NULL); 713 | 714 | priv->idle_redraw_id = 0; 715 | 716 | return FALSE; 717 | } 718 | 719 | static void 720 | na_tray_force_redraw (NaHost *host) 721 | { 722 | NaTray *tray = NA_TRAY (host); 723 | NaTrayPrivate *priv = tray->priv; 724 | 725 | /* Force the icons to redraw their backgrounds. 726 | */ 727 | if (priv->idle_redraw_id == 0) 728 | priv->idle_redraw_id = g_idle_add ((GSourceFunc) idle_redraw_cb, tray); 729 | } -------------------------------------------------------------------------------- /lib/na-tray.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ 2 | /* na-tray-tray.h 3 | * Copyright (C) 2002 Anders Carlsson 4 | * Copyright (C) 2003-2006 Vincent Untz 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the 18 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 19 | * Boston, MA 02110-1301, USA. 20 | * 21 | * Used to be: eggtraytray.h 22 | */ 23 | 24 | #ifndef __NA_TRAY_H__ 25 | #define __NA_TRAY_H__ 26 | 27 | #include 28 | #include 29 | 30 | #include "na-host.h" 31 | 32 | G_BEGIN_DECLS 33 | 34 | #define NA_TYPE_TRAY (na_tray_get_type ()) 35 | #define NA_TRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NA_TYPE_TRAY, NaTray)) 36 | #define NA_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NA_TYPE_TRAY, NaTrayClass)) 37 | #define NA_IS_TRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NA_TYPE_TRAY)) 38 | #define NA_IS_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NA_TYPE_TRAY)) 39 | #define NA_TRAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NA_TYPE_TRAY, NaTrayClass)) 40 | 41 | typedef struct _NaTray NaTray; 42 | typedef struct _NaTrayPrivate NaTrayPrivate; 43 | typedef struct _NaTrayClass NaTrayClass; 44 | 45 | struct _NaTray 46 | { 47 | GObject parent_instance; 48 | 49 | NaTrayPrivate *priv; 50 | }; 51 | 52 | struct _NaTrayClass 53 | { 54 | GObjectClass parent_class; 55 | }; 56 | 57 | GType na_tray_get_type (void); 58 | NaHost *na_tray_new_for_screen (GdkScreen *screen, 59 | GtkOrientation orientation); 60 | void na_tray_set_padding (NaTray *tray, 61 | gint padding); 62 | void na_tray_set_icon_size (NaTray *tray, 63 | gint icon_size); 64 | 65 | G_END_DECLS 66 | 67 | #endif /* __NA_TRAY_H__ */ -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'wingpanel-indicator-x11tray', 3 | 'c', 'vala', 4 | version: '1.0.0', 5 | ) 6 | 7 | add_project_arguments([ 8 | '--vapidir=' + join_paths(meson.source_root(), 'vapi'), 9 | ], 10 | language: 'vala', 11 | ) 12 | 13 | prefix = get_option('prefix') 14 | libdir = join_paths(prefix, get_option('libdir')) 15 | 16 | i18n = import('i18n') 17 | gnome = import('gnome') 18 | pkg = import('pkgconfig') 19 | 20 | glib_dep = dependency('glib-2.0', version: '>=2.32') 21 | gio_dep = dependency('gio-2.0') 22 | gio_unix_dep = dependency('gio-unix-2.0') 23 | gmodule_dep = dependency('gmodule-2.0') 24 | 25 | gdk_x11_dep = dependency('gdk-x11-3.0') 26 | gtk_dep = dependency('gtk+-3.0', version: '>=3.10') 27 | gobject_dep = dependency('gobject-2.0') 28 | gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0') 29 | x11_dep = dependency('x11') 30 | gee_dep = dependency('gee-0.8') 31 | granite_dep = dependency('granite', version: '>=5.4.0') 32 | 33 | cc = meson.get_compiler('c') 34 | m_dep = cc.find_library('m', required : false) 35 | 36 | libwingpanel_dep = dependency('wingpanel-2.0') 37 | wingpanel_indicatorsdir = libwingpanel_dep.get_pkgconfig_variable('indicatorsdir', define_variable: ['libdir', libdir]) 38 | 39 | subdir('lib') 40 | subdir('src') -------------------------------------------------------------------------------- /src/Indicator.vala: -------------------------------------------------------------------------------- 1 | public class Indicator : Wingpanel.Indicator 2 | { 3 | private Gtk.Widget systray; 4 | 5 | public Indicator() 6 | { 7 | /* Some information about the indicator */ 8 | Object 9 | ( 10 | code_name : "sample-indicator" /* Unique name */ 11 | ); 12 | } 13 | 14 | construct 15 | { 16 | systray = Gtkx11Tray.get_systray(Gtk.Orientation.HORIZONTAL); 17 | 18 | this.visible = true; 19 | } 20 | 21 | /* This method is called to get the widget that is displayed in the panel */ 22 | public override Gtk.Widget get_display_widget () 23 | { 24 | return systray; 25 | } 26 | 27 | /* This method is called to get the widget that is displayed in the popover */ 28 | public override Gtk.Widget? get_widget () 29 | { 30 | return new Gtk.Label ("should not be shown"); 31 | } 32 | 33 | /* This method is called when the indicator popover opened */ 34 | public override void opened () 35 | { 36 | /* Use this method to get some extra information while displaying the indicator */ 37 | } 38 | 39 | /* This method is called when the indicator popover closed */ 40 | public override void closed () 41 | { 42 | /* Your stuff isn't shown anymore, now you can free some RAM, stop timers or anything else... */ 43 | } 44 | } 45 | 46 | /* 47 | * This method is called once after your plugin has been loaded. 48 | * Create and return your indicator here if it should be displayed on the current server. 49 | */ 50 | public Wingpanel.Indicator? get_indicator (Module module, Wingpanel.IndicatorManager.ServerType server_type) 51 | { 52 | /* A small message for debugging reasons */ 53 | debug ("Activating Sample Indicator"); 54 | 55 | /* Check which server has loaded the plugin */ 56 | if (server_type != Wingpanel.IndicatorManager.ServerType.SESSION) 57 | { 58 | /* We want to display our sample indicator only in the "normal" session, not on the login screen, so stop here! */ 59 | return null; 60 | } 61 | 62 | /* Create the indicator */ 63 | var indicator = new Indicator (); 64 | 65 | /* Return the newly created indicator */ 66 | return indicator; 67 | } -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | shared_module('indicator-sample', 2 | 'Indicator.vala', 3 | dependencies: [ libwingpanel_dep, lib_gtk_x11tray_dependency ], 4 | install: true, 5 | install_dir: wingpanel_indicatorsdir, 6 | vala_args: [ 7 | '--vapidir=' + meson.source_root() + '/vapi/', 8 | '--pkg=gtk_x11tray' 9 | ] 10 | ) -------------------------------------------------------------------------------- /vapi/gtk_x11tray.vapi: -------------------------------------------------------------------------------- 1 | [CCode (cheader_filename = "na-grid.h")] 2 | namespace Gtkx11Tray 3 | { 4 | [CCode (cname = "na_grid_new")] 5 | public static Gtk.Widget get_systray(Gtk.Orientation orientation); 6 | } --------------------------------------------------------------------------------