1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright (C) 2014 Red Hat, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21
22 #include <gio/gunixinputstream.h>
23 #include <gtk/gtk.h>
24 #include <gdk/gdkx.h>
25 #include <gdk/gdkwayland.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <X11/extensions/sync.h>
30
31 const char *client_id = "0";
32 static gboolean wayland;
33 GHashTable *windows;
34 GQuark event_source_quark;
35 GQuark event_handlers_quark;
36 GQuark can_take_focus_quark;
37
38 typedef void (*XEventHandler) (GtkWidget *window, XEvent *event);
39
40 static void read_next_line (GDataInputStream *in);
41
42 static void
window_export_handle_cb(GdkWindow * window,const char * handle_str,gpointer user_data)43 window_export_handle_cb (GdkWindow *window,
44 const char *handle_str,
45 gpointer user_data)
46 {
47 GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (user_data));
48
49 if (!gdk_wayland_window_set_transient_for_exported (gdk_window,
50 (gchar *) handle_str))
51 g_print ("Fail to set transient_for exported window handle %s\n", handle_str);
52 gdk_window_set_modal_hint (gdk_window, TRUE);
53 }
54
55 static GtkWidget *
lookup_window(const char * window_id)56 lookup_window (const char *window_id)
57 {
58 GtkWidget *window = g_hash_table_lookup (windows, window_id);
59 if (!window)
60 g_print ("Window %s doesn't exist\n", window_id);
61
62 return window;
63 }
64
65 typedef struct {
66 GSource base;
67 GSource **self_ref;
68 GPollFD event_poll_fd;
69 Display *xdisplay;
70 } XClientEventSource;
71
72 static gboolean
x_event_source_prepare(GSource * source,int * timeout)73 x_event_source_prepare (GSource *source,
74 int *timeout)
75 {
76 XClientEventSource *x_source = (XClientEventSource *) source;
77
78 *timeout = -1;
79
80 return XPending (x_source->xdisplay);
81 }
82
83 static gboolean
x_event_source_check(GSource * source)84 x_event_source_check (GSource *source)
85 {
86 XClientEventSource *x_source = (XClientEventSource *) source;
87
88 return XPending (x_source->xdisplay);
89 }
90
91 static gboolean
x_event_source_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)92 x_event_source_dispatch (GSource *source,
93 GSourceFunc callback,
94 gpointer user_data)
95 {
96 XClientEventSource *x_source = (XClientEventSource *) source;
97
98 while (XPending (x_source->xdisplay))
99 {
100 GHashTableIter iter;
101 XEvent event;
102 gpointer value;
103
104 XNextEvent (x_source->xdisplay, &event);
105
106 g_hash_table_iter_init (&iter, windows);
107 while (g_hash_table_iter_next (&iter, NULL, &value))
108 {
109 GList *l;
110 GtkWidget *window = value;
111 GList *handlers =
112 g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
113
114 for (l = handlers; l; l = l->next)
115 {
116 XEventHandler handler = l->data;
117 handler (window, &event);
118 }
119 }
120 }
121
122 return TRUE;
123 }
124
125 static void
x_event_source_finalize(GSource * source)126 x_event_source_finalize (GSource *source)
127 {
128 XClientEventSource *x_source = (XClientEventSource *) source;
129
130 *x_source->self_ref = NULL;
131 }
132
133 static GSourceFuncs x_event_funcs = {
134 x_event_source_prepare,
135 x_event_source_check,
136 x_event_source_dispatch,
137 x_event_source_finalize,
138 };
139
140 static GSource*
ensure_xsource_handler(GdkDisplay * gdkdisplay)141 ensure_xsource_handler (GdkDisplay *gdkdisplay)
142 {
143 static GSource *source = NULL;
144 Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
145 XClientEventSource *x_source;
146
147 if (source)
148 return g_source_ref (source);
149
150 source = g_source_new (&x_event_funcs, sizeof (XClientEventSource));
151 x_source = (XClientEventSource *) source;
152 x_source->self_ref = &source;
153 x_source->xdisplay = xdisplay;
154 x_source->event_poll_fd.fd = ConnectionNumber (xdisplay);
155 x_source->event_poll_fd.events = G_IO_IN;
156 g_source_add_poll (source, &x_source->event_poll_fd);
157
158 g_source_set_priority (source, GDK_PRIORITY_EVENTS - 1);
159 g_source_set_can_recurse (source, TRUE);
160 g_source_attach (source, NULL);
161
162 return source;
163 }
164
165 static gboolean
window_has_x11_event_handler(GtkWidget * window,XEventHandler handler)166 window_has_x11_event_handler (GtkWidget *window,
167 XEventHandler handler)
168 {
169 GList *handlers =
170 g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
171
172 g_return_val_if_fail (handler, FALSE);
173 g_return_val_if_fail (!wayland, FALSE);
174
175 return g_list_find (handlers, handler) != NULL;
176 }
177
178 static void
unref_and_maybe_destroy_gsource(GSource * source)179 unref_and_maybe_destroy_gsource (GSource *source)
180 {
181 g_source_unref (source);
182
183 if (source->ref_count == 1)
184 g_source_destroy (source);
185 }
186
187 static void
window_add_x11_event_handler(GtkWidget * window,XEventHandler handler)188 window_add_x11_event_handler (GtkWidget *window,
189 XEventHandler handler)
190 {
191 GSource *source;
192 GList *handlers =
193 g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
194
195 g_return_if_fail (!window_has_x11_event_handler (window, handler));
196
197 source = ensure_xsource_handler (gtk_widget_get_display (window));
198 g_object_set_qdata_full (G_OBJECT (window), event_source_quark, source,
199 (GDestroyNotify) unref_and_maybe_destroy_gsource);
200
201 handlers = g_list_append (handlers, handler);
202 g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
203 }
204
205 static void
window_remove_x11_event_handler(GtkWidget * window,XEventHandler handler)206 window_remove_x11_event_handler (GtkWidget *window,
207 XEventHandler handler)
208 {
209 GList *handlers =
210 g_object_get_qdata (G_OBJECT (window), event_handlers_quark);
211
212 g_return_if_fail (window_has_x11_event_handler (window, handler));
213
214 g_object_set_qdata (G_OBJECT (window), event_source_quark, NULL);
215
216 handlers = g_list_remove (handlers, handler);
217 g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers);
218 }
219
220 static void
handle_take_focus(GtkWidget * window,XEvent * xevent)221 handle_take_focus (GtkWidget *window,
222 XEvent *xevent)
223 {
224 GdkWindow *gdkwindow = gtk_widget_get_window (window);
225 GdkDisplay *display = gtk_widget_get_display (window);
226 Atom wm_protocols =
227 gdk_x11_get_xatom_by_name_for_display (display, "WM_PROTOCOLS");
228 Atom wm_take_focus =
229 gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
230
231 if (xevent->xany.type != ClientMessage ||
232 xevent->xany.window != GDK_WINDOW_XID (gdkwindow))
233 return;
234
235 if (xevent->xclient.message_type == wm_protocols &&
236 xevent->xclient.data.l[0] == wm_take_focus)
237 {
238 XSetInputFocus (xevent->xany.display,
239 GDK_WINDOW_XID (gdkwindow),
240 RevertToParent,
241 xevent->xclient.data.l[1]);
242 }
243 }
244
245 static int
calculate_titlebar_height(GtkWindow * window)246 calculate_titlebar_height (GtkWindow *window)
247 {
248 GtkWidget *titlebar;
249 GdkWindow *gdk_window;
250
251 gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
252 if (gdk_window_get_state (gdk_window) & GDK_WINDOW_STATE_FULLSCREEN)
253 return 0;
254
255 titlebar = gtk_window_get_titlebar (window);
256 if (!titlebar)
257 return 0;
258
259 return gtk_widget_get_allocated_height (titlebar);
260 }
261
262 static void
process_line(const char * line)263 process_line (const char *line)
264 {
265 GError *error = NULL;
266 int argc;
267 char **argv;
268
269 if (!g_shell_parse_argv (line, &argc, &argv, &error))
270 {
271 g_print ("error parsing command: %s\n", error->message);
272 g_error_free (error);
273 return;
274 }
275
276 if (argc < 1)
277 {
278 g_print ("Empty command\n");
279 goto out;
280 }
281
282 if (strcmp (argv[0], "create") == 0)
283 {
284 int i;
285
286 if (argc < 2)
287 {
288 g_print ("usage: create <id> [override|csd]\n");
289 goto out;
290 }
291
292 if (g_hash_table_lookup (windows, argv[1]))
293 {
294 g_print ("window %s already exists\n", argv[1]);
295 goto out;
296 }
297
298 gboolean override = FALSE;
299 gboolean csd = FALSE;
300 for (i = 2; i < argc; i++)
301 {
302 if (strcmp (argv[i], "override") == 0)
303 override = TRUE;
304 if (strcmp (argv[i], "csd") == 0)
305 csd = TRUE;
306 }
307
308 if (override && csd)
309 {
310 g_print ("override and csd keywords are exclusive\n");
311 goto out;
312 }
313
314 GtkWidget *window = gtk_window_new (override ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL);
315 g_hash_table_insert (windows, g_strdup (argv[1]), window);
316
317 if (csd)
318 {
319 GtkWidget *headerbar = gtk_header_bar_new ();
320 gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
321 gtk_widget_show (headerbar);
322 }
323
324 gtk_window_set_default_size (GTK_WINDOW (window), 100, 100);
325
326 gchar *title = g_strdup_printf ("test/%s/%s", client_id, argv[1]);
327 gtk_window_set_title (GTK_WINDOW (window), title);
328 g_free (title);
329
330 g_object_set_qdata (G_OBJECT (window), can_take_focus_quark,
331 GUINT_TO_POINTER (TRUE));
332
333 gtk_widget_realize (window);
334
335 if (!wayland)
336 {
337 /* The cairo xlib backend creates a window when initialized, which
338 * confuses our testing if it happens asynchronously the first
339 * time a window is painted. By creating an Xlib surface and
340 * destroying it, we force initialization at a more predictable time.
341 */
342 GdkWindow *window_gdk = gtk_widget_get_window (window);
343 cairo_surface_t *surface = gdk_window_create_similar_surface (window_gdk,
344 CAIRO_CONTENT_COLOR,
345 1, 1);
346 cairo_surface_destroy (surface);
347 }
348
349 }
350 else if (strcmp (argv[0], "set_parent") == 0)
351 {
352 if (argc != 3)
353 {
354 g_print ("usage: set_parent <window-id> <parent-id>\n");
355 goto out;
356 }
357
358 GtkWidget *window = lookup_window (argv[1]);
359 if (!window)
360 {
361 g_print ("unknown window %s\n", argv[1]);
362 goto out;
363 }
364
365 GtkWidget *parent_window = lookup_window (argv[2]);
366 if (!parent_window)
367 {
368 g_print ("unknown parent window %s\n", argv[2]);
369 goto out;
370 }
371
372 gtk_window_set_transient_for (GTK_WINDOW (window),
373 GTK_WINDOW (parent_window));
374 }
375 else if (strcmp (argv[0], "set_parent_exported") == 0)
376 {
377 if (argc != 3)
378 {
379 g_print ("usage: set_parent_exported <window-id> <parent-id>\n");
380 goto out;
381 }
382
383 GtkWidget *window = lookup_window (argv[1]);
384 if (!window)
385 {
386 g_print ("unknown window %s\n", argv[1]);
387 goto out;
388 }
389
390 GtkWidget *parent_window = lookup_window (argv[2]);
391 if (!parent_window)
392 {
393 g_print ("unknown parent window %s\n", argv[2]);
394 goto out;
395 }
396
397 GdkWindow *parent_gdk_window = gtk_widget_get_window (parent_window);
398 if (!gdk_wayland_window_export_handle (parent_gdk_window,
399 window_export_handle_cb,
400 window,
401 NULL))
402 g_print ("Fail to export handle for window id %s\n", argv[2]);
403 }
404 else if (strcmp (argv[0], "accept_focus") == 0)
405 {
406 if (argc != 3)
407 {
408 g_print ("usage: %s <window-id> [true|false]\n", argv[0]);
409 goto out;
410 }
411
412 GtkWidget *window = lookup_window (argv[1]);
413 if (!window)
414 {
415 g_print ("unknown window %s\n", argv[1]);
416 goto out;
417 }
418
419 if (!wayland &&
420 window_has_x11_event_handler (window, handle_take_focus))
421 {
422 g_print ("Impossible to use %s for windows accepting take focus\n",
423 argv[1]);
424 goto out;
425 }
426
427 gboolean enabled = g_ascii_strcasecmp (argv[2], "true") == 0;
428 gtk_window_set_accept_focus (GTK_WINDOW (window), enabled);
429 }
430 else if (strcmp (argv[0], "can_take_focus") == 0)
431 {
432 if (argc != 3)
433 {
434 g_print ("usage: %s <window-id> [true|false]\n", argv[0]);
435 goto out;
436 }
437
438 GtkWidget *window = lookup_window (argv[1]);
439 if (!window)
440 {
441 g_print ("unknown window %s\n", argv[1]);
442 goto out;
443 }
444
445 if (wayland)
446 {
447 g_print ("%s not supported under wayland\n", argv[0]);
448 goto out;
449 }
450
451 if (window_has_x11_event_handler (window, handle_take_focus))
452 {
453 g_print ("Impossible to change %s for windows accepting take focus\n",
454 argv[1]);
455 goto out;
456 }
457
458 GdkDisplay *display = gdk_display_get_default ();
459 GdkWindow *gdkwindow = gtk_widget_get_window (window);
460 Display *xdisplay = gdk_x11_display_get_xdisplay (display);
461 Window xwindow = GDK_WINDOW_XID (gdkwindow);
462 Atom wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
463 gboolean add = g_ascii_strcasecmp(argv[2], "true") == 0;
464 Atom *protocols = NULL;
465 Atom *new_protocols;
466 int n_protocols = 0;
467 int i, n = 0;
468
469 gdk_display_sync (display);
470 XGetWMProtocols (xdisplay, xwindow, &protocols, &n_protocols);
471 new_protocols = g_new0 (Atom, n_protocols + (add ? 1 : 0));
472
473 for (i = 0; i < n_protocols; ++i)
474 {
475 if (protocols[i] != wm_take_focus)
476 new_protocols[n++] = protocols[i];
477 }
478
479 if (add)
480 new_protocols[n++] = wm_take_focus;
481
482 XSetWMProtocols (xdisplay, xwindow, new_protocols, n);
483 g_object_set_qdata (G_OBJECT (window), can_take_focus_quark,
484 GUINT_TO_POINTER (add));
485
486 XFree (new_protocols);
487 XFree (protocols);
488 }
489 else if (strcmp (argv[0], "accept_take_focus") == 0)
490 {
491 if (argc != 3)
492 {
493 g_print ("usage: %s <window-id> [true|false]\n", argv[0]);
494 goto out;
495 }
496
497 GtkWidget *window = lookup_window (argv[1]);
498 if (!window)
499 {
500 g_print ("unknown window %s\n", argv[1]);
501 goto out;
502 }
503
504 if (wayland)
505 {
506 g_print ("%s not supported under wayland\n", argv[0]);
507 goto out;
508 }
509
510 if (gtk_window_get_accept_focus (GTK_WINDOW (window)))
511 {
512 g_print ("%s not supported for input windows\n", argv[0]);
513 goto out;
514 }
515
516 if (!g_object_get_qdata (G_OBJECT (window), can_take_focus_quark))
517 {
518 g_print ("%s not supported for windows with no WM_TAKE_FOCUS set\n",
519 argv[0]);
520 goto out;
521 }
522
523 if (g_ascii_strcasecmp (argv[2], "true") == 0)
524 window_add_x11_event_handler (window, handle_take_focus);
525 else
526 window_remove_x11_event_handler (window, handle_take_focus);
527 }
528 else if (strcmp (argv[0], "show") == 0)
529 {
530 if (argc != 2)
531 {
532 g_print ("usage: show <id>\n");
533 goto out;
534 }
535
536 GtkWidget *window = lookup_window (argv[1]);
537 if (!window)
538 goto out;
539
540 gtk_widget_show (window);
541 gdk_display_sync (gdk_display_get_default ());
542 }
543 else if (strcmp (argv[0], "hide") == 0)
544 {
545 if (argc != 2)
546 {
547 g_print ("usage: hide <id>\n");
548 goto out;
549 }
550
551 GtkWidget *window = lookup_window (argv[1]);
552 if (!window)
553 goto out;
554
555 gtk_widget_hide (window);
556 }
557 else if (strcmp (argv[0], "activate") == 0)
558 {
559 if (argc != 2)
560 {
561 g_print ("usage: activate <id>\n");
562 goto out;
563 }
564
565 GtkWidget *window = lookup_window (argv[1]);
566 if (!window)
567 goto out;
568
569 gtk_window_present (GTK_WINDOW (window));
570 }
571 else if (strcmp (argv[0], "resize") == 0)
572 {
573 if (argc != 4)
574 {
575 g_print ("usage: resize <id> <width> <height>\n");
576 goto out;
577 }
578
579 GtkWidget *window = lookup_window (argv[1]);
580 if (!window)
581 goto out;
582
583 int width = atoi (argv[2]);
584 int height = atoi (argv[3]);
585 int titlebar_height = calculate_titlebar_height (GTK_WINDOW (window));
586 gtk_window_resize (GTK_WINDOW (window),
587 width,
588 height - titlebar_height);
589 }
590 else if (strcmp (argv[0], "raise") == 0)
591 {
592 if (argc != 2)
593 {
594 g_print ("usage: raise <id>\n");
595 goto out;
596 }
597
598 GtkWidget *window = lookup_window (argv[1]);
599 if (!window)
600 goto out;
601
602 gdk_window_raise (gtk_widget_get_window (window));
603 }
604 else if (strcmp (argv[0], "lower") == 0)
605 {
606 if (argc != 2)
607 {
608 g_print ("usage: lower <id>\n");
609 goto out;
610 }
611
612 GtkWidget *window = lookup_window (argv[1]);
613 if (!window)
614 goto out;
615
616 gdk_window_lower (gtk_widget_get_window (window));
617 }
618 else if (strcmp (argv[0], "destroy") == 0)
619 {
620 if (argc != 2)
621 {
622 g_print ("usage: destroy <id>\n");
623 goto out;
624 }
625
626 GtkWidget *window = lookup_window (argv[1]);
627 if (!window)
628 goto out;
629
630 g_hash_table_remove (windows, argv[1]);
631 gtk_widget_destroy (window);
632 }
633 else if (strcmp (argv[0], "destroy_all") == 0)
634 {
635 if (argc != 1)
636 {
637 g_print ("usage: destroy_all\n");
638 goto out;
639 }
640
641 GHashTableIter iter;
642 gpointer key, value;
643
644 g_hash_table_iter_init (&iter, windows);
645 while (g_hash_table_iter_next (&iter, &key, &value))
646 gtk_widget_destroy (value);
647
648 g_hash_table_remove_all (windows);
649 }
650 else if (strcmp (argv[0], "sync") == 0)
651 {
652 if (argc != 1)
653 {
654 g_print ("usage: sync\n");
655 goto out;
656 }
657
658 gdk_display_sync (gdk_display_get_default ());
659 }
660 else if (strcmp (argv[0], "set_counter") == 0)
661 {
662 XSyncCounter counter;
663 int value;
664
665 if (argc != 3)
666 {
667 g_print ("usage: set_counter <counter> <value>\n");
668 goto out;
669 }
670
671 if (wayland)
672 {
673 g_print ("usage: set_counter can only be used for X11\n");
674 goto out;
675 }
676
677 counter = strtoul(argv[1], NULL, 10);
678 value = atoi(argv[2]);
679 XSyncValue sync_value;
680 XSyncIntToValue (&sync_value, value);
681
682 XSyncSetCounter (gdk_x11_display_get_xdisplay (gdk_display_get_default ()),
683 counter, sync_value);
684 }
685 else if (strcmp (argv[0], "minimize") == 0)
686 {
687 if (argc != 2)
688 {
689 g_print ("usage: minimize <id>\n");
690 goto out;
691 }
692
693 GtkWidget *window = lookup_window (argv[1]);
694 if (!window)
695 goto out;
696
697 gtk_window_iconify (GTK_WINDOW (window));
698 }
699 else if (strcmp (argv[0], "unminimize") == 0)
700 {
701 if (argc != 2)
702 {
703 g_print ("usage: unminimize <id>\n");
704 goto out;
705 }
706
707 GtkWidget *window = lookup_window (argv[1]);
708 if (!window)
709 goto out;
710
711 gtk_window_deiconify (GTK_WINDOW (window));
712 }
713 else if (strcmp (argv[0], "maximize") == 0)
714 {
715 if (argc != 2)
716 {
717 g_print ("usage: maximize <id>\n");
718 goto out;
719 }
720
721 GtkWidget *window = lookup_window (argv[1]);
722 if (!window)
723 goto out;
724
725 gtk_window_maximize (GTK_WINDOW (window));
726 }
727 else if (strcmp (argv[0], "unmaximize") == 0)
728 {
729 if (argc != 2)
730 {
731 g_print ("usage: unmaximize <id>\n");
732 goto out;
733 }
734
735 GtkWidget *window = lookup_window (argv[1]);
736 if (!window)
737 goto out;
738
739 gtk_window_unmaximize (GTK_WINDOW (window));
740 }
741 else if (strcmp (argv[0], "fullscreen") == 0)
742 {
743 if (argc != 2)
744 {
745 g_print ("usage: fullscreen <id>\n");
746 goto out;
747 }
748
749 GtkWidget *window = lookup_window (argv[1]);
750 if (!window)
751 goto out;
752
753 gtk_window_fullscreen (GTK_WINDOW (window));
754 }
755 else if (strcmp (argv[0], "unfullscreen") == 0)
756 {
757 if (argc != 2)
758 {
759 g_print ("usage: unfullscreen <id>\n");
760 goto out;
761 }
762
763 GtkWidget *window = lookup_window (argv[1]);
764 if (!window)
765 goto out;
766
767 gtk_window_unfullscreen (GTK_WINDOW (window));
768 }
769 else if (strcmp (argv[0], "freeze") == 0)
770 {
771 if (argc != 2)
772 {
773 g_print ("usage: freeze <id>\n");
774 goto out;
775 }
776
777 GtkWidget *window = lookup_window (argv[1]);
778 if (!window)
779 goto out;
780
781 gdk_window_freeze_updates (gtk_widget_get_window (window));
782 }
783 else if (strcmp (argv[0], "thaw") == 0)
784 {
785 if (argc != 2)
786 {
787 g_print ("usage: thaw <id>\n");
788 goto out;
789 }
790
791 GtkWidget *window = lookup_window (argv[1]);
792 if (!window)
793 goto out;
794
795 gdk_window_thaw_updates (gtk_widget_get_window (window));
796 }
797 else if (strcmp (argv[0], "assert_size") == 0)
798 {
799 int expected_width;
800 int expected_height;
801 int width;
802 int height;
803
804 if (argc != 4)
805 {
806 g_print ("usage: assert_size <id> <width> <height>\n");
807 goto out;
808 }
809
810 GtkWidget *window = lookup_window (argv[1]);
811 if (!window)
812 goto out;
813
814 gtk_window_get_size (GTK_WINDOW (window), &width, &height);
815 height += calculate_titlebar_height (GTK_WINDOW (window));
816
817 expected_width = atoi (argv[2]);
818 expected_height = atoi (argv[3]);
819 if (expected_width != width || expected_height != height)
820 {
821 g_print ("Expected size %dx%d didn't match actual size %dx%d\n",
822 expected_width, expected_height,
823 width, height);
824 goto out;
825 }
826 }
827 else
828 {
829 g_print ("Unknown command %s\n", argv[0]);
830 goto out;
831 }
832
833 g_print ("OK\n");
834
835 out:
836 g_strfreev (argv);
837 }
838
839 static void
on_line_received(GObject * source,GAsyncResult * result,gpointer user_data)840 on_line_received (GObject *source,
841 GAsyncResult *result,
842 gpointer user_data)
843 {
844 GDataInputStream *in = G_DATA_INPUT_STREAM (source);
845 GError *error = NULL;
846 gsize length;
847 char *line = g_data_input_stream_read_line_finish_utf8 (in, result, &length, &error);
848
849 if (line == NULL)
850 {
851 if (error != NULL)
852 g_printerr ("Error reading from stdin: %s\n", error->message);
853 gtk_main_quit ();
854 return;
855 }
856
857 process_line (line);
858 g_free (line);
859 read_next_line (in);
860 }
861
862 static void
read_next_line(GDataInputStream * in)863 read_next_line (GDataInputStream *in)
864 {
865 g_data_input_stream_read_line_async (in, G_PRIORITY_DEFAULT, NULL,
866 on_line_received, NULL);
867 }
868
869 const GOptionEntry options[] = {
870 {
871 "wayland", 0, 0, G_OPTION_ARG_NONE,
872 &wayland,
873 "Create a wayland client, not an X11 one",
874 NULL
875 },
876 {
877 "client-id", 0, 0, G_OPTION_ARG_STRING,
878 &client_id,
879 "Identifier used in Window titles for this client",
880 "CLIENT_ID",
881 },
882 { NULL }
883 };
884
885 int
main(int argc,char ** argv)886 main(int argc, char **argv)
887 {
888 GOptionContext *context = g_option_context_new (NULL);
889 GdkScreen *screen;
890 GtkCssProvider *provider;
891 GError *error = NULL;
892
893 g_option_context_add_main_entries (context, options, NULL);
894
895 if (!g_option_context_parse (context,
896 &argc, &argv, &error))
897 {
898 g_printerr ("%s", error->message);
899 return 1;
900 }
901
902 if (wayland)
903 gdk_set_allowed_backends ("wayland");
904 else
905 gdk_set_allowed_backends ("x11");
906
907 gtk_init (NULL, NULL);
908
909 screen = gdk_screen_get_default ();
910 provider = gtk_css_provider_new ();
911 static const char *no_decoration_css =
912 "decoration {"
913 " border-radius: 0 0 0 0;"
914 " border-width: 0;"
915 " padding: 0 0 0 0;"
916 " box-shadow: 0 0 0 0 rgba(0, 0, 0, 0), 0 0 0 0 rgba(0, 0, 0, 0);"
917 " margin: 0px;"
918 "}";
919 if (!gtk_css_provider_load_from_data (provider,
920 no_decoration_css,
921 strlen (no_decoration_css),
922 &error))
923 {
924 g_printerr ("%s", error->message);
925 return 1;
926 }
927 gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider),
928 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
929
930 windows = g_hash_table_new_full (g_str_hash, g_str_equal,
931 g_free, NULL);
932 event_source_quark = g_quark_from_static_string ("event-source");
933 event_handlers_quark = g_quark_from_static_string ("event-handlers");
934 can_take_focus_quark = g_quark_from_static_string ("can-take-focus");
935
936 GInputStream *raw_in = g_unix_input_stream_new (0, FALSE);
937 GDataInputStream *in = g_data_input_stream_new (raw_in);
938
939 read_next_line (in);
940
941 gtk_main ();
942
943 return 0;
944 }
945