]> Pileus Git - ~andy/gtk/blobdiff - gdk/broadway/broadway.c
broadway: Break out broadway protocol in its own header
[~andy/gtk] / gdk / broadway / broadway.c
index bc78de1e58feabf91acdc576e35d2690da6b5894..157b3c1b7e50479a1a32e50e19f0a266e9fe52ae 100644 (file)
@@ -3,7 +3,7 @@
 #include <stdio.h>
 #include <assert.h>
 #include <errno.h>
-#include <zlib.h>
+#include <cairo.h>
 
 #include "broadway.h"
 
@@ -14,7 +14,7 @@
 static const char base64_alphabet[] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-#if 0
+#if 0 /* Unused for now */
 static void
 base64_uint8 (guint8 v, char *c)
 {
@@ -53,391 +53,155 @@ base64_uint32 (guint32 v, char *c)
   c[5] = base64_alphabet[(v >> 30) & 0x2];
 }
 
-/************************************************************************
- *  conversion of raw image data to uncompressed png data: uris         *
- ************************************************************************/
+/***********************************************************
+ *  conversion of raw image data to png data: uris         *
+ ***********************************************************/
 
-/* Table of CRCs of all 8-bit messages. */
-static unsigned long crc_table[256];
+static cairo_status_t
+write_png_data (void             *closure,
+               const unsigned char *data,
+               unsigned int       data_len)
+{
+  GString *buf = closure;
 
-/* Flag: has the table been computed? Initially false. */
-static int crc_table_computed = 0;
+  g_string_append_len (buf,  (char *)data, data_len);
 
-/* Make the table for a fast CRC. */
-static void
-make_crc_table(void)
-{
-  unsigned long c;
-  int n, k;
-
-  for (n = 0; n < 256; n++) {
-    c = (unsigned long) n;
-    for (k = 0; k < 8; k++) {
-      if (c & 1)
-       c = 0xedb88320L ^ (c >> 1);
-      else
-       c = c >> 1;
-    }
-    crc_table[n] = c;
-  }
-  crc_table_computed = 1;
+  return CAIRO_STATUS_SUCCESS;
 }
 
-static unsigned long
-update_crc(unsigned long crc, unsigned char *buf, int len)
+static void
+to_png_rgb (GString *buf, int w, int h, int byte_stride, guint32 *data)
 {
-  unsigned long c = crc;
-  int n;
-
-  if (!crc_table_computed)
-    make_crc_table();
-  for (n = 0; n < len; n++) {
-    c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
-  }
-  return c;
+  cairo_surface_t *surface;
+
+  surface = cairo_image_surface_create_for_data ((guchar *)data,
+                                                CAIRO_FORMAT_RGB24, w, h, byte_stride);
+
+  cairo_surface_write_to_png_stream (surface, write_png_data, buf);
+  cairo_surface_destroy (surface);
 }
 
-static unsigned long
-crc(unsigned char *buf, int len)
+static void
+to_png_rgba (GString *buf, int w, int h, int byte_stride, guint32 *data)
 {
-  return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
+  cairo_surface_t *surface;
+
+  surface = cairo_image_surface_create_for_data ((guchar *)data,
+                                                CAIRO_FORMAT_ARGB32, w, h, byte_stride);
+
+  cairo_surface_write_to_png_stream (surface, write_png_data, buf);
+  cairo_surface_destroy (surface);
 }
 
-#define BASE 65521 /* largest prime smaller than 65536 */
-static unsigned long
-update_adler32(unsigned long adler, unsigned char *buf, int len)
+struct PngTarget {
+  GString *buf;
+  int state;
+  int save;
+};
+
+static cairo_status_t
+write_png_url (void              *closure,
+              const unsigned char *data,
+              unsigned int        data_len)
 {
-  unsigned long s1 = adler & 0xffff;
-  unsigned long s2 = (adler >> 16) & 0xffff;
-  int n;
-
-  for (n = 0; n < len; n++) {
-    s1 = (s1 + buf[n]) % BASE;
-    s2 = (s2 + s1)     % BASE;
-  }
-  return (s2 << 16) + s1;
+  struct PngTarget *target = closure;
+  gsize res, old_len;
+
+  old_len = target->buf->len;
+  g_string_set_size (target->buf,
+                    old_len + (data_len / 3 + 1) * 4 + 4);
+
+  res = g_base64_encode_step (data, data_len, FALSE,
+                             target->buf->str + old_len,
+                             &target->state, &target->save);
+
+  g_string_set_size (target->buf,  old_len + res);
+
+  return CAIRO_STATUS_SUCCESS;
 }
 
-static char *
-to_png_rgb (int w, int h, int byte_stride, guint32 *data)
+static void
+to_png_url_rgb (GString *buf, int w, int h, int byte_stride, guint32 *data)
 {
-  guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
-  guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
-                       /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
-                       /* bpp: */ 8, /* color type: */ 2,
-                       0, 0, 0};
-  guchar idat_start[8] = { /* len: */0, 0, 0, 0,   'I', 'D', 'A', 'T' };
-  guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
-  gsize data_size, row_size;
-  char row_header[6];
-  guint8 *png, *p, *p_row, *p_idat;
-  guint32 *row;
-  unsigned long adler;
-  guint32 pixel;
-  gsize png_size;
-  int x, y;
-  char *url, *url_base64;
-  int state = 0, outlen;
-  int save = 0;
-
-  *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
-  *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
-  *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
-
-  row_size = 1 + w * 3;
-  row_header[0] = 0;
-  row_header[1] = row_size & 0xff;
-  row_header[2] = (row_size >> 8) & 0xff;
-  row_header[3] = ~row_header[1];
-  row_header[4] = ~row_header[2];
-  row_header[5] = 0;
-
-  data_size = 2 + (6 + w * 3) * h + 4;
-
-  *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
-
-  png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
-  png = g_malloc (png_size);
-
-  p = png;
-  memcpy (p, header, sizeof(header));
-  p += sizeof(header);
-  memcpy (p, ihdr, sizeof(ihdr));
-  p += sizeof(ihdr);
-  memcpy (p, idat_start, sizeof(idat_start));
-  p += sizeof(idat_start);
-
-  /* IDAT data:
-
-     zlib header:  0x78, 0x01 ,
-     h * scanline: row_header[] + width * r,g,b
-     checksum: adler32
-  */
-
-  p_idat = p - 4;
-
-  /* zlib header */
-  *p++ = 0x78;
-  *p++ = 0x01;
-
-  adler = 1;
-
-  /* scanline data */
-  for (y = 0; y < h; y++) {
-    if (y == h - 1)
-      row_header[0] = 1; /* final block */
-    memcpy (p, row_header, sizeof(row_header));
-    p += sizeof(row_header);
-    p_row = p - 1;
-    row = data;
-    data += byte_stride / 4;
-    for (x = 0; x < w; x++) {
-      pixel = *row++;
-      *p++ = (pixel >> 16) & 0xff; /* red */
-      *p++ = (pixel >> 8) & 0xff; /* green */
-      *p++ = (pixel >> 0) & 0xff; /* blue */
-    }
-    adler = update_adler32(adler, p_row, p - p_row);
-  }
-
-  /* adler32 */
-  *(guint32 *)p = GUINT32_TO_BE(adler);
-  p += 4;
-  *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
-  p += 4;
+  cairo_surface_t *surface;
+  struct PngTarget target;
+  gsize res, old_len;
 
-  memcpy (p, iend, sizeof(iend));
-  p += sizeof(iend);
+  target.buf = buf;
+  target.state = 0;
+  target.save = 0;
 
-  assert(p - png == png_size);
+  g_string_append (buf, "data:image/png;base64,");
 
-  url = g_malloc (strlen("data:image/png;base64,") +
-                  ((png_size / 3 + 1) * 4 + 4) + 1);
-  strcpy (url, "data:image/png;base64,");
+  surface = cairo_image_surface_create_for_data ((guchar *)data,
+                                                CAIRO_FORMAT_RGB24, w, h, byte_stride);
 
-  url_base64 = url + strlen("data:image/png;base64,");
-  outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
-  outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
-  url_base64[outlen] = 0;
+  cairo_surface_write_to_png_stream (surface, write_png_url, &target);
+  cairo_surface_destroy (surface);
 
-  free (png);
+  old_len = buf->len;
 
-  return url;
+  g_string_set_size (buf, old_len + 4);
+  res = g_base64_encode_close (FALSE,
+                              buf->str + old_len,
+                              &target.state, &target.save);
+  g_string_set_size (buf, old_len + res);
 }
 
-static char *
-to_png_rgba (int w, int h, int byte_stride, guint32 *data)
+static void
+to_png_url_rgba (GString *buf, int w, int h, int byte_stride, guint32 *data)
 {
-  guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
-  guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
-                       /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
-                       /* bpp: */ 8, /* color type: */ 6,
-                       0, 0, 0};
-  guchar idat_start[8] = { /* len: */0, 0, 0, 0,   'I', 'D', 'A', 'T' };
-  guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
-  gsize data_size, row_size;
-  char row_header[6];
-  guint8 *png, *p, *p_row, *p_idat;
-  guint32 *row;
-  unsigned long adler;
-  guint32 pixel;
-  gsize png_size;
-  int x, y;
-  char *url, *url_base64;
-  int state = 0, outlen;
-  int save = 0;
-
-  *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
-  *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
-  *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
-
-  row_size = 1 + w * 4;
-  row_header[0] = 0;
-  row_header[1] = row_size & 0xff;
-  row_header[2] = (row_size >> 8) & 0xff;
-  row_header[3] = ~row_header[1];
-  row_header[4] = ~row_header[2];
-  row_header[5] = 0;
-
-  data_size = 2 + (6 + w * 4) * h + 4;
-
-  *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
-
-  png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
-  png = g_malloc (png_size);
-
-  p = png;
-  memcpy (p, header, sizeof(header));
-  p += sizeof(header);
-  memcpy (p, ihdr, sizeof(ihdr));
-  p += sizeof(ihdr);
-  memcpy (p, idat_start, sizeof(idat_start));
-  p += sizeof(idat_start);
-
-  /* IDAT data:
-
-     zlib header:  0x78, 0x01 ,
-     h * scanline: row_header[] + width * r,g,b,a
-     checksum: adler32
-  */
-
-  p_idat = p - 4;
-
-  /* zlib header */
-  *p++ = 0x78;
-  *p++ = 0x01;
-
-  adler = 1;
-
-  /* scanline data */
-  for (y = 0; y < h; y++) {
-    if (y == h - 1)
-      row_header[0] = 1; /* final block */
-    memcpy (p, row_header, sizeof(row_header));
-    p += sizeof(row_header);
-    p_row = p - 1;
-    row = data;
-    data += byte_stride / 4;
-    for (x = 0; x < w; x++) {
-      pixel = *row++;
-      *p++ = (pixel >> 16) & 0xff; /* red */
-      *p++ = (pixel >> 8) & 0xff; /* green */
-      *p++ = (pixel >> 0) & 0xff; /* blue */
-      *p++ = (pixel >> 24) & 0xff; /* alpha */
-    }
-    adler = update_adler32(adler, p_row, p - p_row);
-  }
+  cairo_surface_t *surface;
+  struct PngTarget target;
+  gsize res, old_len;
 
-  /* adler32 */
-  *(guint32 *)p = GUINT32_TO_BE(adler);
-  p += 4;
-  *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
-  p += 4;
+  target.buf = buf;
+  target.state = 0;
+  target.save = 0;
 
-  memcpy (p, iend, sizeof(iend));
-  p += sizeof(iend);
+  g_string_append (buf, "data:image/png;base64,");
 
-  assert(p - png == png_size);
+  surface = cairo_image_surface_create_for_data ((guchar *)data,
+                                                CAIRO_FORMAT_ARGB32, w, h, byte_stride);
 
-  url = g_malloc (strlen("data:image/png;base64,") +
-                  ((png_size / 3 + 1) * 4 + 4) + 1);
-  strcpy (url, "data:image/png;base64,");
+  cairo_surface_write_to_png_stream (surface, write_png_url, &target);
+  cairo_surface_destroy (surface);
 
-  url_base64 = url + strlen("data:image/png;base64,");
-  outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
-  outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
-  url_base64[outlen] = 0;
+  old_len = buf->len;
 
-  free (png);
-
-  return url;
+  g_string_set_size (buf, old_len + 4);
+  res = g_base64_encode_close (FALSE,
+                              buf->str + old_len,
+                              &target.state, &target.save);
+  g_string_set_size (buf, old_len + res);
 }
 
 #if 0
 static char *
 to_png_a (int w, int h, int byte_stride, guint8 *data)
 {
-  guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
-  guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
-                       /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
-                       /* bpp: */ 8, /* color type: */ 4,
-                       0, 0, 0};
-  guchar idat_start[8] = { /* len: */0, 0, 0, 0,   'I', 'D', 'A', 'T' };
-  guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
-  gsize data_size, row_size;
-  char row_header[6];
-  guint8 *png, *p, *p_row, *p_idat;
-  guint8 *row;
-  unsigned long adler;
-  guint32 pixel;
-  gsize png_size;
-  int x, y;
-  char *url, *url_base64;
-  int state = 0, outlen;
-  int save = 0;
-
-  *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
-  *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
-  *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
-
-  row_size = 1 + w * 2;
-  row_header[0] = 0;
-  row_header[1] = row_size & 0xff;
-  row_header[2] = (row_size >> 8) & 0xff;
-  row_header[3] = ~row_header[1];
-  row_header[4] = ~row_header[2];
-  row_header[5] = 0;
-
-  data_size = 2 + (6 + w * 2) * h + 4;
-
-  *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
-
-  png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
-  png = g_malloc (png_size);
-
-  p = png;
-  memcpy (p, header, sizeof(header));
-  p += sizeof(header);
-  memcpy (p, ihdr, sizeof(ihdr));
-  p += sizeof(ihdr);
-  memcpy (p, idat_start, sizeof(idat_start));
-  p += sizeof(idat_start);
-
-  /* IDAT data:
-
-     zlib header:  0x78, 0x01 ,
-     h * scanline: row_header[] + width * r,g,b,a
-     checksum: adler32
-  */
-
-  p_idat = p - 4;
-
-  /* zlib header */
-  *p++ = 0x78;
-  *p++ = 0x01;
-
-  adler = 1;
-
-  /* scanline data */
-  for (y = 0; y < h; y++) {
-    if (y == h - 1)
-      row_header[0] = 1; /* final block */
-    memcpy (p, row_header, sizeof(row_header));
-    p += sizeof(row_header);
-    p_row = p - 1;
-    row = data;
-    data += byte_stride / 4;
-    for (x = 0; x < w; x++) {
-      pixel = *row++;
-      *p++ = 0x00; /* gray */
-      *p++ = pixel; /* alpha */
-    }
-    adler = update_adler32(adler, p_row, p - p_row);
-  }
+  cairo_surface_t *surface;
+  struct PngTarget target;
+  gsize res, old_len;
 
-  /* adler32 */
-  *(guint32 *)p = GUINT32_TO_BE(adler);
-  p += 4;
-  *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
-  p += 4;
+  target.url = g_string_new ("data:image/png;base64,");
+  target.state = 0;
+  target.save = 0;
 
-  memcpy (p, iend, sizeof(iend));
-  p += sizeof(iend);
+  surface = cairo_image_surface_create_for_data (data,
+                                                CAIRO_FORMAT_A8, w, h, byte_stride);
 
-  assert(p - png == png_size);
+  cairo_surface_write_to_png_stream (surface, write_png_url, &target);
 
-  url = g_malloc (strlen("data:image/png;base64,") +
-                 ((png_size / 3 + 1) * 4 + 4) + 1);
-  strcpy (url, "data:image/png;base64,");
+  old_len = target.url->len;
 
-  url_base64 = url + strlen("data:image/png;base64,");
-  outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
-  outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
-  url_base64[outlen] = 0;
+  g_string_set_size (target.url, old_len + 4);
+  res = g_base64_encode_close (FALSE,
+                              target.url->str + old_len,
+                              &target.state, &target.save);
+  g_string_set_size (target.url, old_len + res);
 
-  free (png);
-
-  return url;
+  return g_string_free (target.url, FALSE);
 }
 #endif
 
@@ -446,112 +210,96 @@ to_png_a (int w, int h, int byte_stride, guint8 *data)
  ************************************************************************/
 
 struct BroadwayOutput {
-  int fd;
-  gzFile *zfd;
+  GOutputStream *out;
+  GString *buf;
   int error;
   guint32 serial;
+  gboolean proto_v7_plus;
+  gboolean binary;
 };
 
 static void
-broadway_output_write_raw (BroadwayOutput *output,
-                          const void *buf, gsize count)
-{
-  gssize res;
-  int errsave;
-  const char *ptr = (const char *)buf;
-
-  if (output->error)
-    return;
-
-  while (count > 0)
+broadway_output_send_cmd (BroadwayOutput *output,
+                         gboolean fin, BroadwayWSOpCode code,
+                         const void *buf, gsize count)
+{
+  gboolean mask = FALSE;
+  guchar header[16];
+  size_t p;
+
+  gboolean mid_header = count > 125 && count <= 65535;
+  gboolean long_header = count > 65535;
+
+  /* NB. big-endian spec => bit 0 == MSB */
+  header[0] = ( (fin ? 0x80 : 0) | (code & 0x0f) );
+  header[1] = ( (mask ? 0x80 : 0) |
+                (mid_header ? 126 : long_header ? 127 : count) );
+  p = 2;
+  if (mid_header)
     {
-      res = write(output->fd, ptr, count);
-      if (res == -1)
-       {
-         errsave = errno;
-         if (errsave == EINTR)
-           continue;
-         output->error = TRUE;
-         return;
-       }
-      if (res == 0)
-       {
-         output->error = TRUE;
-         return;
-       }
-      count -= res;
-      ptr += res;
+      *(guint16 *)(header + p) = GUINT16_TO_BE( (guint16)count );
+      p += 2;
+    }
+  else if (long_header)
+    {
+      *(guint64 *)(header + p) = GUINT64_TO_BE( count );
+      p += 8;
     }
+  // FIXME: if we are paranoid we should 'mask' the data
+  // FIXME: we should really emit these as a single write
+  g_output_stream_write_all (output->out, header, p, NULL, NULL, NULL);
+  g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
 }
 
 static void
-broadway_output_write (BroadwayOutput *output,
-                      const void *buf, gsize count)
+broadway_output_send_cmd_pre_v7 (BroadwayOutput *output,
+                                const void *buf, gsize count)
 {
-  gssize res;
-  const char *ptr = (const char *)buf;
-
-  if (output->error)
-    return;
-
-  while (count > 0)
-    {
-      res = gzwrite(output->zfd, ptr, count);
-      if (res == -1)
-       {
-         output->error = TRUE;
-         return;
-       }
-      if (res == 0)
-       {
-         output->error = TRUE;
-         return;
-       }
-      count -= res;
-      ptr += res;
-    }
+  g_output_stream_write_all (output->out, "\0", 1, NULL, NULL, NULL);
+  g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
+  g_output_stream_write_all (output->out, "\xff", 1, NULL, NULL, NULL);
 }
 
-static void
-broadway_output_write_header (BroadwayOutput *output)
+void broadway_output_pong (BroadwayOutput *output)
 {
-  char *header;
-
-  header =
-    "HTTP/1.1 200 OK\r\n"
-    "Content-type: multipart/x-mixed-replace;boundary=x\r\n"
-    "Content-Encoding: gzip\r\n"
-    "\r\n";
-  broadway_output_write_raw (output,
-                            header, strlen (header));
+  if (output->proto_v7_plus)
+    broadway_output_send_cmd (output, TRUE, BROADWAY_WS_CNX_PONG, NULL, 0);
 }
 
-static void
-send_boundary (BroadwayOutput *output)
+int
+broadway_output_flush (BroadwayOutput *output)
 {
-  char *boundary =
-    "--x\r\n"
-    "\r\n";
+  if (output->buf->len == 0)
+    return TRUE;
+
+  if (!output->proto_v7_plus)
+    broadway_output_send_cmd_pre_v7 (output, output->buf->str, output->buf->len);
+  else if (output->binary)
+    broadway_output_send_cmd (output, TRUE, BROADWAY_WS_BINARY,
+                             output->buf->str, output->buf->len);
+  else
+    broadway_output_send_cmd (output, TRUE, BROADWAY_WS_TEXT,
+                             output->buf->str, output->buf->len);
+
+  g_string_set_size (output->buf, 0);
+
+  return !output->error;
 
-  broadway_output_write (output, boundary, strlen (boundary));
 }
 
 BroadwayOutput *
-broadway_output_new(int fd, guint32 serial)
+broadway_output_new (GOutputStream *out, guint32 serial,
+                    gboolean proto_v7_plus, gboolean binary)
 {
   BroadwayOutput *output;
 
   output = g_new0 (BroadwayOutput, 1);
 
-  output->fd = fd;
+  output->out = g_object_ref (out);
+  output->buf = g_string_new ("");
   output->serial = serial;
-
-  broadway_output_write_header (output);
-
-  output->zfd = gzdopen(fd, "wb");
-
-  /* Need an initial multipart boundary */
-  send_boundary (output);
+  output->proto_v7_plus = proto_v7_plus;
+  output->binary = binary;
 
   return output;
 }
@@ -559,10 +307,7 @@ broadway_output_new(int fd, guint32 serial)
 void
 broadway_output_free (BroadwayOutput *output)
 {
-  if (output->zfd)
-    gzclose (output->zfd);
-  else
-    close (output->fd);
+  g_object_unref (output->out);
   free (output);
 }
 
@@ -572,257 +317,248 @@ broadway_output_get_next_serial (BroadwayOutput *output)
   return output->serial;
 }
 
-int
-broadway_output_flush (BroadwayOutput *output)
-{
-  send_boundary (output);
-  gzflush (output->zfd, Z_SYNC_FLUSH);
-  return !output->error;
-}
-
 
 /************************************************************************
  *                     Core rendering operations                        *
  ************************************************************************/
 
-#define HEADER_LEN (1+6)
-
 static void
-append_uint16 (guint32 v, char *buf, int *p)
+append_char (BroadwayOutput *output, char c)
 {
-  base64_uint16 (v, &buf[*p]);
-  *p += 3;
+  g_string_append_c (output->buf, c);
 }
 
 static void
-append_uint32 (guint32 v, char *buf, int *p)
+append_bool (BroadwayOutput *output, gboolean val)
 {
-  base64_uint32 (v, &buf[*p]);
-  *p += 6;
+  if (output->binary)
+    g_string_append_c (output->buf, val ? 1: 0);
+  else
+    g_string_append_c (output->buf, val ? '1': '0');
 }
 
-static int
-write_header(BroadwayOutput *output, char *buf, char op)
+static void
+append_flags (BroadwayOutput *output, guint32 val)
 {
-  int p;
-
-  p = 0;
-  buf[p++] = op;
-  append_uint32 (output->serial++, buf, &p);
-
-  return p;
+  if (output->binary)
+    g_string_append_c (output->buf, val);
+  else
+    g_string_append_c (output->buf, val + '0');
 }
 
-void
-broadway_output_copy_rectangles (BroadwayOutput *output,  int id,
-                                BroadwayRect *rects, int n_rects,
-                                int dx, int dy)
-{
-  char *buf;
-  int len, i, p;
 
-  len = HEADER_LEN + 3 + 3 + 3*4*n_rects + 3 + 3;
+static void
+append_uint16 (BroadwayOutput *output, guint32 v)
+{
+  gsize old_len = output->buf->len;
+  guint8 *buf;
 
-  buf = g_malloc (len);
-  p = write_header (output, buf, 'b');
-  append_uint16 (id, buf, &p);
-  append_uint16 (n_rects, buf, &p);
-  for (i = 0; i < n_rects; i++)
+  if (output->binary)
+    {
+      g_string_set_size (output->buf, old_len + 2);
+      buf = (guint8 *)output->buf->str + old_len;
+      buf[0] = (v >> 0) & 0xff;
+      buf[1] = (v >> 8) & 0xff;
+    }
+  else
     {
-      append_uint16 (rects[i].x, buf, &p);
-      append_uint16 (rects[i].y, buf, &p);
-      append_uint16 (rects[i].width, buf, &p);
-      append_uint16 (rects[i].height, buf, &p);
+      g_string_set_size (output->buf, old_len + 3);
+      base64_uint16 (v, output->buf->str + old_len);
     }
-  append_uint16 (dx, buf, &p);
-  append_uint16 (dy, buf, &p);
+}
 
-  assert (p == len);
+static void
+append_uint32 (BroadwayOutput *output, guint32 v)
+{
+  gsize old_len = output->buf->len;
+  guint8 *buf;
 
-  broadway_output_write (output, buf, len);
-  free (buf);
+  if (output->binary)
+    {
+      g_string_set_size (output->buf, old_len + 4);
+      buf = (guint8 *)output->buf->str + old_len;
+      buf[0] = (v >> 0) & 0xff;
+      buf[1] = (v >> 8) & 0xff;
+      buf[2] = (v >> 16) & 0xff;
+      buf[3] = (v >> 24) & 0xff;
+    }
+  else
+    {
+      g_string_set_size (output->buf, old_len + 6);
+      base64_uint32 (v, output->buf->str + old_len);
+    }
 }
 
-guint32
-broadway_output_query_pointer (BroadwayOutput *output, int id)
+static void
+overwrite_uint32 (BroadwayOutput *output, gsize pos, guint32 v)
 {
-  char buf[HEADER_LEN + 3];
-  guint32 serial;
-  int p;
+  if (output->binary)
+    {
+      guint8 *buf = (guint8 *)output->buf->str + pos;
 
-  serial = output->serial;
-  p = write_header (output, buf, 'q');
-  append_uint16 (id, buf, &p);
+      buf[0] = (v >> 0) & 0xff;
+      buf[1] = (v >> 8) & 0xff;
+      buf[2] = (v >> 16) & 0xff;
+      buf[3] = (v >> 24) & 0xff;
+    }
+  else
+    {
+      base64_uint32 (v, output->buf->str + pos);
+    }
+}
 
-  assert (p == sizeof (buf));
 
-  broadway_output_write (output, buf, sizeof (buf));
+static void
+write_header(BroadwayOutput *output, char op)
+{
+  append_char (output, op);
+  append_uint32 (output, output->serial++);
+}
 
-  return serial;
+void
+broadway_output_copy_rectangles (BroadwayOutput *output,  int id,
+                                BroadwayRect *rects, int n_rects,
+                                int dx, int dy)
+{
+  int i;
+
+  write_header (output, 'b');
+  append_uint16 (output, id);
+  append_uint16 (output, n_rects);
+  for (i = 0; i < n_rects; i++)
+    {
+      append_uint16 (output, rects[i].x);
+      append_uint16 (output, rects[i].y);
+      append_uint16 (output, rects[i].width);
+      append_uint16 (output, rects[i].height);
+    }
+  append_uint16 (output, dx);
+  append_uint16 (output, dy);
 }
 
-guint32
+void
 broadway_output_grab_pointer (BroadwayOutput *output,
                              int id,
-                             gboolean owner_event,
-                             guint32 time_)
+                             gboolean owner_event)
 {
-  char buf[HEADER_LEN + 3 + 1 + 6];
-  guint32 serial;
-  int p;
-
-  serial = output->serial;
-  p = write_header (output, buf, 'g');
-  append_uint16 (id, buf, &p);
-  buf[p++] = owner_event ? '1': '0';
-  append_uint32 (time_, buf, &p);
-
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
-
-  return serial;
+  write_header (output, 'g');
+  append_uint16 (output, id);
+  append_bool (output, owner_event);
 }
 
 guint32
-broadway_output_ungrab_pointer (BroadwayOutput *output,
-                               guint32 time_)
+broadway_output_ungrab_pointer (BroadwayOutput *output)
 {
-  char buf[HEADER_LEN + 6];
   guint32 serial;
-  int p;
 
   serial = output->serial;
-  p = write_header (output, buf, 'u');
-  append_uint32 (time_, buf, &p);
-
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
+  write_header (output, 'u');
 
   return serial;
 }
 
 void
-broadway_output_new_surface(BroadwayOutput *output,  int id, int x, int y, int w, int h)
+broadway_output_new_surface(BroadwayOutput *output,
+                           int id, int x, int y, int w, int h,
+                           gboolean is_temp)
 {
-  char buf[HEADER_LEN + 15];
-  int p;
-
-  p = write_header (output, buf, 's');
-  append_uint16 (id, buf, &p);
-  append_uint16 (x, buf, &p);
-  append_uint16 (y, buf, &p);
-  append_uint16 (w, buf, &p);
-  append_uint16 (h, buf, &p);
-
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
+  write_header (output, 's');
+  append_uint16 (output, id);
+  append_uint16 (output, x);
+  append_uint16 (output, y);
+  append_uint16 (output, w);
+  append_uint16 (output, h);
+  append_bool (output, is_temp);
 }
 
 void
 broadway_output_show_surface(BroadwayOutput *output,  int id)
 {
-  char buf[HEADER_LEN + 3];
-  int p;
-
-  p = write_header (output, buf, 'S');
-  append_uint16 (id, buf, &p);
-
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
+  write_header (output, 'S');
+  append_uint16 (output, id);
 }
 
 void
 broadway_output_hide_surface(BroadwayOutput *output,  int id)
 {
-  char buf[HEADER_LEN + 3];
-  int p;
-
-  p = write_header (output, buf, 'H');
-  append_uint16 (id, buf, &p);
-
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
+  write_header (output, 'H');
+  append_uint16 (output, id);
 }
 
 void
 broadway_output_destroy_surface(BroadwayOutput *output,  int id)
 {
-  char buf[HEADER_LEN + 3];
-  int p;
-
-  p = write_header (output, buf, 'd');
-  append_uint16 (id, buf, &p);
-
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
+  write_header (output, 'd');
+  append_uint16 (output, id);
 }
 
-void
-broadway_output_move_surface(BroadwayOutput *output,  int id, int x, int y)
-{
-  char buf[HEADER_LEN + 9];
-  int p;
-
-  p = write_header (output, buf, 'm');
-
-  append_uint16 (id, buf, &p);
-  append_uint16 (x, buf, &p);
-  append_uint16 (y, buf, &p);
 
-  assert (p == sizeof (buf));
+void
+broadway_output_move_resize_surface (BroadwayOutput *output,
+                                    int             id,
+                                    gboolean        has_pos,
+                                    int             x,
+                                    int             y,
+                                    gboolean        has_size,
+                                    int             w,
+                                    int             h)
+{
+  int val;
+
+  if (!has_pos && !has_size)
+    return;
 
-  broadway_output_write (output, buf, sizeof (buf));
+  write_header (output, 'm');
+  val = (!!has_pos) | ((!!has_size) << 1);
+  append_uint16 (output, id);
+  append_flags (output, val);
+  if (has_pos)
+    {
+      append_uint16 (output, x);
+      append_uint16 (output, y);
+    }
+  if (has_size)
+    {
+      append_uint16 (output, w);
+      append_uint16 (output, h);
+    }
 }
 
 void
-broadway_output_resize_surface(BroadwayOutput *output,  int id, int w, int h)
+broadway_output_set_transient_for (BroadwayOutput *output,
+                                  int             id,
+                                  int             parent_id)
 {
-  char buf[HEADER_LEN + 9];
-  int p;
-
-  p = write_header (output, buf, 'r');
-
-  append_uint16 (id, buf, &p);
-  append_uint16 (w, buf, &p);
-  append_uint16 (h, buf, &p);
-
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
+  write_header (output, 'p');
+  append_uint16 (output, id);
+  append_uint16 (output, parent_id);
 }
 
+
 void
 broadway_output_put_rgb (BroadwayOutput *output,  int id, int x, int y,
                         int w, int h, int byte_stride, void *data)
 {
-  char buf[HEADER_LEN + 15];
-  gsize len;
-  char *url;
-  int p;
+  gsize size_start, image_start, len;
 
-  p = write_header (output, buf, 'i');
+  write_header (output, 'i');
 
-  append_uint16 (id, buf, &p);
-  append_uint16 (x, buf, &p);
-  append_uint16 (y, buf, &p);
+  append_uint16 (output, id);
+  append_uint16 (output, x);
+  append_uint16 (output, y);
 
-  url = to_png_rgb (w, h, byte_stride, (guint32*)data);
-  len = strlen (url);
-  append_uint32 (len, buf, &p);
+  size_start = output->buf->len;
+  append_uint32 (output, 0);
 
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
+  image_start = output->buf->len;
+  if (output->binary)
+    to_png_rgb (output->buf, w, h, byte_stride, (guint32*)data);
+  else
+    to_png_url_rgb (output->buf, w, h, byte_stride, (guint32*)data);
 
-  broadway_output_write (output, url, len);
+  len = output->buf->len - image_start;
 
-  free (url);
+  overwrite_uint32 (output, size_start, len);
 }
 
 typedef struct  {
@@ -1052,66 +788,48 @@ void
 broadway_output_put_rgba (BroadwayOutput *output,  int id, int x, int y,
                          int w, int h, int byte_stride, void *data)
 {
-  char buf[HEADER_LEN + 15];
-  gsize len;
-  char *url;
   BroadwayBox *rects;
-  int p, i, n_rects;
-  guint8 *subdata;
+  int i, n_rects;
+  gsize size_start, image_start, len;
 
   rects = rgba_find_rects (data, w, h, byte_stride, &n_rects);
 
   for (i = 0; i < n_rects; i++)
     {
-      subdata = (guint8 *)data + rects[i].x1 * 4 + rects[i].y1 * byte_stride;
+      guint8 *subdata;
 
-      p = write_header (output, buf, 'i');
-      append_uint16 (id, buf, &p);
-      append_uint16 (x + rects[i].x1, buf, &p);
-      append_uint16 (y + rects[i].y1, buf, &p);
+      write_header (output, 'i');
+      append_uint16 (output, id);
+      append_uint16 (output, x + rects[i].x1);
+      append_uint16 (output, y + rects[i].y1);
 
-      url = to_png_rgba (rects[i].x2 - rects[i].x1,
-                        rects[i].y2 - rects[i].y1,
-                        byte_stride, (guint32*)subdata);
-      len = strlen (url);
-      append_uint32 (len, buf, &p);
+      size_start = output->buf->len;
+      append_uint32 (output, 0);
 
-      assert (p == sizeof (buf));
+      image_start = output->buf->len;
 
-      broadway_output_write (output, buf, sizeof (buf));
+      subdata = (guint8 *)data + rects[i].x1 * 4 + rects[i].y1 * byte_stride;
+      if (output->binary)
+       to_png_rgba (output->buf, rects[i].x2 - rects[i].x1,
+                    rects[i].y2 - rects[i].y1,
+                    byte_stride, (guint32*)subdata);
+      else
+       to_png_url_rgba (output->buf, rects[i].x2 - rects[i].x1,
+                        rects[i].y2 - rects[i].y1,
+                        byte_stride, (guint32*)subdata);
 
-      broadway_output_write (output, url, len);
+      len = output->buf->len - image_start;
 
-      free (url);
+      overwrite_uint32 (output, size_start, len);
     }
 
   free (rects);
 }
 
-#if 0
-static void
-send_image_a (BroadwayOutput *output,  int id, int x, int y,
-             int w, int h, int byte_stride, guint8 *data)
+void
+broadway_output_surface_flush (BroadwayOutput *output,
+                              int             id)
 {
-  char buf[HEADER_LEN + 15];
-  gsize len;
-  char *url;
-
-  p = write_header (output, buf, 'i');
-  append_uint16 (id, buf, &p);
-  append_uint16 (x, buf, &p);
-  append_uint16 (y, buf, &p);
-
-  url = to_png_a (w, h, byte_stride, data);
-  len = strlen (url);
-  append_uint32 (len, buf, &p);
-
-  assert (p == sizeof (buf));
-
-  broadway_output_write (output, buf, sizeof (buf));
-
-  broadway_output_write (output, url, len);
-
-  free (url);
+  write_header (output, 'f');
+  append_uint16 (output, id);
 }
-#endif