]> Pileus Git - ~andy/gtk/blob - gdk/broadway/gdkdisplay-broadway.c
GdkBroadwayDisplay: include proper port number in error trace
[~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, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include "config.h"
24
25 #include "gdkdisplay-broadway.h"
26
27 #include "gdkdisplay.h"
28 #include "gdkeventsource.h"
29 #include "gdkscreen.h"
30 #include "gdkscreen-broadway.h"
31 #include "gdkinternals.h"
32 #include "gdkdeviceprivate.h"
33 #include "gdkdevicemanager-broadway.h"
34
35 #include <glib.h>
36 #include <glib/gprintf.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
44 #include <netinet/tcp.h>
45
46 static void   gdk_broadway_display_dispose            (GObject            *object);
47 static void   gdk_broadway_display_finalize           (GObject            *object);
48
49 #if 0
50 #define DEBUG_WEBSOCKETS 1
51 #endif
52
53 G_DEFINE_TYPE (GdkBroadwayDisplay, gdk_broadway_display, GDK_TYPE_DISPLAY)
54
55 static void
56 gdk_broadway_display_init (GdkBroadwayDisplay *display)
57 {
58   _gdk_broadway_display_manager_add_display (gdk_display_manager_get (),
59                                              GDK_DISPLAY_OBJECT (display));
60   display->id_ht = g_hash_table_new (NULL, NULL);
61 }
62
63 static void
64 gdk_event_init (GdkDisplay *display)
65 {
66   GdkBroadwayDisplay *broadway_display;
67
68   broadway_display = GDK_BROADWAY_DISPLAY (display);
69   broadway_display->event_source = _gdk_broadway_event_source_new (display);
70   broadway_display->saved_serial = 1;
71   broadway_display->last_seen_time = 1;
72 }
73
74 static void
75 gdk_broadway_display_init_input (GdkDisplay *display)
76 {
77   GdkBroadwayDisplay *broadway_display;
78   GdkDeviceManager *device_manager;
79   GdkDevice *device;
80   GList *list, *l;
81
82   broadway_display = GDK_BROADWAY_DISPLAY (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       broadway_display->input_devices = g_list_prepend (broadway_display->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   broadway_display->input_devices = g_list_prepend (broadway_display->input_devices,
121                                                g_object_ref (display->core_pointer));
122
123   g_list_free (list);
124 }
125
126 typedef struct HttpRequest {
127   GdkDisplay *display;
128   GSocketConnection *connection;
129   GDataInputStream *data;
130   GString *request;
131 }  HttpRequest;
132
133 static void start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary);
134
135 static void
136 http_request_free (HttpRequest *request)
137 {
138   g_object_unref (request->connection);
139   g_object_unref (request->data);
140   g_string_free (request->request, TRUE);
141   g_free (request);
142 }
143
144 struct BroadwayInput {
145   GdkDisplay *display;
146   GSocketConnection *connection;
147   GByteArray *buffer;
148   GSource *source;
149   gboolean seen_time;
150   gint64 time_base;
151   gboolean proto_v7_plus;
152   gboolean binary;
153 };
154
155 static void
156 broadway_input_free (BroadwayInput *input)
157 {
158   g_object_unref (input->connection);
159   g_byte_array_free (input->buffer, FALSE);
160   g_source_destroy (input->source);
161   g_free (input);
162 }
163
164 static void
165 process_input_messages (GdkBroadwayDisplay *broadway_display)
166 {
167   BroadwayInputMsg *message;
168
169   while (broadway_display->input_messages)
170     {
171       message = broadway_display->input_messages->data;
172       broadway_display->input_messages =
173         g_list_delete_link (broadway_display->input_messages,
174                             broadway_display->input_messages);
175
176       _gdk_broadway_events_got_input (GDK_DISPLAY (broadway_display), message);
177       g_free (message);
178     }
179 }
180
181 static char *
182 parse_pointer_data (char *p, BroadwayInputPointerMsg *data)
183 {
184   data->mouse_window_id = strtol (p, &p, 10);
185   p++; /* Skip , */
186   data->event_window_id = strtol (p, &p, 10);
187   p++; /* Skip , */
188   data->root_x = strtol (p, &p, 10);
189   p++; /* Skip , */
190   data->root_y = strtol (p, &p, 10);
191   p++; /* Skip , */
192   data->win_x = strtol (p, &p, 10);
193   p++; /* Skip , */
194   data->win_y = strtol (p, &p, 10);
195   p++; /* Skip , */
196   data->state = strtol (p, &p, 10);
197
198   return p;
199 }
200
201 static void
202 update_future_pointer_info (GdkBroadwayDisplay *broadway_display, BroadwayInputPointerMsg *data)
203 {
204   broadway_display->future_root_x = data->root_x;
205   broadway_display->future_root_y = data->root_y;
206   broadway_display->future_state = data->state;
207   broadway_display->future_mouse_in_toplevel = data->mouse_window_id;
208 }
209
210 static void
211 parse_input_message (BroadwayInput *input, const char *message)
212 {
213   GdkBroadwayDisplay *broadway_display;
214   BroadwayInputMsg msg;
215   char *p;
216   gint64 time_;
217
218   broadway_display = GDK_BROADWAY_DISPLAY (input->display);
219
220   p = (char *)message;
221   msg.base.type = *p++;
222   msg.base.serial = (guint32)strtol (p, &p, 10);
223   p++; /* Skip , */
224   time_ = strtol(p, &p, 10);
225   p++; /* Skip , */
226
227   if (time_ == 0) {
228     time_ = broadway_display->last_seen_time;
229   } else {
230     if (!input->seen_time) {
231       input->seen_time = TRUE;
232       /* Calculate time base so that any following times are normalized to start
233          5 seconds after last_seen_time, to avoid issues that could appear when
234          a long hiatus due to a reconnect seems to be instant */
235       input->time_base = time_ - (broadway_display->last_seen_time + 5000);
236     }
237     time_ = time_ - input->time_base;
238   }
239
240   broadway_display->last_seen_time = time_;
241
242   msg.base.time = time_;
243
244   switch (msg.base.type) {
245   case 'e': /* Enter */
246   case 'l': /* Leave */
247     p = parse_pointer_data (p, &msg.pointer);
248     update_future_pointer_info (broadway_display, &msg.pointer);
249     p++; /* Skip , */
250     msg.crossing.mode = strtol(p, &p, 10);
251     break;
252
253   case 'm': /* Mouse move */
254     p = parse_pointer_data (p, &msg.pointer);
255     update_future_pointer_info (broadway_display, &msg.pointer);
256     break;
257
258   case 'b':
259   case 'B':
260     p = parse_pointer_data (p, &msg.pointer);
261     update_future_pointer_info (broadway_display, &msg.pointer);
262     p++; /* Skip , */
263     msg.button.button = strtol(p, &p, 10);
264     break;
265
266   case 's':
267     p = parse_pointer_data (p, &msg.pointer);
268     update_future_pointer_info (broadway_display, &msg.pointer);
269     p++; /* Skip , */
270     msg.scroll.dir = strtol(p, &p, 10);
271     break;
272
273   case 'k':
274   case 'K':
275     msg.key.key = strtol(p, &p, 10);
276     p++; /* Skip , */
277     msg.key.state = strtol(p, &p, 10);
278     break;
279
280   case 'g':
281   case 'u':
282     msg.grab_reply.res = strtol(p, &p, 10);
283     break;
284
285   case 'w':
286     msg.configure_notify.id = strtol(p, &p, 10);
287     p++; /* Skip , */
288     msg.configure_notify.x = strtol (p, &p, 10);
289     p++; /* Skip , */
290     msg.configure_notify.y = strtol (p, &p, 10);
291     p++; /* Skip , */
292     msg.configure_notify.width = strtol (p, &p, 10);
293     p++; /* Skip , */
294     msg.configure_notify.height = strtol (p, &p, 10);
295     break;
296
297   case 'W':
298     msg.delete_notify.id = strtol(p, &p, 10);
299     break;
300
301   case 'd':
302     msg.screen_resize_notify.width = strtol (p, &p, 10);
303     p++; /* Skip , */
304     msg.screen_resize_notify.height = strtol (p, &p, 10);
305     break;
306
307   default:
308     g_printerr ("Unknown input command %s\n", message);
309     break;
310   }
311
312   broadway_display->input_messages = g_list_append (broadway_display->input_messages, g_memdup (&msg, sizeof (msg)));
313
314 }
315
316 static inline void
317 hex_dump (guchar *data, gsize len)
318 {
319 #ifdef DEBUG_WEBSOCKETS
320   gsize i, j;
321   for (j = 0; j < len + 15; j += 16)
322     {
323       fprintf (stderr, "0x%.4x  ", j);
324       for (i = 0; i < 16; i++)
325         {
326             if ((j + i) < len)
327               fprintf (stderr, "%.2x ", data[j+i]);
328             else
329               fprintf (stderr, "  ");
330             if (i == 8)
331               fprintf (stderr, " ");
332         }
333       fprintf (stderr, " | ");
334
335       for (i = 0; i < 16; i++)
336         if ((j + i) < len && g_ascii_isalnum(data[j+i]))
337           fprintf (stderr, "%c", data[j+i]);
338         else
339           fprintf (stderr, ".");
340       fprintf (stderr, "\n");
341     }
342 #endif
343 }
344
345 static void
346 parse_input (BroadwayInput *input)
347 {
348   GdkBroadwayDisplay *broadway_display;
349
350   broadway_display = GDK_BROADWAY_DISPLAY (input->display);
351
352   if (!input->buffer->len)
353     return;
354
355   if (input->proto_v7_plus)
356     {
357       hex_dump (input->buffer->data, input->buffer->len);
358
359       while (input->buffer->len > 2)
360         {
361           gsize len, payload_len;
362           BroadwayWSOpCode code;
363           gboolean is_mask, fin;
364           guchar *buf, *data, *mask;
365
366           buf = input->buffer->data;
367           len = input->buffer->len;
368
369 #ifdef DEBUG_WEBSOCKETS
370           g_print ("Parse input first byte 0x%2x 0x%2x\n", buf[0], buf[1]);
371 #endif
372
373           fin = buf[0] & 0x80;
374           code = buf[0] & 0x0f;
375           payload_len = buf[1] & 0x7f;
376           is_mask = buf[1] & 0x80;
377           data = buf + 2;
378
379           if (payload_len > 125)
380             {
381               if (len < 4)
382                 return;
383               payload_len = GUINT16_FROM_BE( *(guint16 *) data );
384               data += 2;
385             }
386           else if (payload_len > 126)
387             {
388               if (len < 10)
389                 return;
390               payload_len = GUINT64_FROM_BE( *(guint64 *) data );
391               data += 8;
392             }
393
394           mask = NULL;
395           if (is_mask)
396             {
397               if (data - buf + 4 > len)
398                 return;
399               mask = data;
400               data += 4;
401             }
402
403           if (data - buf + payload_len > len)
404             return; /* wait to accumulate more */
405
406           if (is_mask)
407             {
408               gsize i;
409               for (i = 0; i < payload_len; i++)
410                 data[i] ^= mask[i%4];
411             }
412
413           switch (code) {
414           case BROADWAY_WS_CNX_CLOSE:
415             break; /* hang around anyway */
416           case BROADWAY_WS_TEXT:
417             if (!fin)
418               {
419 #ifdef DEBUG_WEBSOCKETS
420                 g_warning ("can't yet accept fragmented input");
421 #endif
422               }
423             else
424               {
425                 char *terminated = g_strndup((char *)data, payload_len);
426                 parse_input_message (input, terminated);
427                 g_free (terminated);
428               }
429             break;
430           case BROADWAY_WS_CNX_PING:
431             broadway_output_pong (broadway_display->output);
432             break;
433           case BROADWAY_WS_CNX_PONG:
434             break; /* we never send pings, but tolerate pongs */
435           case BROADWAY_WS_BINARY:
436           case BROADWAY_WS_CONTINUATION:
437           default:
438             {
439               g_warning ("fragmented or unknown input code 0x%2x with fin set", code);
440               break;
441             }
442           }
443
444           g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len);
445         }
446     }
447   else /* old style protocol */
448     {
449       char *buf, *ptr;
450       gsize len;
451
452       buf = (char *)input->buffer->data;
453       len = input->buffer->len;
454
455       if (buf[0] != 0)
456         {
457           broadway_display->input = NULL;
458           broadway_input_free (input);
459           return;
460         }
461
462       while ((ptr = memchr (buf, 0xff, len)) != NULL)
463         {
464           *ptr = 0;
465           ptr++;
466
467           parse_input_message (input, buf + 1);
468
469           len -= ptr - buf;
470           buf = ptr;
471
472           if (len > 0 && buf[0] != 0)
473             {
474               broadway_display->input = NULL;
475               broadway_input_free (input);
476               break;
477             }
478         }
479       g_byte_array_remove_range (input->buffer, 0, buf - (char *)input->buffer->data);
480     }
481 }
482
483
484 static gboolean
485 process_input_idle_cb (GdkBroadwayDisplay *display)
486 {
487   display->process_input_idle = 0;
488   process_input_messages (display);
489   return G_SOURCE_REMOVE;
490 }
491
492 static void
493 queue_process_input_at_idle (GdkBroadwayDisplay *broadway_display)
494 {
495   if (broadway_display->process_input_idle == 0)
496     broadway_display->process_input_idle =
497       g_idle_add_full (GDK_PRIORITY_EVENTS, (GSourceFunc)process_input_idle_cb, broadway_display, NULL);
498 }
499
500 static void
501 _gdk_broadway_display_read_all_input_nonblocking (GdkDisplay *display)
502 {
503   GdkBroadwayDisplay *broadway_display;
504   GInputStream *in;
505   gssize res;
506   guint8 buffer[1024];
507   GError *error;
508   BroadwayInput *input;
509
510   broadway_display = GDK_BROADWAY_DISPLAY (display);
511   if (broadway_display->input == NULL)
512     return;
513
514   input = broadway_display->input;
515
516   in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
517
518   error = NULL;
519   res = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (in),
520                                                   buffer, sizeof (buffer), NULL, &error);
521
522   if (res <= 0)
523     {
524       if (res < 0 &&
525           g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
526         {
527           g_error_free (error);
528           return;
529         }
530
531       broadway_display->input = NULL;
532       broadway_input_free (input);
533       if (res < 0)
534         {
535           g_print ("input error %s\n", error->message);
536           g_error_free (error);
537         }
538       return;
539     }
540
541   g_byte_array_append (input->buffer, buffer, res);
542
543   parse_input (input);
544 }
545
546 void
547 _gdk_broadway_display_consume_all_input (GdkDisplay *display)
548 {
549   GdkBroadwayDisplay *broadway_display;
550
551   broadway_display = GDK_BROADWAY_DISPLAY (display);
552   _gdk_broadway_display_read_all_input_nonblocking (display);
553
554   /* Since we're parsing input but not processing the resulting messages
555      we might not get a readable callback on the stream, so queue an idle to
556      process the messages */
557   queue_process_input_at_idle (broadway_display);
558 }
559
560
561 static gboolean
562 input_data_cb (GObject  *stream,
563                BroadwayInput *input)
564 {
565   GdkBroadwayDisplay *broadway_display;
566
567   broadway_display = GDK_BROADWAY_DISPLAY (input->display);
568   _gdk_broadway_display_read_all_input_nonblocking (input->display);
569
570   process_input_messages (broadway_display);
571
572   return TRUE;
573 }
574
575 /* Note: This may be called while handling a message (i.e. sorta recursively) */
576 BroadwayInputMsg *
577 _gdk_broadway_display_block_for_input (GdkDisplay *display, char op,
578                                        guint32 serial, gboolean remove_message)
579 {
580   GdkBroadwayDisplay *broadway_display;
581   BroadwayInputMsg *message;
582   gssize res;
583   guint8 buffer[1024];
584   BroadwayInput *input;
585   GInputStream *in;
586   GList *l;
587
588   gdk_display_flush (display);
589
590   broadway_display = GDK_BROADWAY_DISPLAY (display);
591   if (broadway_display->input == NULL)
592     return NULL;
593
594   input = broadway_display->input;
595
596   while (TRUE) {
597     /* Check for existing reply in queue */
598
599     for (l = broadway_display->input_messages; l != NULL; l = l->next)
600       {
601         message = l->data;
602
603         if (message->base.type == op)
604           {
605             if (message->base.serial == serial)
606               {
607                 if (remove_message)
608                   broadway_display->input_messages =
609                     g_list_delete_link (broadway_display->input_messages, l);
610                 return message;
611               }
612           }
613       }
614
615     /* Not found, read more, blocking */
616
617     in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
618     res = g_input_stream_read (in, buffer, sizeof (buffer), NULL, NULL);
619     if (res <= 0)
620       return NULL;
621     g_byte_array_append (input->buffer, buffer, res);
622
623     parse_input (input);
624
625     /* Since we're parsing input but not processing the resulting messages
626        we might not get a readable callback on the stream, so queue an idle to
627        process the messages */
628     queue_process_input_at_idle (broadway_display);
629   }
630 }
631
632 static char *
633 parse_line (char *line, char *key)
634 {
635   char *p;
636
637   if (!g_str_has_prefix (line, key))
638     return NULL;
639   p = line + strlen (key);
640   if (*p != ':')
641     return NULL;
642   p++;
643   /* Skip optional initial space */
644   if (*p == ' ')
645     p++;
646   return p;
647 }
648 static void
649 send_error (HttpRequest *request,
650             int error_code,
651             const char *reason)
652 {
653   char *res;
654
655   res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n"
656                          "<html><head><title>%d %s</title></head>"
657                          "<body>%s</body></html>",
658                          error_code, reason,
659                          error_code, reason,
660                          reason);
661   /* TODO: This should really be async */
662   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
663                              res, strlen (res), NULL, NULL, NULL);
664   g_free (res);
665   http_request_free (request);
666 }
667
668 /* magic from: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 */
669 #define SEC_WEB_SOCKET_KEY_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
670
671 /* 'x3JJHMbDL1EzLkh9GBhXDw==' generates 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=' */
672 static gchar *
673 generate_handshake_response_wsietf_v7 (const gchar *key)
674 {
675   gsize digest_len = 20;
676   guchar digest[digest_len];
677   GChecksum *checksum;
678
679   checksum = g_checksum_new (G_CHECKSUM_SHA1);
680   if (!checksum)
681     return NULL;
682
683   g_checksum_update (checksum, (guchar *)key, -1);
684   g_checksum_update (checksum, (guchar *)SEC_WEB_SOCKET_KEY_MAGIC, -1);
685
686   g_checksum_get_digest (checksum, digest, &digest_len);
687   g_checksum_free (checksum);
688
689   g_assert (digest_len == 20);
690
691   return g_base64_encode (digest, digest_len);
692 }
693
694 static void
695 start_input (HttpRequest *request, gboolean binary)
696 {
697   char **lines;
698   char *p;
699   int num_key1, num_key2;
700   guint64 key1, key2;
701   int num_space;
702   int i;
703   guint8 challenge[16];
704   char *res;
705   gsize len;
706   GChecksum *checksum;
707   char *origin, *host;
708   GdkBroadwayDisplay *broadway_display;
709   BroadwayInput *input;
710   const void *data_buffer;
711   gsize data_buffer_size;
712   GInputStream *in;
713   char *key_v7;
714   gboolean proto_v7_plus;
715
716   broadway_display = GDK_BROADWAY_DISPLAY (request->display);
717
718   if (broadway_display->input != NULL)
719     {
720       send_error (request, 409, "Input already handled");
721       return;
722     }
723
724 #ifdef DEBUG_WEBSOCKETS
725   g_print ("incoming request:\n%s\n", request->request->str);
726 #endif
727   lines = g_strsplit (request->request->str, "\n", 0);
728
729   num_key1 = 0;
730   num_key2 = 0;
731   key1 = 0;
732   key2 = 0;
733   key_v7 = NULL;
734   origin = NULL;
735   host = NULL;
736   for (i = 0; lines[i] != NULL; i++)
737     {
738       if ((p = parse_line (lines[i], "Sec-WebSocket-Key1")))
739         {
740           num_space = 0;
741           while (*p != 0)
742             {
743               if (g_ascii_isdigit (*p))
744                 key1 = key1 * 10 + g_ascii_digit_value (*p);
745               else if (*p == ' ')
746                 num_space++;
747
748               p++;
749             }
750           key1 /= num_space;
751           num_key1++;
752         }
753       else if ((p = parse_line (lines[i], "Sec-WebSocket-Key2")))
754         {
755           num_space = 0;
756           while (*p != 0)
757             {
758               if (g_ascii_isdigit (*p))
759                 key2 = key2 * 10 + g_ascii_digit_value (*p);
760               else if (*p == ' ')
761                 num_space++;
762
763               p++;
764             }
765           key2 /= num_space;
766           num_key2++;
767         }
768       else if ((p = parse_line (lines[i], "Sec-WebSocket-Key")))
769         {
770           key_v7 = p;
771         }
772       else if ((p = parse_line (lines[i], "Origin")))
773         {
774           origin = p;
775         }
776       else if ((p = parse_line (lines[i], "Host")))
777         {
778           host = p;
779         }
780       else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin")))
781         {
782           origin = p;
783         }
784     }
785
786   if (origin == NULL || host == NULL)
787     {
788       g_strfreev (lines);
789       send_error (request, 400, "Bad websocket request");
790       return;
791     }
792
793   if (key_v7 != NULL)
794     {
795       char* accept = generate_handshake_response_wsietf_v7 (key_v7);
796       res = g_strdup_printf ("HTTP/1.1 101 Switching Protocols\r\n"
797                              "Upgrade: websocket\r\n"
798                              "Connection: Upgrade\r\n"
799                              "Sec-WebSocket-Accept: %s\r\n"
800                              "Sec-WebSocket-Origin: %s\r\n"
801                              "Sec-WebSocket-Location: ws://%s/socket\r\n"
802                              "Sec-WebSocket-Protocol: broadway\r\n"
803                              "\r\n", accept, origin, host);
804       g_free (accept);
805
806 #ifdef DEBUG_WEBSOCKETS
807       g_print ("v7 proto response:\n%s", res);
808 #endif
809
810       g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
811                                  res, strlen (res), NULL, NULL, NULL);
812       g_free (res);
813       proto_v7_plus = TRUE;
814     }
815   else
816     {
817       if (num_key1 != 1 || num_key2 != 1)
818         {
819           g_strfreev (lines);
820           send_error (request, 400, "Bad websocket request");
821           return;
822         }
823
824       challenge[0] = (key1 >> 24) & 0xff;
825       challenge[1] = (key1 >> 16) & 0xff;
826       challenge[2] = (key1 >>  8) & 0xff;
827       challenge[3] = (key1 >>  0) & 0xff;
828       challenge[4] = (key2 >> 24) & 0xff;
829       challenge[5] = (key2 >> 16) & 0xff;
830       challenge[6] = (key2 >>  8) & 0xff;
831       challenge[7] = (key2 >>  0) & 0xff;
832
833       if (!g_input_stream_read_all (G_INPUT_STREAM (request->data), challenge+8, 8, NULL, NULL, NULL))
834         {
835           g_strfreev (lines);
836           send_error (request, 400, "Bad websocket request");
837           return;
838         }
839
840       checksum = g_checksum_new (G_CHECKSUM_MD5);
841       g_checksum_update (checksum, challenge, 16);
842       len = 16;
843       g_checksum_get_digest (checksum, challenge, &len);
844       g_checksum_free (checksum);
845
846       res = g_strdup_printf ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
847                              "Upgrade: WebSocket\r\n"
848                              "Connection: Upgrade\r\n"
849                              "Sec-WebSocket-Origin: %s\r\n"
850                              "Sec-WebSocket-Location: ws://%s/socket\r\n"
851                              "Sec-WebSocket-Protocol: broadway\r\n"
852                              "\r\n",
853                              origin, host);
854
855 #ifdef DEBUG_WEBSOCKETS
856       g_print ("legacy response:\n%s", res);
857 #endif
858       g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
859                                  res, strlen (res), NULL, NULL, NULL);
860       g_free (res);
861       g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
862                                  challenge, 16, NULL, NULL, NULL);
863       proto_v7_plus = FALSE;
864     }
865
866   input = g_new0 (BroadwayInput, 1);
867
868   input->display = request->display;
869   input->connection = g_object_ref (request->connection);
870   input->proto_v7_plus = proto_v7_plus;
871   input->binary = binary;
872
873   data_buffer = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (request->data), &data_buffer_size);
874   input->buffer = g_byte_array_sized_new (data_buffer_size);
875   g_byte_array_append (input->buffer, data_buffer, data_buffer_size);
876
877   broadway_display->input = input;
878
879   start_output (request, proto_v7_plus, binary);
880
881   /* This will free and close the data input stream, but we got all the buffered content already */
882   http_request_free (request);
883
884   in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
885   input->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (in), NULL);
886   g_source_set_callback (input->source, (GSourceFunc)input_data_cb, input, NULL);
887   g_source_attach (input->source, NULL);
888
889   /* Process any data in the pipe already */
890   parse_input (input);
891   process_input_messages (broadway_display);
892
893   g_strfreev (lines);
894 }
895
896 static void
897 start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary)
898 {
899   GSocket *socket;
900   GdkBroadwayDisplay *broadway_display;
901   int flag = 1;
902
903   socket = g_socket_connection_get_socket (request->connection);
904   setsockopt(g_socket_get_fd (socket), IPPROTO_TCP,
905              TCP_NODELAY, (char *) &flag, sizeof(int));
906
907   broadway_display = GDK_BROADWAY_DISPLAY (request->display);
908
909   if (broadway_display->output)
910     {
911       broadway_display->saved_serial = broadway_output_get_next_serial (broadway_display->output);
912       broadway_output_free (broadway_display->output);
913     }
914
915   broadway_display->output =
916     broadway_output_new (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
917                          broadway_display->saved_serial, proto_v7_plus, binary);
918
919   _gdk_broadway_resync_windows ();
920
921   if (broadway_display->pointer_grab_window)
922     broadway_output_grab_pointer (broadway_display->output,
923                                   GDK_WINDOW_IMPL_BROADWAY (broadway_display->pointer_grab_window->impl)->id,
924                                   broadway_display->pointer_grab_owner_events);
925 }
926
927 static void
928 send_data (HttpRequest *request,
929              const char *mimetype,
930              const char *data, gsize len)
931 {
932   char *res;
933
934   res = g_strdup_printf ("HTTP/1.0 200 OK\r\n"
935                          "Content-Type: %s\r\n"
936                          "Content-Length: %"G_GSIZE_FORMAT"\r\n"
937                          "\r\n",
938                          mimetype, len);
939   /* TODO: This should really be async */
940   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
941                              res, strlen (res), NULL, NULL, NULL);
942   g_free (res);
943   g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
944                              data, len, NULL, NULL, NULL);
945   http_request_free (request);
946 }
947
948 #include "clienthtml.h"
949 #include "broadwayjs.h"
950
951 static void
952 got_request (HttpRequest *request)
953 {
954   char *start, *escaped, *tmp, *version, *query;
955
956   if (!g_str_has_prefix (request->request->str, "GET "))
957     {
958       send_error (request, 501, "Only GET implemented");
959       return;
960     }
961
962   start = request->request->str + 4; /* Skip "GET " */
963
964   while (*start == ' ')
965     start++;
966
967   for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++)
968     ;
969   escaped = g_strndup (start, tmp - start);
970   version = NULL;
971   if (*tmp == ' ')
972     {
973       start = tmp;
974       while (*start == ' ')
975         start++;
976       for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++)
977         ;
978       version = g_strndup (start, tmp - start);
979     }
980
981   query = strchr (escaped, '?');
982   if (query)
983     *query = 0;
984
985   if (strcmp (escaped, "/client.html") == 0 || strcmp (escaped, "/") == 0)
986     send_data (request, "text/html", client_html, G_N_ELEMENTS(client_html) - 1);
987   else if (strcmp (escaped, "/broadway.js") == 0)
988     send_data (request, "text/javascript", broadway_js, G_N_ELEMENTS(broadway_js) - 1);
989   else if (strcmp (escaped, "/socket") == 0)
990     start_input (request, FALSE);
991   else if (strcmp (escaped, "/socket-bin") == 0)
992     start_input (request, TRUE);
993   else
994     send_error (request, 404, "File not found");
995
996   g_free (escaped);
997   g_free (version);
998 }
999
1000 static void
1001 got_http_request_line (GInputStream *stream,
1002                        GAsyncResult *result,
1003                        HttpRequest *request)
1004 {
1005   char *line;
1006
1007   line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (stream), result, NULL, NULL);
1008   if (line == NULL)
1009     {
1010       http_request_free (request);
1011       g_printerr ("Error reading request lines\n");
1012       return;
1013     }
1014   if (strlen (line) == 0)
1015     got_request (request);
1016   else
1017     {
1018       /* Protect against overflow in request length */
1019       if (request->request->len > 1024 * 5)
1020         {
1021           send_error (request, 400, "Request too long");
1022         }
1023       else
1024         {
1025           g_string_append_printf (request->request, "%s\n", line);
1026           g_data_input_stream_read_line_async (request->data, 0, NULL,
1027                                                (GAsyncReadyCallback)got_http_request_line, request);
1028         }
1029     }
1030   g_free (line);
1031 }
1032
1033 static gboolean
1034 handle_incoming_connection (GSocketService    *service,
1035                             GSocketConnection *connection,
1036                             GObject           *source_object)
1037 {
1038   HttpRequest *request;
1039   GInputStream *in;
1040
1041   request = g_new0 (HttpRequest, 1);
1042   request->connection = g_object_ref (connection);
1043   request->display = (GdkDisplay *) source_object;
1044   request->request = g_string_new ("");
1045
1046   in = g_io_stream_get_input_stream (G_IO_STREAM (connection));
1047
1048   request->data = g_data_input_stream_new (in);
1049   g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (request->data), FALSE);
1050   /* Be tolerant of input */
1051   g_data_input_stream_set_newline_type (request->data, G_DATA_STREAM_NEWLINE_TYPE_ANY);
1052
1053   g_data_input_stream_read_line_async (request->data, 0, NULL,
1054                                        (GAsyncReadyCallback)got_http_request_line, request);
1055   return TRUE;
1056 }
1057
1058 GdkDisplay *
1059 _gdk_broadway_display_open (const gchar *display_name)
1060 {
1061   GdkDisplay *display;
1062   GdkBroadwayDisplay *broadway_display;
1063   GError *error = NULL;
1064   int port;
1065
1066   display = g_object_new (GDK_TYPE_BROADWAY_DISPLAY, NULL);
1067   broadway_display = GDK_BROADWAY_DISPLAY (display);
1068
1069   broadway_display->output = NULL;
1070
1071   /* initialize the display's screens */
1072   broadway_display->screens = g_new (GdkScreen *, 1);
1073   broadway_display->screens[0] = _gdk_broadway_screen_new (display, 0);
1074
1075   /* We need to initialize events after we have the screen
1076    * structures in places
1077    */
1078   _gdk_broadway_screen_events_init (broadway_display->screens[0]);
1079
1080   /*set the default screen */
1081   broadway_display->default_screen = broadway_display->screens[0];
1082
1083   display->device_manager = _gdk_broadway_device_manager_new (display);
1084
1085   gdk_event_init (display);
1086
1087   gdk_broadway_display_init_input (display);
1088   _gdk_broadway_display_init_dnd (display);
1089
1090   _gdk_broadway_screen_setup (broadway_display->screens[0]);
1091
1092   if (display_name == NULL)
1093     display_name = g_getenv ("BROADWAY_DISPLAY");
1094
1095   port = 0;
1096   if (display_name != NULL)
1097     port = strtol(display_name, NULL, 10);
1098   if (port == 0)
1099     port = 8080;
1100
1101   broadway_display->service = g_socket_service_new ();
1102   if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (broadway_display->service),
1103                                         port,
1104                                         G_OBJECT (display),
1105                                         &error))
1106     {
1107       g_printerr ("Unable to listen to port %d: %s\n", port, error->message);
1108       g_error_free (error);
1109       return NULL;
1110     }
1111   g_signal_connect (broadway_display->service, "incoming", G_CALLBACK (handle_incoming_connection), NULL);
1112
1113   g_signal_emit_by_name (display, "opened");
1114   g_signal_emit_by_name (gdk_display_manager_get (), "display-opened", display);
1115
1116   return display;
1117 }
1118
1119
1120 static const gchar *
1121 gdk_broadway_display_get_name (GdkDisplay *display)
1122 {
1123   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1124
1125   return (gchar *) "Broadway";
1126 }
1127
1128 static gint
1129 gdk_broadway_display_get_n_screens (GdkDisplay *display)
1130 {
1131   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
1132
1133   return 1;
1134 }
1135
1136 static GdkScreen *
1137 gdk_broadway_display_get_screen (GdkDisplay *display,
1138                                  gint        screen_num)
1139 {
1140   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1141   g_return_val_if_fail (screen_num == 0, NULL);
1142
1143   return GDK_BROADWAY_DISPLAY (display)->screens[screen_num];
1144 }
1145
1146 static GdkScreen *
1147 gdk_broadway_display_get_default_screen (GdkDisplay *display)
1148 {
1149   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1150
1151   return GDK_BROADWAY_DISPLAY (display)->default_screen;
1152 }
1153
1154 static void
1155 gdk_broadway_display_beep (GdkDisplay *display)
1156 {
1157   g_return_if_fail (GDK_IS_DISPLAY (display));
1158 }
1159
1160 static void
1161 gdk_broadway_display_sync (GdkDisplay *display)
1162 {
1163   g_return_if_fail (GDK_IS_DISPLAY (display));
1164
1165 }
1166
1167 static void
1168 gdk_broadway_display_flush (GdkDisplay *display)
1169 {
1170   GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (display);
1171
1172   g_return_if_fail (GDK_IS_DISPLAY (display));
1173
1174   if (broadway_display->output &&
1175       !broadway_output_flush (broadway_display->output))
1176     {
1177       broadway_display->saved_serial = broadway_output_get_next_serial (broadway_display->output);
1178       broadway_output_free (broadway_display->output);
1179       broadway_display->output = NULL;
1180     }
1181 }
1182
1183 static gboolean
1184 gdk_broadway_display_has_pending (GdkDisplay *display)
1185 {
1186   return FALSE;
1187 }
1188
1189 static GdkWindow *
1190 gdk_broadway_display_get_default_group (GdkDisplay *display)
1191 {
1192   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1193
1194   return NULL;
1195 }
1196
1197 static void
1198 gdk_broadway_display_dispose (GObject *object)
1199 {
1200   GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (object);
1201
1202   _gdk_broadway_display_manager_remove_display (gdk_display_manager_get (),
1203                                                 GDK_DISPLAY_OBJECT (object));
1204
1205   g_list_foreach (broadway_display->input_devices, (GFunc) g_object_run_dispose, NULL);
1206
1207   _gdk_screen_close (broadway_display->screens[0]);
1208
1209   if (broadway_display->event_source)
1210     {
1211       g_source_destroy (broadway_display->event_source);
1212       g_source_unref (broadway_display->event_source);
1213       broadway_display->event_source = NULL;
1214     }
1215
1216   G_OBJECT_CLASS (gdk_broadway_display_parent_class)->dispose (object);
1217 }
1218
1219 static void
1220 gdk_broadway_display_finalize (GObject *object)
1221 {
1222   GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (object);
1223
1224   /* Keymap */
1225   if (broadway_display->keymap)
1226     g_object_unref (broadway_display->keymap);
1227
1228   _gdk_broadway_cursor_display_finalize (GDK_DISPLAY_OBJECT(broadway_display));
1229
1230   /* input GdkDevice list */
1231   g_list_free_full (broadway_display->input_devices, g_object_unref);
1232   /* Free all GdkScreens */
1233   g_object_unref (broadway_display->screens[0]);
1234   g_free (broadway_display->screens);
1235
1236   G_OBJECT_CLASS (gdk_broadway_display_parent_class)->finalize (object);
1237 }
1238
1239 void
1240 _gdk_broadway_display_make_default (GdkDisplay *display)
1241 {
1242 }
1243
1244 static void
1245 gdk_broadway_display_notify_startup_complete (GdkDisplay  *display,
1246                                               const gchar *startup_id)
1247 {
1248 }
1249
1250 static gboolean
1251 gdk_broadway_display_supports_selection_notification (GdkDisplay *display)
1252 {
1253   return FALSE;
1254 }
1255
1256 static gboolean
1257 gdk_broadway_display_request_selection_notification (GdkDisplay *display,
1258                                                      GdkAtom     selection)
1259
1260 {
1261     return FALSE;
1262 }
1263
1264 static gboolean
1265 gdk_broadway_display_supports_clipboard_persistence (GdkDisplay *display)
1266 {
1267   return FALSE;
1268 }
1269
1270 static void
1271 gdk_broadway_display_store_clipboard (GdkDisplay    *display,
1272                                       GdkWindow     *clipboard_window,
1273                                       guint32        time_,
1274                                       const GdkAtom *targets,
1275                                       gint           n_targets)
1276 {
1277 }
1278
1279 static gboolean
1280 gdk_broadway_display_supports_shapes (GdkDisplay *display)
1281 {
1282   return FALSE;
1283 }
1284
1285 static gboolean
1286 gdk_broadway_display_supports_input_shapes (GdkDisplay *display)
1287 {
1288   return FALSE;
1289 }
1290
1291 static gboolean
1292 gdk_broadway_display_supports_composite (GdkDisplay *display)
1293 {
1294   return FALSE;
1295 }
1296
1297 static GList *
1298 gdk_broadway_display_list_devices (GdkDisplay *display)
1299 {
1300   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
1301
1302   return GDK_BROADWAY_DISPLAY (display)->input_devices;
1303 }
1304
1305 static gulong
1306 gdk_broadway_display_get_next_serial (GdkDisplay *display)
1307 {
1308   GdkBroadwayDisplay *broadway_display;
1309   broadway_display = GDK_BROADWAY_DISPLAY (display);
1310   if (broadway_display->output)
1311     return broadway_output_get_next_serial (broadway_display->output);
1312   return broadway_display->saved_serial;
1313 }
1314
1315
1316 static void
1317 gdk_broadway_display_event_data_copy (GdkDisplay    *display,
1318                                       const GdkEvent *src,
1319                                       GdkEvent       *dst)
1320 {
1321 }
1322
1323 static void
1324 gdk_broadway_display_event_data_free (GdkDisplay    *display,
1325                                       GdkEvent *event)
1326 {
1327 }
1328
1329 static void
1330 gdk_broadway_display_class_init (GdkBroadwayDisplayClass * class)
1331 {
1332   GObjectClass *object_class = G_OBJECT_CLASS (class);
1333   GdkDisplayClass *display_class = GDK_DISPLAY_CLASS (class);
1334
1335   object_class->dispose = gdk_broadway_display_dispose;
1336   object_class->finalize = gdk_broadway_display_finalize;
1337
1338   display_class->window_type = GDK_TYPE_BROADWAY_WINDOW;
1339
1340   display_class->get_name = gdk_broadway_display_get_name;
1341   display_class->get_n_screens = gdk_broadway_display_get_n_screens;
1342   display_class->get_screen = gdk_broadway_display_get_screen;
1343   display_class->get_default_screen = gdk_broadway_display_get_default_screen;
1344   display_class->beep = gdk_broadway_display_beep;
1345   display_class->sync = gdk_broadway_display_sync;
1346   display_class->flush = gdk_broadway_display_flush;
1347   display_class->has_pending = gdk_broadway_display_has_pending;
1348   display_class->queue_events = _gdk_broadway_display_queue_events;
1349   display_class->get_default_group = gdk_broadway_display_get_default_group;
1350   display_class->supports_selection_notification = gdk_broadway_display_supports_selection_notification;
1351   display_class->request_selection_notification = gdk_broadway_display_request_selection_notification;
1352   display_class->supports_clipboard_persistence = gdk_broadway_display_supports_clipboard_persistence;
1353   display_class->store_clipboard = gdk_broadway_display_store_clipboard;
1354   display_class->supports_shapes = gdk_broadway_display_supports_shapes;
1355   display_class->supports_input_shapes = gdk_broadway_display_supports_input_shapes;
1356   display_class->supports_composite = gdk_broadway_display_supports_composite;
1357   display_class->list_devices = gdk_broadway_display_list_devices;
1358   display_class->get_cursor_for_type = _gdk_broadway_display_get_cursor_for_type;
1359   display_class->get_cursor_for_name = _gdk_broadway_display_get_cursor_for_name;
1360   display_class->get_cursor_for_pixbuf = _gdk_broadway_display_get_cursor_for_pixbuf;
1361   display_class->get_default_cursor_size = _gdk_broadway_display_get_default_cursor_size;
1362   display_class->get_maximal_cursor_size = _gdk_broadway_display_get_maximal_cursor_size;
1363   display_class->supports_cursor_alpha = _gdk_broadway_display_supports_cursor_alpha;
1364   display_class->supports_cursor_color = _gdk_broadway_display_supports_cursor_color;
1365
1366   display_class->before_process_all_updates = _gdk_broadway_display_before_process_all_updates;
1367   display_class->after_process_all_updates = _gdk_broadway_display_after_process_all_updates;
1368   display_class->get_next_serial = gdk_broadway_display_get_next_serial;
1369   display_class->notify_startup_complete = gdk_broadway_display_notify_startup_complete;
1370   display_class->event_data_copy = gdk_broadway_display_event_data_copy;
1371   display_class->event_data_free = gdk_broadway_display_event_data_free;
1372   display_class->create_window_impl = _gdk_broadway_display_create_window_impl;
1373   display_class->get_keymap = _gdk_broadway_display_get_keymap;
1374   display_class->get_selection_owner = _gdk_broadway_display_get_selection_owner;
1375   display_class->set_selection_owner = _gdk_broadway_display_set_selection_owner;
1376   display_class->send_selection_notify = _gdk_broadway_display_send_selection_notify;
1377   display_class->get_selection_property = _gdk_broadway_display_get_selection_property;
1378   display_class->convert_selection = _gdk_broadway_display_convert_selection;
1379   display_class->text_property_to_utf8_list = _gdk_broadway_display_text_property_to_utf8_list;
1380   display_class->utf8_to_string_target = _gdk_broadway_display_utf8_to_string_target;
1381 }
1382