broadway-server.c 46.6 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 11 12
#include <stdlib.h>
#include <string.h>
#include <errno.h>
13

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

typedef struct BroadwayInput BroadwayInput;
39
typedef struct BroadwayWindow BroadwayWindow;
40
struct _BroadwayServer {
41 42
  GObject parent_instance;

43
  char *address;
44
  int port;
45 46
  char *ssl_cert;
  char *ssl_key;
47 48 49 50 51 52 53 54 55 56 57
  GSocketService *service;
  BroadwayOutput *output;
  guint32 id_counter;
  guint32 saved_serial;
  guint64 last_seen_time;
  BroadwayInput *input;
  GList *input_messages;
  guint process_input_idle;

  GHashTable *id_ht;
  GList *toplevels;
58
  BroadwayWindow *root;
59
  gint32 focused_window_id; /* -1 => none */
60
  gint show_keyboard;
61 62 63

  guint32 screen_width;
  guint32 screen_height;
64 65 66 67 68 69 70 71

  gint32 mouse_in_toplevel_id;
  int last_x, last_y; /* in root coords */
  guint32 last_state;
  gint32 real_mouse_in_toplevel_id; /* Not affected by grabs */

  /* Explicit pointer grabs: */
  gint32 pointer_grab_window_id; /* -1 => none */
72
  gint32 pointer_grab_client_id; /* -1 => none */
73 74 75 76 77 78 79 80 81 82
  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;
  int future_mouse_in_toplevel;
};

83
struct _BroadwayServerClass
84 85 86 87 88
{
  GObjectClass parent_class;
};

typedef struct HttpRequest {
89
  BroadwayServer *server;
90 91
  GSocketConnection *socket_connection;
  GIOStream *connection;
92 93 94 95 96
  GDataInputStream *data;
  GString *request;
}  HttpRequest;

struct BroadwayInput {
97
  BroadwayServer *server;
98
  BroadwayOutput *output;
99
  GIOStream *connection;
100 101 102 103
  GByteArray *buffer;
  GSource *source;
  gboolean seen_time;
  gint64 time_base;
104
  gboolean active;
105 106
};

107
struct BroadwayWindow {
108 109 110 111 112 113 114 115 116
  gint32 id;
  gint32 x;
  gint32 y;
  gint32 width;
  gint32 height;
  gboolean is_temp;
  gboolean visible;
  gint32 transient_for;

117 118
  BroadwayBuffer *buffer;
  gboolean buffer_synced;
119 120 121

  char *cached_surface_name;
  cairo_surface_t *cached_surface;
122
};
123

124
static void broadway_server_resync_windows (BroadwayServer *server);
125

126 127
static GType broadway_server_get_type (void);

128
G_DEFINE_TYPE (BroadwayServer, broadway_server, G_TYPE_OBJECT)
129 130

static void
131
broadway_server_init (BroadwayServer *server)
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
{
  BroadwayWindow *root;

  server->service = g_socket_service_new ();
  server->pointer_grab_window_id = -1;
  server->saved_serial = 1;
  server->last_seen_time = 1;
  server->id_ht = g_hash_table_new (NULL, NULL);
  server->id_counter = 0;

  root = g_new0 (BroadwayWindow, 1);
  root->id = server->id_counter++;
  root->width = 1024;
  root->height = 768;
  root->visible = TRUE;

148 149
  server->root = root;

150 151 152 153 154 155
  g_hash_table_insert (server->id_ht,
		       GINT_TO_POINTER (root->id),
		       root);
}

static void
156
broadway_server_finalize (GObject *object)
157
{
158 159 160
  BroadwayServer *server = BROADWAY_SERVER (object);

  g_free (server->address);
161 162
  g_free (server->ssl_cert);
  g_free (server->ssl_key);
163

164
  G_OBJECT_CLASS (broadway_server_parent_class)->finalize (object);
165 166 167
}

static void
168
broadway_server_class_init (BroadwayServerClass * class)
169 170 171
{
  GObjectClass *object_class = G_OBJECT_CLASS (class);

172
  object_class->finalize = broadway_server_finalize;
173 174
}

175
static void start (BroadwayInput *input);
176 177 178 179

static void
http_request_free (HttpRequest *request)
{
180
  g_object_unref (request->socket_connection);
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
  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
197
update_event_state (BroadwayServer *server,
198 199
		    BroadwayInputMsg *message)
{
200 201
  BroadwayWindow *window;

202
  switch (message->base.type) {
203
  case BROADWAY_EVENT_ENTER:
204 205 206 207 208 209 210 211
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
    server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;

    /* TODO: Unset when it dies */
    server->mouse_in_toplevel_id = message->pointer.event_window_id;
    break;
212
  case BROADWAY_EVENT_LEAVE:
213 214 215 216 217 218 219
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
    server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;

    server->mouse_in_toplevel_id = 0;
    break;
220
  case BROADWAY_EVENT_POINTER_MOVE:
221 222 223 224 225
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
    server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;
    break;
226 227
  case BROADWAY_EVENT_BUTTON_PRESS:
  case BROADWAY_EVENT_BUTTON_RELEASE:
228
    if (message->base.type == BROADWAY_EVENT_BUTTON_PRESS &&
229 230 231 232 233 234 235
        server->focused_window_id != message->pointer.mouse_window_id &&
        server->pointer_grab_window_id == -1)
      {
        broadway_server_window_raise (server, message->pointer.mouse_window_id);
        broadway_server_focus_window (server, message->pointer.mouse_window_id);
        broadway_server_flush (server);
      }
236

237 238 239 240 241
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
    server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;
    break;
242
  case BROADWAY_EVENT_SCROLL:
243 244 245 246 247
    server->last_x = message->pointer.root_x;
    server->last_y = message->pointer.root_y;
    server->last_state = message->pointer.state;
    server->real_mouse_in_toplevel_id = message->pointer.mouse_window_id;
    break;
248
  case BROADWAY_EVENT_TOUCH:
249
    if (message->touch.touch_type == 0 && message->touch.is_emulated &&
250 251 252 253 254 255 256
        server->focused_window_id != message->touch.event_window_id)
      {
        broadway_server_window_raise (server, message->touch.event_window_id);
        broadway_server_focus_window (server, message->touch.event_window_id);
        broadway_server_flush (server);
      }

257 258 259 260 261 262
    if (message->touch.is_emulated)
      {
        server->last_x = message->pointer.root_x;
        server->last_y = message->pointer.root_y;
      }

263 264
    server->last_state = message->touch.state;
    break;
265 266
  case BROADWAY_EVENT_KEY_PRESS:
  case BROADWAY_EVENT_KEY_RELEASE:
267 268
    server->last_state = message->key.state;
    break;
269 270
  case BROADWAY_EVENT_GRAB_NOTIFY:
  case BROADWAY_EVENT_UNGRAB_NOTIFY:
271
    break;
272
  case BROADWAY_EVENT_CONFIGURE_NOTIFY:
273 274 275 276 277 278 279
    window = g_hash_table_lookup (server->id_ht,
				  GINT_TO_POINTER (message->configure_notify.id));
    if (window != NULL)
      {
	window->x = message->configure_notify.x;
	window->y = message->configure_notify.y;
      }
280
    break;
281
  case BROADWAY_EVENT_DELETE_NOTIFY:
282
    break;
283
  case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
284 285
    server->root->width = message->screen_resize_notify.width;
    server->root->height = message->screen_resize_notify.height;
286 287 288 289 290 291 292 293 294
    break;

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

gboolean
295 296
broadway_server_lookahead_event (BroadwayServer  *server,
				 const char         *types)
297 298 299 300 301 302 303 304 305 306 307 308 309 310
{
  BroadwayInputMsg *message;
  GList *l;

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

  return FALSE;
}

311 312 313 314 315 316 317 318 319 320 321 322 323 324
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;
}

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
static void
process_input_message (BroadwayServer *server,
		       BroadwayInputMsg *message)
{
  gint32 client;

  update_event_state (server, message);
  client = -1;
  if (is_pointer_event (message) &&
      server->pointer_grab_window_id != -1)
    client = server->pointer_grab_client_id;

  broadway_events_got_input (message, client);
}

340
static void
341
process_input_messages (BroadwayServer *server)
342 343 344 345 346 347 348 349 350 351
{
  BroadwayInputMsg *message;

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

352 353 354 355 356 357 358
      if (message->base.serial == 0)
	{
	  /* 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;
	}
359

360
      process_input_message (server, message);
361 362 363 364
      g_free (message);
    }
}

365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
static void
fake_configure_notify (BroadwayServer *server,
		       BroadwayWindow *window)
{
  BroadwayInputMsg ev = { {0} };

  ev.base.type = BROADWAY_EVENT_CONFIGURE_NOTIFY;
  ev.base.serial = server->saved_serial - 1;
  ev.base.time = server->last_seen_time;
  ev.configure_notify.id = window->id;
  ev.configure_notify.x = window->x;
  ev.configure_notify.y = window->y;
  ev.configure_notify.width = window->width;
  ev.configure_notify.height = window->height;

  process_input_message (server, &ev);
}

383 384
static guint32 *
parse_pointer_data (guint32 *p, BroadwayInputPointerMsg *data)
385
{
386 387 388 389 390 391 392
  data->mouse_window_id = ntohl (*p++);
  data->event_window_id = ntohl (*p++);
  data->root_x = ntohl (*p++);
  data->root_y = ntohl (*p++);
  data->win_x = ntohl (*p++);
  data->win_y = ntohl (*p++);
  data->state = ntohl (*p++);
393 394 395 396

  return p;
}

397 398 399 400 401 402
static guint32 *
parse_touch_data (guint32 *p, BroadwayInputTouchMsg *data)
{
  data->touch_type = ntohl (*p++);
  data->event_window_id = ntohl (*p++);
  data->sequence_id = ntohl (*p++);
403
  data->is_emulated = ntohl (*p++);
404 405 406 407 408 409 410 411 412
  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;
}

413
static void
414
update_future_pointer_info (BroadwayServer *server, BroadwayInputPointerMsg *data)
415 416 417 418 419 420 421 422
{
  server->future_root_x = data->root_x;
  server->future_root_y = data->root_y;
  server->future_state = data->state;
  server->future_mouse_in_toplevel = data->mouse_window_id;
}

static void
423
parse_input_message (BroadwayInput *input, const unsigned char *message)
424
{
425
  BroadwayServer *server = input->server;
426
  BroadwayInputMsg msg;
427
  guint32 *p;
428 429
  gint64 time_;

430 431
  memset (&msg, 0, sizeof (msg));

432 433
  p = (guint32 *) message;

434 435 436
  msg.base.type = ntohl (*p++);
  msg.base.serial = ntohl (*p++);
  time_ = ntohl (*p++);
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455

  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
	 5 seconds after last_seen_time, to avoid issues that could appear when
	 a long hiatus due to a reconnect seems to be instant */
      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) {
456 457
  case BROADWAY_EVENT_ENTER:
  case BROADWAY_EVENT_LEAVE:
458 459
    p = parse_pointer_data (p, &msg.pointer);
    update_future_pointer_info (server, &msg.pointer);
460
    msg.crossing.mode = ntohl (*p++);
461 462
    break;

463
  case BROADWAY_EVENT_POINTER_MOVE: /* Mouse move */
464 465 466 467
    p = parse_pointer_data (p, &msg.pointer);
    update_future_pointer_info (server, &msg.pointer);
    break;

468 469
  case BROADWAY_EVENT_BUTTON_PRESS:
  case BROADWAY_EVENT_BUTTON_RELEASE:
470 471
    p = parse_pointer_data (p, &msg.pointer);
    update_future_pointer_info (server, &msg.pointer);
472
    msg.button.button = ntohl (*p++);
473 474
    break;

475
  case BROADWAY_EVENT_SCROLL:
476 477
    p = parse_pointer_data (p, &msg.pointer);
    update_future_pointer_info (server, &msg.pointer);
478
    msg.scroll.dir = ntohl (*p++);
479 480
    break;

481 482 483 484
  case BROADWAY_EVENT_TOUCH:
    p = parse_touch_data (p, &msg.touch);
    break;

485 486
  case BROADWAY_EVENT_KEY_PRESS:
  case BROADWAY_EVENT_KEY_RELEASE:
487
    msg.key.window_id = server->focused_window_id;
488 489
    msg.key.key = ntohl (*p++);
    msg.key.state = ntohl (*p++);
490 491
    break;

492 493
  case BROADWAY_EVENT_GRAB_NOTIFY:
  case BROADWAY_EVENT_UNGRAB_NOTIFY:
494
    msg.grab_reply.res = ntohl (*p++);
495 496
    break;

497
  case BROADWAY_EVENT_CONFIGURE_NOTIFY:
498 499 500 501 502
    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++);
503 504
    break;

505
  case BROADWAY_EVENT_DELETE_NOTIFY:
506
    msg.delete_notify.id = ntohl (*p++);
507 508
    break;

509
  case BROADWAY_EVENT_SCREEN_SIZE_CHANGED:
510 511
    msg.screen_resize_notify.width = ntohl (*p++);
    msg.screen_resize_notify.height = ntohl (*p++);
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
    break;

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

  server->input_messages = g_list_append (server->input_messages, g_memdup (&msg, sizeof (msg)));

}

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++)
	{
	    if ((j + i) < len)
	      fprintf (stderr, "%.2x ", data[j+i]);
	    else
	      fprintf (stderr, "  ");
	    if (i == 8)
	      fprintf (stderr, " ");
	}
      fprintf (stderr, " | ");

      for (i = 0; i < 16; i++)
	if ((j + i) < len && g_ascii_isalnum(data[j+i]))
	  fprintf (stderr, "%c", data[j+i]);
	else
	  fprintf (stderr, ".");
      fprintf (stderr, "\n");
    }
#endif
}

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

558
  hex_dump (input->buffer->data, input->buffer->len);
559

560 561 562 563 564 565
  while (input->buffer->len > 2)
    {
      gsize len, payload_len;
      BroadwayWSOpCode code;
      gboolean is_mask, fin;
      guchar *buf, *data, *mask;
566

567 568
      buf = input->buffer->data;
      len = input->buffer->len;
569 570

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

574 575 576 577 578 579
      fin = buf[0] & 0x80;
      code = buf[0] & 0x0f;
      payload_len = buf[1] & 0x7f;
      is_mask = buf[1] & 0x80;
      data = buf + 2;

580
      if (payload_len == 126)
581 582 583 584 585 586
        {
          if (len < 4)
            return;
          payload_len = GUINT16_FROM_BE( *(guint16 *) data );
          data += 2;
        }
587
      else if (payload_len == 127)
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
        {
          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 */
617
      case BROADWAY_WS_BINARY:
618 619
        if (!fin)
          {
620
#ifdef DEBUG_WEBSOCKETS
621
            g_warning ("can't yet accept fragmented input");
622
#endif
623 624 625
          }
        else
          {
626
            parse_input_message (input, data);
627 628 629 630 631 632 633
          }
        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 */
634
      case BROADWAY_WS_TEXT:
635 636 637 638 639 640 641
      case BROADWAY_WS_CONTINUATION:
      default:
        {
          g_warning ("fragmented or unknown input code 0x%2x with fin set", code);
          break;
        }
      }
642

643
      g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len);
644 645 646 647 648
    }
}


static gboolean
649
process_input_idle_cb (BroadwayServer *server)
650 651 652 653 654 655 656
{
  server->process_input_idle = 0;
  process_input_messages (server);
  return G_SOURCE_REMOVE;
}

static void
657
queue_process_input_at_idle (BroadwayServer *server)
658 659 660 661 662 663
{
  if (server->process_input_idle == 0)
    server->process_input_idle =
      g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)process_input_idle_cb, server, NULL);
}

664
static gboolean
665
broadway_server_read_all_input_nonblocking (BroadwayInput *input)
666 667 668 669
{
  GInputStream *in;
  gssize res;
  guint8 buffer[1024];
670
  GError *error = NULL;
671

672
  if (input == NULL)
673
    return FALSE;
674

675
  in = g_io_stream_get_input_stream (input->connection);
676 677 678 679 680 681 682 683 684 685

  res = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (in),
						  buffer, sizeof (buffer), NULL, &error);

  if (res <= 0)
    {
      if (res < 0 &&
	  g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
	{
	  g_error_free (error);
686
	  return TRUE;
687 688
	}

689 690
      if (input->server->input == input)
	input->server->input = NULL;
691 692 693
      broadway_input_free (input);
      if (res < 0)
	{
694
	  g_printerr ("input error %s\n", error->message);
695 696
	  g_error_free (error);
	}
697
      return FALSE;
698 699 700 701 702
    }

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

  parse_input (input);
703
  return TRUE;
704 705 706
}

static void
707
broadway_server_consume_all_input (BroadwayServer *server)
708
{
709
  broadway_server_read_all_input_nonblocking (server->input);
710 711 712 713 714 715 716 717 718 719 720 721

  /* 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,
	       BroadwayInput *input)
{
722
  BroadwayServer *server = input->server;
723

724 725
  if (!broadway_server_read_all_input_nonblocking (input))
    return FALSE;
726

727 728
  if (input->active)
    process_input_messages (server);
729 730 731 732

  return TRUE;
}

733
guint32
734
broadway_server_get_next_serial (BroadwayServer *server)
735 736 737 738 739 740 741
{
  if (server->output)
    return broadway_output_get_next_serial (server->output);

  return server->saved_serial;
}

742 743 744 745 746 747 748 749 750 751
void
broadway_server_get_screen_size (BroadwayServer   *server,
				 guint32          *width,
				 guint32          *height)
{
  *width = server->root->width;
  *height = server->root->height;
}


752
void
753
broadway_server_flush (BroadwayServer *server)
754 755 756 757 758 759 760 761 762 763
{
  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;
    }
}

764
#if 0
765 766
/* TODO: This is not used atm, is it needed? */
/* Note: This may be called while handling a message (i.e. sorta recursively) */
767
static BroadwayInputMsg *
768 769
broadway_server_block_for_input (BroadwayServer *server, char op,
				 guint32 serial, gboolean remove_message)
770 771 772 773 774 775 776 777
{
  BroadwayInputMsg *message;
  gssize res;
  guint8 buffer[1024];
  BroadwayInput *input;
  GInputStream *in;
  GList *l;

778
  broadway_server_flush (server);
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805

  if (server->input == NULL)
    return NULL;

  input = server->input;

  while (TRUE) {
    /* Check for existing reply in queue */

    for (l = server->input_messages; l != NULL; l = l->next)
      {
	message = l->data;

	if (message->base.type == op)
	  {
	    if (message->base.serial == serial)
	      {
		if (remove_message)
		  server->input_messages =
		    g_list_delete_link (server->input_messages, l);
		return message;
	      }
	  }
      }

    /* Not found, read more, blocking */

806 807
    in = g_io_stream_get_input_stream (input->connection);

808 809 810 811 812 813 814 815 816 817 818 819 820
    res = g_input_stream_read (in, buffer, sizeof (buffer), NULL, NULL);
    if (res <= 0)
      return NULL;
    g_byte_array_append (input->buffer, buffer, res);

    parse_input (input);

    /* 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);
  }
}
821
#endif
822

Tarnyko's avatar
Tarnyko committed
823 824 825 826 827 828 829
static void *
map_named_shm (char *name, gsize size)
{
#ifdef G_OS_UNIX

  int fd;
  void *ptr;
830
  char *filename = NULL;
Tarnyko's avatar
Tarnyko committed
831

832
  fd = shm_open (name, O_RDONLY, 0600);
Tarnyko's avatar
Tarnyko committed
833 834
  if (fd == -1)
    {
835 836 837 838 839
      filename = g_build_filename (g_get_tmp_dir (), name, NULL);
      fd = open (filename, O_RDONLY);
      if (fd == -1)
	{
	  perror ("Failed to map shm");
840 841
	  g_free (filename);

842 843
	  return NULL;
	}
Tarnyko's avatar
Tarnyko committed
844 845
    }

846
  ptr = mmap (0, size, PROT_READ, MAP_SHARED, fd, 0);
Tarnyko's avatar
Tarnyko committed
847

848
  (void) close (fd);
Tarnyko's avatar
Tarnyko committed
849

850 851 852 853 854 855 856
  if (filename)
    {
      unlink (filename);
      g_free (filename);
    }
  else
    shm_unlink (name);
Tarnyko's avatar
Tarnyko committed
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903

  return ptr;

#elif defined(G_OS_WIN32)

  int fd;
  void *ptr;
  char *shmpath;
  void *map = ((void *)-1);

  if (*name == '/')
    ++name;
  shmpath = g_build_filename (g_get_tmp_dir (), name, NULL);

  fd = open(shmpath, O_RDONLY, 0600);
  if (fd == -1)
    {
      g_free (shmpath);
      perror ("Failed to shm_open");
      return NULL;
    }

  if (size == 0)
    ptr = map;
  else
    {
      HANDLE h, fm;
      h = (HANDLE)_get_osfhandle (fd);
      fm = CreateFileMapping (h, NULL, PAGE_READONLY, 0, (DWORD)size, NULL);
      ptr = MapViewOfFile (fm, FILE_MAP_READ, 0, 0, (size_t)size);
      CloseHandle (fm);
    }

  (void) close(fd);

  remove (shmpath);
  g_free (shmpath);

  return ptr;

#else
#error "No shm mapping supported"

  return NULL;
#endif
}

904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
static char *
parse_line (char *line, char *key)
{
  char *p;

  if (!g_str_has_prefix (line, key))
    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
920

921 922 923 924 925 926 927 928 929 930 931 932 933
static void
send_error (HttpRequest *request,
	    int error_code,
	    const char *reason)
{
  char *res;

  res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n"
			 "<html><head><title>%d %s</title></head>"
			 "<body>%s</body></html>",
			 error_code, reason,
			 error_code, reason,
			 reason);
934

935
  /* TODO: This should really be async */
936 937 938
  g_output_stream_write_all (g_io_stream_get_output_stream (request->connection),
                             res, strlen (res), NULL, NULL, NULL);

939 940 941 942 943 944 945 946 947 948 949 950
  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=' */
static gchar *
generate_handshake_response_wsietf_v7 (const gchar *key)
{
  gsize digest_len = 20;
951
  guchar digest[20];
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
  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
970
start_input (HttpRequest *request)
971 972 973 974 975 976 977 978 979 980
{
  char **lines;
  char *p;
  int i;
  char *res;
  char *origin, *host;
  BroadwayInput *input;
  const void *data_buffer;
  gsize data_buffer_size;
  GInputStream *in;
981
  char *key;
982 983
  GSocket *socket;
  int flag = 1;
984 985 986 987 988 989

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

990
  key = NULL;
991 992 993 994
  origin = NULL;
  host = NULL;
  for (i = 0; lines[i] != NULL; i++)
    {
995 996
      if ((p = parse_line (lines[i], "Sec-WebSocket-Key")))
        key = p;
997
      else if ((p = parse_line (lines[i], "Origin")))
998
        origin = p;
999
      else if ((p = parse_line (lines[i], "Host")))
1000
        host = p;
1001
      else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin")))
1002
        origin = p;
1003 1004
    }

1005
  if (host == NULL)
1006 1007 1008 1009 1010 1011
    {
      g_strfreev (lines);
      send_error (request, 400, "Bad websocket request");
      return;
    }

1012
  if (key != NULL)
1013
    {
1014
      char* accept = generate_handshake_response_wsietf_v7 (key);
1015 1016 1017 1018
      res = g_strdup_printf ("HTTP/1.1 101 Switching Protocols\r\n"
			     "Upgrade: websocket\r\n"
			     "Connection: Upgrade\r\n"
			     "Sec-WebSocket-Accept: %s\r\n"
1019
			     "%s%s%s"
1020 1021
			     "Sec-WebSocket-Location: ws://%s/socket\r\n"
			     "Sec-WebSocket-Protocol: broadway\r\n"
1022 1023 1024
			     "\r\n", accept,
			     origin?"Sec-WebSocket-Origin: ":"", origin?origin:"", origin?"\r\n":"",
			     host);
1025 1026 1027 1028 1029 1030
      g_free (accept);

#ifdef DEBUG_WEBSOCKETS
      g_print ("v7 proto response:\n%s", res);
#endif

1031 1032
      g_output_stream_write_all (g_io_stream_get_output_stream (request->connection),
                                 res, strlen (res), NULL, NULL, NULL);
1033 1034 1035 1036
      g_free (res);
    }
  else
    {
1037 1038 1039
      g_strfreev (lines);
      send_error (request, 400, "Bad websocket request");
      return;
1040 1041
    }

1042
  socket = g_socket_connection_get_socket (request->socket_connection);
1043 1044
  setsockopt (g_socket_get_fd (socket), IPPROTO_TCP,
	      TCP_NODELAY, (char *) &flag, sizeof(int));
1045

1046 1047 1048 1049 1050 1051 1052 1053
  input = g_new0 (BroadwayInput, 1);
  input->server = request->server;
  input->connection = g_object_ref (request->connection);

  data_buffer = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (request->data), &data_buffer_size);
  input->buffer = g_byte_array_sized_new (data_buffer_size);
  g_byte_array_append (input->buffer, data_buffer, data_buffer_size);

1054
  input->output =
1055
    broadway_output_new (g_io_stream_get_output_stream (request->connection), 0);
1056 1057 1058 1059

  /* This will free and close the data input stream, but we got all the buffered content already */
  http_request_free (request);

1060 1061
  in = g_io_stream_get_input_stream (input->connection);

1062 1063 1064 1065
  input->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (in), NULL);
  g_source_set_callback (input->source, (GSourceFunc)input_data_cb, input, NULL);
  g_source_attach (input->source, NULL);

1066
  start (input);
1067

1068 1069 1070 1071 1072 1073 1074
  /* Process any data in the pipe already */
  parse_input (input);

  g_strfreev (lines);
}

static void
1075
start (BroadwayInput *input)
1076
{
1077
  BroadwayServer *server;
1078

1079
  input->active = TRUE;
1080

1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
  server = BROADWAY_SERVER (input->server);

  if (server->output)
    {
      broadway_output_disconnected (server->output);
      broadway_output_flush (server->output);
    }

  if (server->input != NULL)
    {
      broadway_input_free (server->input);
      server->input = NULL;
    }

  server->input = input;
1096 1097 1098 1099 1100 1101

  if (server->output)
    {
      server->saved_serial = broadway_output_get_next_serial (server->output);
      broadway_output_free (server->output);
    }
1102
  server->output = input->output;
1103

1104 1105
  broadway_output_set_next_serial (server->output, server->saved_serial);
  broadway_output_flush (server->output);
1106

1107
  broadway_server_resync_windows (server);
1108 1109 1110 1111 1112

  if (server->pointer_grab_window_id != -1)
    broadway_output_grab_pointer (server->output,
				  server->pointer_grab_window_id,
				  server->pointer_grab_owner_events);
1113 1114

  process_input_messages (server);
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
}

static void
send_data (HttpRequest *request,
	     const char *mimetype,
	     const char *data, gsize len)
{
  char *res;

  res = g_strdup_printf ("HTTP/1.0 200 OK\r\n"
			 "Content-Type: %s\r\n"
			 "Content-Length: %"G_GSIZE_FORMAT"\r\n"
			 "\r\n",
			 mimetype, len);
1129

1130
  /* TODO: This should really be async */
1131
  g_output_stream_write_all (g_io_stream_get_output_stream (request->connection),
1132 1133
			     res, strlen (res), NULL, NULL, NULL);
  g_free (res);
1134
  g_output_stream_write_all (g_io_stream_get_output_stream (request->connection),
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
			     data, len, NULL, NULL, NULL);
  http_request_free (request);
}

#include "clienthtml.h"
#include "broadwayjs.h"

static void
got_request (HttpRequest *request)
{
  char *start, *escaped, *tmp, *version, *query;

  if (!g_str_has_prefix (request->request->str, "GET "))
    {
      send_error (request, 501, "Only GET implemented");
      return;
    }

  start = request->request->str + 4; /* Skip "GET " */

  while (*start == ' ')
    start++;

  for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++)
    ;
  escaped = g_strndup (start, tmp - start);
  version = NULL;
  if (*tmp == ' ')
    {
      start = tmp;
      while (*start == ' ')
	start++;
      for (tmp = start; *tmp != 0 && *tmp != ' ' && *tmp != '\n'; tmp++)
	;
      version = g_strndup (start, tmp - start);
    }

  query = strchr (escaped, '?');
  if (query)
    *query = 0;

  if (strcmp (escaped, "/client.html") == 0 || strcmp (escaped, "/") == 0)
    send_data (request, "text/html", client_html, G_N_ELEMENTS(client_html) - 1);
  else if (strcmp (escaped, "/broadway.js") == 0)
    send_data (request, "text/javascript", broadway_js, G_N_ELEMENTS(broadway_js) - 1);
  else if (strcmp (escaped, "/socket") == 0)
1181
    start_input (request);
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
  else
    send_error (request, 404, "File not found");

  g_free (escaped);
  g_free (version);
}

static void
got_http_request_line (GInputStream *stream,
		       GAsyncResult *result,
		       HttpRequest *request)
{
  char *line;

  line = g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (stream), result, NULL, NULL);
  if (line == NULL)
    {
      http_request_free (request);
      g_printerr ("Error reading request lines\n");
      return;
    }
  if (strlen (line) == 0)
    got_request (request);
  else
    {
      /* Protect against overflow in request length */
      if (request->request->len > 1024 * 5)
	{
	  send_error (request, 400, "Request too long");
	}
      else
	{
	  g_string_append_printf (request->request, "%s\n", line);
	  g_data_input_stream_read_line_async (request->data, 0, NULL,
					       (GAsyncReadyCallback)got_http_request_line, request);
	}
    }
  g_free (line);
}

static gboolean
handle_incoming_connection (GSocketService    *service,
			    GSocketConnection *connection,
			    GObject           *source_object)
{
  HttpRequest *request;
  GInputStream *in;

  request = g_new0 (HttpRequest, 1);
1231
  request->socket_connection = g_object_ref (connection);
1232
  request->server = BROADWAY_SERVER (source_object);
1233 1234
  request->request = g_string_new ("");

1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
  if (request->server->ssl_cert && request->server->ssl_key)
    {
      GError *error = NULL;
      GTlsCertificate *certificate;

      certificate = g_tls_certificate_new_from_files (request->server->ssl_cert,
                                                      request->server->ssl_key,
                                                      &error);
      if (!certificate)
        {
          g_warning ("Cannot create TLS certificate: %s", error->message);
          g_error_free (error);
          return FALSE;
        }

      request->connection = g_tls_server_connection_new (G_IO_STREAM (connection),
                                                         certificate,
                                                         &error);
      if (!request->connection)
        {
          g_warning ("Cannot create TLS connection: %s", error->message);
          g_error_free (error);
          return FALSE;
        }

      if (!g_tls_connection_handshake (G_TLS_CONNECTION (request->connection),
                                       NULL, &error))
        {
          g_warning ("Cannot create TLS connection: %s", error->message);
          g_error_free (error);
          return FALSE;
        }
    }
  else
    {
1270
      request->connection = G_IO_STREAM (g_object_ref (connection));
1271 1272 1273
    }

  in = g_io_stream_get_input_stream (request->connection);
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284

  request->data = g_data_input_stream_new (in);
  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (request->data), FALSE);
  /* Be tolerant of input */
  g_data_input_stream_set_newline_type (request->data, G_DATA_STREAM_NEWLINE_TYPE_ANY);

  g_data_input_stream_read_line_async (request->data, 0, NULL,
				       (GAsyncReadyCallback)got_http_request_line, request);
  return TRUE;
}

1285
BroadwayServer *
1286 1287 1288 1289 1290
broadway_server_new (char        *address,
                     int          port,
                     const char  *ssl_cert,
                     const char  *ssl_key,
                     GError     **error)
1291
{
1292
  BroadwayServer *server;
1293 1294
  GInetAddress *inet_address;
  GSocketAddress *socket_address;
1295

1296
  server = g_object_new (BROADWAY_TYPE_SERVER, NULL);
1297
  server->port = port;
1298
  server->address = g_strdup (address);
1299 1300
  server->ssl_cert = g_strdup (ssl_cert);
  server->ssl_key = g_strdup (ssl_key);
1301

1302
  if (address == NULL)
1303
    {
1304 1305 1306 1307 1308 1309
      if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (server->service),
					    server->port,
					    G_OBJECT (server),
					    error))
	{
	  g_prefix_error (error, "Unable to listen to port %d: ", server->port);
1310
	  g_object_unref (server);
1311 1312 1313 1314 1315 1316 1317 1318 1319
	  return NULL;
	}
    }
  else
    {
      inet_address = g_inet_address_new_from_string (address);
      if (inet_address == NULL)
	{
	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid ip address %s: ", address);
1320
	  g_object_unref (server);
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
	  return NULL;
	}
      socket_address = g_inet_socket_address_new (inet_address, port);
      g_object_unref (inet_address);
      if (!g_socket_listener_add_address (G_SOCKET_LISTENER (server->service),
					  socket_address,
					  G_SOCKET_TYPE_STREAM,
					  G_SOCKET_PROTOCOL_TCP,
					  G_OBJECT (server),
					  NULL,
					  error))
	{
	  g_prefix_error (error, "Unable to listen to %s:%d: ", server->address, server->port);
	  g_object_unref (socket_address);
1335
	  g_object_unref (server);
1336 1337 1338
	  return NULL;
	}
      g_object_unref (socket_address);
1339 1340 1341 1342 1343 1344 1345
    }

  g_signal_connect (server->service, "incoming",
		    G_CALLBACK (handle_incoming_connection), NULL);
  return server;
}

1346 1347 1348 1349
BroadwayServer *
broadway_server_on_unix_socket_new (char *address, GError **error)
{
  BroadwayServer *server;
1350
  GSocketAddress *socket_address = NULL;
1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363

  server = g_object_new (BROADWAY_TYPE_SERVER, NULL);
  server->port = -1;
  server->address = g_strdup (address);

  if (address == NULL)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unspecified unix domain socket address");
      g_object_unref (server);
      return NULL;
    }
  else
    {
1364
#ifdef HAVE_GIO_UNIX
1365
      socket_address = g_unix_socket_address_new (address);
1366
#endif
1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393
      if (socket_address == NULL)
	{
	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid unix domain socket address %s: ", address);
	  g_object_unref (server);
	  return NULL;
	}
      if (!g_socket_listener_add_address (G_SOCKET_LISTENER (server->service),
					  socket_address,
					  G_SOCKET_TYPE_STREAM,
					  G_SOCKET_PROTOCOL_DEFAULT,
					  G_OBJECT (server),
					  NULL,
					  error))
	{
	  g_prefix_error (error, "Unable to listen to %s: ", server->address);
	  g_object_unref (socket_address);
	  g_object_unref (server);
	  return NULL;
	}
      g_object_unref (socket_address);
    }

  g_signal_connect (server->service, "incoming",
		    G_CALLBACK (handle_incoming_connection), NULL);
  return server;
}

1394
guint32
1395
broadway_server_get_last_seen_time (BroadwayServer *server)
1396
{
1397
  broadway_server_consume_all_input (server);
1398 1399 1400 1401
  return (guint32) server->last_seen_time;
}

void
1402 1403 1404 1405 1406
broadway_server_query_mouse (BroadwayServer *server,
			     guint32            *toplevel,
			     gint32             *root_x,
			     gint32             *root_y,
			     guint32            *mask)
1407 1408 1409
{
  if (server->output)
    {
1410
      broadway_server_consume_all_input (server);
1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433
      if (root_x)
	*root_x = server->future_root_x;
      if (root_y)
	*root_y = server->future_root_y;
      if (mask)
	*mask = server->future_state;
      if (toplevel)
	*toplevel = server->future_mouse_in_toplevel;
      return;
    }

  /* Fallback when unconnected */
  if (root_x)
    *root_x = server->last_x;
  if (root_y)
    *root_y = server->last_y;
  if (mask)
    *mask = server->last_state;
  if (toplevel)
    *toplevel = server->mouse_in_toplevel_id;
}

void
1434 1435
broadway_server_destroy_window (BroadwayServer *server,
				gint id)
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458
{
  BroadwayWindow *window;

  if (server->mouse_in_toplevel_id == id)
    {
      /* TODO: Send leave + enter event, update cursors, etc */
      server->mouse_in_toplevel_id = 0;
    }

  if (server->pointer_grab_window_id == id)
    server->pointer_grab_window_id = -1;

  if (server->output)
    broadway_output_destroy_surface (server->output,
				     id);

  window = g_hash_table_lookup (server->id_ht,
				GINT_TO_POINTER (id));
  if (window != NULL)
    {
      server->toplevels = g_list_remove (server->toplevels, window);
      g_hash_table_remove (server->id_ht,
			   GINT_TO_POINTER (id));
1459

1460
      g_free (window->cached_surface_name);
1461 1462 1463
      if (window->cached_surface != NULL)
	cairo_surface_destroy (window->cached_surface);

1464 1465 1466 1467 1468
      g_free (window);
    }
}

gboolean
1469 1470
broadway_server_window_show (BroadwayServer *server,
			     gint id)
1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491
{
  BroadwayWindow *window;
  gboolean sent = FALSE;

  window = g_hash_table_lookup (server->id_ht,
				GINT_TO_POINTER (id));
  if (window == NULL)
    return FALSE;

  window->visible = TRUE;

  if (server->output)
    {
      broadway_output_show_surface (server->output, window->id);
      sent = TRUE;
    }

  return sent;
}

gboolean
1492 1493
broadway_server_window_hide (BroadwayServer *server,
			     gint id)
1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510
{
  BroadwayWindow *window;
  gboolean sent = FALSE;

  window = g_hash_table_lookup (server->id_ht,
				GINT_TO_POINTER (id));
  if (window == NULL)
    return FALSE;

  window->visible = FALSE;

  if (server->mouse_in_toplevel_id == id)
    {
      /* TODO: Send leave + enter event, update cursors, etc */
      server->mouse_in_toplevel_id = 0;
    }

1511 1512 1513
  if (server->pointer_grab_window_id == id)
    server->pointer_grab_window_id = -1;

1514 1515 1516 1517 1518 1519 1520 1521
  if (server->output)
    {
      broadway_output_hide_surface (server->output, window->id);
      sent = TRUE;
    }
  return sent;
}

1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
void
broadway_server_window_raise (BroadwayServer *server,
                              gint id)
{
  BroadwayWindow *window;

  window = g_hash_table_lookup (server->id_ht,
				GINT_TO_POINTER (id));
  if (window == NULL)
    return;

  server->toplevels = g_list_remove (server->toplevels, window);
  server->toplevels = g_list_append (server->toplevels, window);

  if (server->output)
    broadway_output_raise_surface (server->output, window->id);
}

1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552
void
broadway_server_set_show_keyboard (BroadwayServer *server,
                                   gboolean show)
{
  server->show_keyboard = show;

  if (server->output)
    {
      broadway_output_set_show_keyboard (server->output, server->show_keyboard);
      broadway_server_flush (server);
   }
}

1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570
void
broadway_server_window_lower (BroadwayServer *server,
                              gint id)
{
  BroadwayWindow *window;

  window = g_hash_table_lookup (server->id_ht,
				GINT_TO_POINTER (id));
  if (window == NULL)
    return;

  server->toplevels = g_list_remove (server->toplevels, window);
  server->toplevels = g_list_prepend (server->toplevels, window);

  if (server->output)
    broadway_output_lower_surface (server->output, window->id);
}

1571
void
1572 1573
broadway_server_window_set_transient_for (BroadwayServer *server,
					  gint id, gint parent)
1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586
{
  BroadwayWindow *window;

  window = g_hash_table_lookup (server->id_ht,
				GINT_TO_POINTER (id));
  if (window == NULL)
    return;

  window->transient_for = parent;

  if (server->output)
    {
      broadway_output_set_transient_for (server->output, window->id, window->transient_for);
1587
      broadway_server_flush (server);
1588 1589 1590 1591
    }
}

gboolean
1592
broadway_server_has_client (BroadwayServer *server)
1593 1594 1595 1596 1597
{
  return server->output != NULL;
}

void
1598 1599 1600
broadway_server_window_update (BroadwayServer *server,
			       gint id,
			       cairo_surface_t *surface)
1601 1602
{
  BroadwayWindow *window;
1603
  BroadwayBuffer *buffer;
1604 1605 1606 1607 1608 1609 1610 1611 1612

  if (surface == NULL)
    return;

  window = g_hash_table_lookup (server->id_ht,
				GINT_TO_POINTER (id));
  if (window == NULL)
    return;

1613 1614 1615
  g_assert (window->width == cairo_image_surface_get_width (surface));
  g_assert (window->height == cairo_image_surface_get_height (surface));

1616 1617 1618 1619
  buffer = broadway_buffer_create (window->width, window->height,
                                   cairo_image_surface_get_data (surface),
                                   cairo_image_surface_get_stride (surface));

1620 1621
  if (server->output != NULL)
    {
1622 1623 1624
      window->buffer_synced = TRUE;
      broadway_output_put_buffer (server->output, window->id,
                                  window->buffer, buffer);
1625 1626
    }

1627 1628 1629 1630
  if (window->buffer)
    broadway_buffer_destroy (window->buffer);

  window->buffer = buffer;
1631 1632 1633
}

gboolean
1634 1635
broadway_server_window_move_resize (BroadwayServer *server,
				    gint id,
1636
				    gboolean with_move,
1637 1638 1639 1640
				    int x,
				    int y,
				    int width,
				    int height)
1641 1642
{
  BroadwayWindow *window;
1643
  gboolean with_resize;
1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658
  gboolean sent = FALSE;

  window = g_hash_table_lookup (server->id_ht,
				GINT_TO_POINTER (id));
  if (window == NULL)
    return FALSE;

  with_resize = width != window->width || height != window->height;
  window->width = width;
  window->height = height;

  if (server->output != NULL)
    {
      broadway_output_move_resize_surface (server->output,
					   window->id,
1659
					   with_move, x, y,
1660 1661 1662
					   with_resize, window->width, window->height);
      sent = TRUE;
    }
1663 1664 1665 1666 1667 1668 1669 1670 1671 1672
  else
    {
      if (with_move)
	{
	  window->x = x;
	  window->y = y;
	}

      fake_configure_notify (server, window);
    }
1673 1674 1675 1676

  return sent;
}

1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688
void
broadway_server_focus_window (BroadwayServer *server,
                              gint new_focused_window)
{
  BroadwayInputMsg focus_msg;

  if (server->focused_window_id == new_focused_window)
    return;

  memset (&focus_msg, 0, sizeof (focus_msg));
  focus_msg.base.type = BROADWAY_EVENT_FOCUS;
  focus_msg.base.time = broadway_server_get_last_seen_time (server);
1689 1690
  focus_msg.focus.old_id = server->focused_window_id;
  focus_msg.focus.new_id = new_focused_window;
1691 1692

  broadway_events_got_input (&focus_msg, -1);
1693 1694 1695

  /* Keep track of the new focused window */
  server->focused_window_id = new_focused_window;
1696 1697
}

1698 1699
guint32
broadway_server_grab_pointer (BroadwayServer *server,
1700
			      gint client_id,
1701 1702 1703 1704
			      gint id,
			      gboolean owner_events,
			      guint32 event_mask,
			      guint32 time_)