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 }