No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /* gnome-shell-perf-helper: a program to create windows for performance tests
4 *
5 * Running performance tests with whatever windows a user has open results
6 * in unreliable results, so instead we hide all other windows and talk
7 * to this program over D-Bus to create just the windows we want.
8 */
9
10 #include "config.h"
11
12 #include <gtk/gtk.h>
13 #include <gdk/gdkx.h>
14
15 #define BUS_NAME "org.gnome.Shell.PerfHelper"
16
17 static void destroy_windows (void);
18 static void finish_wait_windows (void);
19 static void check_finish_wait_windows (void);
20
21 static const gchar introspection_xml[] =
22 "<node>"
23 " <interface name='org.gnome.Shell.PerfHelper'>"
24 " <method name='Exit'/>"
25 " <method name='CreateWindow'>"
26 " <arg type='i' name='width' direction='in'/>"
27 " <arg type='i' name='height' direction='in'/>"
28 " <arg type='b' name='alpha' direction='in'/>"
29 " <arg type='b' name='maximized' direction='in'/>"
30 " </method>"
31 " <method name='WaitWindows'/>"
32 " <method name='DestroyWindows'/>"
33 " </interface>"
34 "</node>";
35
36 typedef struct {
37 GtkWidget *window;
38 int width;
39 int height;
40
41 guint alpha : 1;
42 guint maximized : 1;
43 guint mapped : 1;
44 guint exposed : 1;
45 guint pending : 1;
46 } WindowInfo;
47
48 static int opt_idle_timeout = 30;
49
50 static GOptionEntry opt_entries[] =
51 {
52 { "idle-timeout", 'r', 0, G_OPTION_ARG_INT, &opt_idle_timeout, "Exit after N seconds", "N" },
53 { NULL }
54 };
55
56 static Display *xdisplay;
57 static Window xroot;
58 static Atom atom_wm_state;
59 static Atom atom__net_wm_name;
60 static Atom atom_utf8_string;
61
62 static guint timeout_id;
63 static GList *our_windows;
64 static GList *wait_windows_invocations;
65
66 static gboolean
67 on_timeout (gpointer data)
68 {
69 timeout_id = 0;
70
71 destroy_windows ();
72 gtk_main_quit ();
73
74 return FALSE;
75 }
76
77 static void
78 establish_timeout ()
79 {
80 if (timeout_id != 0)
81 g_source_remove (timeout_id);
82
83 timeout_id = g_timeout_add (opt_idle_timeout * 1000, on_timeout, NULL);
84 }
85
86 static void
87 destroy_windows (void)
88 {
89 GList *l;
90
91 for (l = our_windows; l; l = l->next)
92 {
93 WindowInfo *info = l->data;
94 gtk_widget_destroy (info->window);
95 g_free (info);
96 }
97
98 g_list_free (our_windows);
99 our_windows = NULL;
100
101 check_finish_wait_windows ();
102 }
103
104 static gboolean
105 on_window_map_event (GtkWidget *window,
106 GdkEventAny *event,
107 WindowInfo *info)
108 {
109 info->mapped = TRUE;
110
111 return FALSE;
112 }
113
114 static gboolean
115 on_window_draw (GtkWidget *window,
116 cairo_t *cr,
117 WindowInfo *info)
118 {
119 cairo_rectangle_int_t allocation;
120 gtk_widget_get_allocation (window, &allocation);
121
122 /* We draw an arbitrary pattern of red lines near the border of the
123 * window to make it more clear than empty windows if something
124 * is drastrically wrong.
125 */
126
127 cairo_save (cr);
128 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
129
130 if (info->alpha)
131 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
132 else
133 cairo_set_source_rgb (cr, 1, 1, 1);
134
135 cairo_paint (cr);
136 cairo_restore (cr);
137
138 cairo_set_source_rgb (cr, 1, 0, 0);
139 cairo_set_line_width (cr, 10);
140 cairo_move_to (cr, 0, 40);
141 cairo_line_to (cr, allocation.width, 40);
142 cairo_move_to (cr, 0, allocation.height - 40);
143 cairo_line_to (cr, allocation.width, allocation.height - 40);
144 cairo_move_to (cr, 40, 0);
145 cairo_line_to (cr, 40, allocation.height);
146 cairo_move_to (cr, allocation.width - 40, 0);
147 cairo_line_to (cr, allocation.width - 40, allocation.height);
148 cairo_stroke (cr);
149
150 info->exposed = TRUE;
151
152 if (info->exposed && info->mapped && info->pending)
153 {
154 info->pending = FALSE;
155 check_finish_wait_windows ();
156 }
157
158 return FALSE;
159 }
160
161 static void
162 create_window (int width,
163 int height,
164 gboolean alpha,
165 gboolean maximized)
166 {
167 WindowInfo *info;
168
169 info = g_new0 (WindowInfo, 1);
170 info->width = width;
171 info->height = height;
172 info->alpha = alpha;
173 info->maximized = maximized;
174 info->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
175 if (alpha)
176 gtk_widget_set_visual (info->window, gdk_screen_get_rgba_visual (gdk_screen_get_default ()));
177 if (maximized)
178 gtk_window_maximize (GTK_WINDOW (info->window));
179 info->pending = TRUE;
180
181 gtk_widget_set_size_request (info->window, width, height);
182 gtk_widget_set_app_paintable (info->window, TRUE);
183 g_signal_connect (info->window, "map-event", G_CALLBACK (on_window_map_event), info);
184 g_signal_connect (info->window, "draw", G_CALLBACK (on_window_draw), info);
185 gtk_widget_show (info->window);
186
187 our_windows = g_list_prepend (our_windows, info);
188 }
189
190 static void
191 finish_wait_windows (void)
192 {
193 GList *l;
194
195 for (l = wait_windows_invocations; l; l = l->next)
196 g_dbus_method_invocation_return_value (l->data, NULL);
197
198 g_list_free (wait_windows_invocations);
199 wait_windows_invocations = NULL;
200 }
201
202 static void
203 check_finish_wait_windows (void)
204 {
205 GList *l;
206 gboolean have_pending = FALSE;
207
208 for (l = our_windows; l; l = l->next)
209 {
210 WindowInfo *info = l->data;
211 if (info->pending)
212 have_pending = TRUE;
213 }
214
215 if (!have_pending)
216 finish_wait_windows ();
217 }
218
219 static void
220 handle_method_call (GDBusConnection *connection,
221 const gchar *sender,
222 const gchar *object_path,
223 const gchar *interface_name,
224 const gchar *method_name,
225 GVariant *parameters,
226 GDBusMethodInvocation *invocation,
227 gpointer user_data)
228 {
229 /* Push off the idle timeout */
230 establish_timeout ();
231
232 if (g_strcmp0 (method_name, "Exit") == 0)
233 {
234 destroy_windows ();
235
236 g_dbus_method_invocation_return_value (invocation, NULL);
237 g_dbus_connection_flush_sync (connection, NULL, NULL);
238
239 gtk_main_quit ();
240 }
241 else if (g_strcmp0 (method_name, "CreateWindow") == 0)
242 {
243 int width, height;
244 gboolean alpha, maximized;
245
246 g_variant_get (parameters, "(iibb)", &width, &height, &alpha, &maximized);
247
248 create_window (width, height, alpha, maximized);
249 g_dbus_method_invocation_return_value (invocation, NULL);
250 }
251 else if (g_strcmp0 (method_name, "WaitWindows") == 0)
252 {
253 wait_windows_invocations = g_list_prepend (wait_windows_invocations, invocation);
254 check_finish_wait_windows ();
255 }
256 else if (g_strcmp0 (method_name, "DestroyWindows") == 0)
257 {
258 destroy_windows ();
259 g_dbus_method_invocation_return_value (invocation, NULL);
260 }
261 }
262
263 static const GDBusInterfaceVTable interface_vtable =
264 {
265 handle_method_call,
266 NULL,
267 NULL
268 };
269
270 static void
271 on_bus_acquired (GDBusConnection *connection,
272 const gchar *name,
273 gpointer user_data)
274 {
275 GDBusNodeInfo *introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
276
277 g_dbus_connection_register_object (connection,
278 "/org/gnome/Shell/PerfHelper",
279 introspection_data->interfaces[0],
280 &interface_vtable,
281 NULL, /* user_data */
282 NULL, /* user_data_free_func */
283 NULL); /* GError** */
284 }
285
286 static void
287 on_name_acquired (GDBusConnection *connection,
288 const gchar *name,
289 gpointer user_data)
290 {
291 }
292
293 static void
294 on_name_lost (GDBusConnection *connection,
295 const gchar *name,
296 gpointer user_data)
297 {
298 destroy_windows ();
299 gtk_main_quit ();
300 }
301
302 int
303 main (int argc, char **argv)
304 {
305 GdkDisplay *display;
306 GdkScreen *screen;
307 GOptionContext *context;
308 GError *error = NULL;
309
310 /* Since we depend on this, avoid the possibility of lt-gnome-shell-perf-helper */
311 g_set_prgname ("gnome-shell-perf-helper");
312
313 context = g_option_context_new (" - server to create windows for performance testing");
314 g_option_context_add_main_entries (context, opt_entries, NULL);
315 g_option_context_add_group (context, gtk_get_option_group (TRUE));
316 if (!g_option_context_parse (context, &argc, &argv, &error))
317 {
318 g_print ("option parsing failed: %s\n", error->message);
319 return 1;
320 }
321
322 display = gdk_display_get_default ();
323 screen = gdk_screen_get_default ();
324
325 xdisplay = gdk_x11_display_get_xdisplay (display);
326 xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (screen));
327 atom_wm_state = gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE");
328 atom__net_wm_name = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
329 atom_utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
330
331 g_bus_own_name (G_BUS_TYPE_SESSION,
332 BUS_NAME,
333 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
334 G_BUS_NAME_OWNER_FLAGS_REPLACE,
335 on_bus_acquired,
336 on_name_acquired,
337 on_name_lost,
338 NULL,
339 NULL);
340
341 establish_timeout ();
342
343 gtk_main ();
344
345 return 0;
346 }