gnome-shell-3.6.3.1/src/shell-tray-icon.c

No issues found

  1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2 
  3 #include "config.h"
  4 
  5 #include "shell-tray-icon.h"
  6 #include "shell-gtk-embed.h"
  7 #include "shell-window-tracker.h"
  8 #include "tray/na-tray-child.h"
  9 #include <gdk/gdkx.h>
 10 #include "st.h"
 11 
 12 enum {
 13    PROP_0,
 14 
 15    PROP_PID,
 16    PROP_TITLE,
 17    PROP_WM_CLASS
 18 };
 19 
 20 struct _ShellTrayIconPrivate
 21 {
 22   NaTrayChild *socket;
 23 
 24   pid_t pid;
 25   char *title, *wm_class;
 26 };
 27 
 28 G_DEFINE_TYPE (ShellTrayIcon, shell_tray_icon, SHELL_TYPE_GTK_EMBED);
 29 
 30 static void
 31 shell_tray_icon_finalize (GObject *object)
 32 {
 33   ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
 34 
 35   g_free (icon->priv->title);
 36   g_free (icon->priv->wm_class);
 37 
 38   G_OBJECT_CLASS (shell_tray_icon_parent_class)->finalize (object);
 39 }
 40 
 41 static void
 42 shell_tray_icon_constructed (GObject *object)
 43 {
 44   GdkWindow *icon_app_window;
 45   ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
 46   ShellEmbeddedWindow *window;
 47   GdkDisplay *display;
 48   Window plug_xid;
 49   Atom _NET_WM_PID, type;
 50   int result, format;
 51   gulong nitems, bytes_after, *val = NULL;
 52 
 53   /* We do all this now rather than computing it on the fly later,
 54    * because the shell may want to see their values from a
 55    * tray-icon-removed signal handler, at which point the plug has
 56    * already been removed from the socket.
 57    */
 58 
 59   g_object_get (object, "window", &window, NULL);
 60   g_return_if_fail (window != NULL);
 61   icon->priv->socket = NA_TRAY_CHILD (gtk_bin_get_child (GTK_BIN (window)));
 62   g_object_unref (window);
 63 
 64   icon->priv->title = na_tray_child_get_title (icon->priv->socket);
 65   na_tray_child_get_wm_class (icon->priv->socket, NULL, &icon->priv->wm_class);
 66 
 67   icon_app_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket));
 68   plug_xid = GDK_WINDOW_XID (icon_app_window);
 69 
 70   display = gtk_widget_get_display (GTK_WIDGET (icon->priv->socket));
 71   gdk_error_trap_push ();
 72   _NET_WM_PID = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PID");
 73   result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), plug_xid,
 74                                _NET_WM_PID, 0, G_MAXLONG, False, XA_CARDINAL,
 75                                &type, &format, &nitems,
 76                                &bytes_after, (guchar **)&val);
 77   if (!gdk_error_trap_pop () &&
 78       result == Success &&
 79       type == XA_CARDINAL &&
 80       nitems == 1)
 81     icon->priv->pid = *val;
 82 
 83   if (val)
 84     XFree (val);
 85 }
 86 
 87 static void
 88 shell_tray_icon_get_property (GObject         *object,
 89                               guint            prop_id,
 90                               GValue          *value,
 91                               GParamSpec      *pspec)
 92 {
 93   ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
 94 
 95   switch (prop_id)
 96     {
 97     case PROP_PID:
 98       g_value_set_uint (value, icon->priv->pid);
 99       break;
100 
101     case PROP_TITLE:
102       g_value_set_string (value, icon->priv->title);
103       break;
104 
105     case PROP_WM_CLASS:
106       g_value_set_string (value, icon->priv->wm_class);
107       break;
108 
109     default:
110       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111       break;
112     }
113 }
114 
115 static void
116 shell_tray_icon_class_init (ShellTrayIconClass *klass)
117 {
118   GObjectClass *object_class = G_OBJECT_CLASS (klass);
119 
120   g_type_class_add_private (klass, sizeof (ShellTrayIconPrivate));
121 
122   object_class->get_property = shell_tray_icon_get_property;
123   object_class->constructed  = shell_tray_icon_constructed;
124   object_class->finalize     = shell_tray_icon_finalize;
125 
126   g_object_class_install_property (object_class,
127                                    PROP_PID,
128                                    g_param_spec_uint ("pid",
129                                                       "PID",
130                                                       "The PID of the icon's application",
131                                                       0, G_MAXUINT, 0,
132                                                       G_PARAM_READABLE));
133   g_object_class_install_property (object_class,
134                                    PROP_TITLE,
135                                    g_param_spec_string ("title",
136                                                         "Title",
137                                                         "The icon's window title",
138                                                         NULL,
139                                                         G_PARAM_READABLE));
140   g_object_class_install_property (object_class,
141                                    PROP_WM_CLASS,
142                                    g_param_spec_string ("wm-class",
143                                                         "WM Class",
144                                                         "The icon's window WM_CLASS",
145                                                         NULL,
146                                                         G_PARAM_READABLE));
147 }
148 
149 static void
150 shell_tray_icon_init (ShellTrayIcon *icon)
151 {
152   icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, SHELL_TYPE_TRAY_ICON,
153                                             ShellTrayIconPrivate);
154 }
155 
156 /*
157  * Public API
158  */
159 ClutterActor *
160 shell_tray_icon_new (ShellEmbeddedWindow *window)
161 {
162   g_return_val_if_fail (SHELL_IS_EMBEDDED_WINDOW (window), NULL);
163 
164   return g_object_new (SHELL_TYPE_TRAY_ICON,
165                        "window", window,
166                        NULL);
167 }
168 
169 /**
170  * shell_tray_icon_click:
171  * @icon: a #ShellTrayIcon
172  * @event: the #ClutterEvent triggering the fake click
173  *
174  * Fakes a press and release on @icon. @event must be a
175  * %CLUTTER_BUTTON_RELEASE event. Its relevant details will be passed
176  * on to the icon, but its coordinates will be ignored; the click is
177  * always made on the center of @icon.
178  */
179 void
180 shell_tray_icon_click (ShellTrayIcon *icon,
181                        ClutterEvent  *event)
182 {
183   XButtonEvent xbevent;
184   XCrossingEvent xcevent;
185   GdkWindow *remote_window;
186   GdkScreen *screen;
187   int x_root, y_root;
188   Display *xdisplay;
189   Window xwindow, xrootwindow;
190 
191   g_return_if_fail (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE);
192 
193   gdk_error_trap_push ();
194 
195   remote_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket));
196   xwindow = GDK_WINDOW_XID (remote_window);
197   xdisplay = GDK_WINDOW_XDISPLAY (remote_window);
198   screen = gdk_window_get_screen (remote_window);
199   xrootwindow = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
200   gdk_window_get_origin (remote_window, &x_root, &y_root);
201 
202   /* First make the icon believe the pointer is inside it */
203   xcevent.type = EnterNotify;
204   xcevent.window = xwindow;
205   xcevent.root = xrootwindow;
206   xcevent.subwindow = None;
207   xcevent.time = clutter_event_get_time (event);
208   xcevent.x = gdk_window_get_width (remote_window) / 2;
209   xcevent.y = gdk_window_get_height (remote_window) / 2;
210   xcevent.x_root = x_root + xcevent.x;
211   xcevent.y_root = y_root + xcevent.y;
212   xcevent.mode = NotifyNormal;
213   xcevent.detail = NotifyNonlinear;
214   xcevent.same_screen = True;
215   XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
216 
217   /* Now do the click */
218   xbevent.type = ButtonPress;
219   xbevent.window = xwindow;
220   xbevent.root = xrootwindow;
221   xbevent.subwindow = None;
222   xbevent.time = xcevent.time;
223   xbevent.x = xcevent.x;
224   xbevent.y = xcevent.y;
225   xbevent.x_root = xcevent.x_root;
226   xbevent.y_root = xcevent.y_root;
227   xbevent.state = clutter_event_get_state (event);
228   xbevent.button = clutter_event_get_button (event);
229   xbevent.same_screen = True;
230   XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
231 
232   xbevent.type = ButtonRelease;
233   XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
234 
235   /* And move the pointer back out */
236   xcevent.type = LeaveNotify;
237   XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
238 
239   gdk_error_trap_pop_ignored ();
240 }