broadway-server.c 58.1 KB
Newer Older
1
2
#include "config.h"

3
#include "broadway-server.h"
4

5
#include "broadway-output.h"
6
7
8

#include <glib.h>
#include <glib/gprintf.h>
9
#include "gdktypes.h"
10
#include "gdkdeviceprivate.h"
11
12
13
#include <stdlib.h>
#include <string.h>
#include <errno.h>
14

15
#ifdef HAVE_UNISTD_H
16
#include <unistd.h>
17
18
19
#elif defined (G_OS_WIN32)
#include <io.h>
#endif
20
#ifdef HAVE_SYS_MMAN_H
21
#include <sys/mman.h>
22
#endif
23
24
#include <sys/stat.h>
#include <fcntl.h>
25
#include <sys/types.h>
Tarnyko's avatar
Tarnyko committed
26
#ifdef G_OS_UNIX
27
28
29
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
Tarnyko's avatar
Tarnyko committed
30
#endif
31
32
33
#ifdef HAVE_GIO_UNIX
#include <gio/gunixsocketaddress.h>
#endif
Tarnyko's avatar
Tarnyko committed
34
35
36
37
#ifdef G_OS_WIN32
#include <windows.h>
#include <string.h>
#endif
38

39
40
41
42
#if !GLIB_CHECK_VERSION (2, 67, 3)
# define g_memdup2(mem,size)    g_memdup((mem), (size))
#endif

43
44
45
46
47
typedef struct {
  int id;
  guint32 tag;
} BroadwayOutstandingRoundtrip;

48
typedef struct BroadwayInput BroadwayInput;
49
typedef struct BroadwaySurface BroadwaySurface;
50
struct _BroadwayServer {
51
52
  GObject parent_instance;

53
  char *address;
54
  int port;
55
56
  char *ssl_cert;
  char *ssl_key;
57
58
59
60
61
62
63
64
65
  GSocketService *service;
  BroadwayOutput *output;
  guint32 id_counter;
  guint32 saved_serial;
  guint64 last_seen_time;
  BroadwayInput *input;
  GList *input_messages;
  guint process_input_idle;

66
  GHashTable *surface_id_hash;
67
68
69
  GList *surfaces;
  BroadwaySurface *root;
  gint32 focused_surface_id; /* -1 => none */
Benjamin Otte's avatar
Benjamin Otte committed
70
  int show_keyboard;
71

72
73
74
  guint32 next_texture_id;
  GHashTable *textures;

75
  guint32 screen_scale;
76

77
  gint32 mouse_in_surface_id;
78
79
  int last_x, last_y; /* in root coords */
  guint32 last_state;
80
  gint32 real_mouse_in_surface_id; /* Not affected by grabs */
81
82

  /* Explicit pointer grabs: */
83
  gint32 pointer_grab_surface_id; /* -1 => none */
84
  gint32 pointer_grab_client_id; /* -1 => none */
85
86
87
88
89
90
91
  guint32 pointer_grab_time;
  gboolean pointer_grab_owner_events;

  /* Future data, from the currently queued events */
  int future_root_x;
  int future_root_y;
  guint32 future_state;
92
  int future_mouse_in_surface;
93
94

  GList *outstanding_roundtrips;
95
96
};

97
struct _BroadwayServerClass
98
99
100
101
102
{
  GObjectClass parent_class;
};

typedef struct HttpRequest {
103
  BroadwayServer *server;
104
105
  GSocketConnection *socket_connection;
  GIOStream *connection;
106
107
108
109
110
  GDataInputStream *data;
  GString *request;
}  HttpRequest;

struct BroadwayInput {
111
  BroadwayServer *server;
112
  BroadwayOutput *output;
113
  GIOStream *connection;
114
115
116
117
  GByteArray *buffer;
  GSource *source;
  gboolean seen_time;
  gint64 time_base;
118
  gboolean active;
119
120
};

121
struct BroadwaySurface {
122
  guint32 owner;
123
124
125
126
127
128
129
  gint32 id;
  gint32 x;
  gint32 y;
  gint32 width;
  gint32 height;
  gboolean visible;
  gint32 transient_for;
130
  guint32 texture;
131
  BroadwayNode *nodes;
132
133
134
135
136
137
138
  GHashTable *node_lookup;
};

struct _BroadwayTexture {
  grefcount refcount;
  guint32 id;
  GBytes *bytes;
139
};
140

141
static void broadway_server_resync_surfaces (BroadwayServer *server);
142
static void send_outstanding_roundtrips (BroadwayServer *server);
143

144
145
146
static void broadway_server_ref_texture (BroadwayServer   *server,
                                         guint32           id);

147
148
static GType broadway_server_get_type (void);

149
G_DEFINE_TYPE (BroadwayServer, broadway_server, G_TYPE_OBJECT)
150

151
static void
152
153
154
155
156
157
158
159
160
broadway_texture_free (BroadwayTexture *texture)
{
  g_bytes_unref (texture->bytes);
  g_free (texture);
}

static void
broadway_node_unref (BroadwayServer *server,
                     BroadwayNode *node)
161
162
163
{
  int i;

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  if (g_ref_count_dec (&node->refcount))
    {
      for (i = 0; i < node->n_children; i++)
        broadway_node_unref (server, node->children[i]);

      if (node->texture_id)
        broadway_server_release_texture (server, node->texture_id);

      g_free (node);
    }
}

static BroadwayNode *
broadway_node_ref (BroadwayNode *node)
{
  g_ref_count_inc (&node->refcount);

  return node;
182
183
}

184
185
gboolean
broadway_node_equal (BroadwayNode     *a,
186
                     BroadwayNode     *b)
187
188
189
190
191
192
193
194
195
{
  int i;

  if (a->type != b->type)
    return FALSE;

  if (a->n_data != b->n_data)
    return FALSE;

196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  /* Don't check data for containers, that is just n_children, which
     we don't want to compare for a shallow equal */
  if (a->type != BROADWAY_NODE_CONTAINER)
    {
      for (i = 0; i < a->n_data; i++)
        if (a->data[i] != b->data[i])
          return FALSE;
    }

  return TRUE;
}

gboolean
broadway_node_deep_equal (BroadwayNode     *a,
                          BroadwayNode     *b)
{
  int i;

  if (a->hash != b->hash)
215
216
    return FALSE;

217
218
219
220
221
  if (!broadway_node_equal (a,b))
    return FALSE;

  if (a->n_children != b->n_children)
    return FALSE;
222
223

  for (i = 0; i < a->n_children; i++)
224
    if (!broadway_node_deep_equal (a->children[i], b->children[i]))
225
226
227
228
      return FALSE;

  return TRUE;
}
229

230

231
void
232
233
234
235
236
237
238
239
240
241
242
broadway_node_mark_deep_reused (BroadwayNode    *node,
                                gboolean         reused)
{
  node->reused = reused;
  for (int i = 0; i < node->n_children; i++)
    broadway_node_mark_deep_reused (node->children[i], reused);
}

void
broadway_node_mark_deep_consumed (BroadwayNode    *node,
                                  gboolean         consumed)
243
{
244
  node->consumed = consumed;
245
  for (int i = 0; i < node->n_children; i++)
246
    broadway_node_mark_deep_consumed (node->children[i], consumed);
247
248
249
250
251
252
253
254
255
256
257
}

void
broadway_node_add_to_lookup (BroadwayNode    *node,
                             GHashTable      *node_lookup)
{
  g_hash_table_insert (node_lookup, GINT_TO_POINTER(node->id), node);
  for (int i = 0; i < node->n_children; i++)
    broadway_node_add_to_lookup (node->children[i], node_lookup);
}

258
static void
259
broadway_server_init (BroadwayServer *server)
260
{
261
  BroadwaySurface *root;
262
263

  server->service = g_socket_service_new ();
264
  server->pointer_grab_surface_id = -1;
265
266
  server->saved_serial = 1;
  server->last_seen_time = 1;
267
  server->surface_id_hash = g_hash_table_new (NULL, NULL);
268
  server->id_counter = 0;
269
  server->textures = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
270
                                            (GDestroyNotify)broadway_texture_free);
271

272
  root = g_new0 (BroadwaySurface, 1);
273
274
275
276
277
  root->id = server->id_counter++;
  root->width = 1024;
  root->height = 768;
  root->visible = TRUE;

278
  server->root = root;
279
  server->screen_scale = 1;
280

281
  g_hash_table_insert (server->surface_id_hash,
282
283
                       GINT_TO_POINTER (root->id),
                       root);
284
285
286
}

static void
287
broadway_server_finalize (GObject *object)
288
{
289
290
291
  BroadwayServer *server = BROADWAY_SERVER (object);

  g_free (server->address);
292
293
  g_free (server->ssl_cert);
  g_free (server->ssl_key);
294
  g_hash_table_destroy (server->textures);
295

296
  G_OBJECT_CLASS (broadway_server_parent_class)->finalize (object);
297
298
299
}

static void
300
broadway_server_class_init (BroadwayServerClass * class)
301
302
303
{
  GObjectClass *object_class = G_OBJECT_CLASS (class);

304
  object_class->finalize = broadway_server_finalize;
305
306
}

307
static void
308
309
broadway_surface_free (BroadwayServer *server,
                       BroadwaySurface *surface)
310
{
311
  if (surface->nodes)
312
313
    broadway_node_unref (server, surface->nodes);
  g_hash_table_unref (surface->node_lookup);
314
  g_free (surface);
315
316
}

317
318
319
320
321
322
323
324
static BroadwaySurface *
broadway_server_lookup_surface (BroadwayServer   *server,
                                guint32           id)
{
  return g_hash_table_lookup (server->surface_id_hash,
                              GINT_TO_POINTER (id));
}

325
static void start (BroadwayInput *input);
326
327
328
329

static void
http_request_free (HttpRequest *request)
{
330
  g_object_unref (request->socket_connection);
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
  g_object_unref (request->connection);
  g_object_unref (request->data);
  g_string_free (request->request, TRUE);
  g_free (request);
}

static void
broadway_input_free (BroadwayInput *input)
{
  g_object_unref (input->connection);
  g_byte_array_free (input->buffer, FALSE);
  g_source_destroy (input->source);
  g_free (input);
}

static void
347
update_event_state (BroadwayServer *server,
348
                    BroadwayInputMsg *message)
349
{
350
  BroadwaySurface *surface;
351

352
  switch (message->base.type) {
353
  case BROADWAY_EVENT_ENTER:
354
355
356
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
357
    server->real_mouse_in_surface_id = message->pointer.mouse_surface_id;
358
359

    /* TODO: Unset when it dies */
360
    server->mouse_in_surface_id = message->pointer.event_surface_id;
361
    break;
362
  case BROADWAY_EVENT_LEAVE:
363
364
365
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
366
    server->real_mouse_in_surface_id = message->pointer.mouse_surface_id;
367

368
    server->mouse_in_surface_id = 0;
369
    break;
370
  case BROADWAY_EVENT_POINTER_MOVE:
371
372
373
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
374
    server->real_mouse_in_surface_id = message->pointer.mouse_surface_id;
375
    break;
376
377
  case BROADWAY_EVENT_BUTTON_PRESS:
  case BROADWAY_EVENT_BUTTON_RELEASE:
378
    if (message->base.type == BROADWAY_EVENT_BUTTON_PRESS &&
379
380
        server->focused_surface_id != message->pointer.mouse_surface_id &&
        server->pointer_grab_surface_id == -1)
381
      {
382
383
        broadway_server_surface_raise (server, message->pointer.mouse_surface_id);
        broadway_server_focus_surface (server, message->pointer.mouse_surface_id);
384
385
        broadway_server_flush (server);
      }
386

387
388
389
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
390
    server->real_mouse_in_surface_id = message->pointer.mouse_surface_id;
391
    break;
392
  case BROADWAY_EVENT_SCROLL:
393
394
395
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
396
    server->real_mouse_in_surface_id = message->pointer.mouse_surface_id;
397
    break;
398
  case BROADWAY_EVENT_TOUCH:
399
    if (message->touch.touch_type == 0 && message->touch.is_emulated &&
400
        server->focused_surface_id != message->touch.event_surface_id)
401
      {
402
403
        broadway_server_surface_raise (server, message->touch.event_surface_id);
        broadway_server_focus_surface (server, message->touch.event_surface_id);
404
405
406
        broadway_server_flush (server);
      }

407
408
409
410
411
412
    if (message->touch.is_emulated)
      {
        server->last_x = message->pointer.root_x;
        server->last_y = message->pointer.root_y;
      }

413
414
    server->last_state = message->touch.state;
    break;
415
416
  case BROADWAY_EVENT_KEY_PRESS:
  case BROADWAY_EVENT_KEY_RELEASE:
417
418
    server->last_state = message->key.state;
    break;
419
420
  case BROADWAY_EVENT_GRAB_NOTIFY:
  case BROADWAY_EVENT_UNGRAB_NOTIFY:
421
    break;
422
  case BROADWAY_EVENT_CONFIGURE_NOTIFY:
423
    surface = broadway_server_lookup_surface (server, message->configure_notify.id);
424
    if (surface != NULL)
425
      {
426
427
        surface->x = message->configure_notify.x;
        surface->y = message->configure_notify.y;
428
      }
429
    break;
430
431
  case BROADWAY_EVENT_ROUNDTRIP_NOTIFY:
    break;
432
  case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
433
434
    server->root->width = message->screen_resize_notify.width;
    server->root->height = message->screen_resize_notify.height;
435
    server->screen_scale = message->screen_resize_notify.scale;
436
437
438
439
440
441
442
443
444
    break;

  default:
    g_printerr ("update_event_state - Unknown input command %c\n", message->base.type);
    break;
  }
}

gboolean
445
broadway_server_lookahead_event (BroadwayServer  *server,
446
                                 const char      *types)
447
448
449
450
451
452
453
454
{
  BroadwayInputMsg *message;
  GList *l;

  for (l = server->input_messages; l != NULL; l = l->next)
    {
      message = l->data;
      if (strchr (types, message->base.type) != NULL)
455
        return TRUE;
456
457
458
459
460
    }

  return FALSE;
}

461
462
463
464
465
466
467
468
469
470
471
472
473
474
static gboolean
is_pointer_event (BroadwayInputMsg *message)
{
  return
    message->base.type == BROADWAY_EVENT_ENTER ||
    message->base.type == BROADWAY_EVENT_LEAVE ||
    message->base.type == BROADWAY_EVENT_POINTER_MOVE ||
    message->base.type == BROADWAY_EVENT_BUTTON_PRESS ||
    message->base.type == BROADWAY_EVENT_BUTTON_RELEASE ||
    message->base.type == BROADWAY_EVENT_SCROLL ||
    message->base.type == BROADWAY_EVENT_GRAB_NOTIFY ||
    message->base.type == BROADWAY_EVENT_UNGRAB_NOTIFY;
}

475
476
static void
process_input_message (BroadwayServer *server,
477
                       BroadwayInputMsg *message)
478
479
{
  gint32 client;
480
  BroadwaySurface *surface;
481
482

  update_event_state (server, message);
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519


  switch (message->base.type) {
  case BROADWAY_EVENT_ENTER:
  case BROADWAY_EVENT_LEAVE:
  case BROADWAY_EVENT_POINTER_MOVE:
  case BROADWAY_EVENT_BUTTON_PRESS:
  case BROADWAY_EVENT_BUTTON_RELEASE:
  case BROADWAY_EVENT_SCROLL:
  case BROADWAY_EVENT_GRAB_NOTIFY:
  case BROADWAY_EVENT_UNGRAB_NOTIFY:
    surface = broadway_server_lookup_surface (server, message->pointer.event_surface_id);
    break;
  case BROADWAY_EVENT_TOUCH:
    surface = broadway_server_lookup_surface (server, message->touch.event_surface_id);
    break;
  case BROADWAY_EVENT_CONFIGURE_NOTIFY:
    surface = broadway_server_lookup_surface (server, message->configure_notify.id);
    break;
  case BROADWAY_EVENT_ROUNDTRIP_NOTIFY:
    surface = broadway_server_lookup_surface (server, message->roundtrip_notify.id);
    break;
  case BROADWAY_EVENT_KEY_PRESS:
  case BROADWAY_EVENT_KEY_RELEASE:
    /* TODO: Send to keys focused clients only... */
  case BROADWAY_EVENT_FOCUS:
  case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
  default:
    surface = NULL;
    break;
  }

  if (surface)
    client = surface->owner;
  else
    client = -1;

520
  if (is_pointer_event (message) &&
521
      server->pointer_grab_surface_id != -1)
522
523
524
525
526
    client = server->pointer_grab_client_id;

  broadway_events_got_input (message, client);
}

527
static void
528
process_input_messages (BroadwayServer *server)
529
530
531
532
533
534
535
{
  BroadwayInputMsg *message;

  while (server->input_messages)
    {
      message = server->input_messages->data;
      server->input_messages =
536
537
        g_list_delete_link (server->input_messages,
                            server->input_messages);
538

539
      if (message->base.serial == 0)
540
541
542
543
544
545
        {
          /* This was sent before we got any requests, but we don't want the
             daemon serials to go backwards, so we fix it up to be the last used
             serial */
          message->base.serial = server->saved_serial - 1;
        }
546

547
      process_input_message (server, message);
548
549
550
551
      g_free (message);
    }
}

552
553
static void
fake_configure_notify (BroadwayServer *server,
554
                       BroadwaySurface *surface)
555
556
557
558
559
560
{
  BroadwayInputMsg ev = { {0} };

  ev.base.type = BROADWAY_EVENT_CONFIGURE_NOTIFY;
  ev.base.serial = server->saved_serial - 1;
  ev.base.time = server->last_seen_time;
561
562
563
564
565
  ev.configure_notify.id = surface->id;
  ev.configure_notify.x = surface->x;
  ev.configure_notify.y = surface->y;
  ev.configure_notify.width = surface->width;
  ev.configure_notify.height = surface->height;
566
567
568
569

  process_input_message (server, &ev);
}

570
571
static guint32 *
parse_pointer_data (guint32 *p, BroadwayInputPointerMsg *data)
572
{
573
574
  data->mouse_surface_id = ntohl (*p++);
  data->event_surface_id = ntohl (*p++);
575
576
577
578
579
  data->root_x = ntohl (*p++);
  data->root_y = ntohl (*p++);
  data->win_x = ntohl (*p++);
  data->win_y = ntohl (*p++);
  data->state = ntohl (*p++);
580
581
582
583

  return p;
}

584
585
586
587
static guint32 *
parse_touch_data (guint32 *p, BroadwayInputTouchMsg *data)
{
  data->touch_type = ntohl (*p++);
588
  data->event_surface_id = ntohl (*p++);
589
  data->sequence_id = ntohl (*p++);
590
  data->is_emulated = ntohl (*p++);
591
592
593
594
595
596
597
598
599
  data->root_x = ntohl (*p++);
  data->root_y = ntohl (*p++);
  data->win_x = ntohl (*p++);
  data->win_y = ntohl (*p++);
  data->state = ntohl (*p++);

  return p;
}

600
static void
601
update_future_pointer_info (BroadwayServer *server, BroadwayInputPointerMsg *data)
602
603
604
605
{
  server->future_root_x = data->root_x;
  server->future_root_y = data->root_y;
  server->future_state = data->state;
606
  server->future_mouse_in_surface = data->mouse_surface_id;
607
608
}

609
610
611
static void
queue_input_message (BroadwayServer *server, BroadwayInputMsg *msg)
{
612
  server->input_messages = g_list_append (server->input_messages, g_memdup2 (msg, sizeof (BroadwayInputMsg)));
613
614
}

615
static void
616
parse_input_message (BroadwayInput *input, const unsigned char *message)
617
{
618
  BroadwayServer *server = input->server;
619
  BroadwayInputMsg msg;
620
  guint32 *p;
621
  gint64 time_;
622
  GList *l;
623

624
625
  memset (&msg, 0, sizeof (msg));

626
627
  p = (guint32 *) message;

628
629
  msg.base.type = ntohl (*p++);
  msg.base.serial = ntohl (*p++);
630

631
  time_ = ntohl (*p++);
632
633
634
635
636
637
638

  if (time_ == 0) {
    time_ = server->last_seen_time;
  } else {
    if (!input->seen_time) {
      input->seen_time = TRUE;
      /* Calculate time base so that any following times are normalized to start
639
640
         5 seconds after last_seen_time, to avoid issues that could appear when
         a long hiatus due to a reconnect seems to be instant */
641
642
643
644
645
646
647
648
649
650
      input->time_base = time_ - (server->last_seen_time + 5000);
    }
    time_ = time_ - input->time_base;
  }

  server->last_seen_time = time_;

  msg.base.time = time_;

  switch (msg.base.type) {
651
652
  case BROADWAY_EVENT_ENTER:
  case BROADWAY_EVENT_LEAVE:
653
654
    p = parse_pointer_data (p, &msg.pointer);
    update_future_pointer_info (server, &msg.pointer);
655
    msg.crossing.mode = ntohl (*p++);
656
657
    break;

658
  case BROADWAY_EVENT_POINTER_MOVE: /* Mouse move */
659
660
661
662
    p = parse_pointer_data (p, &msg.pointer);
    update_future_pointer_info (server, &msg.pointer);
    break;

663
664
  case BROADWAY_EVENT_BUTTON_PRESS:
  case BROADWAY_EVENT_BUTTON_RELEASE:
665
666
    p = parse_pointer_data (p, &msg.pointer);
    update_future_pointer_info (server, &msg.pointer);
667
    msg.button.button = ntohl (*p++);
668
669
    break;

670
  case BROADWAY_EVENT_SCROLL:
671
672
    p = parse_pointer_data (p, &msg.pointer);
    update_future_pointer_info (server, &msg.pointer);
673
    msg.scroll.dir = ntohl (*p++);
674
675
    break;

676
677
678
679
  case BROADWAY_EVENT_TOUCH:
    p = parse_touch_data (p, &msg.touch);
    break;

680
681
  case BROADWAY_EVENT_KEY_PRESS:
  case BROADWAY_EVENT_KEY_RELEASE:
682
    msg.key.surface_id = server->focused_surface_id;
683
684
    msg.key.key = ntohl (*p++);
    msg.key.state = ntohl (*p++);
685
686
    break;

687
688
  case BROADWAY_EVENT_GRAB_NOTIFY:
  case BROADWAY_EVENT_UNGRAB_NOTIFY:
689
    msg.grab_reply.res = ntohl (*p++);
690
691
    break;

692
  case BROADWAY_EVENT_CONFIGURE_NOTIFY:
693
694
695
696
697
    msg.configure_notify.id = ntohl (*p++);
    msg.configure_notify.x = ntohl (*p++);
    msg.configure_notify.y = ntohl (*p++);
    msg.configure_notify.width = ntohl (*p++);
    msg.configure_notify.height = ntohl (*p++);
698
699
    break;

700
701
702
703
704
705
706
707
708
709
710
711
  case BROADWAY_EVENT_ROUNDTRIP_NOTIFY:
    msg.roundtrip_notify.id = ntohl (*p++);
    msg.roundtrip_notify.tag = ntohl (*p++);
    msg.roundtrip_notify.local = FALSE;

    /* Remove matched outstanding roundtrips */
    for (l = server->outstanding_roundtrips; l != NULL; l = l->next)
      {
        BroadwayOutstandingRoundtrip *rt = l->data;

        if (rt->id == msg.roundtrip_notify.id &&
            rt->tag == msg.roundtrip_notify.tag)
712
          break;
713
      }
714
715

    if (l == NULL)
716
      g_warning ("Got unexpected roundtrip reply for id %d, tag %d\n", msg.roundtrip_notify.id, msg.roundtrip_notify.tag);
717
718
719
720
721
722
723
724
    else
      {
        BroadwayOutstandingRoundtrip *rt = l->data;

        server->outstanding_roundtrips = g_list_delete_link (server->outstanding_roundtrips, l);
        g_free (rt);
      }

725
726
    break;

727
  case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
728
729
    msg.screen_resize_notify.width = ntohl (*p++);
    msg.screen_resize_notify.height = ntohl (*p++);
730
    msg.screen_resize_notify.scale = ntohl (*p++);
731
732
733
734
735
736
737
    break;

  default:
    g_printerr ("parse_input_message - Unknown input command %c (%s)\n", msg.base.type, message);
    break;
  }

738
  queue_input_message (server, &msg);
739
740
741
742
743
744
745
746
747
748
749
}

static inline void
hex_dump (guchar *data, gsize len)
{
#ifdef DEBUG_WEBSOCKETS
  gsize i, j;
  for (j = 0; j < len + 15; j += 16)
    {
      fprintf (stderr, "0x%.4x  ", j);
      for (i = 0; i < 16; i++)
750
751
752
753
754
755
756
757
        {
          if ((j + i) < len)
            fprintf (stderr, "%.2x ", data[j+i]);
          else
            fprintf (stderr, "  ");
          if (i == 8)
            fprintf (stderr, " ");
        }
758
759
760
      fprintf (stderr, " | ");

      for (i = 0; i < 16; i++)
761
762
763
764
        if ((j + i) < len && g_ascii_isalnum(data[j+i]))
          fprintf (stderr, "%c", data[j+i]);
        else
          fprintf (stderr, ".");
765
766
767
768
769
770
771
772
773
774
775
      fprintf (stderr, "\n");
    }
#endif
}

static void
parse_input (BroadwayInput *input)
{
  if (!input->buffer->len)
    return;

776
  hex_dump (input->buffer->data, input->buffer->len);
777

778
779
780
781
782
783
  while (input->buffer->len > 2)
    {
      gsize len, payload_len;
      BroadwayWSOpCode code;
      gboolean is_mask, fin;
      guchar *buf, *data, *mask;
784

785
786
      buf = input->buffer->data;
      len = input->buffer->len;
787
788

#ifdef DEBUG_WEBSOCKETS
789
      g_print ("Parse input first byte 0x%2x 0x%2x\n", buf[0], buf[1]);
790
791
#endif

792
793
794
795
796
797
      fin = buf[0] & 0x80;
      code = buf[0] & 0x0f;
      payload_len = buf[1] & 0x7f;
      is_mask = buf[1] & 0x80;
      data = buf + 2;

798
      if (payload_len == 126)
799
800
801
802
803
804
        {
          if (len < 4)
            return;
          payload_len = GUINT16_FROM_BE( *(guint16 *) data );
          data += 2;
        }
805
      else if (payload_len == 127)
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
        {
          if (len < 10)
            return;
          payload_len = GUINT64_FROM_BE( *(guint64 *) data );
          data += 8;
        }

      mask = NULL;
      if (is_mask)
        {
          if (data - buf + 4 > len)
            return;
          mask = data;
          data += 4;
        }

      if (data - buf + payload_len > len)
        return; /* wait to accumulate more */

      if (is_mask)
        {
          gsize i;
          for (i = 0; i < payload_len; i++)
            data[i] ^= mask[i%4];
        }

      switch (code) {
      case BROADWAY_WS_CNX_CLOSE:
        break; /* hang around anyway */
835
      case BROADWAY_WS_BINARY:
836
837
        if (!fin)
          {
838
#ifdef DEBUG_WEBSOCKETS
839
            g_warning ("can't yet accept fragmented input");
840
#endif
841
842
843
          }
        else
          {
844
            parse_input_message (input, data);
845
846
847
848
849
850
851
          }
        break;
      case BROADWAY_WS_CNX_PING:
        broadway_output_pong (input->output);
        break;
      case BROADWAY_WS_CNX_PONG:
        break; /* we never send pings, but tolerate pongs */
852
      case BROADWAY_WS_TEXT:
853
854
855
856
857
858
859
      case BROADWAY_WS_CONTINUATION:
      default:
        {
          g_warning ("fragmented or unknown input code 0x%2x with fin set", code);
          break;
        }
      }
860

861
      g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len);
862
863
864
865
866
    }
}


static gboolean
867
process_input_idle_cb (BroadwayServer *server)
868
869
870
871
872
873
874
{
  server->process_input_idle = 0;
  process_input_messages (server);
  return G_SOURCE_REMOVE;
}

static void
875
queue_process_input_at_idle (BroadwayServer *server)
876
877
878
879
880
881
{
  if (server->process_input_idle == 0)
    server->process_input_idle =
      g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)process_input_idle_cb, server, NULL);
}

882
static gboolean
883
broadway_server_read_all_input_nonblocking (BroadwayInput *input)
884
885
886
887
{
  GInputStream *in;
  gssize res;
  guint8 buffer[1024];
888
  GError *error = NULL;
889

890
  if (input == NULL)
891
    return FALSE;
892

893
  in = g_io_stream_get_input_stream (input->connection);
894
895

  res = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (in),
896
                                                  buffer, sizeof (buffer), NULL, &error);
897
898
899
900

  if (res <= 0)
    {
      if (res < 0 &&
901
902
903
904
905
          g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
        {
          g_error_free (error);
          return TRUE;
        }
906

907
      if (input->server->input == input)
908
909
910
911
912
        {
          send_outstanding_roundtrips (input->server);

          input->server->input = NULL;
        }
913
914
      broadway_input_free (input);
      if (res < 0)
915
916
917
918
        {
          g_printerr ("input error %s\n", error->message);
          g_error_free (error);
        }
919
      return FALSE;
920
921
922
923
924
    }

  g_byte_array_append (input->buffer, buffer, res);

  parse_input (input);
925
  return TRUE;
926
927
928
}

static void
929
broadway_server_consume_all_input (BroadwayServer *server)
930
{
931
  broadway_server_read_all_input_nonblocking (server->input);
932
933
934
935
936
937
938
939
940
941

  /* Since we're parsing input but not processing the resulting messages
     we might not get a readable callback on the stream, so queue an idle to
     process the messages */
  queue_process_input_at_idle (server);
}


static gboolean
input_data_cb (GObject  *stream,
942
               BroadwayInput *input)
943
{
944
  BroadwayServer *server = input->server;
945

946
947
  if (!broadway_server_read_all_input_nonblocking (input))
    return FALSE;
948

949
950
  if (input->active)
    process_input_messages (server);
951
952
953
954

  return TRUE;
}

955
guint32
956
broadway_server_get_next_serial (BroadwayServer *server)
957
958
959
960
961
962
963
{
  if (server->output)
    return broadway_output_get_next_serial (server->output);

  return server->saved_serial;
}

964
965
void
broadway_server_get_screen_size (BroadwayServer   *server,
966
                                 guint32          *width,
967
968
                                 guint32          *height,
                                 guint32          *scale)
969
970
971
{
  *width = server->root->width;
  *height = server->root->height;
972
  *scale = server->screen_scale;
973
974
}

975
976
static void
broadway_server_fake_roundtrip_reply (BroadwayServer *server,
Benjamin Otte's avatar
Benjamin Otte committed
977
                                      int             id,
978
979
980
981
982
983
984
985
986
987
988
989
990
991
                                      guint32         tag)
{
  BroadwayInputMsg msg;

  msg.base.type = BROADWAY_EVENT_ROUNDTRIP_NOTIFY;
  msg.base.serial = 0;
  msg.base.time = server->last_seen_time;
  msg.roundtrip_notify.id = id;
  msg.roundtrip_notify.tag = tag;
  msg.roundtrip_notify.local = 1;

  queue_input_message (server, &msg);
  queue_process_input_at_idle (server);
}
992

993
void
994
broadway_server_flush (BroadwayServer *server)
995
996
997
998
999
1000
1001
{
  if (server->output &&
      !broadway_output_flush (server->output))
    {
      server->saved_serial = broadway_output_get_next_serial (server->output);
      broadway_output_free (server->output);
      server->output = NULL;
1002
      send_outstanding_roundtrips (server);
1003
1004
1005
    }
}

1006
1007
void
broadway_server_roundtrip (BroadwayServer *server,
Benjamin Otte's avatar
Benjamin Otte committed
1008
                           int             id,
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
                           guint32         tag)
{
  if (server->output)
    {
      BroadwayOutstandingRoundtrip *rt = g_new0 (BroadwayOutstandingRoundtrip, 1);
      rt->id = id;
      rt->tag = tag;
      server->outstanding_roundtrips = g_list_prepend (server->outstanding_roundtrips, rt);

      broadway_output_roundtrip (server->output, id, tag);
    }
  else
    broadway_server_fake_roundtrip_reply (server, id, tag);
}

1024
1025
static const char *
parse_line (const char *line, const char *key)
1026
{
1027
  const char *p;
1028

1029
  if (g_ascii_strncasecmp (line, key, strlen (key)) != 0)
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
    return NULL;
  p = line + strlen (key);
  if (*p != ':')
    return NULL;
  p++;
  /* Skip optional initial space */
  if (*p == ' ')
    p++;
  return p;
}
Tarnyko's avatar
Tarnyko committed
1040

1041
1042
static void
send_error (HttpRequest *request,
1043
1044
            int error_code,
            const char *reason)
1045
1046
1047
1048
{
  char *res;

  res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n"
1049
1050
1051
1052
1053
                         "<html><head><title>%d %s</title></head>"
                         "<body>%s</body></html>",
                         error_code, reason,
                         error_code, reason,
                         reason);
1054

1055
  /* TODO: This should really be async */
1056
1057
1058
  g_output_stream_write_all (g_io_stream_get_output_stream (request->connection),
                             res, strlen (res), NULL, NULL, NULL);

1059
1060
1061
1062
1063
1064
1065
1066
  g_free (res);
  http_request_free (request);
}

/* magic from: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 */
#define SEC_WEB_SOCKET_KEY_MAGIC "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"

/* 'x3JJHMbDL1EzLkh9GBhXDw==' generates 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=' */
Benjamin Otte's avatar
Benjamin Otte committed
1067
1068
static char *
generate_handshake_response_wsietf_v7 (const char *key)
1069
1070
{
  gsize digest_len = 20;
1071
  guchar digest[20];
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
  GChecksum *checksum;

  checksum = g_checksum_new (G_CHECKSUM_SHA1);
  if (!checksum)
    return NULL;

  g_checksum_update (checksum, (guchar *)key, -1);
  g_checksum_update (checksum, (guchar *)SEC_WEB_SOCKET_KEY_MAGIC, -1);

  g_checksum_get_digest (checksum, digest, &digest_len);
  g_checksum_free (checksum);

  g_assert (digest_len == 20);

  return g_base64_encode (digest, digest_len);
}

static void
1090
start_input (HttpRequest *request)
1091
1092
{
  char **lines;
1093
  const char *p;
1094
1095
  int i;
  char *res;
1096
  const char *origin, *host;
1097
1098
1099
1100
  BroadwayInput *input;
  const void *data_buffer;
  gsize data_buffer_size;
  GInputStream *in;
1101
  const char *key;
1102
1103
  GSocket *socket;
  int flag = 1;
1104
1105
1106
1107
1108
1109

#ifdef DEBUG_WEBSOCKETS
  g_print ("incoming request:\n%s\n", request->request->str);
#endif
  lines = g_strsplit (request->request->str, "\n", 0);

1110
  key = NULL;
1111
1112
1113
1114
  origin = NULL;
  host = NULL;
  for (i = 0; lines[i] != NULL; i++)
    {
1115
1116
      if ((p = parse_line (lines[i], "Sec-WebSocket-Key")))
        key = p;
1117
      else if ((p = parse_line (lines[i], "Origin")))
1118
        origin = p;
1119
      else if ((p = parse_line (lines[i], "Host")))
1120
        host = p;
1121
      else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin")))
1122
        origin = p;
1123
1124
    }

1125
  if (host == NULL)
1126
1127
1128
1129
1130
1131
    {
      g_strfreev (lines);
      send_error (request, 400, "Bad websocket request");
      return;
    }

1132
  if (key != NULL)
1133
    {
1134
      char* accept = generate_handshake_response_wsietf_v7 (key);
1135
      res = g_strdup_printf ("HTTP/1.1 101 Switching Protocols\r\n"