[gtk+] broadway: Separate out the server parts



commit 0a808bea5422a9aa4c65070064973f3c09c95e21
Author: Alexander Larsson <alexl redhat com>
Date:   Wed Dec 19 12:37:02 2012 +0100

    broadway: Separate out the server parts
    
    This (shouldn't) change any behaviour, but it moves the
    webserver parts to a separate file, making the broadway display file
    smaller and preparing for later separating out the server to its own
    process.

 gdk/broadway/Makefile.am           |    9 +
 gdk/broadway/broadway-server.c     |   32 +
 gdk/broadway/broadway.h            |    5 +
 gdk/broadway/broadway.js           |    6 +-
 gdk/broadway/gdkbroadway-server.c  | 1695 ++++++++++++++++++++++++++++++++++++
 gdk/broadway/gdkbroadway-server.h  |  145 +++
 gdk/broadway/gdkdevice-broadway.c  |  109 +--
 gdk/broadway/gdkdisplay-broadway.c |  970 +--------------------
 gdk/broadway/gdkdisplay-broadway.h |  100 +---
 gdk/broadway/gdkeventsource.c      |   47 +-
 gdk/broadway/gdkprivate-broadway.h |    3 +-
 gdk/broadway/gdkwindow-broadway.c  |  282 +------
 12 files changed, 1969 insertions(+), 1434 deletions(-)
---
diff --git a/gdk/broadway/Makefile.am b/gdk/broadway/Makefile.am
index 592322e..d608b6e 100644
--- a/gdk/broadway/Makefile.am
+++ b/gdk/broadway/Makefile.am
@@ -17,6 +17,8 @@ LDADDS = $(GDK_DEP_LIBS)
 
 noinst_LTLIBRARIES = libbroadway.la libgdk-broadway.la
 
+libexec_PROGRAMS = broadway-server
+
 libgdkinclude_HEADERS = 	\
 	gdkbroadway.h
 
@@ -27,6 +29,8 @@ libgdkbroadwayinclude_HEADERS = 	\
 	gdkbroadwayvisual.h
 
 libbroadway_la_SOURCES =		\
+	gdkbroadway-server.h		\
+	gdkbroadway-server.c		\
 	broadway.h			\
 	broadway.c
 
@@ -77,6 +81,11 @@ libgdk_broadway_la_SOURCES =		\
 
 libgdk_broadway_la_LIBADD = libbroadway.la
 
+broadway_server_SOURCES = \
+	broadway-server.c
+
+broadway_server_LDADD = libbroadway.la $(GDK_DEP_LIBS)
+
 MAINTAINERCLEANFILES = $(broadway_built_sources)
 EXTRA_DIST += $(broadway_built_sources)
 
diff --git a/gdk/broadway/broadway-server.c b/gdk/broadway/broadway-server.c
new file mode 100644
index 0000000..7395abd
--- /dev/null
+++ b/gdk/broadway/broadway-server.c
@@ -0,0 +1,32 @@
+#include <glib.h>
+
+#include "gdkbroadway-server.h"
+
+int
+main (int argc, char *argv[])
+{
+  GdkBroadwayServer *server;
+  GError *error;
+  GMainLoop *loop;
+
+  error = NULL;
+  server = _gdk_broadway_server_new (8080, &error);
+  if (server == NULL)
+    {
+      g_printerr ("%s\n", error->message);
+      return 1;
+    }
+
+  loop = g_main_loop_new (NULL, FALSE);
+  g_main_loop_run (loop);
+  
+  return 0;
+}
+
+
+/* TODO: */
+
+void
+_gdk_broadway_events_got_input (BroadwayInputMsg *message)
+{
+}
diff --git a/gdk/broadway/broadway.h b/gdk/broadway/broadway.h
index e44f332..269a59d 100644
--- a/gdk/broadway/broadway.h
+++ b/gdk/broadway/broadway.h
@@ -1,3 +1,6 @@
+#ifndef __BROADWAY_H__
+#define __BROADWAY_H__
+
 #include <glib.h>
 #include <gio/gio.h>
 
@@ -78,3 +81,5 @@ void            broadway_output_grab_pointer    (BroadwayOutput *output,
 						 gboolean owner_event);
 guint32         broadway_output_ungrab_pointer  (BroadwayOutput *output);
 void            broadway_output_pong            (BroadwayOutput *output);
+
+#endif /* __BROADWAY_H__ */
diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js
index cea91db..acfec42 100644
--- a/gdk/broadway/broadway.js
+++ b/gdk/broadway/broadway.js
@@ -2671,7 +2671,7 @@ function handleKeyDown(e) {
 	// browser behaviors or it has no corresponding keyPress
 	// event, then send it immediately
 	if (!ignoreKeyEvent(ev))
-	    sendInput("k", [keysym, lastState]);
+	    sendInput("k", [realWindowWithMouse, keysym, lastState]);
 	suppress = true;
     }
 
@@ -2716,7 +2716,7 @@ function handleKeyPress(e) {
 
     // Send the translated keysym
     if (keysym > 0)
-	sendInput ("k", [keysym, lastState]);
+	sendInput ("k", [realWindowWithMouse, keysym, lastState]);
 
     // Stop keypress events just in case
     return cancelEvent(ev);
@@ -2735,7 +2735,7 @@ function handleKeyUp(e) {
     }
 
     if (keysym > 0)
-	sendInput ("K", [keysym, lastState]);
+	sendInput ("K", [realWindowWithMouse, keysym, lastState]);
     return cancelEvent(ev);
 }
 
diff --git a/gdk/broadway/gdkbroadway-server.c b/gdk/broadway/gdkbroadway-server.c
new file mode 100644
index 0000000..f0992aa
--- /dev/null
+++ b/gdk/broadway/gdkbroadway-server.c
@@ -0,0 +1,1695 @@
+#include "gdkbroadway-server.h"
+
+#include "broadway.h"
+#include "gdkprivate-broadway.h"
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+typedef struct BroadwayInput BroadwayInput;
+
+struct _GdkBroadwayServer {
+  GObject parent_instance;
+
+  int port;
+  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;
+
+  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 */
+  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;
+};
+
+struct _GdkBroadwayServerClass
+{
+  GObjectClass parent_class;
+};
+
+typedef struct HttpRequest {
+  GdkBroadwayServer *server;
+  GSocketConnection *connection;
+  GDataInputStream *data;
+  GString *request;
+}  HttpRequest;
+
+struct BroadwayInput {
+  GdkBroadwayServer *server;
+  GSocketConnection *connection;
+  GByteArray *buffer;
+  GSource *source;
+  gboolean seen_time;
+  gint64 time_base;
+  gboolean proto_v7_plus;
+  gboolean binary;
+};
+
+typedef struct {
+  gint32 id;
+  gint32 x;
+  gint32 y;
+  gint32 width;
+  gint32 height;
+  gboolean is_temp;
+  gboolean last_synced;
+  gboolean visible;
+  gint32 transient_for;
+
+  cairo_surface_t *last_surface;
+} BroadwayWindow;
+
+static void _gdk_broadway_server_resync_windows (GdkBroadwayServer *server);
+
+G_DEFINE_TYPE (GdkBroadwayServer, gdk_broadway_server, G_TYPE_OBJECT)
+
+static void
+gdk_broadway_server_init (GdkBroadwayServer *server)
+{
+  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;
+
+  g_hash_table_insert (server->id_ht,
+		       GINT_TO_POINTER (root->id),
+		       root);
+}
+
+static void
+gdk_broadway_server_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (gdk_broadway_server_parent_class)->finalize (object);
+}
+
+static void
+gdk_broadway_server_class_init (GdkBroadwayServerClass * class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = gdk_broadway_server_finalize;
+}
+
+static void start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary);
+
+static void
+http_request_free (HttpRequest *request)
+{
+  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
+update_event_state (GdkBroadwayServer *server,
+		    BroadwayInputMsg *message)
+{
+  switch (message->base.type) {
+  case 'e': /* Enter */
+    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;
+  case 'l': /* Leave */
+    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;
+  case 'm': /* Mouse move */
+    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;
+  case 'b':
+  case 'B':
+    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;
+  case 's':
+    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;
+  case 'k':
+  case 'K':
+    server->last_state = message->key.state;
+    break;
+  case 'g':
+  case 'u':
+    break;
+  case 'w':
+    break;
+  case 'W':
+    break;
+  case 'd':
+    break;
+
+  default:
+    g_printerr ("update_event_state - Unknown input command %c\n", message->base.type);
+    break;
+  }
+}
+
+gboolean
+_gdk_broadway_server_lookahead_event (GdkBroadwayServer  *server,
+				      const char         *types)
+{
+  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;
+}
+
+static void
+process_input_messages (GdkBroadwayServer *server)
+{
+  BroadwayInputMsg *message;
+
+  while (server->input_messages)
+    {
+      message = server->input_messages->data;
+      server->input_messages =
+	g_list_delete_link (server->input_messages,
+			    server->input_messages);
+
+
+      update_event_state (server, message);
+      _gdk_broadway_events_got_input (message);
+      g_free (message);
+    }
+}
+
+static char *
+parse_pointer_data (char *p, BroadwayInputPointerMsg *data)
+{
+  data->mouse_window_id = strtol (p, &p, 10);
+  p++; /* Skip , */
+  data->event_window_id = strtol (p, &p, 10);
+  p++; /* Skip , */
+  data->root_x = strtol (p, &p, 10);
+  p++; /* Skip , */
+  data->root_y = strtol (p, &p, 10);
+  p++; /* Skip , */
+  data->win_x = strtol (p, &p, 10);
+  p++; /* Skip , */
+  data->win_y = strtol (p, &p, 10);
+  p++; /* Skip , */
+  data->state = strtol (p, &p, 10);
+
+  return p;
+}
+
+static void
+update_future_pointer_info (GdkBroadwayServer *server, BroadwayInputPointerMsg *data)
+{
+  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
+parse_input_message (BroadwayInput *input, const char *message)
+{
+  GdkBroadwayServer *server = input->server;
+  BroadwayInputMsg msg;
+  char *p;
+  gint64 time_;
+
+  p = (char *)message;
+  msg.base.type = *p++;
+  msg.base.serial = (guint32)strtol (p, &p, 10);
+  p++; /* Skip , */
+  time_ = strtol(p, &p, 10);
+  p++; /* Skip , */
+
+  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) {
+  case 'e': /* Enter */
+  case 'l': /* Leave */
+    p = parse_pointer_data (p, &msg.pointer);
+    update_future_pointer_info (server, &msg.pointer);
+    p++; /* Skip , */
+    msg.crossing.mode = strtol(p, &p, 10);
+    break;
+
+  case 'm': /* Mouse move */
+    p = parse_pointer_data (p, &msg.pointer);
+    update_future_pointer_info (server, &msg.pointer);
+    break;
+
+  case 'b':
+  case 'B':
+    p = parse_pointer_data (p, &msg.pointer);
+    update_future_pointer_info (server, &msg.pointer);
+    p++; /* Skip , */
+    msg.button.button = strtol(p, &p, 10);
+    break;
+
+  case 's':
+    p = parse_pointer_data (p, &msg.pointer);
+    update_future_pointer_info (server, &msg.pointer);
+    p++; /* Skip , */
+    msg.scroll.dir = strtol(p, &p, 10);
+    break;
+
+  case 'k':
+  case 'K':
+    msg.key.mouse_window_id = strtol(p, &p, 10);
+    p++; /* Skip , */
+    msg.key.key = strtol(p, &p, 10);
+    p++; /* Skip , */
+    msg.key.state = strtol(p, &p, 10);
+    break;
+
+  case 'g':
+  case 'u':
+    msg.grab_reply.res = strtol(p, &p, 10);
+    break;
+
+  case 'w':
+    msg.configure_notify.id = strtol(p, &p, 10);
+    p++; /* Skip , */
+    msg.configure_notify.x = strtol (p, &p, 10);
+    p++; /* Skip , */
+    msg.configure_notify.y = strtol (p, &p, 10);
+    p++; /* Skip , */
+    msg.configure_notify.width = strtol (p, &p, 10);
+    p++; /* Skip , */
+    msg.configure_notify.height = strtol (p, &p, 10);
+    break;
+
+  case 'W':
+    msg.delete_notify.id = strtol(p, &p, 10);
+    break;
+
+  case 'd':
+    msg.screen_resize_notify.width = strtol (p, &p, 10);
+    p++; /* Skip , */
+    msg.screen_resize_notify.height = strtol (p, &p, 10);
+    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)
+{
+  GdkBroadwayServer *server = input->server;
+
+  if (!input->buffer->len)
+    return;
+
+  if (input->proto_v7_plus)
+    {
+      hex_dump (input->buffer->data, input->buffer->len);
+
+      while (input->buffer->len > 2)
+	{
+	  gsize len, payload_len;
+	  BroadwayWSOpCode code;
+	  gboolean is_mask, fin;
+	  guchar *buf, *data, *mask;
+
+	  buf = input->buffer->data;
+	  len = input->buffer->len;
+
+#ifdef DEBUG_WEBSOCKETS
+	  g_print ("Parse input first byte 0x%2x 0x%2x\n", buf[0], buf[1]);
+#endif
+
+	  fin = buf[0] & 0x80;
+	  code = buf[0] & 0x0f;
+	  payload_len = buf[1] & 0x7f;
+	  is_mask = buf[1] & 0x80;
+	  data = buf + 2;
+
+	  if (payload_len > 125)
+	    {
+	      if (len < 4)
+		return;
+	      payload_len = GUINT16_FROM_BE( *(guint16 *) data );
+	      data += 2;
+	    }
+	  else if (payload_len > 126)
+	    {
+	      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 */
+	  case BROADWAY_WS_TEXT:
+	    if (!fin)
+	      {
+#ifdef DEBUG_WEBSOCKETS
+		g_warning ("can't yet accept fragmented input");
+#endif
+	      }
+	    else
+	      {
+		char *terminated = g_strndup((char *)data, payload_len);
+	        parse_input_message (input, terminated);
+		g_free (terminated);
+	      }
+	    break;
+	  case BROADWAY_WS_CNX_PING:
+	    broadway_output_pong (server->output);
+	    break;
+	  case BROADWAY_WS_CNX_PONG:
+	    break; /* we never send pings, but tolerate pongs */
+	  case BROADWAY_WS_BINARY:
+	  case BROADWAY_WS_CONTINUATION:
+	  default:
+	    {
+	      g_warning ("fragmented or unknown input code 0x%2x with fin set", code);
+	      break;
+	    }
+	  }
+
+	  g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len);
+	}
+    }
+  else /* old style protocol */
+    {
+      char *buf, *ptr;
+      gsize len;
+
+      buf = (char *)input->buffer->data;
+      len = input->buffer->len;
+
+      if (buf[0] != 0)
+	{
+	  server->input = NULL;
+	  broadway_input_free (input);
+	  return;
+	}
+
+      while ((ptr = memchr (buf, 0xff, len)) != NULL)
+	{
+	  *ptr = 0;
+	  ptr++;
+
+	  parse_input_message (input, buf + 1);
+
+	  len -= ptr - buf;
+	  buf = ptr;
+
+	  if (len > 0 && buf[0] != 0)
+	    {
+	      server->input = NULL;
+	      broadway_input_free (input);
+	      break;
+	    }
+	}
+      g_byte_array_remove_range (input->buffer, 0, buf - (char *)input->buffer->data);
+    }
+}
+
+
+static gboolean
+process_input_idle_cb (GdkBroadwayServer *server)
+{
+  server->process_input_idle = 0;
+  process_input_messages (server);
+  return G_SOURCE_REMOVE;
+}
+
+static void
+queue_process_input_at_idle (GdkBroadwayServer *server)
+{
+  if (server->process_input_idle == 0)
+    server->process_input_idle =
+      g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)process_input_idle_cb, server, NULL);
+}
+
+static void
+_gdk_broadway_server_read_all_input_nonblocking (GdkBroadwayServer *server)
+{
+  GInputStream *in;
+  gssize res;
+  guint8 buffer[1024];
+  GError *error;
+  BroadwayInput *input;
+
+  if (server->input == NULL)
+    return;
+
+  input = server->input;
+
+  in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
+
+  error = NULL;
+  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);
+	  return;
+	}
+
+      server->input = NULL;
+      broadway_input_free (input);
+      if (res < 0)
+	{
+	  g_print ("input error %s\n", error->message);
+	  g_error_free (error);
+	}
+      return;
+    }
+
+  g_byte_array_append (input->buffer, buffer, res);
+
+  parse_input (input);
+}
+
+static void
+_gdk_broadway_server_consume_all_input (GdkBroadwayServer *server)
+{
+  _gdk_broadway_server_read_all_input_nonblocking (server);
+
+  /* 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)
+{
+  GdkBroadwayServer *server = input->server;
+
+  _gdk_broadway_server_read_all_input_nonblocking (server);
+
+  process_input_messages (server);
+
+  return TRUE;
+}
+
+gulong
+_gdk_broadway_server_get_next_serial (GdkBroadwayServer *server)
+{
+  if (server->output)
+    return broadway_output_get_next_serial (server->output);
+
+  return server->saved_serial;
+}
+
+void
+_gdk_broadway_server_flush (GdkBroadwayServer *server)
+{
+  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;
+    }
+}
+
+void
+_gdk_broadway_server_sync (GdkBroadwayServer *server)
+{
+  _gdk_broadway_server_flush (server);
+}
+
+
+/* TODO: This is not used atm, is it needed? */
+/* Note: This may be called while handling a message (i.e. sorta recursively) */
+BroadwayInputMsg *
+_gdk_broadway_server_block_for_input (GdkBroadwayServer *server, char op,
+				       guint32 serial, gboolean remove_message)
+{
+  BroadwayInputMsg *message;
+  gssize res;
+  guint8 buffer[1024];
+  BroadwayInput *input;
+  GInputStream *in;
+  GList *l;
+
+  _gdk_broadway_server_flush (server);
+
+  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 */
+
+    in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
+    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);
+  }
+}
+
+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;
+}
+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);
+  /* TODO: This should really be async */
+  g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+			     res, strlen (res), NULL, NULL, NULL);
+  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;
+  guchar digest[digest_len];
+  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
+start_input (HttpRequest *request, gboolean binary)
+{
+  char **lines;
+  char *p;
+  int num_key1, num_key2;
+  guint64 key1, key2;
+  int num_space;
+  int i;
+  guint8 challenge[16];
+  char *res;
+  gsize len;
+  GChecksum *checksum;
+  char *origin, *host;
+  GdkBroadwayServer *server;
+  BroadwayInput *input;
+  const void *data_buffer;
+  gsize data_buffer_size;
+  GInputStream *in;
+  char *key_v7;
+  gboolean proto_v7_plus;
+
+  server = GDK_BROADWAY_SERVER (request->server);
+
+  if (server->input != NULL)
+    {
+      send_error (request, 409, "Input already handled");
+      return;
+    }
+
+#ifdef DEBUG_WEBSOCKETS
+  g_print ("incoming request:\n%s\n", request->request->str);
+#endif
+  lines = g_strsplit (request->request->str, "\n", 0);
+
+  num_key1 = 0;
+  num_key2 = 0;
+  key1 = 0;
+  key2 = 0;
+  key_v7 = NULL;
+  origin = NULL;
+  host = NULL;
+  for (i = 0; lines[i] != NULL; i++)
+    {
+      if ((p = parse_line (lines[i], "Sec-WebSocket-Key1")))
+	{
+	  num_space = 0;
+	  while (*p != 0)
+	    {
+	      if (g_ascii_isdigit (*p))
+		key1 = key1 * 10 + g_ascii_digit_value (*p);
+	      else if (*p == ' ')
+		num_space++;
+
+	      p++;
+	    }
+	  key1 /= num_space;
+	  num_key1++;
+	}
+      else if ((p = parse_line (lines[i], "Sec-WebSocket-Key2")))
+	{
+	  num_space = 0;
+	  while (*p != 0)
+	    {
+	      if (g_ascii_isdigit (*p))
+		key2 = key2 * 10 + g_ascii_digit_value (*p);
+	      else if (*p == ' ')
+		num_space++;
+
+	      p++;
+	    }
+	  key2 /= num_space;
+	  num_key2++;
+	}
+      else if ((p = parse_line (lines[i], "Sec-WebSocket-Key")))
+	{
+	  key_v7 = p;
+	}
+      else if ((p = parse_line (lines[i], "Origin")))
+	{
+	  origin = p;
+	}
+      else if ((p = parse_line (lines[i], "Host")))
+	{
+	  host = p;
+	}
+      else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin")))
+	{
+	  origin = p;
+	}
+    }
+
+  if (origin == NULL || host == NULL)
+    {
+      g_strfreev (lines);
+      send_error (request, 400, "Bad websocket request");
+      return;
+    }
+
+  if (key_v7 != NULL)
+    {
+      char* accept = generate_handshake_response_wsietf_v7 (key_v7);
+      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"
+			     "Sec-WebSocket-Origin: %s\r\n"
+			     "Sec-WebSocket-Location: ws://%s/socket\r\n"
+			     "Sec-WebSocket-Protocol: broadway\r\n"
+			     "\r\n", accept, origin, host);
+      g_free (accept);
+
+#ifdef DEBUG_WEBSOCKETS
+      g_print ("v7 proto response:\n%s", res);
+#endif
+
+      g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+				 res, strlen (res), NULL, NULL, NULL);
+      g_free (res);
+      proto_v7_plus = TRUE;
+    }
+  else
+    {
+      if (num_key1 != 1 || num_key2 != 1)
+	{
+	  g_strfreev (lines);
+	  send_error (request, 400, "Bad websocket request");
+	  return;
+	}
+
+      challenge[0] = (key1 >> 24) & 0xff;
+      challenge[1] = (key1 >> 16) & 0xff;
+      challenge[2] = (key1 >>  8) & 0xff;
+      challenge[3] = (key1 >>  0) & 0xff;
+      challenge[4] = (key2 >> 24) & 0xff;
+      challenge[5] = (key2 >> 16) & 0xff;
+      challenge[6] = (key2 >>  8) & 0xff;
+      challenge[7] = (key2 >>  0) & 0xff;
+
+      if (!g_input_stream_read_all (G_INPUT_STREAM (request->data), challenge+8, 8, NULL, NULL, NULL))
+	{
+	  g_strfreev (lines);
+	  send_error (request, 400, "Bad websocket request");
+	  return;
+	}
+
+      checksum = g_checksum_new (G_CHECKSUM_MD5);
+      g_checksum_update (checksum, challenge, 16);
+      len = 16;
+      g_checksum_get_digest (checksum, challenge, &len);
+      g_checksum_free (checksum);
+
+      res = g_strdup_printf ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+			     "Upgrade: WebSocket\r\n"
+			     "Connection: Upgrade\r\n"
+			     "Sec-WebSocket-Origin: %s\r\n"
+			     "Sec-WebSocket-Location: ws://%s/socket\r\n"
+			     "Sec-WebSocket-Protocol: broadway\r\n"
+			     "\r\n",
+			     origin, host);
+
+#ifdef DEBUG_WEBSOCKETS
+      g_print ("legacy response:\n%s", res);
+#endif
+      g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+				 res, strlen (res), NULL, NULL, NULL);
+      g_free (res);
+      g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+				 challenge, 16, NULL, NULL, NULL);
+      proto_v7_plus = FALSE;
+    }
+
+  input = g_new0 (BroadwayInput, 1);
+
+  input->server = request->server;
+  input->connection = g_object_ref (request->connection);
+  input->proto_v7_plus = proto_v7_plus;
+  input->binary = binary;
+
+  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);
+
+  server->input = input;
+
+  start_output (request, proto_v7_plus, binary);
+
+  /* This will free and close the data input stream, but we got all the buffered content already */
+  http_request_free (request);
+
+  in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
+  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);
+
+  /* Process any data in the pipe already */
+  parse_input (input);
+  process_input_messages (server);
+
+  g_strfreev (lines);
+}
+
+static void
+start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary)
+{
+  GSocket *socket;
+  GdkBroadwayServer *server;
+  int flag = 1;
+
+  socket = g_socket_connection_get_socket (request->connection);
+  setsockopt(g_socket_get_fd (socket), IPPROTO_TCP,
+	     TCP_NODELAY, (char *) &flag, sizeof(int));
+
+  server = GDK_BROADWAY_SERVER (request->server);
+
+  if (server->output)
+    {
+      server->saved_serial = broadway_output_get_next_serial (server->output);
+      broadway_output_free (server->output);
+    }
+
+  server->output =
+    broadway_output_new (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+			 server->saved_serial, proto_v7_plus, binary);
+
+  _gdk_broadway_server_resync_windows (server);
+
+  if (server->pointer_grab_window_id != -1)
+    broadway_output_grab_pointer (server->output,
+				  server->pointer_grab_window_id,
+				  server->pointer_grab_owner_events);
+}
+
+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);
+  /* TODO: This should really be async */
+  g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+			     res, strlen (res), NULL, NULL, NULL);
+  g_free (res);
+  g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
+			     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)
+    start_input (request, FALSE);
+  else if (strcmp (escaped, "/socket-bin") == 0)
+    start_input (request, TRUE);
+  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);
+  request->connection = g_object_ref (connection);
+  request->server = GDK_BROADWAY_SERVER (source_object);
+  request->request = g_string_new ("");
+
+  in = g_io_stream_get_input_stream (G_IO_STREAM (connection));
+
+  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;
+}
+
+GdkBroadwayServer *
+_gdk_broadway_server_new (int port, GError **error)
+{
+  GdkBroadwayServer *server;
+
+  server = g_object_new (GDK_TYPE_BROADWAY_SERVER, NULL);
+  server->port = port;
+
+  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);
+      return NULL;
+    }
+
+  g_signal_connect (server->service, "incoming",
+		    G_CALLBACK (handle_incoming_connection), NULL);
+  return server;
+}
+
+guint32
+_gdk_broadway_server_get_last_seen_time (GdkBroadwayServer *server)
+{
+  _gdk_broadway_server_consume_all_input (server);
+  return (guint32) server->last_seen_time;
+}
+
+void
+_gdk_broadway_server_query_mouse (GdkBroadwayServer *server,
+				  gint32             *toplevel,
+				  gint32             *root_x,
+				  gint32             *root_y,
+				  guint32            *mask)
+{
+  if (server->output)
+    {
+      _gdk_broadway_server_consume_all_input (server);
+      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
+_gdk_broadway_server_destroy_window (GdkBroadwayServer *server,
+				     gint id)
+{
+  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));
+      g_free (window);
+    }
+}
+
+gboolean
+_gdk_broadway_server_window_show (GdkBroadwayServer *server,
+				  gint id)
+{
+  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
+_gdk_broadway_server_window_hide (GdkBroadwayServer *server,
+				  gint id)
+{
+  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;
+    }
+
+  if (server->output)
+    {
+      broadway_output_hide_surface (server->output, window->id);
+      sent = TRUE;
+    }
+  return sent;
+}
+
+void
+_gdk_broadway_server_window_set_transient_for (GdkBroadwayServer *server,
+					       gint id, gint parent)
+{
+  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);
+      _gdk_broadway_server_flush (server);
+    }
+}
+
+gboolean
+_gdk_broadway_server_has_client (GdkBroadwayServer *server)
+{
+  return server->output != NULL;
+}
+
+static void
+_cairo_region (cairo_t         *cr,
+	       const cairo_region_t *region)
+{
+  cairo_rectangle_int_t box;
+  gint n_boxes, i;
+
+  g_return_if_fail (cr != NULL);
+  g_return_if_fail (region != NULL);
+
+  n_boxes = cairo_region_num_rectangles (region);
+
+  for (i = 0; i < n_boxes; i++)
+    {
+      cairo_region_get_rectangle (region, i, &box);
+      cairo_rectangle (cr, box.x, box.y, box.width, box.height);
+    }
+}
+
+
+static void
+copy_region (cairo_surface_t *surface,
+	     cairo_region_t *area,
+	     gint            dx,
+	     gint            dy)
+{
+  cairo_t *cr;
+
+  cr = cairo_create (surface);
+
+  _cairo_region (cr, area);
+  cairo_clip (cr);
+
+  /* NB: This is a self-copy and Cairo doesn't support that yet.
+   * So we do a litle trick.
+   */
+  cairo_push_group (cr);
+
+  cairo_set_source_surface (cr, surface, dx, dy);
+  cairo_paint (cr);
+
+  cairo_pop_group_to_source (cr);
+  cairo_paint (cr);
+
+  cairo_destroy (cr);
+}
+
+gboolean
+_gdk_broadway_server_window_translate (GdkBroadwayServer *server,
+				       gint id,
+				       cairo_region_t *area,
+				       gint            dx,
+				       gint            dy)
+{
+  BroadwayWindow *window;
+  gboolean sent = FALSE;
+
+  window = g_hash_table_lookup (server->id_ht,
+				GINT_TO_POINTER (id));
+  if (window == NULL)
+    return FALSE;
+
+  if (window->last_synced &&
+      server->output)
+    {
+      BroadwayRect *rects;
+      cairo_rectangle_int_t rect;
+      int i, n_rects;
+
+      copy_region (window->last_surface, area, dx, dy);
+      n_rects = cairo_region_num_rectangles (area);
+      rects = g_new (BroadwayRect, n_rects);
+      for (i = 0; i < n_rects; i++)
+	{
+	  cairo_region_get_rectangle (area, i, &rect);
+	  rects[i].x = rect.x;
+	  rects[i].y = rect.y;
+	  rects[i].width = rect.width;
+	  rects[i].height = rect.height;
+	}
+      broadway_output_copy_rectangles (server->output,
+				       window->id,
+				       rects, n_rects, dx, dy);
+      g_free (rects);
+      sent = TRUE;
+    }
+
+  return sent;
+}
+
+static void
+diff_surfaces (cairo_surface_t *surface,
+	       cairo_surface_t *old_surface)
+{
+  guint8 *data, *old_data;
+  guint32 *line, *old_line;
+  int w, h, stride, old_stride;
+  int x, y;
+
+  data = cairo_image_surface_get_data (surface);
+  old_data = cairo_image_surface_get_data (old_surface);
+
+  w = cairo_image_surface_get_width (surface);
+  h = cairo_image_surface_get_height (surface);
+
+  stride = cairo_image_surface_get_stride (surface);
+  old_stride = cairo_image_surface_get_stride (old_surface);
+
+  for (y = 0; y < h; y++)
+    {
+      line = (guint32 *)data;
+      old_line = (guint32 *)old_data;
+
+      for (x = 0; x < w; x++)
+	{
+	  if ((*line & 0xffffff) == (*old_line & 0xffffff))
+	    *old_line = 0;
+	  else
+	    *old_line = *line | 0xff000000;
+	  line ++;
+	  old_line ++;
+	}
+
+      data += stride;
+      old_data += old_stride;
+    }
+}
+
+void
+_gdk_broadway_server_window_update (GdkBroadwayServer *server,
+				    gint id,
+				    cairo_surface_t *surface)
+{
+  cairo_t *cr;
+  BroadwayWindow *window;
+
+  if (surface == NULL)
+    return;
+
+  window = g_hash_table_lookup (server->id_ht,
+				GINT_TO_POINTER (id));
+  if (window == NULL)
+    return;
+
+  if (window->last_surface == NULL)
+    window->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+						       window->width,
+						       window->height);
+
+  if (server->output != NULL)
+    {
+      if (window->last_synced)
+	{
+	  diff_surfaces (surface,
+			 window->last_surface);
+	  broadway_output_put_rgba (server->output, window->id, 0, 0,
+				    cairo_image_surface_get_width (window->last_surface),
+				    cairo_image_surface_get_height (window->last_surface),
+				    cairo_image_surface_get_stride (window->last_surface),
+				    cairo_image_surface_get_data (window->last_surface));
+	}
+      else
+	{
+	  window->last_synced = TRUE;
+	  broadway_output_put_rgb (server->output, window->id, 0, 0,
+				   cairo_image_surface_get_width (surface),
+				   cairo_image_surface_get_height (surface),
+				   cairo_image_surface_get_stride (surface),
+				   cairo_image_surface_get_data (surface));
+	}
+
+      broadway_output_surface_flush (server->output, window->id);
+    }
+
+  cr = cairo_create (window->last_surface);
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  cairo_set_source_surface (cr, surface, 0, 0);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+}
+
+gboolean
+_gdk_broadway_server_window_move_resize (GdkBroadwayServer *server,
+					 gint id,
+					 int x,
+					 int y,
+					 int width,
+					 int height)
+{
+  BroadwayWindow *window;
+  gboolean with_move, with_resize;
+  gboolean sent = FALSE;
+  cairo_t *cr;
+
+  window = g_hash_table_lookup (server->id_ht,
+				GINT_TO_POINTER (id));
+  if (window == NULL)
+    return FALSE;
+
+  with_move = x != window->x || y != window->y;
+  with_resize = width != window->width || height != window->height;
+  window->x = x;
+  window->y = y;
+  window->width = width;
+  window->height = height;
+
+  if (with_resize && window->last_surface != NULL)
+    {
+      cairo_surface_t *old;
+
+      old = window->last_surface;
+
+      window->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+							 width, height);
+
+
+      cr = cairo_create (window->last_surface);
+      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+      cairo_set_source_surface (cr, old, 0, 0);
+      cairo_paint (cr);
+      cairo_destroy (cr);
+
+      cairo_surface_destroy (old);
+    }
+
+  if (server->output != NULL)
+    {
+      broadway_output_move_resize_surface (server->output,
+					   window->id,
+					   with_move, window->x, window->y,
+					   with_resize, window->width, window->height);
+      sent = TRUE;
+    }
+
+  return sent;
+}
+
+GdkGrabStatus
+_gdk_broadway_server_grab_pointer (GdkBroadwayServer *server,
+				   gint id,
+				   gboolean owner_events,
+				   guint32 event_mask,
+				   guint32 time_)
+{
+  if (server->pointer_grab_window_id != -1 &&
+      time_ != 0 && server->pointer_grab_time > time_)
+    return GDK_GRAB_ALREADY_GRABBED;
+
+  if (time_ == 0)
+    time_ = server->last_seen_time;
+
+  server->pointer_grab_window_id = id;
+  server->pointer_grab_owner_events = owner_events;
+  server->pointer_grab_time = time_;
+
+  if (server->output)
+    {
+      broadway_output_grab_pointer (server->output,
+				    id,
+				    owner_events);
+      _gdk_broadway_server_flush (server);
+    }
+
+  /* TODO: What about toplevel grab events if we're not connected? */
+
+  return GDK_GRAB_SUCCESS;
+}
+
+guint32
+_gdk_broadway_server_ungrab_pointer (GdkBroadwayServer *server,
+				     guint32    time_)
+{
+  guint32 serial;
+
+  if (server->pointer_grab_window_id != -1 &&
+      time_ != 0 && server->pointer_grab_time > time_)
+    return 0;
+
+  /* TODO: What about toplevel grab events if we're not connected? */
+
+  if (server->output)
+    {
+      serial = broadway_output_ungrab_pointer (server->output);
+      _gdk_broadway_server_flush (server);
+    }
+  else
+    {
+      serial = server->saved_serial;
+    }
+
+  server->pointer_grab_window_id = -1;
+
+  return serial;
+}
+
+guint32
+_gdk_broadway_server_new_window (GdkBroadwayServer *server,
+				 int x,
+				 int y,
+				 int width,
+				 int height,
+				 gboolean is_temp)
+{
+  BroadwayWindow *window;
+
+  window = g_new0 (BroadwayWindow, 1);
+  window->id = server->id_counter++;
+  window->x = x;
+  window->y = y;
+  window->width = width;
+  window->height = height;
+  window->is_temp = is_temp;
+
+  g_hash_table_insert (server->id_ht,
+		       GINT_TO_POINTER (window->id),
+		       window);
+
+  server->toplevels = g_list_prepend (server->toplevels, window);
+
+  if (server->output)
+    broadway_output_new_surface (server->output,
+				 window->id,
+				 window->x,
+				 window->y,
+				 window->width,
+				 window->height,
+				 window->is_temp);
+
+  return window->id;
+}
+
+static void
+_gdk_broadway_server_resync_windows (GdkBroadwayServer *server)
+{
+  GList *l;
+
+  if (server->output == NULL)
+    return;
+
+  /* First create all windows */
+  for (l = server->toplevels; l != NULL; l = l->next)
+    {
+      BroadwayWindow *window = l->data;
+
+      if (window->id == 0)
+	continue; /* Skip root */
+
+      window->last_synced = FALSE;
+      broadway_output_new_surface (server->output,
+				   window->id,
+				   window->x,
+				   window->y,
+				   window->width,
+				   window->height,
+				   window->is_temp);
+    }
+
+  /* Then do everything that may reference other windows */
+  for (l = server->toplevels; l != NULL; l = l->next)
+    {
+      BroadwayWindow *window = l->data;
+
+      if (window->id == 0)
+	continue; /* Skip root */
+
+      if (window->transient_for != -1)
+	broadway_output_set_transient_for (server->output, window->id, window->transient_for);
+      if (window->visible)
+	{
+	  broadway_output_show_surface (server->output, window->id);
+
+	  if (window->last_surface != NULL)
+	    {
+	      window->last_synced = TRUE;
+	      broadway_output_put_rgb (server->output, window->id, 0, 0,
+				       cairo_image_surface_get_width (window->last_surface),
+				       cairo_image_surface_get_height (window->last_surface),
+				       cairo_image_surface_get_stride (window->last_surface),
+				       cairo_image_surface_get_data (window->last_surface));
+	    }
+	}
+    }
+
+  _gdk_broadway_server_flush (server);
+}
diff --git a/gdk/broadway/gdkbroadway-server.h b/gdk/broadway/gdkbroadway-server.h
new file mode 100644
index 0000000..e9f7742
--- /dev/null
+++ b/gdk/broadway/gdkbroadway-server.h
@@ -0,0 +1,145 @@
+#ifndef __GDK_BROADWAY_SERVER__
+#define __GDK_BROADWAY_SERVER__
+
+#include <gdk/gdktypes.h>
+
+typedef struct _GdkBroadwayServer GdkBroadwayServer;
+typedef struct _GdkBroadwayServerClass GdkBroadwayServerClass;
+
+#define GDK_TYPE_BROADWAY_SERVER              (gdk_broadway_server_get_type())
+#define GDK_BROADWAY_SERVER(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_BROADWAY_SERVER, GdkBroadwayServer))
+#define GDK_BROADWAY_SERVER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_BROADWAY_SERVER, GdkBroadwayServerClass))
+#define GDK_IS_BROADWAY_SERVER(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_BROADWAY_SERVER))
+#define GDK_IS_BROADWAY_SERVER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_BROADWAY_SERVER))
+#define GDK_BROADWAY_SERVER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_BROADWAY_SERVER, GdkBroadwayServerClass))
+
+typedef struct {
+  guint8 type;
+  guint32 serial;
+  guint64 time;
+} BroadwayInputBaseMsg;
+
+typedef struct {
+  BroadwayInputBaseMsg base;
+  guint32 mouse_window_id; /* The real window, not taking grabs into account */
+  guint32 event_window_id;
+  gint32 root_x;
+  gint32 root_y;
+  gint32 win_x;
+  gint32 win_y;
+  guint32 state;
+} BroadwayInputPointerMsg;
+
+typedef struct {
+  BroadwayInputPointerMsg pointer;
+  guint32 mode;
+} BroadwayInputCrossingMsg;
+
+typedef struct {
+  BroadwayInputPointerMsg pointer;
+  guint32 button;
+} BroadwayInputButtonMsg;
+
+typedef struct {
+  BroadwayInputPointerMsg pointer;
+  gint32 dir;
+} BroadwayInputScrollMsg;
+
+typedef struct {
+  BroadwayInputBaseMsg base;
+  guint32 mouse_window_id; /* The real window, not taking grabs into account */
+  guint32 state;
+  gint32 key;
+} BroadwayInputKeyMsg;
+
+typedef struct {
+  BroadwayInputBaseMsg base;
+  gint32 res;
+} BroadwayInputGrabReply;
+
+typedef struct {
+  BroadwayInputBaseMsg base;
+  gint32 id;
+  gint32 x;
+  gint32 y;
+  gint32 width;
+  gint32 height;
+} BroadwayInputConfigureNotify;
+
+typedef struct {
+  BroadwayInputBaseMsg base;
+  gint32 width;
+  gint32 height;
+} BroadwayInputScreenResizeNotify;
+
+typedef struct {
+  BroadwayInputBaseMsg base;
+  gint32 id;
+} BroadwayInputDeleteNotify;
+
+typedef union {
+  BroadwayInputBaseMsg base;
+  BroadwayInputPointerMsg pointer;
+  BroadwayInputCrossingMsg crossing;
+  BroadwayInputButtonMsg button;
+  BroadwayInputScrollMsg scroll;
+  BroadwayInputKeyMsg key;
+  BroadwayInputGrabReply grab_reply;
+  BroadwayInputConfigureNotify configure_notify;
+  BroadwayInputDeleteNotify delete_notify;
+  BroadwayInputScreenResizeNotify screen_resize_notify;
+} BroadwayInputMsg;
+
+GdkBroadwayServer *_gdk_broadway_server_new                      (int                 port,
+								  GError            **error);
+gboolean           _gdk_broadway_server_has_client               (GdkBroadwayServer  *server);
+void               _gdk_broadway_server_flush                    (GdkBroadwayServer  *server);
+void               _gdk_broadway_server_sync                     (GdkBroadwayServer  *server);
+gulong             _gdk_broadway_server_get_next_serial          (GdkBroadwayServer  *server);
+guint32            _gdk_broadway_server_get_last_seen_time       (GdkBroadwayServer  *server);
+gboolean           _gdk_broadway_server_lookahead_event          (GdkBroadwayServer  *server,
+								  const char         *types);
+void               _gdk_broadway_server_query_mouse              (GdkBroadwayServer  *server,
+								  gint               *toplevel,
+								  gint               *root_x,
+								  gint               *root_y,
+								  guint32            *mask);
+GdkGrabStatus      _gdk_broadway_server_grab_pointer             (GdkBroadwayServer  *server,
+								  gint                id,
+								  gboolean            owner_events,
+								  guint32             event_mask,
+								  guint32             time_);
+guint32            _gdk_broadway_server_ungrab_pointer           (GdkBroadwayServer  *server,
+								  guint32             time_);
+gint32             _gdk_broadway_server_get_mouse_toplevel       (GdkBroadwayServer  *server);
+guint32            _gdk_broadway_server_new_window               (GdkBroadwayServer  *server,
+								  int                 x,
+								  int                 y,
+								  int                 width,
+								  int                 height,
+								  gboolean            is_temp);
+void               _gdk_broadway_server_destroy_window           (GdkBroadwayServer  *server,
+								  gint                id);
+gboolean           _gdk_broadway_server_window_show              (GdkBroadwayServer  *server,
+								  gint                id);
+gboolean           _gdk_broadway_server_window_hide              (GdkBroadwayServer  *server,
+								  gint                id);
+void               _gdk_broadway_server_window_set_transient_for (GdkBroadwayServer  *server,
+								  gint                id,
+								  gint                parent);
+gboolean           _gdk_broadway_server_window_translate         (GdkBroadwayServer  *server,
+								  gint                id,
+								  cairo_region_t     *area,
+								  gint                dx,
+								  gint                dy);
+void               _gdk_broadway_server_window_update            (GdkBroadwayServer  *server,
+								  gint                id,
+								  cairo_surface_t    *surface);
+gboolean           _gdk_broadway_server_window_move_resize       (GdkBroadwayServer  *server,
+								  gint                id,
+								  int                 x,
+								  int                 y,
+								  int                 width,
+								  int                 height);
+
+#endif /* __GDK_BROADWAY_SERVER__ */
diff --git a/gdk/broadway/gdkdevice-broadway.c b/gdk/broadway/gdkdevice-broadway.c
index 6af7270..40e4a2f 100644
--- a/gdk/broadway/gdkdevice-broadway.c
+++ b/gdk/broadway/gdkdevice-broadway.c
@@ -156,7 +156,10 @@ gdk_broadway_device_query_state (GdkDevice        *device,
   GdkDisplay *display;
   GdkBroadwayDisplay *broadway_display;
   GdkScreen *screen;
-  gint device_root_x, device_root_y;
+  gint32 device_root_x, device_root_y;
+  gint32 mouse_toplevel_id;
+  GdkWindow *mouse_toplevel;
+  guint32 mask32;
 
   if (gdk_device_get_source (device) != GDK_SOURCE_MOUSE)
     return;
@@ -173,36 +176,12 @@ gdk_broadway_device_query_state (GdkDevice        *device,
       *root_window = gdk_screen_get_root_window (screen);
     }
 
-  if (broadway_display->output)
-    {
-      _gdk_broadway_display_consume_all_input (display);
-      if (root_x)
-	*root_x = broadway_display->future_root_x;
-      if (root_y)
-	*root_y = broadway_display->future_root_y;
-      /* TODO: Should really use future_x/y when we get configure events */
-      if (win_x)
-	*win_x = broadway_display->future_root_x - toplevel->x;
-      if (win_y)
-	*win_y = broadway_display->future_root_y - toplevel->y;
-      if (mask)
-	*mask = broadway_display->future_state;
-      if (child_window)
-	{
-	  if (gdk_window_get_window_type (toplevel) == GDK_WINDOW_ROOT)
-	    *child_window =
-	      g_hash_table_lookup (broadway_display->id_ht,
-				   GINT_TO_POINTER (broadway_display->future_mouse_in_toplevel));
-	  else
-	    *child_window = toplevel; /* No native children */
-	}
-      return;
-    }
-
-  /* Fallback when unconnected */
-
-  device_root_x = broadway_display->last_x;
-  device_root_y = broadway_display->last_y;
+  _gdk_broadway_server_query_mouse (broadway_display->server,
+				    &mouse_toplevel_id,
+				    &device_root_x,
+				    &device_root_y,
+				    &mask32);
+  mouse_toplevel = g_hash_table_lookup (broadway_display->id_ht, GINT_TO_POINTER (mouse_toplevel_id));
 
   if (root_x)
     *root_x = device_root_x;
@@ -213,12 +192,12 @@ gdk_broadway_device_query_state (GdkDevice        *device,
   if (win_y)
     *win_y = device_root_y - toplevel->y;
   if (mask)
-    *mask = broadway_display->last_state;
+    *mask = mask32;
   if (child_window)
     {
       if (gdk_window_get_window_type (toplevel) == GDK_WINDOW_ROOT)
 	{
-	  *child_window = broadway_display->mouse_in_toplevel;
+	  *child_window = mouse_toplevel;
 	  if (*child_window == NULL)
 	    *child_window = toplevel;
 	}
@@ -236,13 +215,10 @@ void
 _gdk_broadway_window_grab_check_destroy (GdkWindow *window)
 {
   GdkDisplay *display = gdk_window_get_display (window);
-  GdkBroadwayDisplay *broadway_display;
   GdkDeviceManager *device_manager;
   GdkDeviceGrabInfo *grab;
   GList *devices, *d;
 
-  broadway_display = GDK_BROADWAY_DISPLAY (display);
-
   device_manager = gdk_display_get_device_manager (display);
 
   /* Get all devices */
@@ -257,8 +233,6 @@ _gdk_broadway_window_grab_check_destroy (GdkWindow *window)
 	{
 	  grab->serial_end = grab->serial_start;
 	  grab->implicit_ungrab = TRUE;
-
-	  broadway_display->pointer_grab_window = NULL;
 	}
 
     }
@@ -290,29 +264,11 @@ gdk_broadway_device_grab (GdkDevice    *device,
   else
     {
       /* Device is a pointer */
-
-      if (broadway_display->pointer_grab_window != NULL &&
-	  time_ != 0 && broadway_display->pointer_grab_time > time_)
-	return GDK_GRAB_ALREADY_GRABBED;
-
-      if (time_ == 0)
-	time_ = broadway_display->last_seen_time;
-
-      broadway_display->pointer_grab_window = window;
-      broadway_display->pointer_grab_owner_events = owner_events;
-      broadway_display->pointer_grab_time = time_;
-
-      if (broadway_display->output)
-	{
-	  broadway_output_grab_pointer (broadway_display->output,
-					GDK_WINDOW_IMPL_BROADWAY (window->impl)->id,
-					owner_events);
-	  gdk_display_flush (display);
-	}
-
-      /* TODO: What about toplevel grab events if we're not connected? */
-
-      return GDK_GRAB_SUCCESS;
+      return _gdk_broadway_server_grab_pointer (broadway_display->server,
+						GDK_WINDOW_IMPL_BROADWAY (window->impl)->id,
+						owner_events,
+						event_mask,
+						time_);
     }
 }
 
@@ -340,31 +296,17 @@ gdk_broadway_device_ungrab (GdkDevice *device,
   else
     {
       /* Device is a pointer */
+      serial = _gdk_broadway_server_ungrab_pointer (broadway_display->server, time_);
 
-      if (broadway_display->pointer_grab_window != NULL &&
-	  time_ != 0 && broadway_display->pointer_grab_time > time_)
-	return;
-
-      /* TODO: What about toplevel grab events if we're not connected? */
-
-      if (broadway_display->output)
-	{
-	  serial = broadway_output_ungrab_pointer (broadway_display->output);
-	  gdk_display_flush (display);
-	}
-      else
+      if (serial != 0)
 	{
-	  serial = broadway_display->saved_serial;
+	  grab = _gdk_display_get_last_device_grab (display, device);
+	  if (grab &&
+	      (time_ == GDK_CURRENT_TIME ||
+	       grab->time == GDK_CURRENT_TIME ||
+	       !TIME_IS_LATER (grab->time, time_)))
+	    grab->serial_end = serial;
 	}
-
-      grab = _gdk_display_get_last_device_grab (display, device);
-      if (grab &&
-	  (time_ == GDK_CURRENT_TIME ||
-	   grab->time == GDK_CURRENT_TIME ||
-	   !TIME_IS_LATER (grab->time, time_)))
-	grab->serial_end = serial;
-
-      broadway_display->pointer_grab_window = NULL;
     }
 }
 
@@ -375,7 +317,6 @@ gdk_broadway_device_window_at_position (GdkDevice       *device,
 					GdkModifierType *mask,
 					gboolean         get_toplevel)
 {
-  gboolean res;
   GdkScreen *screen;
   GdkWindow *root_window;
   GdkWindow *window;
diff --git a/gdk/broadway/gdkdisplay-broadway.c b/gdk/broadway/gdkdisplay-broadway.c
index 97dfe87..ad5d24b 100644
--- a/gdk/broadway/gdkdisplay-broadway.c
+++ b/gdk/broadway/gdkdisplay-broadway.c
@@ -67,8 +67,6 @@ gdk_event_init (GdkDisplay *display)
 
   broadway_display = GDK_BROADWAY_DISPLAY (display);
   broadway_display->event_source = _gdk_broadway_event_source_new (display);
-  broadway_display->saved_serial = 1;
-  broadway_display->last_seen_time = 1;
 }
 
 static void
@@ -123,951 +121,16 @@ gdk_broadway_display_init_input (GdkDisplay *display)
   g_list_free (list);
 }
 
-typedef struct HttpRequest {
-  GdkDisplay *display;
-  GSocketConnection *connection;
-  GDataInputStream *data;
-  GString *request;
-}  HttpRequest;
-
-static void start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary);
-
-static void
-http_request_free (HttpRequest *request)
-{
-  g_object_unref (request->connection);
-  g_object_unref (request->data);
-  g_string_free (request->request, TRUE);
-  g_free (request);
-}
-
-struct BroadwayInput {
-  GdkDisplay *display;
-  GSocketConnection *connection;
-  GByteArray *buffer;
-  GSource *source;
-  gboolean seen_time;
-  gint64 time_base;
-  gboolean proto_v7_plus;
-  gboolean binary;
-};
-
-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
-process_input_messages (GdkBroadwayDisplay *broadway_display)
-{
-  BroadwayInputMsg *message;
-
-  while (broadway_display->input_messages)
-    {
-      message = broadway_display->input_messages->data;
-      broadway_display->input_messages =
-	g_list_delete_link (broadway_display->input_messages,
-			    broadway_display->input_messages);
-
-      _gdk_broadway_events_got_input (GDK_DISPLAY (broadway_display), message);
-      g_free (message);
-    }
-}
-
-static char *
-parse_pointer_data (char *p, BroadwayInputPointerMsg *data)
-{
-  data->mouse_window_id = strtol (p, &p, 10);
-  p++; /* Skip , */
-  data->event_window_id = strtol (p, &p, 10);
-  p++; /* Skip , */
-  data->root_x = strtol (p, &p, 10);
-  p++; /* Skip , */
-  data->root_y = strtol (p, &p, 10);
-  p++; /* Skip , */
-  data->win_x = strtol (p, &p, 10);
-  p++; /* Skip , */
-  data->win_y = strtol (p, &p, 10);
-  p++; /* Skip , */
-  data->state = strtol (p, &p, 10);
-
-  return p;
-}
-
-static void
-update_future_pointer_info (GdkBroadwayDisplay *broadway_display, BroadwayInputPointerMsg *data)
-{
-  broadway_display->future_root_x = data->root_x;
-  broadway_display->future_root_y = data->root_y;
-  broadway_display->future_state = data->state;
-  broadway_display->future_mouse_in_toplevel = data->mouse_window_id;
-}
-
-static void
-parse_input_message (BroadwayInput *input, const char *message)
-{
-  GdkBroadwayDisplay *broadway_display;
-  BroadwayInputMsg msg;
-  char *p;
-  gint64 time_;
-
-  broadway_display = GDK_BROADWAY_DISPLAY (input->display);
-
-  p = (char *)message;
-  msg.base.type = *p++;
-  msg.base.serial = (guint32)strtol (p, &p, 10);
-  p++; /* Skip , */
-  time_ = strtol(p, &p, 10);
-  p++; /* Skip , */
-
-  if (time_ == 0) {
-    time_ = broadway_display->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_ - (broadway_display->last_seen_time + 5000);
-    }
-    time_ = time_ - input->time_base;
-  }
-
-  broadway_display->last_seen_time = time_;
-
-  msg.base.time = time_;
-
-  switch (msg.base.type) {
-  case 'e': /* Enter */
-  case 'l': /* Leave */
-    p = parse_pointer_data (p, &msg.pointer);
-    update_future_pointer_info (broadway_display, &msg.pointer);
-    p++; /* Skip , */
-    msg.crossing.mode = strtol(p, &p, 10);
-    break;
-
-  case 'm': /* Mouse move */
-    p = parse_pointer_data (p, &msg.pointer);
-    update_future_pointer_info (broadway_display, &msg.pointer);
-    break;
-
-  case 'b':
-  case 'B':
-    p = parse_pointer_data (p, &msg.pointer);
-    update_future_pointer_info (broadway_display, &msg.pointer);
-    p++; /* Skip , */
-    msg.button.button = strtol(p, &p, 10);
-    break;
-
-  case 's':
-    p = parse_pointer_data (p, &msg.pointer);
-    update_future_pointer_info (broadway_display, &msg.pointer);
-    p++; /* Skip , */
-    msg.scroll.dir = strtol(p, &p, 10);
-    break;
-
-  case 'k':
-  case 'K':
-    msg.key.key = strtol(p, &p, 10);
-    p++; /* Skip , */
-    msg.key.state = strtol(p, &p, 10);
-    break;
-
-  case 'g':
-  case 'u':
-    msg.grab_reply.res = strtol(p, &p, 10);
-    break;
-
-  case 'w':
-    msg.configure_notify.id = strtol(p, &p, 10);
-    p++; /* Skip , */
-    msg.configure_notify.x = strtol (p, &p, 10);
-    p++; /* Skip , */
-    msg.configure_notify.y = strtol (p, &p, 10);
-    p++; /* Skip , */
-    msg.configure_notify.width = strtol (p, &p, 10);
-    p++; /* Skip , */
-    msg.configure_notify.height = strtol (p, &p, 10);
-    break;
-
-  case 'W':
-    msg.delete_notify.id = strtol(p, &p, 10);
-    break;
-
-  case 'd':
-    msg.screen_resize_notify.width = strtol (p, &p, 10);
-    p++; /* Skip , */
-    msg.screen_resize_notify.height = strtol (p, &p, 10);
-    break;
-
-  default:
-    g_printerr ("Unknown input command %s\n", message);
-    break;
-  }
-
-  broadway_display->input_messages = g_list_append (broadway_display->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)
-{
-  GdkBroadwayDisplay *broadway_display;
-
-  broadway_display = GDK_BROADWAY_DISPLAY (input->display);
-
-  if (!input->buffer->len)
-    return;
-
-  if (input->proto_v7_plus)
-    {
-      hex_dump (input->buffer->data, input->buffer->len);
-
-      while (input->buffer->len > 2)
-	{
-	  gsize len, payload_len;
-	  BroadwayWSOpCode code;
-	  gboolean is_mask, fin;
-	  guchar *buf, *data, *mask;
-
-	  buf = input->buffer->data;
-	  len = input->buffer->len;
-
-#ifdef DEBUG_WEBSOCKETS
-	  g_print ("Parse input first byte 0x%2x 0x%2x\n", buf[0], buf[1]);
-#endif
-
-	  fin = buf[0] & 0x80;
-	  code = buf[0] & 0x0f;
-	  payload_len = buf[1] & 0x7f;
-	  is_mask = buf[1] & 0x80;
-	  data = buf + 2;
-
-	  if (payload_len > 125)
-	    {
-	      if (len < 4)
-		return;
-	      payload_len = GUINT16_FROM_BE( *(guint16 *) data );
-	      data += 2;
-	    }
-	  else if (payload_len > 126)
-	    {
-	      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 */
-	  case BROADWAY_WS_TEXT:
-	    if (!fin)
-	      {
-#ifdef DEBUG_WEBSOCKETS
-		g_warning ("can't yet accept fragmented input");
-#endif
-	      }
-	    else
-	      {
-		char *terminated = g_strndup((char *)data, payload_len);
-	        parse_input_message (input, terminated);
-		g_free (terminated);
-	      }
-	    break;
-	  case BROADWAY_WS_CNX_PING:
-	    broadway_output_pong (broadway_display->output);
-	    break;
-	  case BROADWAY_WS_CNX_PONG:
-	    break; /* we never send pings, but tolerate pongs */
-	  case BROADWAY_WS_BINARY:
-	  case BROADWAY_WS_CONTINUATION:
-	  default:
-	    {
-	      g_warning ("fragmented or unknown input code 0x%2x with fin set", code);
-	      break;
-	    }
-	  }
-
-	  g_byte_array_remove_range (input->buffer, 0, data - buf + payload_len);
-	}
-    }
-  else /* old style protocol */
-    {
-      char *buf, *ptr;
-      gsize len;
-
-      buf = (char *)input->buffer->data;
-      len = input->buffer->len;
-
-      if (buf[0] != 0)
-	{
-	  broadway_display->input = NULL;
-	  broadway_input_free (input);
-	  return;
-	}
-
-      while ((ptr = memchr (buf, 0xff, len)) != NULL)
-	{
-	  *ptr = 0;
-	  ptr++;
-
-	  parse_input_message (input, buf + 1);
-
-	  len -= ptr - buf;
-	  buf = ptr;
-
-	  if (len > 0 && buf[0] != 0)
-	    {
-	      broadway_display->input = NULL;
-	      broadway_input_free (input);
-	      break;
-	    }
-	}
-      g_byte_array_remove_range (input->buffer, 0, buf - (char *)input->buffer->data);
-    }
-}
-
-
-static gboolean
-process_input_idle_cb (GdkBroadwayDisplay *display)
-{
-  display->process_input_idle = 0;
-  process_input_messages (display);
-  return G_SOURCE_REMOVE;
-}
-
-static void
-queue_process_input_at_idle (GdkBroadwayDisplay *broadway_display)
-{
-  if (broadway_display->process_input_idle == 0)
-    broadway_display->process_input_idle =
-      g_idle_add_full (GDK_PRIORITY_EVENTS, (GSourceFunc)process_input_idle_cb, broadway_display, NULL);
-}
-
-static void
-_gdk_broadway_display_read_all_input_nonblocking (GdkDisplay *display)
-{
-  GdkBroadwayDisplay *broadway_display;
-  GInputStream *in;
-  gssize res;
-  guint8 buffer[1024];
-  GError *error;
-  BroadwayInput *input;
-
-  broadway_display = GDK_BROADWAY_DISPLAY (display);
-  if (broadway_display->input == NULL)
-    return;
-
-  input = broadway_display->input;
-
-  in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
-
-  error = NULL;
-  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);
-	  return;
-	}
-
-      broadway_display->input = NULL;
-      broadway_input_free (input);
-      if (res < 0)
-	{
-	  g_print ("input error %s\n", error->message);
-	  g_error_free (error);
-	}
-      return;
-    }
-
-  g_byte_array_append (input->buffer, buffer, res);
-
-  parse_input (input);
-}
-
-void
-_gdk_broadway_display_consume_all_input (GdkDisplay *display)
-{
-  GdkBroadwayDisplay *broadway_display;
-
-  broadway_display = GDK_BROADWAY_DISPLAY (display);
-  _gdk_broadway_display_read_all_input_nonblocking (display);
-
-  /* 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 (broadway_display);
-}
-
-
-static gboolean
-input_data_cb (GObject  *stream,
-	       BroadwayInput *input)
-{
-  GdkBroadwayDisplay *broadway_display;
-
-  broadway_display = GDK_BROADWAY_DISPLAY (input->display);
-  _gdk_broadway_display_read_all_input_nonblocking (input->display);
-
-  process_input_messages (broadway_display);
-
-  return TRUE;
-}
-
-/* Note: This may be called while handling a message (i.e. sorta recursively) */
-BroadwayInputMsg *
-_gdk_broadway_display_block_for_input (GdkDisplay *display, char op,
-				       guint32 serial, gboolean remove_message)
-{
-  GdkBroadwayDisplay *broadway_display;
-  BroadwayInputMsg *message;
-  gssize res;
-  guint8 buffer[1024];
-  BroadwayInput *input;
-  GInputStream *in;
-  GList *l;
-
-  gdk_display_flush (display);
-
-  broadway_display = GDK_BROADWAY_DISPLAY (display);
-  if (broadway_display->input == NULL)
-    return NULL;
-
-  input = broadway_display->input;
-
-  while (TRUE) {
-    /* Check for existing reply in queue */
-
-    for (l = broadway_display->input_messages; l != NULL; l = l->next)
-      {
-	message = l->data;
-
-	if (message->base.type == op)
-	  {
-	    if (message->base.serial == serial)
-	      {
-		if (remove_message)
-		  broadway_display->input_messages =
-		    g_list_delete_link (broadway_display->input_messages, l);
-		return message;
-	      }
-	  }
-      }
-
-    /* Not found, read more, blocking */
-
-    in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
-    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 (broadway_display);
-  }
-}
-
-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;
-}
-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);
-  /* TODO: This should really be async */
-  g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-			     res, strlen (res), NULL, NULL, NULL);
-  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;
-  guchar digest[digest_len];
-  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
-start_input (HttpRequest *request, gboolean binary)
-{
-  char **lines;
-  char *p;
-  int num_key1, num_key2;
-  guint64 key1, key2;
-  int num_space;
-  int i;
-  guint8 challenge[16];
-  char *res;
-  gsize len;
-  GChecksum *checksum;
-  char *origin, *host;
-  GdkBroadwayDisplay *broadway_display;
-  BroadwayInput *input;
-  const void *data_buffer;
-  gsize data_buffer_size;
-  GInputStream *in;
-  char *key_v7;
-  gboolean proto_v7_plus;
-
-  broadway_display = GDK_BROADWAY_DISPLAY (request->display);
-
-  if (broadway_display->input != NULL)
-    {
-      send_error (request, 409, "Input already handled");
-      return;
-    }
-
-#ifdef DEBUG_WEBSOCKETS
-  g_print ("incoming request:\n%s\n", request->request->str);
-#endif
-  lines = g_strsplit (request->request->str, "\n", 0);
-
-  num_key1 = 0;
-  num_key2 = 0;
-  key1 = 0;
-  key2 = 0;
-  key_v7 = NULL;
-  origin = NULL;
-  host = NULL;
-  for (i = 0; lines[i] != NULL; i++)
-    {
-      if ((p = parse_line (lines[i], "Sec-WebSocket-Key1")))
-	{
-	  num_space = 0;
-	  while (*p != 0)
-	    {
-	      if (g_ascii_isdigit (*p))
-		key1 = key1 * 10 + g_ascii_digit_value (*p);
-	      else if (*p == ' ')
-		num_space++;
-
-	      p++;
-	    }
-	  key1 /= num_space;
-	  num_key1++;
-	}
-      else if ((p = parse_line (lines[i], "Sec-WebSocket-Key2")))
-	{
-	  num_space = 0;
-	  while (*p != 0)
-	    {
-	      if (g_ascii_isdigit (*p))
-		key2 = key2 * 10 + g_ascii_digit_value (*p);
-	      else if (*p == ' ')
-		num_space++;
-
-	      p++;
-	    }
-	  key2 /= num_space;
-	  num_key2++;
-	}
-      else if ((p = parse_line (lines[i], "Sec-WebSocket-Key")))
-	{
-	  key_v7 = p;
-	}
-      else if ((p = parse_line (lines[i], "Origin")))
-	{
-	  origin = p;
-	}
-      else if ((p = parse_line (lines[i], "Host")))
-	{
-	  host = p;
-	}
-      else if ((p = parse_line (lines[i], "Sec-WebSocket-Origin")))
-	{
-	  origin = p;
-	}
-    }
-
-  if (origin == NULL || host == NULL)
-    {
-      g_strfreev (lines);
-      send_error (request, 400, "Bad websocket request");
-      return;
-    }
-
-  if (key_v7 != NULL)
-    {
-      char* accept = generate_handshake_response_wsietf_v7 (key_v7);
-      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"
-			     "Sec-WebSocket-Origin: %s\r\n"
-			     "Sec-WebSocket-Location: ws://%s/socket\r\n"
-			     "Sec-WebSocket-Protocol: broadway\r\n"
-			     "\r\n", accept, origin, host);
-      g_free (accept);
-
-#ifdef DEBUG_WEBSOCKETS
-      g_print ("v7 proto response:\n%s", res);
-#endif
-
-      g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-				 res, strlen (res), NULL, NULL, NULL);
-      g_free (res);
-      proto_v7_plus = TRUE;
-    }
-  else
-    {
-      if (num_key1 != 1 || num_key2 != 1)
-	{
-	  g_strfreev (lines);
-	  send_error (request, 400, "Bad websocket request");
-	  return;
-	}
-
-      challenge[0] = (key1 >> 24) & 0xff;
-      challenge[1] = (key1 >> 16) & 0xff;
-      challenge[2] = (key1 >>  8) & 0xff;
-      challenge[3] = (key1 >>  0) & 0xff;
-      challenge[4] = (key2 >> 24) & 0xff;
-      challenge[5] = (key2 >> 16) & 0xff;
-      challenge[6] = (key2 >>  8) & 0xff;
-      challenge[7] = (key2 >>  0) & 0xff;
-
-      if (!g_input_stream_read_all (G_INPUT_STREAM (request->data), challenge+8, 8, NULL, NULL, NULL))
-	{
-	  g_strfreev (lines);
-	  send_error (request, 400, "Bad websocket request");
-	  return;
-	}
-
-      checksum = g_checksum_new (G_CHECKSUM_MD5);
-      g_checksum_update (checksum, challenge, 16);
-      len = 16;
-      g_checksum_get_digest (checksum, challenge, &len);
-      g_checksum_free (checksum);
-
-      res = g_strdup_printf ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
-			     "Upgrade: WebSocket\r\n"
-			     "Connection: Upgrade\r\n"
-			     "Sec-WebSocket-Origin: %s\r\n"
-			     "Sec-WebSocket-Location: ws://%s/socket\r\n"
-			     "Sec-WebSocket-Protocol: broadway\r\n"
-			     "\r\n",
-			     origin, host);
-
-#ifdef DEBUG_WEBSOCKETS
-      g_print ("legacy response:\n%s", res);
-#endif
-      g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-				 res, strlen (res), NULL, NULL, NULL);
-      g_free (res);
-      g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-				 challenge, 16, NULL, NULL, NULL);
-      proto_v7_plus = FALSE;
-    }
-
-  input = g_new0 (BroadwayInput, 1);
-
-  input->display = request->display;
-  input->connection = g_object_ref (request->connection);
-  input->proto_v7_plus = proto_v7_plus;
-  input->binary = binary;
-
-  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);
-
-  broadway_display->input = input;
-
-  start_output (request, proto_v7_plus, binary);
-
-  /* This will free and close the data input stream, but we got all the buffered content already */
-  http_request_free (request);
-
-  in = g_io_stream_get_input_stream (G_IO_STREAM (input->connection));
-  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);
-
-  /* Process any data in the pipe already */
-  parse_input (input);
-  process_input_messages (broadway_display);
-
-  g_strfreev (lines);
-}
-
-static void
-start_output (HttpRequest *request, gboolean proto_v7_plus, gboolean binary)
-{
-  GSocket *socket;
-  GdkBroadwayDisplay *broadway_display;
-  int flag = 1;
-
-  socket = g_socket_connection_get_socket (request->connection);
-  setsockopt(g_socket_get_fd (socket), IPPROTO_TCP,
-	     TCP_NODELAY, (char *) &flag, sizeof(int));
-
-  broadway_display = GDK_BROADWAY_DISPLAY (request->display);
-
-  if (broadway_display->output)
-    {
-      broadway_display->saved_serial = broadway_output_get_next_serial (broadway_display->output);
-      broadway_output_free (broadway_display->output);
-    }
-
-  broadway_display->output =
-    broadway_output_new (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-			 broadway_display->saved_serial, proto_v7_plus, binary);
-
-  _gdk_broadway_resync_windows ();
-
-  if (broadway_display->pointer_grab_window)
-    broadway_output_grab_pointer (broadway_display->output,
-				  GDK_WINDOW_IMPL_BROADWAY (broadway_display->pointer_grab_window->impl)->id,
-				  broadway_display->pointer_grab_owner_events);
-}
-
-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);
-  /* TODO: This should really be async */
-  g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-			     res, strlen (res), NULL, NULL, NULL);
-  g_free (res);
-  g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (request->connection)),
-			     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)
-    start_input (request, FALSE);
-  else if (strcmp (escaped, "/socket-bin") == 0)
-    start_input (request, TRUE);
-  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);
-  request->connection = g_object_ref (connection);
-  request->display = (GdkDisplay *) source_object;
-  request->request = g_string_new ("");
-
-  in = g_io_stream_get_input_stream (G_IO_STREAM (connection));
-
-  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;
-}
-
 GdkDisplay *
 _gdk_broadway_display_open (const gchar *display_name)
 {
   GdkDisplay *display;
   GdkBroadwayDisplay *broadway_display;
-  GError *error = NULL;
   int port;
 
   display = g_object_new (GDK_TYPE_BROADWAY_DISPLAY, NULL);
   broadway_display = GDK_BROADWAY_DISPLAY (display);
 
-  broadway_display->output = NULL;
-
   /* initialize the display's screens */
   broadway_display->screens = g_new (GdkScreen *, 1);
   broadway_display->screens[0] = _gdk_broadway_screen_new (display, 0);
@@ -1098,17 +161,12 @@ _gdk_broadway_display_open (const gchar *display_name)
   if (port == 0)
     port = 8080;
 
-  broadway_display->service = g_socket_service_new ();
-  if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (broadway_display->service),
-					port,
-					G_OBJECT (display),
-					&error))
+  broadway_display->server = _gdk_broadway_server_new (port, NULL);
+  if (broadway_display->server == NULL)
     {
-      g_printerr ("Unable to listen to port %d: %s\n", port, error->message);
-      g_error_free (error);
+      g_printerr ("Unable to init server\n");
       return NULL;
     }
-  g_signal_connect (broadway_display->service, "incoming", G_CALLBACK (handle_incoming_connection), NULL);
 
   g_signal_emit_by_name (display, "opened");
   g_signal_emit_by_name (gdk_display_manager_get (), "display-opened", display);
@@ -1116,7 +174,6 @@ _gdk_broadway_display_open (const gchar *display_name)
   return display;
 }
 
-
 static const gchar *
 gdk_broadway_display_get_name (GdkDisplay *display)
 {
@@ -1160,8 +217,11 @@ gdk_broadway_display_beep (GdkDisplay *display)
 static void
 gdk_broadway_display_sync (GdkDisplay *display)
 {
-  g_return_if_fail (GDK_IS_DISPLAY (display));
+  GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (display);
 
+  g_return_if_fail (GDK_IS_BROADWAY_DISPLAY (display));
+
+  _gdk_broadway_server_sync (broadway_display->server);
 }
 
 static void
@@ -1169,15 +229,9 @@ gdk_broadway_display_flush (GdkDisplay *display)
 {
   GdkBroadwayDisplay *broadway_display = GDK_BROADWAY_DISPLAY (display);
 
-  g_return_if_fail (GDK_IS_DISPLAY (display));
+  g_return_if_fail (GDK_IS_BROADWAY_DISPLAY (display));
 
-  if (broadway_display->output &&
-      !broadway_output_flush (broadway_display->output))
-    {
-      broadway_display->saved_serial = broadway_output_get_next_serial (broadway_display->output);
-      broadway_output_free (broadway_display->output);
-      broadway_display->output = NULL;
-    }
+  _gdk_broadway_server_flush (broadway_display->server);
 }
 
 static gboolean
@@ -1307,11 +361,9 @@ gdk_broadway_display_get_next_serial (GdkDisplay *display)
 {
   GdkBroadwayDisplay *broadway_display;
   broadway_display = GDK_BROADWAY_DISPLAY (display);
-  if (broadway_display->output)
-    return broadway_output_get_next_serial (broadway_display->output);
-  return broadway_display->saved_serial;
-}
 
+  return _gdk_broadway_server_get_next_serial (broadway_display->server);
+}
 
 static void
 gdk_broadway_display_event_data_copy (GdkDisplay    *display,
diff --git a/gdk/broadway/gdkdisplay-broadway.h b/gdk/broadway/gdkdisplay-broadway.h
index d3699f8..db24a01 100644
--- a/gdk/broadway/gdkdisplay-broadway.h
+++ b/gdk/broadway/gdkdisplay-broadway.h
@@ -27,6 +27,7 @@
 #include "gdkwindow.h"
 #include "gdkinternals.h"
 #include "gdkmain.h"
+#include "gdkbroadway-server.h"
 #include "broadway.h"
 
 G_BEGIN_DECLS
@@ -43,82 +44,6 @@ typedef struct BroadwayInput BroadwayInput;
 #define GDK_IS_BROADWAY_DISPLAY_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_BROADWAY_DISPLAY))
 #define GDK_BROADWAY_DISPLAY_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_BROADWAY_DISPLAY, GdkBroadwayDisplayClass))
 
-typedef struct {
-  char type;
-  guint32 serial;
-  guint64 time;
-} BroadwayInputBaseMsg;
-
-typedef struct {
-  BroadwayInputBaseMsg base;
-  guint32 mouse_window_id; /* The real window, not taking grabs into account */
-  guint32 event_window_id;
-  int root_x;
-  int root_y;
-  int win_x;
-  int win_y;
-  guint32 state;
-} BroadwayInputPointerMsg;
-
-typedef struct {
-  BroadwayInputPointerMsg pointer;
-  guint32 mode;
-} BroadwayInputCrossingMsg;
-
-typedef struct {
-  BroadwayInputPointerMsg pointer;
-  guint32 button;
-} BroadwayInputButtonMsg;
-
-typedef struct {
-  BroadwayInputPointerMsg pointer;
-  int dir;
-} BroadwayInputScrollMsg;
-
-typedef struct {
-  BroadwayInputBaseMsg base;
-  guint32 state;
-  int key;
-} BroadwayInputKeyMsg;
-
-typedef struct {
-  BroadwayInputBaseMsg base;
-  int res;
-} BroadwayInputGrabReply;
-
-typedef struct {
-  BroadwayInputBaseMsg base;
-  int id;
-  int x;
-  int y;
-  int width;
-  int height;
-} BroadwayInputConfigureNotify;
-
-typedef struct {
-  BroadwayInputBaseMsg base;
-  int width;
-  int height;
-} BroadwayInputScreenResizeNotify;
-
-typedef struct {
-  BroadwayInputBaseMsg base;
-  int id;
-} BroadwayInputDeleteNotify;
-
-typedef union {
-  BroadwayInputBaseMsg base;
-  BroadwayInputPointerMsg pointer;
-  BroadwayInputCrossingMsg crossing;
-  BroadwayInputButtonMsg button;
-  BroadwayInputScrollMsg scroll;
-  BroadwayInputKeyMsg key;
-  BroadwayInputGrabReply grab_reply;
-  BroadwayInputConfigureNotify configure_notify;
-  BroadwayInputDeleteNotify delete_notify;
-  BroadwayInputScreenResizeNotify screen_resize_notify;
-} BroadwayInputMsg;
-
 struct _GdkBroadwayDisplay
 {
   GdkDisplay parent_instance;
@@ -129,10 +54,6 @@ struct _GdkBroadwayDisplay
   GList *toplevels;
 
   GSource *event_source;
-  GdkWindow *mouse_in_toplevel;
-  int last_x, last_y; /* in root coords */
-  guint32 last_state;
-  GdkWindow *real_mouse_in_toplevel; /* Not affected by grabs */
 
   /* Keyboard related information */
   GdkKeymap *keymap;
@@ -147,24 +68,7 @@ struct _GdkBroadwayDisplay
   /* The offscreen window that has the pointer in it (if any) */
   GdkWindow *active_offscreen_window;
 
-  GSocketService *service;
-  BroadwayOutput *output;
-  guint32 saved_serial;
-  guint64 last_seen_time;
-  BroadwayInput *input;
-  GList *input_messages;
-  guint process_input_idle;
-
-  /* Explicit pointer grabs: */
-  GdkWindow *pointer_grab_window;
-  guint32 pointer_grab_time;
-  gboolean pointer_grab_owner_events;
-
-  /* Future data, from the currently queued events */
-  int future_root_x;
-  int future_root_y;
-  GdkModifierType future_state;
-  int future_mouse_in_toplevel;
+  GdkBroadwayServer *server;
 };
 
 struct _GdkBroadwayDisplayClass
diff --git a/gdk/broadway/gdkeventsource.c b/gdk/broadway/gdkeventsource.c
index 14fa479..ff8430d 100644
--- a/gdk/broadway/gdkeventsource.c
+++ b/gdk/broadway/gdkeventsource.c
@@ -87,9 +87,9 @@ gdk_event_source_check (GSource *source)
 }
 
 void
-_gdk_broadway_events_got_input (GdkDisplay *display,
-				BroadwayInputMsg *message)
+_gdk_broadway_events_got_input (BroadwayInputMsg *message)
 {
+  GdkDisplay *display = gdk_display_get_default ();
   GdkBroadwayDisplay *display_broadway = GDK_BROADWAY_DISPLAY (display);
   GdkScreen *screen;
   GdkWindow *window;
@@ -98,16 +98,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
 
   switch (message->base.type) {
   case 'e': /* Enter */
-    display_broadway->last_x = message->pointer.root_x;
-    display_broadway->last_y = message->pointer.root_y;
-    display_broadway->last_state = message->pointer.state;
-    display_broadway->real_mouse_in_toplevel =
-      g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id));
-
     window = g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.event_window_id));
-
-    /* TODO: Unset when it dies */
-    display_broadway->mouse_in_toplevel = window;
     if (window)
       {
 	event = gdk_event_new (GDK_ENTER_NOTIFY);
@@ -135,15 +126,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
       }
     break;
   case 'l': /* Leave */
-    display_broadway->last_x = message->pointer.root_x;
-    display_broadway->last_y = message->pointer.root_y;
-    display_broadway->last_state = message->pointer.state;
-    display_broadway->real_mouse_in_toplevel =
-      g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id));
-
     window = g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.event_window_id));
-
-    display_broadway->mouse_in_toplevel = NULL;
     if (window)
       {
 	event = gdk_event_new (GDK_LEAVE_NOTIFY);
@@ -171,12 +154,6 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
       }
     break;
   case 'm': /* Mouse move */
-    display_broadway->last_x = message->pointer.root_x;
-    display_broadway->last_y = message->pointer.root_y;
-    display_broadway->last_state = message->pointer.state;
-    display_broadway->real_mouse_in_toplevel =
-      g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id));
-
     if (_gdk_broadway_moveresize_handle_event (display, message))
       break;
 
@@ -200,12 +177,6 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
     break;
   case 'b':
   case 'B':
-    display_broadway->last_x = message->pointer.root_x;
-    display_broadway->last_y = message->pointer.root_y;
-    display_broadway->last_state = message->pointer.state;
-    display_broadway->real_mouse_in_toplevel =
-      g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id));
-
     if (message->base.type != 'b' &&
 	_gdk_broadway_moveresize_handle_event (display, message))
       break;
@@ -230,12 +201,6 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
 
     break;
   case 's':
-    display_broadway->last_x = message->pointer.root_x;
-    display_broadway->last_y = message->pointer.root_y;
-    display_broadway->last_state = message->pointer.state;
-    display_broadway->real_mouse_in_toplevel =
-      g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.mouse_window_id));
-
     window = g_hash_table_lookup (display_broadway->id_ht, GINT_TO_POINTER (message->pointer.event_window_id));
     if (window)
       {
@@ -256,8 +221,8 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
     break;
   case 'k':
   case 'K':
-    window = display_broadway->mouse_in_toplevel;
-
+    window = g_hash_table_lookup (display_broadway->id_ht,
+				  GINT_TO_POINTER (message->key.mouse_window_id));
     if (window)
       {
 	event = gdk_event_new (message->base.type == 'k' ? GDK_KEY_PRESS : GDK_KEY_RELEASE);
@@ -269,8 +234,6 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
 	event->key.length = 0;
 	gdk_event_set_device (event, display->core_pointer);
 
-	display_broadway->last_state = message->key.state;
-
 	node = _gdk_event_queue_append (display, event);
 	_gdk_windowing_got_event (display, node, event, message->base.serial);
       }
@@ -335,7 +298,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display,
     break;
 
   default:
-    g_printerr ("Unknown input command %c\n", message->base.type);
+    g_printerr ("_gdk_broadway_events_got_input - Unknown input command %c\n", message->base.type);
     break;
   }
 }
diff --git a/gdk/broadway/gdkprivate-broadway.h b/gdk/broadway/gdkprivate-broadway.h
index bcc06d2..8277c08 100644
--- a/gdk/broadway/gdkprivate-broadway.h
+++ b/gdk/broadway/gdkprivate-broadway.h
@@ -126,8 +126,7 @@ GList *_gdk_broadway_screen_list_visuals (GdkScreen *screen);
 void _gdk_broadway_screen_size_changed (GdkScreen *screen, 
 					BroadwayInputScreenResizeNotify *msg);
 
-void _gdk_broadway_events_got_input      (GdkDisplay *display,
-					  BroadwayInputMsg *message);
+void _gdk_broadway_events_got_input      (BroadwayInputMsg *message);
 
 void _gdk_broadway_screen_init_root_window (GdkScreen *screen);
 void _gdk_broadway_screen_init_visuals (GdkScreen *screen);
diff --git a/gdk/broadway/gdkwindow-broadway.c b/gdk/broadway/gdkwindow-broadway.c
index 08e23af..9709ff6 100644
--- a/gdk/broadway/gdkwindow-broadway.c
+++ b/gdk/broadway/gdkwindow-broadway.c
@@ -83,95 +83,17 @@ G_DEFINE_TYPE (GdkWindowImplBroadway,
 	       gdk_window_impl_broadway,
 	       GDK_TYPE_WINDOW_IMPL)
 
-static void
-diff_surfaces (cairo_surface_t *surface,
-	       cairo_surface_t *old_surface)
-{
-  guint8 *data, *old_data;
-  guint32 *line, *old_line;
-  int w, h, stride, old_stride;
-  int x, y;
-
-  data = cairo_image_surface_get_data (surface);
-  old_data = cairo_image_surface_get_data (old_surface);
-
-  w = cairo_image_surface_get_width (surface);
-  h = cairo_image_surface_get_height (surface);
-
-  stride = cairo_image_surface_get_stride (surface);
-  old_stride = cairo_image_surface_get_stride (old_surface);
-
-  for (y = 0; y < h; y++)
-    {
-      line = (guint32 *)data;
-      old_line = (guint32 *)old_data;
-
-      for (x = 0; x < w; x++)
-	{
-	  if ((*line & 0xffffff) == (*old_line & 0xffffff))
-	    *old_line = 0;
-	  else
-	    *old_line = *line | 0xff000000;
-	  line ++;
-	  old_line ++;
-	}
-
-      data += stride;
-      old_data += old_stride;
-    }
-}
-
 static guint dirty_flush_id = 0;
 
-static void
-window_data_send (BroadwayOutput *output, GdkWindowImplBroadway *impl)
-{
-  cairo_t *cr;
-
-  if (impl->surface == NULL)
-    return;
-
-  if (impl->last_synced)
-    {
-      diff_surfaces (impl->surface,
-		     impl->last_surface);
-      broadway_output_put_rgba (output, impl->id, 0, 0,
-				cairo_image_surface_get_width (impl->last_surface),
-				cairo_image_surface_get_height (impl->last_surface),
-				cairo_image_surface_get_stride (impl->last_surface),
-				cairo_image_surface_get_data (impl->last_surface));
-    }
-  else
-    {
-      impl->last_synced = TRUE;
-      broadway_output_put_rgb (output, impl->id, 0, 0,
-			       cairo_image_surface_get_width (impl->surface),
-			       cairo_image_surface_get_height (impl->surface),
-			       cairo_image_surface_get_stride (impl->surface),
-			       cairo_image_surface_get_data (impl->surface));
-    }
-
-  broadway_output_surface_flush (output, impl->id);
-
-  cr = cairo_create (impl->last_surface);
-  cairo_set_source_surface (cr, impl->surface, 0, 0);
-  cairo_paint (cr);
-  cairo_destroy (cr);
-}
-
 static gboolean
 dirty_flush_idle (gpointer data)
 {
   GList *l;
   GdkBroadwayDisplay *display;
-  BroadwayOutput *output;
 
   dirty_flush_id = 0;
 
   display = GDK_BROADWAY_DISPLAY (gdk_display_get_default ());
-  output = display->output;
-  if (output == NULL)
-    return FALSE;
 
   for (l = display->toplevels; l != NULL; l = l->next)
     {
@@ -180,11 +102,15 @@ dirty_flush_idle (gpointer data)
       if (impl->dirty)
 	{
 	  impl->dirty = FALSE;
-	  window_data_send (display->output, impl);
+	  _gdk_broadway_server_window_update (display->server,
+					      impl->id,
+					      impl->surface);
 	}
     }
 
-  gdk_display_flush (GDK_DISPLAY (display));
+  /* We sync here to ensure all references to the impl->surface memory
+     is done, as we may later paint new data in them. */
+  gdk_display_sync (GDK_DISPLAY (display));
 
   return FALSE;
 }
@@ -192,64 +118,10 @@ dirty_flush_idle (gpointer data)
 static void
 queue_dirty_flush (GdkBroadwayDisplay *display)
 {
-  if (dirty_flush_id == 0 && display->output != NULL)
+  if (dirty_flush_id == 0)
     dirty_flush_id = gdk_threads_add_idle (dirty_flush_idle, NULL);
 }
 
-void
-_gdk_broadway_resync_windows (void)
-{
-  GdkBroadwayDisplay *display;
-  GList *l;
-
-  dirty_flush_id = 0;
-
-  display = GDK_BROADWAY_DISPLAY (gdk_display_get_default ());
-
-  /* First create all windows */
-  for (l = display->toplevels; l != NULL; l = l->next)
-    {
-      GdkWindowImplBroadway *impl = l->data;
-      GdkWindow *window;
-
-      window = impl->wrapper;
-
-      if (impl->id == 0)
-	continue; /* Skip root */
-
-      impl->dirty = FALSE;
-      impl->last_synced = FALSE;
-      broadway_output_new_surface (display->output,
-				   impl->id,
-				   window->x,
-				   window->y,
-				   window->width,
-				   window->height,
-				   window->window_type == GDK_WINDOW_TEMP);
-    }
-
-  /* Then do everything that may reference other windows */
-  for (l = display->toplevels; l != NULL; l = l->next)
-    {
-      GdkWindowImplBroadway *impl = l->data;
-
-      if (impl->id == 0)
-	continue; /* Skip root */
-
-      if (impl->transient_for)
-	broadway_output_set_transient_for (display->output, impl->id, impl->transient_for);
-      /* Can't check GDK_WINDOW_IS_MAPPED here, because that doesn't correctly handle
-	 withdrawn windows like menus */
-      if (impl->visible)
-	{
-	  broadway_output_show_surface (display->output, impl->id);
-	  window_data_send (display->output, impl);
-	}
-    }
-
-  gdk_display_flush (GDK_DISPLAY (display));
-}
-
 static void
 gdk_window_impl_broadway_init (GdkWindowImplBroadway *impl)
 {
@@ -275,12 +147,6 @@ gdk_window_impl_broadway_finalize (GObject *object)
 
   broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (impl->wrapper));
 
-  if (broadway_display->mouse_in_toplevel == GDK_WINDOW (wrapper))
-    {
-      /* TODO: Send leave + enter event, update cursors, etc */
-      broadway_display->mouse_in_toplevel = NULL;
-    }
-
   g_hash_table_remove (broadway_display->id_ht, GINT_TO_POINTER(impl->id));
 
   if (impl->cursor)
@@ -315,6 +181,7 @@ _gdk_broadway_screen_init_root_window (GdkScreen * screen)
 
   impl->screen = screen;
   impl->wrapper = window;
+  impl->id = 0;
 
   window->window_type = GDK_WINDOW_ROOT;
   window->depth = 24;
@@ -341,13 +208,17 @@ _gdk_broadway_display_create_window_impl (GdkDisplay    *display,
 {
   GdkWindowImplBroadway *impl;
   GdkBroadwayDisplay *broadway_display;
-  static int current_id = 1; /* 0 is the root window */
 
   broadway_display = GDK_BROADWAY_DISPLAY (display);
 
   impl = g_object_new (GDK_TYPE_WINDOW_IMPL_BROADWAY, NULL);
   window->impl = (GdkWindowImpl *)impl;
-  impl->id = current_id++;
+  impl->id = _gdk_broadway_server_new_window (broadway_display->server,
+					      window->x,
+					      window->y,
+					      window->width,
+					      window->height,
+					      window->window_type == GDK_WINDOW_TEMP);
   g_hash_table_insert (broadway_display->id_ht, GINT_TO_POINTER(impl->id), window);
   impl->wrapper = window;
 
@@ -358,37 +229,23 @@ _gdk_broadway_display_create_window_impl (GdkDisplay    *display,
   g_assert (GDK_WINDOW_TYPE (window->parent) == GDK_WINDOW_ROOT);
 
   broadway_display->toplevels = g_list_prepend (broadway_display->toplevels, impl);
-
-  if (broadway_display->output)
-    broadway_output_new_surface (broadway_display->output,
-				 impl->id,
-				 window->x,
-				 window->y,
-				 window->width,
-				 window->height,
-				 window->window_type == GDK_WINDOW_TEMP);
 }
 
 void
 _gdk_broadway_window_resize_surface (GdkWindow *window)
 {
   GdkWindowImplBroadway *impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
-  cairo_surface_t *old, *last_old;
+  cairo_surface_t *old;
 
   if (impl->surface)
     {
       old = impl->surface;
-      last_old = impl->last_surface;
 
       impl->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
 						  gdk_window_get_width (impl->wrapper),
 						  gdk_window_get_height (impl->wrapper));
-      impl->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
-						       gdk_window_get_width (impl->wrapper),
-						       gdk_window_get_height (impl->wrapper));
 
       cairo_surface_destroy (old);
-      cairo_surface_destroy (last_old);
     }
 
   if (impl->ref_surface)
@@ -413,7 +270,6 @@ static cairo_surface_t *
 gdk_window_broadway_ref_cairo_surface (GdkWindow *window)
 {
   GdkWindowImplBroadway *impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
-  cairo_t *cr;
   int w, h;
 
   if (GDK_IS_WINDOW_IMPL_BROADWAY (window) &&
@@ -425,22 +281,7 @@ gdk_window_broadway_ref_cairo_surface (GdkWindow *window)
 
   /* Create actual backing store if missing */
   if (!impl->surface)
-    {
-      impl->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
-      impl->last_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
-
-      cr = cairo_create (impl->surface);
-      cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
-      cairo_rectangle (cr, 0, 0, w, h);
-      cairo_fill (cr);
-      cairo_destroy (cr);
-
-      cr = cairo_create (impl->last_surface);
-      cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
-      cairo_rectangle (cr, 0, 0, w, h);
-      cairo_fill (cr);
-      cairo_destroy (cr);
-    }
+    impl->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
 
   /* Create a destroyable surface referencing the real one */
   if (!impl->ref_surface)
@@ -485,16 +326,13 @@ _gdk_broadway_window_destroy (GdkWindow *window,
     {
       cairo_surface_destroy (impl->surface);
       impl->surface = NULL;
-      cairo_surface_destroy (impl->last_surface);
-      impl->last_surface = NULL;
     }
 
   broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window));
   g_hash_table_remove (broadway_display->id_ht, GINT_TO_POINTER(impl->id));
 
-  if (broadway_display->output)
-    broadway_output_destroy_surface (broadway_display->output,
-				     impl->id);
+  _gdk_broadway_server_destroy_window (broadway_display->server,
+				       impl->id);
 }
 
 static cairo_surface_t *
@@ -546,11 +384,9 @@ gdk_window_broadway_show (GdkWindow *window, gboolean already_mapped)
     _gdk_make_event (GDK_WINDOW (window), GDK_MAP, NULL, FALSE);
 
   broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window));
-  if (broadway_display->output)
-    {
-      broadway_output_show_surface (broadway_display->output, impl->id);
-      queue_dirty_flush (broadway_display);
-    }
+  if (_gdk_broadway_server_window_show (broadway_display->server, impl->id))
+    queue_dirty_flush (broadway_display);
+
 }
 
 static void
@@ -569,17 +405,8 @@ gdk_window_broadway_hide (GdkWindow *window)
     _gdk_make_event (GDK_WINDOW (window), GDK_UNMAP, NULL, FALSE);
 
   broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window));
-  if (broadway_display->output)
-    {
-      broadway_output_hide_surface (broadway_display->output, impl->id);
-      queue_dirty_flush (broadway_display);
-    }
-
-  if (broadway_display->mouse_in_toplevel == window)
-    {
-      /* TODO: Send leave + enter event, update cursors, etc */
-      broadway_display->mouse_in_toplevel = NULL;
-    }
+  if (_gdk_broadway_server_window_hide (broadway_display->server, impl->id))
+    queue_dirty_flush (broadway_display);
 
   _gdk_window_clear_update_area (window);
 }
@@ -601,7 +428,6 @@ gdk_window_broadway_move_resize (GdkWindow *window,
   GdkWindowImplBroadway *impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
   GdkBroadwayDisplay *broadway_display;
   gboolean changed, size_changed;;
-  gboolean with_resize;
 
   size_changed = changed = FALSE;
 
@@ -613,10 +439,8 @@ gdk_window_broadway_move_resize (GdkWindow *window,
       window->y = y;
     }
 
-  with_resize = FALSE;
   if (width > 0 || height > 0)
     {
-      with_resize = TRUE;
       if (width < 1)
 	width = 1;
 
@@ -644,12 +468,11 @@ gdk_window_broadway_move_resize (GdkWindow *window,
       GdkEvent *event;
       GList *node;
 
-      if (broadway_display->output != NULL)
+      if (_gdk_broadway_server_window_move_resize (broadway_display->server,
+						   impl->id,
+						   window->x, window->y,
+						   window->width, window->height))
 	{
-	  broadway_output_move_resize_surface (broadway_display->output,
-					       impl->id,
-					       with_move, window->x, window->y,
-					       with_resize, window->width, window->height);
 	  queue_dirty_flush (broadway_display);
 	  if (size_changed)
 	    window->resize_count++;
@@ -787,11 +610,7 @@ gdk_broadway_window_set_transient_for (GdkWindow *window,
   impl->transient_for = parent_id;
 
   display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (impl->wrapper));
-  if (display->output)
-    {
-      broadway_output_set_transient_for (display->output, impl->id, impl->transient_for);
-      gdk_display_flush (GDK_DISPLAY (display));
-    }
+  _gdk_broadway_server_window_set_transient_for (display->server, impl->id, impl->transient_for);
 }
 
 static void
@@ -1324,20 +1143,10 @@ moveresize_lookahead (GdkDisplay *display,
 		      BroadwayInputMsg *event)
 {
   GdkBroadwayDisplay *broadway_display;
-  BroadwayInputMsg *message;
-  GList *l;
 
   broadway_display = GDK_BROADWAY_DISPLAY (display);
-  for (l = broadway_display->input_messages; l != NULL; l = l->next)
-    {
-      message = l->data;
-      if (message->base.type == 'm')
-	return FALSE;
-      if (message->base.type == 'b')
-	return FALSE;
-    }
 
-  return TRUE;
+  return !_gdk_broadway_server_lookahead_event (broadway_display->server, "mb");
 }
 
 gboolean
@@ -1480,7 +1289,7 @@ gdk_broadway_window_begin_resize_drag (GdkWindow     *window,
 
   /* We need a connection to be able to get mouse events, if not, punt */
   broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window));
-  if (!broadway_display->output)
+  if (!_gdk_broadway_server_has_client (broadway_display->server))
     return;
 
   mv_resize = get_move_resize_data (GDK_WINDOW_DISPLAY (window), TRUE);
@@ -1622,9 +1431,6 @@ _gdk_broadway_window_translate (GdkWindow      *window,
 {
   GdkWindowImplBroadway *impl;
   GdkBroadwayDisplay *broadway_display;
-  int n_rects, i;
-  BroadwayRect *rects;
-  cairo_rectangle_int_t rect;
 
   impl = GDK_WINDOW_IMPL_BROADWAY (window->impl);
 
@@ -1632,26 +1438,11 @@ _gdk_broadway_window_translate (GdkWindow      *window,
     {
       copy_region (impl->surface, area, dx, dy);
       broadway_display = GDK_BROADWAY_DISPLAY (gdk_window_get_display (window));
-      if (GDK_WINDOW_IMPL_BROADWAY (impl)->last_synced &&
-	  broadway_display->output)
-	{
-	  copy_region (impl->last_surface, area, dx, dy);
-	  n_rects = cairo_region_num_rectangles (area);
-	  rects = g_new (BroadwayRect, n_rects);
-	  for (i = 0; i < n_rects; i++)
-	    {
-	      cairo_region_get_rectangle (area, i, &rect);
-	      rects[i].x = rect.x;
-	      rects[i].y = rect.y;
-	      rects[i].width = rect.width;
-	      rects[i].height = rect.height;
-	    }
-	  broadway_output_copy_rectangles (broadway_display->output,
-					   GDK_WINDOW_IMPL_BROADWAY (impl)->id,
-					   rects, n_rects, dx, dy);
-	  queue_dirty_flush (broadway_display);
-	  g_free (rects);
-	}
+
+      if (_gdk_broadway_server_window_translate (broadway_display->server,
+						 impl->id,
+						 area, dx, dy))
+	queue_dirty_flush (broadway_display);
     }
 }
 
@@ -1661,8 +1452,7 @@ gdk_broadway_get_last_seen_time (GdkWindow  *window)
   GdkDisplay *display;
 
   display = gdk_window_get_display (window);
-  _gdk_broadway_display_consume_all_input (display);
-  return (guint32) GDK_BROADWAY_DISPLAY (display)->last_seen_time;
+  return _gdk_broadway_server_get_last_seen_time (GDK_BROADWAY_DISPLAY (display)->server);
 }
 
 static void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]