]> Pileus Git - ~andy/gtk/blob - gdk/broadway/gdkdisplay-broadway.c
[broadway] Track last mouse position
[~andy/gtk] / gdk / broadway / gdkdisplay-broadway.c
1 /* GDK - The GIMP Drawing Kit
2  * gdkdisplay-broadway.c
3  * 
4  * Copyright 2001 Sun Microsystems Inc.
5  * Copyright (C) 2004 Nokia Corporation
6  *
7  * Erwann Chenede <erwann.chenede@sun.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include "config.h"
26
27 #include "gdkdisplay-broadway.h"
28
29 #include "gdkdisplay.h"
30 #include "gdkeventsource.h"
31 #include "gdkscreen.h"
32 #include "gdkscreen-broadway.h"
33 #include "gdkinternals.h"
34 #include "gdkdeviceprivate.h"
35 #include "gdkdevicemanager.h"
36
37 #include <glib.h>
38 #include <glib/gprintf.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <unistd.h>
43
44 static void   gdk_display_broadway_dispose            (GObject            *object);
45 static void   gdk_display_broadway_finalize           (GObject            *object);
46
47 G_DEFINE_TYPE (GdkDisplayBroadway, _gdk_display_broadway, GDK_TYPE_DISPLAY)
48
49
50 static void
51 _gdk_display_broadway_class_init (GdkDisplayBroadwayClass * class)
52 {
53   GObjectClass *object_class = G_OBJECT_CLASS (class);
54
55   object_class->dispose = gdk_display_broadway_dispose;
56   object_class->finalize = gdk_display_broadway_finalize;
57 }
58
59 static void
60 _gdk_display_broadway_init (GdkDisplayBroadway *display)
61 {
62   display->id_ht = g_hash_table_new (NULL, NULL);
63 }
64
65 static void
66 _gdk_event_init (GdkDisplay *display)
67 {
68   GdkDisplayBroadway *display_broadway;
69
70   display_broadway = GDK_DISPLAY_BROADWAY (display);
71   display_broadway->event_source = gdk_event_source_new (display);
72 }
73
74 static void
75 _gdk_input_init (GdkDisplay *display)
76 {
77   GdkDisplayBroadway *display_broadway;
78   GdkDeviceManager *device_manager;
79   GdkDevice *device;
80   GList *list, *l;
81
82   display_broadway = GDK_DISPLAY_BROADWAY (display);
83   device_manager = gdk_display_get_device_manager (display);
84
85   /* For backwards compatibility, just add
86    * floating devices that are not keyboards.
87    */
88   list = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING);
89
90   for (l = list; l; l = l->next)
91     {
92       device = l->data;
93
94       if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
95         continue;
96
97       display_broadway->input_devices = g_list_prepend (display_broadway->input_devices,
98                                                    g_object_ref (l->data));
99     }
100
101   g_list_free (list);
102
103   /* Now set "core" pointer to the first
104    * master device that is a pointer.
105    */
106   list = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
107
108   for (l = list; l; l = l->next)
109     {
110       device = list->data;
111
112       if (gdk_device_get_source (device) != GDK_SOURCE_MOUSE)
113         continue;
114
115       display->core_pointer = device;
116       break;
117     }
118
119   /* Add the core pointer to the devices list */
120   display_broadway->input_devices = g_list_prepend (display_broadway->input_devices,
121                                                g_object_ref (display->core_pointer));
122
123   g_list_free (list);
124 }
125
126 typedef struct {
127   GdkDisplay *display;
128   GSocketConnection *connection;
129   GDataInputStream *data;
130   GString *request;
131 } HttpRequest;
132
133 static void
134 http_request_free (HttpRequest *request)
135 {
136   g_object_unref (request->connection);
137   g_object_unref (request->data);
138   g_string_free (request->request, TRUE);
139   g_free (request);
140 }
141
142 #include <unistd.h>
143 #include <fcntl.h>
144 static void
145 set_fd_blocking (int fd)
146 {
147   glong arg;
148
149   if ((arg = fcntl (fd, F_GETFL, NULL)) < 0)
150     arg = 0;
151
152   arg = arg & ~O_NONBLOCK;
153
154   fcntl (fd, F_SETFL, arg);
155 }
156
157 static char *
158 parse_line (char *line, char *key)
159 {
160   char *p;
161
162   if (!g_str_has_prefix (line, key))
163     return NULL;
164   p = line + strlen (key);
165   if (*p != ':')
166     return NULL;
167   p++;
168   /* Skip optional initial space */
169   if (*p == ' ')
170     p++;
171   return p;
172 }
173
174 static void
175 got_input (GInputStream *stream,
176            GAsyncResult *result,
177            HttpRequest *request)
178 {
179   GdkScreen *screen;
180   GdkWindow *root, *window;
181   char *message, *p;
182   gsize len;
183   GError *error = NULL;
184   int x, y, button, id;
185   guint64 time;
186   GdkEvent *event = NULL;
187   char cmd;
188   GList *node;
189   GdkDisplayBroadway *display_broadway = GDK_DISPLAY_BROADWAY (request->display);
190
191   message = g_data_input_stream_read_upto_finish (G_DATA_INPUT_STREAM (stream), result, &len, &error);
192   if (message == NULL)
193     {
194       g_print (error->message);
195       g_error_free (error);
196       exit (1);
197     }
198   g_assert (message[0] == 0);
199
200   screen = gdk_display_get_default_screen (request->display);
201   root = gdk_screen_get_root_window (screen);
202
203   p = message + 1;
204   cmd = *p++;
205   switch (cmd) {
206   case 'm':
207     id = strtol(p, &p, 10);
208     p++; /* Skip , */
209     x = strtol(p, &p, 10);
210     p++; /* Skip , */
211     y = strtol(p, &p, 10);
212     p++; /* Skip , */
213     time = strtol(p, &p, 10);
214     display_broadway->last_x = x;
215     display_broadway->last_y = y;
216
217     window = g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (id));
218
219     if (display_broadway->mouse_in_toplevel != window)
220       {
221         if (display_broadway->mouse_in_toplevel != NULL)
222           {
223             event = gdk_event_new (GDK_LEAVE_NOTIFY);
224             event->crossing.window = g_object_ref (display_broadway->mouse_in_toplevel);
225             event->crossing.time = time;
226             event->crossing.x = x - GDK_WINDOW_OBJECT (display_broadway->mouse_in_toplevel)->x;
227             event->crossing.y = y - GDK_WINDOW_OBJECT (display_broadway->mouse_in_toplevel)->y;
228             event->crossing.x_root = x;
229             event->crossing.y_root = y;
230             event->crossing.mode = GDK_CROSSING_NORMAL;
231             event->crossing.detail = GDK_NOTIFY_ANCESTOR;
232             gdk_event_set_device (event, request->display->core_pointer);
233
234             node = _gdk_event_queue_append (request->display, event);
235             _gdk_windowing_got_event (request->display, node, event, 0);
236
237             event = gdk_event_new (GDK_FOCUS_CHANGE);
238             event->focus_change.window = g_object_ref (display_broadway->mouse_in_toplevel);
239             event->focus_change.in = FALSE;
240             gdk_event_set_device (event, request->display->core_pointer);
241
242             node = _gdk_event_queue_append (request->display, event);
243             _gdk_windowing_got_event (request->display, node, event, 0);
244           }
245
246         /* TODO: Unset when it dies */
247         display_broadway->mouse_in_toplevel = window;
248
249         if (window)
250           {
251             event = gdk_event_new (GDK_ENTER_NOTIFY);
252             event->crossing.window = g_object_ref (window);
253             event->crossing.time = time;
254             event->crossing.x = x - GDK_WINDOW_OBJECT (window)->x;
255             event->crossing.y = y - GDK_WINDOW_OBJECT (window)->y;
256             event->crossing.x_root = x;
257             event->crossing.y_root = y;
258             event->crossing.mode = GDK_CROSSING_NORMAL;
259             event->crossing.detail = GDK_NOTIFY_ANCESTOR;
260             gdk_event_set_device (event, request->display->core_pointer);
261
262             node = _gdk_event_queue_append (request->display, event);
263             _gdk_windowing_got_event (request->display, node, event, 0);
264
265             event = gdk_event_new (GDK_FOCUS_CHANGE);
266             event->focus_change.window = g_object_ref (window);
267             event->focus_change.in = TRUE;
268             gdk_event_set_device (event, request->display->core_pointer);
269
270             node = _gdk_event_queue_append (request->display, event);
271             _gdk_windowing_got_event (request->display, node, event, 0);
272
273           }
274       }
275
276     if (window)
277       {
278         event = gdk_event_new (GDK_MOTION_NOTIFY);
279         event->motion.window = g_object_ref (window);
280         event->motion.time = time;
281         event->motion.x = x - GDK_WINDOW_OBJECT (window)->x;
282         event->motion.y = y - GDK_WINDOW_OBJECT (window)->y;
283         event->motion.x_root = x;
284         event->motion.y_root = y;
285         gdk_event_set_device (event, request->display->core_pointer);
286
287         node = _gdk_event_queue_append (request->display, event);
288         _gdk_windowing_got_event (request->display, node, event, 0);
289       }
290
291     break;
292   case 'b':
293   case 'B':
294     id = strtol(p, &p, 10);
295     p++; /* Skip , */
296     x = strtol(p, &p, 10);
297     p++; /* Skip , */
298     y = strtol(p, &p, 10);
299     p++; /* Skip , */
300     button = strtol(p, &p, 10);
301     p++; /* Skip , */
302     time = strtol(p, &p, 10);
303     display_broadway->last_x = x;
304     display_broadway->last_y = y;
305
306     window = g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (id));
307
308     if (window)
309       {
310         event = gdk_event_new (cmd == 'b' ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE);
311         event->button.window = g_object_ref (window);
312         event->button.time = time;
313         event->button.x = x - GDK_WINDOW_OBJECT (window)->x;
314         event->button.y = y - GDK_WINDOW_OBJECT (window)->y;
315         event->button.x_root = x;
316         event->button.y_root = y;
317         event->button.button = button + 1;
318         gdk_event_set_device (event, request->display->core_pointer);
319
320         node = _gdk_event_queue_append (request->display, event);
321         _gdk_windowing_got_event (request->display, node, event, 0);
322       }
323
324     break;
325   }
326
327   /* Skip past ending 0xff */
328   g_data_input_stream_read_byte (request->data, NULL, NULL);
329   g_data_input_stream_read_upto_async (request->data, "\xff", 1, 0, NULL,
330                                        (GAsyncReadyCallback)got_input, request);
331 }
332
333 static void
334 start_input (HttpRequest *request)
335 {
336   char **lines;
337   char *p;
338   int num_key1, num_key2;
339   guint64 key1, key2;
340   int num_space;
341   int i;
342   guint8 challenge[16];
343   char *res;
344   gsize len;
345   GChecksum *checksum;
346   char *origin, *host;
347
348   lines = g_strsplit (request->request->str, "\n", 0);
349
350   num_key1 = 0;
351   num_key2 = 0;
352   key1 = 0;
353   key2 = 0;
354   origin = NULL;
355   host = NULL;
356   for (i = 0; lines[i] != NULL; i++)
357     {
358       if ((p = parse_line (lines[i], "Sec-WebSocket-Key1")))
359         {
360           num_space = 0;
361           while (*p != 0)
362             {
363               if (g_ascii_isdigit (*p))
364                 key1 = key1 * 10 + g_ascii_digit_value (*p);
365               else if (*p == ' ')
366                 num_space++;
367
368               p++;
369             }
370           key1 /= num_space;
371           num_key1++;
372         }
373       else if ((p = parse_line (lines[i], "Sec-WebSocket-Key2")))
374         {
375           num_space = 0;
376           while (*p != 0)
377             {
378               if (g_ascii_isdigit (*p))
379                 key2 = key2 * 10 + g_ascii_digit_value (*p);
380               else if (*p == ' ')
381                 num_space++;
382
383               p++;
384             }
385           key2 /= num_space;
386           num_key2++;
387         }
388       else if ((p = parse_line (lines[i], "Origin")))
389         {
390           origin = p;
391         }
392       else if ((p = parse_line (lines[i], "Host")))
393         {
394           host = p;
395         }
396     }
397
398
399   if (num_key1 != 1 || num_key2 != 1 || origin == NULL || host == NULL)
400     {
401       g_print ("error");
402       exit (1);
403     }
404
405   challenge[0] = (key1 >> 24) & 0xff;
406   challenge[1] = (key1 >> 16) & 0xff;
407   challenge[2] = (key1 >>  8) & 0xff;
408   challenge[3] = (key1 >>  0) & 0xff;
409   challenge[4] = (key2 >> 24) & 0xff;
410   challenge[5] = (key2 >> 16) & 0xff;
411   challenge[6] = (key2 >>  8) & 0xff;
412   challenge[7] = (key2 >>  0) & 0xff;
413
414   if (!g_input_stream_read_all (G_INPUT_STREAM (request->data), challenge+8, 8, NULL, NULL, NULL))
415     {
416       g_print ("error");
417       exit (1);
418     }
419
420   checksum = g_checksum_new (G_CHECKSUM_MD5);
421   g_checksum_update (checksum, challenge, 16);
422   len = 16;
423   g_checksum_get_digest (checksum, challenge, &len);
424   g_checksum_free (checksum);
425
426   res = g_strdup_printf ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
427                          "Upgrade: WebSocket\r\n"
428                          "Connection: Upgrade\r\n"
429                          "Sec-WebSocket-Origin: %s\r\n"
430                          "Sec-WebSocket-Location: ws://%s/input\r\n"
431                          "Sec-WebSocket-Protocol: broadway\r\n"
432                          "\r\n",
433                          origin, host);
434
435   /* TODO: This should really be async */
436   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
437                              res, strlen (res), NULL, NULL, NULL);
438   g_free (res);
439   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
440                              challenge, 16, NULL, NULL, NULL);
441
442   g_data_input_stream_read_upto_async (request->data, "\xff", 1, 0, NULL,
443                                        (GAsyncReadyCallback)got_input, request);
444 }
445
446 static void
447 start_output (HttpRequest *request)
448 {
449   GSocket *socket;
450   GdkDisplayBroadway *display_broadway;
451   int fd;
452
453   socket = g_socket_connection_get_socket (request->connection);
454
455   display_broadway = GDK_DISPLAY_BROADWAY (request->display);
456   fd = g_socket_get_fd (socket);
457   set_fd_blocking (fd);
458   display_broadway->output = broadway_client_new (fd);
459   _gdk_broadway_resync_windows ();
460
461   /* TODO: This leaks the connection since we just keep the fd,
462      we want to avoid using the fd at all here */
463   g_object_ref (request->connection);
464   http_request_free (request);
465 }
466
467 static void
468 send_error (HttpRequest *request,
469             int error_code,
470             const char *reason)
471 {
472   char *res;
473
474   res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n"
475                          "<html><head><title>%d %s</title></head>"
476                          "<body>%s</body></html>",
477                          error_code, reason,
478                          error_code, reason,
479                          reason);
480   /* TODO: This should really be async */
481   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
482                              res, strlen (res), NULL, NULL, NULL);
483   g_free (res);
484   http_request_free (request);
485 }
486
487 static void
488 send_data (HttpRequest *request,
489              const char *mimetype,
490              const char *data, gsize len)
491 {
492   char *res;
493
494   res = g_strdup_printf ("HTTP/1.0 200 OK\r\n"
495                          "Content-Type: %s\r\n"
496                          "Content-Length: %"G_GSIZE_FORMAT"\r\n"
497                          "\r\n",
498                          mimetype, len);
499   /* TODO: This should really be async */
500   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
501                              res, strlen (res), NULL, NULL, NULL);
502   g_free (res);
503   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
504                              data, len, NULL, NULL, NULL);
505   http_request_free (request);
506 }
507
508 #include "clienthtml.h"
509 #include "broadwayjs.h"
510
511 static void
512 got_request (HttpRequest *request)
513 {
514   char *start, *escaped, *tmp, *version;
515
516   if (!g_str_has_prefix (request->request->str, "GET "))
517     {
518       send_error (request, 501, "Only GET implemented");
519       return;
520     }
521
522   start = request->request->str + 4; /* Skip "GET " */
523
524   while (*start == ' ')
525     start++;
526
527   for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++)
528     ;
529   escaped = g_strndup (start, tmp - start);
530   version = NULL;
531   if (*tmp == ' ')
532     {
533       start = tmp;
534       while (*start == ' ')
535         start++;
536       for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++)
537         ;
538       version = g_strndup (start, tmp - start);
539     }
540
541   if (strcmp (escaped, "/client.html") == 0 || strcmp (escaped, "/") == 0)
542     send_data (request, "text/html", client_html, G_N_ELEMENTS(client_html) - 1);
543   else if (strcmp (escaped, "/broadway.js") == 0)
544     send_data (request, "text/javascript", broadway_js, G_N_ELEMENTS(broadway_js) - 1);
545   else if (strcmp (escaped, "/output") == 0)
546     start_output (request);
547   else if (strcmp (escaped, "/input") == 0)
548     start_input (request);
549   else
550     send_error (request, 404, "File not found");
551 }
552
553 static void
554 got_http_request_line (GInputStream *stream,
555                        GAsyncResult *result,
556                        HttpRequest *request)
557 {
558   char *line;
559
560   line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (stream), result, NULL, NULL);
561   if (line == NULL)
562     {
563       http_request_free (request);
564       g_printerr ("Error reading request lines\n");
565       return;
566     }
567   if (strlen (line) == 0)
568     got_request (request);
569   else
570     {
571       /* Protect against overflow in request length */
572       if (request->request->len > 1024 * 5)
573         {
574           send_error (request, 400, "Request to long");
575         }
576       else
577         {
578           g_string_append_printf (request->request, "%s\n", line);
579           g_data_input_stream_read_line_async (request->data, 0, NULL,
580                                                (GAsyncReadyCallback)got_http_request_line, request);
581         }
582     }
583   g_free (line);
584 }
585
586 static gboolean
587 handle_incoming_connection (GSocketService    *service,
588                             GSocketConnection *connection,
589                             GObject           *source_object)
590 {
591   HttpRequest *request;
592   GInputStream *in;
593
594   request = g_new0 (HttpRequest, 1);
595   request->connection = g_object_ref (connection);
596   request->display = (GdkDisplay *) source_object;
597   request->request = g_string_new ("");
598
599   in = g_io_stream_get_input_stream (G_IO_STREAM (connection));
600
601   request->data = g_data_input_stream_new (in);
602   g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (request->data), FALSE);
603   /* Be tolerant of input */
604   g_data_input_stream_set_newline_type (request->data, G_DATA_STREAM_NEWLINE_TYPE_ANY);
605
606   g_data_input_stream_read_line_async (request->data, 0, NULL,
607                                        (GAsyncReadyCallback)got_http_request_line, request);
608   return TRUE;
609 }
610
611 GdkDisplay *
612 gdk_display_open (const gchar *display_name)
613 {
614   GdkDisplay *display;
615   GdkDisplayBroadway *display_broadway;
616   const char *sm_client_id;
617   GError *error;
618
619   display = g_object_new (GDK_TYPE_DISPLAY_BROADWAY, NULL);
620   display_broadway = GDK_DISPLAY_BROADWAY (display);
621
622   display_broadway->output = NULL;
623
624   /* initialize the display's screens */
625   display_broadway->screens = g_new (GdkScreen *, 1);
626   display_broadway->screens[0] = _gdk_broadway_screen_new (display, 0);
627
628   /* We need to initialize events after we have the screen
629    * structures in places
630    */
631   _gdk_screen_broadway_events_init (display_broadway->screens[0]);
632
633   /*set the default screen */
634   display_broadway->default_screen = display_broadway->screens[0];
635
636   display->device_manager = _gdk_device_manager_new (display);
637
638   _gdk_event_init (display);
639
640   sm_client_id = _gdk_get_sm_client_id ();
641   if (sm_client_id)
642     _gdk_windowing_display_set_sm_client_id (display, sm_client_id);
643
644   _gdk_input_init (display);
645   _gdk_dnd_init (display);
646
647   _gdk_broadway_screen_setup (display_broadway->screens[0]);
648
649   display_broadway->service = g_socket_service_new ();
650   if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (display_broadway->service),
651                                         8080,
652                                         G_OBJECT (display),
653                                         &error))
654     {
655       g_printerr ("Unable to listen to port %d: %s\n", 8080, error->message);
656       g_error_free (error);
657       return NULL;
658     }
659   g_signal_connect (display_broadway->service, "incoming", G_CALLBACK (handle_incoming_connection), NULL);
660
661   g_signal_emit_by_name (display, "opened");
662   g_signal_emit_by_name (gdk_display_manager_get (), "display-opened", display);
663
664   return display;
665 }
666
667
668 G_CONST_RETURN gchar *
669 gdk_display_get_name (GdkDisplay *display)
670 {
671   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
672
673   return (gchar *) "Broadway";
674 }
675
676 gint
677 gdk_display_get_n_screens (GdkDisplay *display)
678 {
679   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
680
681   return 1;
682 }
683
684 GdkScreen *
685 gdk_display_get_screen (GdkDisplay *display,
686                         gint        screen_num)
687 {
688   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
689   g_return_val_if_fail (screen_num == 0, NULL);
690
691   return GDK_DISPLAY_BROADWAY (display)->screens[screen_num];
692 }
693
694 GdkScreen *
695 gdk_display_get_default_screen (GdkDisplay *display)
696 {
697   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
698
699   return GDK_DISPLAY_BROADWAY (display)->default_screen;
700 }
701
702 void
703 gdk_device_ungrab (GdkDevice  *device,
704                    guint32     time_)
705 {
706 }
707
708 void
709 gdk_display_beep (GdkDisplay *display)
710 {
711   g_return_if_fail (GDK_IS_DISPLAY (display));
712 }
713
714 void
715 gdk_display_sync (GdkDisplay *display)
716 {
717   g_return_if_fail (GDK_IS_DISPLAY (display));
718
719 }
720
721 void
722 gdk_display_flush (GdkDisplay *display)
723 {
724   g_return_if_fail (GDK_IS_DISPLAY (display));
725
726 }
727
728 GdkWindow *
729 gdk_display_get_default_group (GdkDisplay *display)
730 {
731   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
732
733   return NULL;
734 }
735
736 void
737 gdk_broadway_display_grab (GdkDisplay *display)
738 {
739 }
740
741 void
742 gdk_broadway_display_ungrab (GdkDisplay *display)
743 {
744 }
745
746 static void
747 gdk_display_broadway_dispose (GObject *object)
748 {
749   GdkDisplayBroadway *display_broadway = GDK_DISPLAY_BROADWAY (object);
750
751   g_list_foreach (display_broadway->input_devices, (GFunc) g_object_run_dispose, NULL);
752
753   _gdk_screen_close (display_broadway->screens[0]);
754
755   if (display_broadway->event_source)
756     {
757       g_source_destroy (display_broadway->event_source);
758       g_source_unref (display_broadway->event_source);
759       display_broadway->event_source = NULL;
760     }
761
762   G_OBJECT_CLASS (_gdk_display_broadway_parent_class)->dispose (object);
763 }
764
765 static void
766 gdk_display_broadway_finalize (GObject *object)
767 {
768   GdkDisplayBroadway *display_broadway = GDK_DISPLAY_BROADWAY (object);
769
770   /* Keymap */
771   if (display_broadway->keymap)
772     g_object_unref (display_broadway->keymap);
773
774   _gdk_broadway_cursor_display_finalize (GDK_DISPLAY_OBJECT(display_broadway));
775
776   /* Atom Hashtable */
777   g_hash_table_destroy (display_broadway->atom_from_virtual);
778   g_hash_table_destroy (display_broadway->atom_to_virtual);
779
780   /* input GdkDevice list */
781   g_list_foreach (display_broadway->input_devices, (GFunc) g_object_unref, NULL);
782   g_list_free (display_broadway->input_devices);
783   /* Free all GdkScreens */
784   g_object_unref (display_broadway->screens[0]);
785   g_free (display_broadway->screens);
786
787   G_OBJECT_CLASS (_gdk_display_broadway_parent_class)->finalize (object);
788 }
789
790 void
791 _gdk_windowing_set_default_display (GdkDisplay *display)
792 {
793 }
794
795 void
796 gdk_notify_startup_complete (void)
797 {
798 }
799
800 void
801 gdk_notify_startup_complete_with_id (const gchar* startup_id)
802 {
803 }
804
805 gboolean
806 gdk_display_supports_selection_notification (GdkDisplay *display)
807 {
808   return FALSE;
809 }
810
811 gboolean
812 gdk_display_request_selection_notification (GdkDisplay *display,
813                                             GdkAtom     selection)
814
815 {
816     return FALSE;
817 }
818
819 gboolean
820 gdk_display_supports_clipboard_persistence (GdkDisplay *display)
821 {
822   return FALSE;
823 }
824
825 void
826 gdk_display_store_clipboard (GdkDisplay    *display,
827                              GdkWindow     *clipboard_window,
828                              guint32        time_,
829                              const GdkAtom *targets,
830                              gint           n_targets)
831 {
832 }
833
834 guint32
835 gdk_broadway_display_get_user_time (GdkDisplay *display)
836 {
837   return GDK_DISPLAY_BROADWAY (display)->user_time;
838 }
839
840 gboolean
841 gdk_display_supports_shapes (GdkDisplay *display)
842 {
843   return FALSE;
844 }
845
846 gboolean
847 gdk_display_supports_input_shapes (GdkDisplay *display)
848 {
849   return FALSE;
850 }
851
852 gboolean
853 gdk_display_supports_composite (GdkDisplay *display)
854 {
855   return FALSE;
856 }
857
858 GList *
859 gdk_display_list_devices (GdkDisplay *display)
860 {
861   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
862
863   return GDK_DISPLAY_BROADWAY (display)->input_devices;
864 }
865
866 gboolean
867 gdk_event_send_client_message_for_display (GdkDisplay     *display,
868                                            GdkEvent       *event,
869                                            GdkNativeWindow winid)
870 {
871   return FALSE;
872 }
873
874 void
875 gdk_display_add_client_message_filter (GdkDisplay   *display,
876                                        GdkAtom       message_type,
877                                        GdkFilterFunc func,
878                                        gpointer      data)
879 {
880 }
881
882 void
883 gdk_add_client_message_filter (GdkAtom       message_type,
884                                GdkFilterFunc func,
885                                gpointer      data)
886 {
887 }
888
889 void
890 gdk_flush (void)
891 {
892   GSList *tmp_list = _gdk_displays;
893
894   while (tmp_list)
895     {
896       gdk_display_flush (GDK_DISPLAY_OBJECT (tmp_list->data));
897       tmp_list = tmp_list->next;
898     }
899 }
900
901 gulong
902 _gdk_windowing_window_get_next_serial (GdkDisplay *display)
903 {
904   return 0;
905 }