]> Pileus Git - ~andy/gtk/blob - gdk/broadway/gdkbroadway-server.c
b2546eccc47d9e09c4d5a68d8b1a2900cf1d0523
[~andy/gtk] / gdk / broadway / gdkbroadway-server.c
1 #include "gdkbroadway-server.h"
2
3 #include "broadway-output.h"
4 #include "gdkprivate-broadway.h"
5
6 #include <glib.h>
7 #include <glib/gprintf.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <unistd.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <netinet/tcp.h>
16
17 typedef struct BroadwayInput BroadwayInput;
18
19 struct _GdkBroadwayServer {
20   GObject parent_instance;
21
22   int port;
23   GSocketService *service;
24   BroadwayOutput *output;
25   guint32 id_counter;
26   guint32 saved_serial;
27   guint64 last_seen_time;
28   BroadwayInput *input;
29   GList *input_messages;
30   guint process_input_idle;
31
32   GHashTable *id_ht;
33   GList *toplevels;
34
35   gint32 mouse_in_toplevel_id;
36   int last_x, last_y; /* in root coords */
37   guint32 last_state;
38   gint32 real_mouse_in_toplevel_id; /* Not affected by grabs */
39
40   /* Explicit pointer grabs: */
41   gint32 pointer_grab_window_id; /* -1 => none */
42   guint32 pointer_grab_time;
43   gboolean pointer_grab_owner_events;
44
45   /* Future data, from the currently queued events */
46   int future_root_x;
47   int future_root_y;
48   guint32 future_state;
49   int future_mouse_in_toplevel;
50 };
51
52 struct _GdkBroadwayServerClass
53 {
54   GObjectClass parent_class;
55 };
56
57 typedef struct HttpRequest {
58   GdkBroadwayServer *server;
59   GSocketConnection *connection;
60   GDataInputStream *data;
61   GString *request;
62 }  HttpRequest;
63
64 struct BroadwayInput {
65   GdkBroadwayServer *server;
66   GSocketConnection *connection;
67   GByteArray *buffer;
68   GSource *source;
69   gboolean seen_time;
70   gint64 time_base;
71   gboolean proto_v7_plus;
72   gboolean binary;
73 };
74
75 typedef struct {
76   gint32 id;
77   gint32 x;
78   gint32 y;
79   gint32 width;
80   gint32 height;
81   gboolean is_temp;
82   gboolean last_synced;
83   gboolean visible;
84   gint32 transient_for;
85
86   cairo_surface_t *last_surface;
87 } BroadwayWindow;
88
89 static void _gdk_broadway_server_resync_windows (GdkBroadwayServer *server);
90
91 G_DEFINE_TYPE (GdkBroadwayServer, gdk_broadway_server, G_TYPE_OBJECT)
92
93 static void
94 gdk_broadway_server_init (GdkBroadwayServer *server)
95 {
96   BroadwayWindow *root;
97
98   server->service = g_socket_service_new ();
99   server->pointer_grab_window_id = -1;
100   server->saved_serial = 1;
101   server->last_seen_time = 1;
102   server->id_ht = g_hash_table_new (NULL, NULL);
103   server->id_counter = 0;
104
105   root = g_new0 (BroadwayWindow, 1);
106   root->id = server->id_counter++;
107   root->width = 1024;
108   root->height = 768;
109   root->visible = TRUE;
110
111   g_hash_table_insert (server->id_ht,
112                        GINT_TO_POINTER (root->id),
113                        root);
114 }
115
116 static void
117 gdk_broadway_server_finalize (GObject *object)
118 {
119   G_OBJECT_CLASS (gdk_broadway_server_parent_class)->finalize (object);
120 }
121
122 static void
123 gdk_broadway_server_class_init (GdkBroadwayServerClass * class)
124 {
125   GObjectClass *object_class = G_OBJECT_CLASS (class);
126
127   object_class->finalize = gdk_broadway_server_finalize;
128 }
129
130 static void start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary);
131
132 static void
133 http_request_free (HttpRequest *request)
134 {
135   g_object_unref (request->connection);
136   g_object_unref (request->data);
137   g_string_free (request->request, TRUE);
138   g_free (request);
139 }
140
141 static void
142 broadway_input_free (BroadwayInput *input)
143 {
144   g_object_unref (input->connection);
145   g_byte_array_free (input->buffer, FALSE);
146   g_source_destroy (input->source);
147   g_free (input);
148 }
149
150 static void
151 update_event_state (GdkBroadwayServer *server,
152                     BroadwayInputMsg *message)
153 {
154   switch (message->base.type) {
155   case BROADWAY_EVENT_ENTER:
156     server->last_x = message->pointer.root_x;
157     server->last_y = message->pointer.root_y;
158     server->last_state = message->pointer.state;
159     server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;
160
161     /* TODO: Unset when it dies */
162     server->mouse_in_toplevel_id = message->pointer.event_window_id;
163     break;
164   case BROADWAY_EVENT_LEAVE:
165     server->last_x = message->pointer.root_x;
166     server->last_y = message->pointer.root_y;
167     server->last_state = message->pointer.state;
168     server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;
169
170     server->mouse_in_toplevel_id = 0;
171     break;
172   case BROADWAY_EVENT_POINTER_MOVE:
173     server->last_x = message->pointer.root_x;
174     server->last_y = message->pointer.root_y;
175     server->last_state = message->pointer.state;
176     server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;
177     break;
178   case BROADWAY_EVENT_BUTTON_PRESS:
179   case BROADWAY_EVENT_BUTTON_RELEASE:
180     server->last_x = message->pointer.root_x;
181     server->last_y = message->pointer.root_y;
182     server->last_state = message->pointer.state;
183     server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;
184     break;
185   case BROADWAY_EVENT_SCROLL:
186     server->last_x = message->pointer.root_x;
187     server->last_y = message->pointer.root_y;
188     server->last_state = message->pointer.state;
189     server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;
190     break;
191   case BROADWAY_EVENT_KEY_PRESS:
192   case BROADWAY_EVENT_KEY_RELEASE:
193     server->last_state = message->key.state;
194     break;
195   case BROADWAY_EVENT_GRAB_NOTIFY:
196   case BROADWAY_EVENT_UNGRAB_NOTIFY:
197     break;
198   case BROADWAY_EVENT_CONFIGURE_NOTIFY:
199     break;
200   case BROADWAY_EVENT_DELETE_NOTIFY:
201     break;
202   case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
203     break;
204
205   default:
206     g_printerr ("update_event_state - Unknown input command %c\n", message->base.type);
207     break;
208   }
209 }
210
211 gboolean
212 _gdk_broadway_server_lookahead_event (GdkBroadwayServer  *server,
213                                       const char         *types)
214 {
215   BroadwayInputMsg *message;
216   GList *l;
217
218   for (l = server->input_messages; l != NULL; l = l->next)
219     {
220       message = l->data;
221       if (strchr (types, message->base.type) != NULL)
222         return TRUE;
223     }
224
225   return FALSE;
226 }
227
228 static void
229 process_input_messages (GdkBroadwayServer *server)
230 {
231   BroadwayInputMsg *message;
232
233   while (server->input_messages)
234     {
235       message = server->input_messages->data;
236       server->input_messages =
237         g_list_delete_link (server->input_messages,
238                             server->input_messages);
239
240
241       update_event_state (server, message);
242       _gdk_broadway_events_got_input (message);
243       g_free (message);
244     }
245 }
246
247 static char *
248 parse_pointer_data (char *p, BroadwayInputPointerMsg *data)
249 {
250   data->mouse_window_id = strtol (p, &p, 10);
251   p++; /* Skip , */
252   data->event_window_id = strtol (p, &p, 10);
253   p++; /* Skip , */
254   data->root_x = strtol (p, &p, 10);
255   p++; /* Skip , */
256   data->root_y = strtol (p, &p, 10);
257   p++; /* Skip , */
258   data->win_x = strtol (p, &p, 10);
259   p++; /* Skip , */
260   data->win_y = strtol (p, &p, 10);
261   p++; /* Skip , */
262   data->state = strtol (p, &p, 10);
263
264   return p;
265 }
266
267 static void
268 update_future_pointer_info (GdkBroadwayServer *server, BroadwayInputPointerMsg *data)
269 {
270   server->future_root_x = data->root_x;
271   server->future_root_y = data->root_y;
272   server->future_state = data->state;
273   server->future_mouse_in_toplevel = data->mouse_window_id;
274 }
275
276 static void
277 parse_input_message (BroadwayInput *input, const char *message)
278 {
279   GdkBroadwayServer *server = input->server;
280   BroadwayInputMsg msg;
281   char *p;
282   gint64 time_;
283
284   p = (char *)message;
285   msg.base.type = *p++;
286   msg.base.serial = (guint32)strtol (p, &p, 10);
287   p++; /* Skip , */
288   time_ = strtol(p, &p, 10);
289   p++; /* Skip , */
290
291   if (time_ == 0) {
292     time_ = server->last_seen_time;
293   } else {
294     if (!input->seen_time) {
295       input->seen_time = TRUE;
296       /* Calculate time base so that any following times are normalized to start
297          5 seconds after last_seen_time, to avoid issues that could appear when
298          a long hiatus due to a reconnect seems to be instant */
299       input->time_base = time_ - (server->last_seen_time + 5000);
300     }
301     time_ = time_ - input->time_base;
302   }
303
304   server->last_seen_time = time_;
305
306   msg.base.time = time_;
307
308   switch (msg.base.type) {
309   case BROADWAY_EVENT_ENTER:
310   case BROADWAY_EVENT_LEAVE:
311     p = parse_pointer_data (p, &msg.pointer);
312     update_future_pointer_info (server, &msg.pointer);
313     p++; /* Skip , */
314     msg.crossing.mode = strtol(p, &p, 10);
315     break;
316
317   case BROADWAY_EVENT_POINTER_MOVE: /* Mouse move */
318     p = parse_pointer_data (p, &msg.pointer);
319     update_future_pointer_info (server, &msg.pointer);
320     break;
321
322   case BROADWAY_EVENT_BUTTON_PRESS:
323   case BROADWAY_EVENT_BUTTON_RELEASE:
324     p = parse_pointer_data (p, &msg.pointer);
325     update_future_pointer_info (server, &msg.pointer);
326     p++; /* Skip , */
327     msg.button.button = strtol(p, &p, 10);
328     break;
329
330   case BROADWAY_EVENT_SCROLL:
331     p = parse_pointer_data (p, &msg.pointer);
332     update_future_pointer_info (server, &msg.pointer);
333     p++; /* Skip , */
334     msg.scroll.dir = strtol(p, &p, 10);
335     break;
336
337   case BROADWAY_EVENT_KEY_PRESS:
338   case BROADWAY_EVENT_KEY_RELEASE:
339     msg.key.mouse_window_id = strtol(p, &p, 10);
340     p++; /* Skip , */
341     msg.key.key = strtol(p, &p, 10);
342     p++; /* Skip , */
343     msg.key.state = strtol(p, &p, 10);
344     break;
345
346   case BROADWAY_EVENT_GRAB_NOTIFY:
347   case BROADWAY_EVENT_UNGRAB_NOTIFY:
348     msg.grab_reply.res = strtol(p, &p, 10);
349     break;
350
351   case BROADWAY_EVENT_CONFIGURE_NOTIFY:
352     msg.configure_notify.id = strtol(p, &p, 10);
353     p++; /* Skip , */
354     msg.configure_notify.x = strtol (p, &p, 10);
355     p++; /* Skip , */
356     msg.configure_notify.y = strtol (p, &p, 10);
357     p++; /* Skip , */
358     msg.configure_notify.width = strtol (p, &p, 10);
359     p++; /* Skip , */
360     msg.configure_notify.height = strtol (p, &p, 10);
361     break;
362
363   case BROADWAY_EVENT_DELETE_NOTIFY:
364     msg.delete_notify.id = strtol(p, &p, 10);
365     break;
366
367   case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
368     msg.screen_resize_notify.width = strtol (p, &p, 10);
369     p++; /* Skip , */
370     msg.screen_resize_notify.height = strtol (p, &p, 10);
371     break;
372
373   default:
374     g_printerr ("parse_input_message - Unknown input command %c (%s)\n", msg.base.type, message);
375     break;
376   }
377
378   server->input_messages = g_list_append (server->input_messages, g_memdup (&msg, sizeof (msg)));
379
380 }
381
382 static inline void
383 hex_dump (guchar *data, gsize len)
384 {
385 #ifdef DEBUG_WEBSOCKETS
386   gsize i, j;
387   for (j = 0; j < len + 15; j += 16)
388     {
389       fprintf (stderr, "0x%.4x  ", j);
390       for (i = 0; i < 16; i++)
391         {
392             if ((j + i) < len)
393               fprintf (stderr, "%.2x ", data[j+i]);
394             else
395               fprintf (stderr, "  ");
396             if (i == 8)
397               fprintf (stderr, " ");
398         }
399       fprintf (stderr, " | ");
400
401       for (i = 0; i < 16; i++)
402         if ((j + i) < len && g_ascii_isalnum(data[j+i]))
403           fprintf (stderr, "%c", data[j+i]);
404         else
405           fprintf (stderr, ".");
406       fprintf (stderr, "\n");
407     }
408 #endif
409 }
410
411 static void
412 parse_input (BroadwayInput *input)
413 {
414   GdkBroadwayServer *server = input->server;
415
416   if (!input->buffer->len)
417     return;
418
419   if (input->proto_v7_plus)
420     {
421       hex_dump (input->buffer->data, input->buffer->len);
422
423       while (input->buffer->len > 2)
424         {
425           gsize len, payload_len;
426           BroadwayWSOpCode code;
427           gboolean is_mask, fin;
428           guchar *buf, *data, *mask;
429
430           buf = input->buffer->data;
431           len = input->buffer->len;
432
433 #ifdef DEBUG_WEBSOCKETS
434           g_print ("Parse input first byte 0x%2x 0x%2x\n", buf[0], buf[1]);
435 #endif
436
437           fin = buf[0] & 0x80;
438           code = buf[0] & 0x0f;
439           payload_len = buf[1] & 0x7f;
440           is_mask = buf[1] & 0x80;
441           data = buf + 2;
442
443           if (payload_len > 125)
444             {
445               if (len < 4)
446                 return;
447               payload_len = GUINT16_FROM_BE( *(guint16 *) data );
448               data += 2;
449             }
450           else if (payload_len > 126)
451             {
452               if (len < 10)
453                 return;
454               payload_len = GUINT64_FROM_BE( *(guint64 *) data );
455               data += 8;
456             }
457
458           mask = NULL;
459           if (is_mask)
460             {
461               if (data - buf + 4 > len)
462                 return;
463               mask = data;
464               data += 4;
465             }
466
467           if (data - buf + payload_len > len)
468             return; /* wait to accumulate more */
469
470           if (is_mask)
471             {
472               gsize i;
473               for (i = 0; i < payload_len; i++)
474                 data[i] ^= mask[i%4];
475             }
476
477           switch (code) {
478           case BROADWAY_WS_CNX_CLOSE:
479             break; /* hang around anyway */
480           case BROADWAY_WS_TEXT:
481             if (!fin)
482               {
483 #ifdef DEBUG_WEBSOCKETS
484                 g_warning ("can't yet accept fragmented input");
485 #endif
486               }
487             else
488               {
489                 char *terminated = g_strndup((char *)data, payload_len);
490                 parse_input_message (input, terminated);
491                 g_free (terminated);
492               }
493             break;
494           case BROADWAY_WS_CNX_PING:
495             broadway_output_pong (server->output);
496             break;
497           case BROADWAY_WS_CNX_PONG:
498             break; /* we never send pings, but tolerate pongs */
499           case BROADWAY_WS_BINARY:
500           case BROADWAY_WS_CONTINUATION:
501           default:
502             {
503               g_warning ("fragmented or unknown input code 0x%2x with fin set", code);
504               break;
505             }
506           }
507
508           g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len);
509         }
510     }
511   else /* old style protocol */
512     {
513       char *buf, *ptr;
514       gsize len;
515
516       buf = (char *)input->buffer->data;
517       len = input->buffer->len;
518
519       if (buf[0] != 0)
520         {
521           server->input = NULL;
522           broadway_input_free (input);
523           return;
524         }
525
526       while ((ptr = memchr (buf, 0xff, len)) != NULL)
527         {
528           *ptr = 0;
529           ptr++;
530
531           parse_input_message (input, buf + 1);
532
533           len -= ptr - buf;
534           buf = ptr;
535
536           if (len > 0 && buf[0] != 0)
537             {
538               server->input = NULL;
539               broadway_input_free (input);
540               break;
541             }
542         }
543       g_byte_array_remove_range (input->buffer, 0, buf - (char *)input->buffer->data);
544     }
545 }
546
547
548 static gboolean
549 process_input_idle_cb (GdkBroadwayServer *server)
550 {
551   server->process_input_idle = 0;
552   process_input_messages (server);
553   return G_SOURCE_REMOVE;
554 }
555
556 static void
557 queue_process_input_at_idle (GdkBroadwayServer *server)
558 {
559   if (server->process_input_idle == 0)
560     server->process_input_idle =
561       g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)process_input_idle_cb, server, NULL);
562 }
563
564 static void
565 _gdk_broadway_server_read_all_input_nonblocking (GdkBroadwayServer *server)
566 {
567   GInputStream *in;
568   gssize res;
569   guint8 buffer[1024];
570   GError *error;
571   BroadwayInput *input;
572
573   if (server->input == NULL)
574     return;
575
576   input = server->input;
577
578   in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
579
580   error = NULL;
581   res = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (in),
582                                                   buffer, sizeof (buffer), NULL, &error);
583
584   if (res <= 0)
585     {
586       if (res < 0 &&
587           g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
588         {
589           g_error_free (error);
590           return;
591         }
592
593       server->input = NULL;
594       broadway_input_free (input);
595       if (res < 0)
596         {
597           g_printerr ("input error %s\n", error->message);
598           g_error_free (error);
599         }
600       return;
601     }
602
603   g_byte_array_append (input->buffer, buffer, res);
604
605   parse_input (input);
606 }
607
608 static void
609 _gdk_broadway_server_consume_all_input (GdkBroadwayServer *server)
610 {
611   _gdk_broadway_server_read_all_input_nonblocking (server);
612
613   /* Since we're parsing input but not processing the resulting messages
614      we might not get a readable callback on the stream, so queue an idle to
615      process the messages */
616   queue_process_input_at_idle (server);
617 }
618
619
620 static gboolean
621 input_data_cb (GObject  *stream,
622                BroadwayInput *input)
623 {
624   GdkBroadwayServer *server = input->server;
625
626   _gdk_broadway_server_read_all_input_nonblocking (server);
627
628   process_input_messages (server);
629
630   return TRUE;
631 }
632
633 gulong
634 _gdk_broadway_server_get_next_serial (GdkBroadwayServer *server)
635 {
636   if (server->output)
637     return broadway_output_get_next_serial (server->output);
638
639   return server->saved_serial;
640 }
641
642 void
643 _gdk_broadway_server_flush (GdkBroadwayServer *server)
644 {
645   if (server->output &&
646       !broadway_output_flush (server->output))
647     {
648       server->saved_serial = broadway_output_get_next_serial (server->output);
649       broadway_output_free (server->output);
650       server->output = NULL;
651     }
652 }
653
654 void
655 _gdk_broadway_server_sync (GdkBroadwayServer *server)
656 {
657   _gdk_broadway_server_flush (server);
658 }
659
660
661 /* TODO: This is not used atm, is it needed? */
662 /* Note: This may be called while handling a message (i.e. sorta recursively) */
663 BroadwayInputMsg *
664 _gdk_broadway_server_block_for_input (GdkBroadwayServer *server, char op,
665                                        guint32 serial, gboolean remove_message)
666 {
667   BroadwayInputMsg *message;
668   gssize res;
669   guint8 buffer[1024];
670   BroadwayInput *input;
671   GInputStream *in;
672   GList *l;
673
674   _gdk_broadway_server_flush (server);
675
676   if (server->input == NULL)
677     return NULL;
678
679   input = server->input;
680
681   while (TRUE) {
682     /* Check for existing reply in queue */
683
684     for (l = server->input_messages; l != NULL; l = l->next)
685       {
686         message = l->data;
687
688         if (message->base.type == op)
689           {
690             if (message->base.serial == serial)
691               {
692                 if (remove_message)
693                   server->input_messages =
694                     g_list_delete_link (server->input_messages, l);
695                 return message;
696               }
697           }
698       }
699
700     /* Not found, read more, blocking */
701
702     in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
703     res = g_input_stream_read (in, buffer, sizeof (buffer), NULL, NULL);
704     if (res <= 0)
705       return NULL;
706     g_byte_array_append (input->buffer, buffer, res);
707
708     parse_input (input);
709
710     /* Since we're parsing input but not processing the resulting messages
711        we might not get a readable callback on the stream, so queue an idle to
712        process the messages */
713     queue_process_input_at_idle (server);
714   }
715 }
716
717 static char *
718 parse_line (char *line, char *key)
719 {
720   char *p;
721
722   if (!g_str_has_prefix (line, key))
723     return NULL;
724   p = line + strlen (key);
725   if (*p != ':')
726     return NULL;
727   p++;
728   /* Skip optional initial space */
729   if (*p == ' ')
730     p++;
731   return p;
732 }
733 static void
734 send_error (HttpRequest *request,
735             int error_code,
736             const char *reason)
737 {
738   char *res;
739
740   res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n"
741                          "<html><head><title>%d %s</title></head>"
742                          "<body>%s</body></html>",
743                          error_code, reason,
744                          error_code, reason,
745                          reason);
746   /* TODO: This should really be async */
747   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
748                              res, strlen (res), NULL, NULL, NULL);
749   g_free (res);
750   http_request_free (request);
751 }
752
753 /* magic from: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 */
754 #define SEC_WEB_SOCKET_KEY_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
755
756 /* 'x3JJHMbDL1EzLkh9GBhXDw==' generates 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=' */
757 static gchar *
758 generate_handshake_response_wsietf_v7 (const gchar *key)
759 {
760   gsize digest_len = 20;
761   guchar digest[digest_len];
762   GChecksum *checksum;
763
764   checksum = g_checksum_new (G_CHECKSUM_SHA1);
765   if (!checksum)
766     return NULL;
767
768   g_checksum_update (checksum, (guchar *)key, -1);
769   g_checksum_update (checksum, (guchar *)SEC_WEB_SOCKET_KEY_MAGIC, -1);
770
771   g_checksum_get_digest (checksum, digest, &digest_len);
772   g_checksum_free (checksum);
773
774   g_assert (digest_len == 20);
775
776   return g_base64_encode (digest, digest_len);
777 }
778
779 static void
780 start_input (HttpRequest *request, gboolean binary)
781 {
782   char **lines;
783   char *p;
784   int num_key1, num_key2;
785   guint64 key1, key2;
786   int num_space;
787   int i;
788   guint8 challenge[16];
789   char *res;
790   gsize len;
791   GChecksum *checksum;
792   char *origin, *host;
793   GdkBroadwayServer *server;
794   BroadwayInput *input;
795   const void *data_buffer;
796   gsize data_buffer_size;
797   GInputStream *in;
798   char *key_v7;
799   gboolean proto_v7_plus;
800
801   server = GDK_BROADWAY_SERVER (request->server);
802
803 #ifdef DEBUG_WEBSOCKETS
804   g_print ("incoming request:\n%s\n", request->request->str);
805 #endif
806   lines = g_strsplit (request->request->str, "\n", 0);
807
808   num_key1 = 0;
809   num_key2 = 0;
810   key1 = 0;
811   key2 = 0;
812   key_v7 = NULL;
813   origin = NULL;
814   host = NULL;
815   for (i = 0; lines[i] != NULL; i++)
816     {
817       if ((p = parse_line (lines[i], "Sec-WebSocket-Key1")))
818         {
819           num_space = 0;
820           while (*p != 0)
821             {
822               if (g_ascii_isdigit (*p))
823                 key1 = key1 * 10 + g_ascii_digit_value (*p);
824               else if (*p == ' ')
825                 num_space++;
826
827               p++;
828             }
829           key1 /= num_space;
830           num_key1++;
831         }
832       else if ((p = parse_line (lines[i], "Sec-WebSocket-Key2")))
833         {
834           num_space = 0;
835           while (*p != 0)
836             {
837               if (g_ascii_isdigit (*p))
838                 key2 = key2 * 10 + g_ascii_digit_value (*p);
839               else if (*p == ' ')
840                 num_space++;
841
842               p++;
843             }
844           key2 /= num_space;
845           num_key2++;
846         }
847       else if ((p = parse_line (lines[i], "Sec-WebSocket-Key")))
848         {
849           key_v7 = p;
850         }
851       else if ((p = parse_line (lines[i], "Origin")))
852         {
853           origin = p;
854         }
855       else if ((p = parse_line (lines[i], "Host")))
856         {
857           host = p;
858         }
859       else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin")))
860         {
861           origin = p;
862         }
863     }
864
865   if (origin == NULL || host == NULL)
866     {
867       g_strfreev (lines);
868       send_error (request, 400, "Bad websocket request");
869       return;
870     }
871
872   if (key_v7 != NULL)
873     {
874       char* accept = generate_handshake_response_wsietf_v7 (key_v7);
875       res = g_strdup_printf ("HTTP/1.1 101 Switching Protocols\r\n"
876                              "Upgrade: websocket\r\n"
877                              "Connection: Upgrade\r\n"
878                              "Sec-WebSocket-Accept: %s\r\n"
879                              "Sec-WebSocket-Origin: %s\r\n"
880                              "Sec-WebSocket-Location: ws://%s/socket\r\n"
881                              "Sec-WebSocket-Protocol: broadway\r\n"
882                              "\r\n", accept, origin, host);
883       g_free (accept);
884
885 #ifdef DEBUG_WEBSOCKETS
886       g_print ("v7 proto response:\n%s", res);
887 #endif
888
889       g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
890                                  res, strlen (res), NULL, NULL, NULL);
891       g_free (res);
892       proto_v7_plus = TRUE;
893     }
894   else
895     {
896       if (num_key1 != 1 || num_key2 != 1)
897         {
898           g_strfreev (lines);
899           send_error (request, 400, "Bad websocket request");
900           return;
901         }
902
903       challenge[0] = (key1 >> 24) & 0xff;
904       challenge[1] = (key1 >> 16) & 0xff;
905       challenge[2] = (key1 >>  8) & 0xff;
906       challenge[3] = (key1 >>  0) & 0xff;
907       challenge[4] = (key2 >> 24) & 0xff;
908       challenge[5] = (key2 >> 16) & 0xff;
909       challenge[6] = (key2 >>  8) & 0xff;
910       challenge[7] = (key2 >>  0) & 0xff;
911
912       if (!g_input_stream_read_all (G_INPUT_STREAM (request->data), challenge+8, 8, NULL, NULL, NULL))
913         {
914           g_strfreev (lines);
915           send_error (request, 400, "Bad websocket request");
916           return;
917         }
918
919       checksum = g_checksum_new (G_CHECKSUM_MD5);
920       g_checksum_update (checksum, challenge, 16);
921       len = 16;
922       g_checksum_get_digest (checksum, challenge, &len);
923       g_checksum_free (checksum);
924
925       res = g_strdup_printf ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
926                              "Upgrade: WebSocket\r\n"
927                              "Connection: Upgrade\r\n"
928                              "Sec-WebSocket-Origin: %s\r\n"
929                              "Sec-WebSocket-Location: ws://%s/socket\r\n"
930                              "Sec-WebSocket-Protocol: broadway\r\n"
931                              "\r\n",
932                              origin, host);
933
934 #ifdef DEBUG_WEBSOCKETS
935       g_print ("legacy response:\n%s", res);
936 #endif
937       g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
938                                  res, strlen (res), NULL, NULL, NULL);
939       g_free (res);
940       g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
941                                  challenge, 16, NULL, NULL, NULL);
942       proto_v7_plus = FALSE;
943     }
944
945
946   if (server->input != NULL)
947     {
948       broadway_input_free (server->input);
949       server->input = NULL;
950     }
951
952   input = g_new0 (BroadwayInput, 1);
953
954   input->server = request->server;
955   input->connection = g_object_ref (request->connection);
956   input->proto_v7_plus = proto_v7_plus;
957   input->binary = binary;
958
959   data_buffer = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (request->data), &data_buffer_size);
960   input->buffer = g_byte_array_sized_new (data_buffer_size);
961   g_byte_array_append (input->buffer, data_buffer, data_buffer_size);
962
963   server->input = input;
964
965   start_output (request, proto_v7_plus, binary);
966
967   /* This will free and close the data input stream, but we got all the buffered content already */
968   http_request_free (request);
969
970   in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
971   input->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (in), NULL);
972   g_source_set_callback (input->source, (GSourceFunc)input_data_cb, input, NULL);
973   g_source_attach (input->source, NULL);
974
975   /* Process any data in the pipe already */
976   parse_input (input);
977   process_input_messages (server);
978
979   g_strfreev (lines);
980 }
981
982 static void
983 start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary)
984 {
985   GSocket *socket;
986   GdkBroadwayServer *server;
987   int flag = 1;
988
989   socket = g_socket_connection_get_socket (request->connection);
990   setsockopt(g_socket_get_fd (socket), IPPROTO_TCP,
991              TCP_NODELAY, (char *) &flag, sizeof(int));
992
993   server = GDK_BROADWAY_SERVER (request->server);
994
995   if (server->output)
996     {
997       server->saved_serial = broadway_output_get_next_serial (server->output);
998       broadway_output_free (server->output);
999     }
1000
1001   server->output =
1002     broadway_output_new (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
1003                          server->saved_serial, proto_v7_plus, binary);
1004
1005   _gdk_broadway_server_resync_windows (server);
1006
1007   if (server->pointer_grab_window_id != -1)
1008     broadway_output_grab_pointer (server->output,
1009                                   server->pointer_grab_window_id,
1010                                   server->pointer_grab_owner_events);
1011 }
1012
1013 static void
1014 send_data (HttpRequest *request,
1015              const char *mimetype,
1016              const char *data, gsize len)
1017 {
1018   char *res;
1019
1020   res = g_strdup_printf ("HTTP/1.0 200 OK\r\n"
1021                          "Content-Type: %s\r\n"
1022                          "Content-Length: %"G_GSIZE_FORMAT"\r\n"
1023                          "\r\n",
1024                          mimetype, len);
1025   /* TODO: This should really be async */
1026   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
1027                              res, strlen (res), NULL, NULL, NULL);
1028   g_free (res);
1029   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
1030                              data, len, NULL, NULL, NULL);
1031   http_request_free (request);
1032 }
1033
1034 #include "clienthtml.h"
1035 #include "broadwayjs.h"
1036
1037 static void
1038 got_request (HttpRequest *request)
1039 {
1040   char *start, *escaped, *tmp, *version, *query;
1041
1042   if (!g_str_has_prefix (request->request->str, "GET "))
1043     {
1044       send_error (request, 501, "Only GET implemented");
1045       return;
1046     }
1047
1048   start = request->request->str + 4; /* Skip "GET " */
1049
1050   while (*start == ' ')
1051     start++;
1052
1053   for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++)
1054     ;
1055   escaped = g_strndup (start, tmp - start);
1056   version = NULL;
1057   if (*tmp == ' ')
1058     {
1059       start = tmp;
1060       while (*start == ' ')
1061         start++;
1062       for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++)
1063         ;
1064       version = g_strndup (start, tmp - start);
1065     }
1066
1067   query = strchr (escaped, '?');
1068   if (query)
1069     *query = 0;
1070
1071   if (strcmp (escaped, "/client.html") == 0 || strcmp (escaped, "/") == 0)
1072     send_data (request, "text/html", client_html, G_N_ELEMENTS(client_html) - 1);
1073   else if (strcmp (escaped, "/broadway.js") == 0)
1074     send_data (request, "text/javascript", broadway_js, G_N_ELEMENTS(broadway_js) - 1);
1075   else if (strcmp (escaped, "/socket") == 0)
1076     start_input (request, FALSE);
1077   else if (strcmp (escaped, "/socket-bin") == 0)
1078     start_input (request, TRUE);
1079   else
1080     send_error (request, 404, "File not found");
1081
1082   g_free (escaped);
1083   g_free (version);
1084 }
1085
1086 static void
1087 got_http_request_line (GInputStream *stream,
1088                        GAsyncResult *result,
1089                        HttpRequest *request)
1090 {
1091   char *line;
1092
1093   line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (stream), result, NULL, NULL);
1094   if (line == NULL)
1095     {
1096       http_request_free (request);
1097       g_printerr ("Error reading request lines\n");
1098       return;
1099     }
1100   if (strlen (line) == 0)
1101     got_request (request);
1102   else
1103     {
1104       /* Protect against overflow in request length */
1105       if (request->request->len > 1024 * 5)
1106         {
1107           send_error (request, 400, "Request too long");
1108         }
1109       else
1110         {
1111           g_string_append_printf (request->request, "%s\n", line);
1112           g_data_input_stream_read_line_async (request->data, 0, NULL,
1113                                                (GAsyncReadyCallback)got_http_request_line, request);
1114         }
1115     }
1116   g_free (line);
1117 }
1118
1119 static gboolean
1120 handle_incoming_connection (GSocketService    *service,
1121                             GSocketConnection *connection,
1122                             GObject           *source_object)
1123 {
1124   HttpRequest *request;
1125   GInputStream *in;
1126
1127   request = g_new0 (HttpRequest, 1);
1128   request->connection = g_object_ref (connection);
1129   request->server = GDK_BROADWAY_SERVER (source_object);
1130   request->request = g_string_new ("");
1131
1132   in = g_io_stream_get_input_stream (G_IO_STREAM (connection));
1133
1134   request->data = g_data_input_stream_new (in);
1135   g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (request->data), FALSE);
1136   /* Be tolerant of input */
1137   g_data_input_stream_set_newline_type (request->data, G_DATA_STREAM_NEWLINE_TYPE_ANY);
1138
1139   g_data_input_stream_read_line_async (request->data, 0, NULL,
1140                                        (GAsyncReadyCallback)got_http_request_line, request);
1141   return TRUE;
1142 }
1143
1144 GdkBroadwayServer *
1145 _gdk_broadway_server_new (int port, GError **error)
1146 {
1147   GdkBroadwayServer *server;
1148
1149   server = g_object_new (GDK_TYPE_BROADWAY_SERVER, NULL);
1150   server->port = port;
1151
1152   if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (server->service),
1153                                         server->port,
1154                                         G_OBJECT (server),
1155                                         error))
1156     {
1157       g_prefix_error (error, "Unable to listen to port %d: ", server->port);
1158       return NULL;
1159     }
1160
1161   g_signal_connect (server->service, "incoming",
1162                     G_CALLBACK (handle_incoming_connection), NULL);
1163   return server;
1164 }
1165
1166 guint32
1167 _gdk_broadway_server_get_last_seen_time (GdkBroadwayServer *server)
1168 {
1169   _gdk_broadway_server_consume_all_input (server);
1170   return (guint32) server->last_seen_time;
1171 }
1172
1173 void
1174 _gdk_broadway_server_query_mouse (GdkBroadwayServer *server,
1175                                   guint32            *toplevel,
1176                                   gint32             *root_x,
1177                                   gint32             *root_y,
1178                                   guint32            *mask)
1179 {
1180   if (server->output)
1181     {
1182       _gdk_broadway_server_consume_all_input (server);
1183       if (root_x)
1184         *root_x = server->future_root_x;
1185       if (root_y)
1186         *root_y = server->future_root_y;
1187       if (mask)
1188         *mask = server->future_state;
1189       if (toplevel)
1190         *toplevel = server->future_mouse_in_toplevel;
1191       return;
1192     }
1193
1194   /* Fallback when unconnected */
1195   if (root_x)
1196     *root_x = server->last_x;
1197   if (root_y)
1198     *root_y = server->last_y;
1199   if (mask)
1200     *mask = server->last_state;
1201   if (toplevel)
1202     *toplevel = server->mouse_in_toplevel_id;
1203 }
1204
1205 void
1206 _gdk_broadway_server_destroy_window (GdkBroadwayServer *server,
1207                                      gint id)
1208 {
1209   BroadwayWindow *window;
1210
1211   if (server->mouse_in_toplevel_id == id)
1212     {
1213       /* TODO: Send leave + enter event, update cursors, etc */
1214       server->mouse_in_toplevel_id = 0;
1215     }
1216
1217   if (server->pointer_grab_window_id == id)
1218     server->pointer_grab_window_id = -1;
1219
1220   if (server->output)
1221     broadway_output_destroy_surface (server->output,
1222                                      id);
1223
1224   window = g_hash_table_lookup (server->id_ht,
1225                                 GINT_TO_POINTER (id));
1226   if (window != NULL)
1227     {
1228       server->toplevels = g_list_remove (server->toplevels, window);
1229       g_hash_table_remove (server->id_ht,
1230                            GINT_TO_POINTER (id));
1231       g_free (window);
1232     }
1233 }
1234
1235 gboolean
1236 _gdk_broadway_server_window_show (GdkBroadwayServer *server,
1237                                   gint id)
1238 {
1239   BroadwayWindow *window;
1240   gboolean sent = FALSE;
1241
1242   window = g_hash_table_lookup (server->id_ht,
1243                                 GINT_TO_POINTER (id));
1244   if (window == NULL)
1245     return FALSE;
1246
1247   window->visible = TRUE;
1248
1249   if (server->output)
1250     {
1251       broadway_output_show_surface (server->output, window->id);
1252       sent = TRUE;
1253     }
1254
1255   return sent;
1256 }
1257
1258 gboolean
1259 _gdk_broadway_server_window_hide (GdkBroadwayServer *server,
1260                                   gint id)
1261 {
1262   BroadwayWindow *window;
1263   gboolean sent = FALSE;
1264
1265   window = g_hash_table_lookup (server->id_ht,
1266                                 GINT_TO_POINTER (id));
1267   if (window == NULL)
1268     return FALSE;
1269
1270   window->visible = FALSE;
1271
1272   if (server->mouse_in_toplevel_id == id)
1273     {
1274       /* TODO: Send leave + enter event, update cursors, etc */
1275       server->mouse_in_toplevel_id = 0;
1276     }
1277
1278   if (server->output)
1279     {
1280       broadway_output_hide_surface (server->output, window->id);
1281       sent = TRUE;
1282     }
1283   return sent;
1284 }
1285
1286 void
1287 _gdk_broadway_server_window_set_transient_for (GdkBroadwayServer *server,
1288                                                gint id, gint parent)
1289 {
1290   BroadwayWindow *window;
1291
1292   window = g_hash_table_lookup (server->id_ht,
1293                                 GINT_TO_POINTER (id));
1294   if (window == NULL)
1295     return;
1296
1297   window->transient_for = parent;
1298
1299   if (server->output)
1300     {
1301       broadway_output_set_transient_for (server->output, window->id, window->transient_for);
1302       _gdk_broadway_server_flush (server);
1303     }
1304 }
1305
1306 gboolean
1307 _gdk_broadway_server_has_client (GdkBroadwayServer *server)
1308 {
1309   return server->output != NULL;
1310 }
1311
1312 static void
1313 _cairo_region (cairo_t         *cr,
1314                const cairo_region_t *region)
1315 {
1316   cairo_rectangle_int_t box;
1317   gint n_boxes, i;
1318
1319   g_return_if_fail (cr != NULL);
1320   g_return_if_fail (region != NULL);
1321
1322   n_boxes = cairo_region_num_rectangles (region);
1323
1324   for (i = 0; i < n_boxes; i++)
1325     {
1326       cairo_region_get_rectangle (region, i, &box);
1327       cairo_rectangle (cr, box.x, box.y, box.width, box.height);
1328     }
1329 }
1330
1331
1332 static void
1333 copy_region (cairo_surface_t *surface,
1334              cairo_region_t *area,
1335              gint            dx,
1336              gint            dy)
1337 {
1338   cairo_t *cr;
1339
1340   cr = cairo_create (surface);
1341   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
1342
1343   _cairo_region (cr, area);
1344   cairo_clip (cr);
1345
1346   /* NB: This is a self-copy and Cairo doesn't support that yet.
1347    * So we do a litle trick.
1348    */
1349   cairo_push_group (cr);
1350
1351   cairo_set_source_surface (cr, surface, dx, dy);
1352   cairo_paint (cr);
1353
1354   cairo_pop_group_to_source (cr);
1355   cairo_paint (cr);
1356
1357   cairo_destroy (cr);
1358 }
1359
1360 gboolean
1361 _gdk_broadway_server_window_translate (GdkBroadwayServer *server,
1362                                        gint id,
1363                                        cairo_region_t *area,
1364                                        gint            dx,
1365                                        gint            dy)
1366 {
1367   BroadwayWindow *window;
1368   gboolean sent = FALSE;
1369
1370   window = g_hash_table_lookup (server->id_ht,
1371                                 GINT_TO_POINTER (id));
1372   if (window == NULL)
1373     return FALSE;
1374
1375   if (window->last_synced &&
1376       server->output)
1377     {
1378       BroadwayRect *rects;
1379       cairo_rectangle_int_t rect;
1380       int i, n_rects;
1381
1382       copy_region (window->last_surface, area, dx, dy);
1383       n_rects = cairo_region_num_rectangles (area);
1384       rects = g_new (BroadwayRect, n_rects);
1385       for (i = 0; i < n_rects; i++)
1386         {
1387           cairo_region_get_rectangle (area, i, &rect);
1388           rects[i].x = rect.x;
1389           rects[i].y = rect.y;
1390           rects[i].width = rect.width;
1391           rects[i].height = rect.height;
1392         }
1393       broadway_output_copy_rectangles (server->output,
1394                                        window->id,
1395                                        rects, n_rects, dx, dy);
1396       g_free (rects);
1397       sent = TRUE;
1398     }
1399
1400   return sent;
1401 }
1402
1403 static void
1404 diff_surfaces (cairo_surface_t *surface,
1405                cairo_surface_t *old_surface)
1406 {
1407   guint8 *data, *old_data;
1408   guint32 *line, *old_line;
1409   int w, h, stride, old_stride;
1410   int x, y;
1411
1412   data = cairo_image_surface_get_data (surface);
1413   old_data = cairo_image_surface_get_data (old_surface);
1414
1415   w = cairo_image_surface_get_width (surface);
1416   h = cairo_image_surface_get_height (surface);
1417
1418   stride = cairo_image_surface_get_stride (surface);
1419   old_stride = cairo_image_surface_get_stride (old_surface);
1420
1421   for (y = 0; y < h; y++)
1422     {
1423       line = (guint32 *)data;
1424       old_line = (guint32 *)old_data;
1425
1426       for (x = 0; x < w; x++)
1427         {
1428           if ((*line & 0xffffff) == (*old_line & 0xffffff))
1429             *old_line = 0;
1430           else
1431             *old_line = *line | 0xff000000;
1432           line ++;
1433           old_line ++;
1434         }
1435
1436       data += stride;
1437       old_data += old_stride;
1438     }
1439 }
1440
1441 void
1442 _gdk_broadway_server_window_update (GdkBroadwayServer *server,
1443                                     gint id,
1444                                     cairo_surface_t *surface)
1445 {
1446   cairo_t *cr;
1447   BroadwayWindow *window;
1448
1449   if (surface == NULL)
1450     return;
1451
1452   window = g_hash_table_lookup (server->id_ht,
1453                                 GINT_TO_POINTER (id));
1454   if (window == NULL)
1455     return;
1456
1457   if (window->last_surface == NULL)
1458     window->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
1459                                                        window->width,
1460                                                        window->height);
1461
1462   if (server->output != NULL)
1463     {
1464       if (window->last_synced)
1465         {
1466           diff_surfaces (surface,
1467                          window->last_surface);
1468           broadway_output_put_rgba (server->output, window->id, 0, 0,
1469                                     cairo_image_surface_get_width (window->last_surface),
1470                                     cairo_image_surface_get_height (window->last_surface),
1471                                     cairo_image_surface_get_stride (window->last_surface),
1472                                     cairo_image_surface_get_data (window->last_surface));
1473         }
1474       else
1475         {
1476           window->last_synced = TRUE;
1477           broadway_output_put_rgb (server->output, window->id, 0, 0,
1478                                    cairo_image_surface_get_width (surface),
1479                                    cairo_image_surface_get_height (surface),
1480                                    cairo_image_surface_get_stride (surface),
1481                                    cairo_image_surface_get_data (surface));
1482         }
1483
1484       broadway_output_surface_flush (server->output, window->id);
1485     }
1486
1487   cr = cairo_create (window->last_surface);
1488   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
1489   cairo_set_source_surface (cr, surface, 0, 0);
1490   cairo_paint (cr);
1491   cairo_destroy (cr);
1492 }
1493
1494 gboolean
1495 _gdk_broadway_server_window_move_resize (GdkBroadwayServer *server,
1496                                          gint id,
1497                                          int x,
1498                                          int y,
1499                                          int width,
1500                                          int height)
1501 {
1502   BroadwayWindow *window;
1503   gboolean with_move, with_resize;
1504   gboolean sent = FALSE;
1505   cairo_t *cr;
1506
1507   window = g_hash_table_lookup (server->id_ht,
1508                                 GINT_TO_POINTER (id));
1509   if (window == NULL)
1510     return FALSE;
1511
1512   with_move = x != window->x || y != window->y;
1513   with_resize = width != window->width || height != window->height;
1514   window->x = x;
1515   window->y = y;
1516   window->width = width;
1517   window->height = height;
1518
1519   if (with_resize && window->last_surface != NULL)
1520     {
1521       cairo_surface_t *old;
1522
1523       old = window->last_surface;
1524
1525       window->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
1526                                                          width, height);
1527
1528
1529       cr = cairo_create (window->last_surface);
1530       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
1531       cairo_set_source_surface (cr, old, 0, 0);
1532       cairo_paint (cr);
1533       cairo_destroy (cr);
1534
1535       cairo_surface_destroy (old);
1536     }
1537
1538   if (server->output != NULL)
1539     {
1540       broadway_output_move_resize_surface (server->output,
1541                                            window->id,
1542                                            with_move, window->x, window->y,
1543                                            with_resize, window->width, window->height);
1544       sent = TRUE;
1545     }
1546
1547   return sent;
1548 }
1549
1550 GdkGrabStatus
1551 _gdk_broadway_server_grab_pointer (GdkBroadwayServer *server,
1552                                    gint id,
1553                                    gboolean owner_events,
1554                                    guint32 event_mask,
1555                                    guint32 time_)
1556 {
1557   if (server->pointer_grab_window_id != -1 &&
1558       time_ != 0 && server->pointer_grab_time > time_)
1559     return GDK_GRAB_ALREADY_GRABBED;
1560
1561   if (time_ == 0)
1562     time_ = server->last_seen_time;
1563
1564   server->pointer_grab_window_id = id;
1565   server->pointer_grab_owner_events = owner_events;
1566   server->pointer_grab_time = time_;
1567
1568   if (server->output)
1569     {
1570       broadway_output_grab_pointer (server->output,
1571                                     id,
1572                                     owner_events);
1573       _gdk_broadway_server_flush (server);
1574     }
1575
1576   /* TODO: What about toplevel grab events if we're not connected? */
1577
1578   return GDK_GRAB_SUCCESS;
1579 }
1580
1581 guint32
1582 _gdk_broadway_server_ungrab_pointer (GdkBroadwayServer *server,
1583                                      guint32    time_)
1584 {
1585   guint32 serial;
1586
1587   if (server->pointer_grab_window_id != -1 &&
1588       time_ != 0 && server->pointer_grab_time > time_)
1589     return 0;
1590
1591   /* TODO: What about toplevel grab events if we're not connected? */
1592
1593   if (server->output)
1594     {
1595       serial = broadway_output_ungrab_pointer (server->output);
1596       _gdk_broadway_server_flush (server);
1597     }
1598   else
1599     {
1600       serial = server->saved_serial;
1601     }
1602
1603   server->pointer_grab_window_id = -1;
1604
1605   return serial;
1606 }
1607
1608 guint32
1609 _gdk_broadway_server_new_window (GdkBroadwayServer *server,
1610                                  int x,
1611                                  int y,
1612                                  int width,
1613                                  int height,
1614                                  gboolean is_temp)
1615 {
1616   BroadwayWindow *window;
1617
1618   window = g_new0 (BroadwayWindow, 1);
1619   window->id = server->id_counter++;
1620   window->x = x;
1621   window->y = y;
1622   window->width = width;
1623   window->height = height;
1624   window->is_temp = is_temp;
1625
1626   g_hash_table_insert (server->id_ht,
1627                        GINT_TO_POINTER (window->id),
1628                        window);
1629
1630   server->toplevels = g_list_prepend (server->toplevels, window);
1631
1632   if (server->output)
1633     broadway_output_new_surface (server->output,
1634                                  window->id,
1635                                  window->x,
1636                                  window->y,
1637                                  window->width,
1638                                  window->height,
1639                                  window->is_temp);
1640
1641   return window->id;
1642 }
1643
1644 static void
1645 _gdk_broadway_server_resync_windows (GdkBroadwayServer *server)
1646 {
1647   GList *l;
1648
1649   if (server->output == NULL)
1650     return;
1651
1652   /* First create all windows */
1653   for (l = server->toplevels; l != NULL; l = l->next)
1654     {
1655       BroadwayWindow *window = l->data;
1656
1657       if (window->id == 0)
1658         continue; /* Skip root */
1659
1660       window->last_synced = FALSE;
1661       broadway_output_new_surface (server->output,
1662                                    window->id,
1663                                    window->x,
1664                                    window->y,
1665                                    window->width,
1666                                    window->height,
1667                                    window->is_temp);
1668     }
1669
1670   /* Then do everything that may reference other windows */
1671   for (l = server->toplevels; l != NULL; l = l->next)
1672     {
1673       BroadwayWindow *window = l->data;
1674
1675       if (window->id == 0)
1676         continue; /* Skip root */
1677
1678       if (window->transient_for != -1)
1679         broadway_output_set_transient_for (server->output, window->id, window->transient_for);
1680       if (window->visible)
1681         {
1682           broadway_output_show_surface (server->output, window->id);
1683
1684           if (window->last_surface != NULL)
1685             {
1686               window->last_synced = TRUE;
1687               broadway_output_put_rgb (server->output, window->id, 0, 0,
1688                                        cairo_image_surface_get_width (window->last_surface),
1689                                        cairo_image_surface_get_height (window->last_surface),
1690                                        cairo_image_surface_get_stride (window->last_surface),
1691                                        cairo_image_surface_get_data (window->last_surface));
1692             }
1693           broadway_output_surface_flush (server->output, window->id);
1694         }
1695     }
1696
1697   _gdk_broadway_server_flush (server);
1698 }