gtk/gdk/broadway/broadway-output.c
Alexander Larsson 149b3af105 broadway: Use zlib compression for broadway buffers
This imports the MIT licensed js unzip from:
https://github.com/imaya/zlib.js

and uses it to decompress in js on the client side.
2013-11-07 14:09:53 +01:00

312 lines
7.2 KiB
C

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <cairo.h>
#include "broadway-output.h"
/************************************************************************
* Basic I/O primitives *
************************************************************************/
struct BroadwayOutput {
GOutputStream *out;
GString *buf;
int error;
guint32 serial;
};
static void
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)
{
*(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);
}
void broadway_output_pong (BroadwayOutput *output)
{
broadway_output_send_cmd (output, TRUE, BROADWAY_WS_CNX_PONG, NULL, 0);
}
int
broadway_output_flush (BroadwayOutput *output)
{
if (output->buf->len == 0)
return TRUE;
broadway_output_send_cmd (output, TRUE, BROADWAY_WS_BINARY,
output->buf->str, output->buf->len);
g_string_set_size (output->buf, 0);
return !output->error;
}
BroadwayOutput *
broadway_output_new (GOutputStream *out, guint32 serial)
{
BroadwayOutput *output;
output = g_new0 (BroadwayOutput, 1);
output->out = g_object_ref (out);
output->buf = g_string_new ("");
output->serial = serial;
return output;
}
void
broadway_output_free (BroadwayOutput *output)
{
g_object_unref (output->out);
free (output);
}
guint32
broadway_output_get_next_serial (BroadwayOutput *output)
{
return output->serial;
}
void
broadway_output_set_next_serial (BroadwayOutput *output,
guint32 serial)
{
output->serial = serial;
}
/************************************************************************
* Core rendering operations *
************************************************************************/
static void
append_char (BroadwayOutput *output, char c)
{
g_string_append_c (output->buf, c);
}
static void
append_bool (BroadwayOutput *output, gboolean val)
{
g_string_append_c (output->buf, val ? 1: 0);
}
static void
append_flags (BroadwayOutput *output, guint32 val)
{
g_string_append_c (output->buf, val);
}
static void
append_uint16 (BroadwayOutput *output, guint32 v)
{
gsize old_len = output->buf->len;
guint8 *buf;
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;
}
static void
append_uint32 (BroadwayOutput *output, guint32 v)
{
gsize old_len = output->buf->len;
guint8 *buf;
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;
}
static void
write_header(BroadwayOutput *output, char op)
{
append_char (output, op);
append_uint32 (output, output->serial++);
}
void
broadway_output_grab_pointer (BroadwayOutput *output,
int id,
gboolean owner_event)
{
write_header (output, BROADWAY_OP_GRAB_POINTER);
append_uint16 (output, id);
append_bool (output, owner_event);
}
guint32
broadway_output_ungrab_pointer (BroadwayOutput *output)
{
guint32 serial;
serial = output->serial;
write_header (output, BROADWAY_OP_UNGRAB_POINTER);
return serial;
}
void
broadway_output_new_surface(BroadwayOutput *output,
int id, int x, int y, int w, int h,
gboolean is_temp)
{
write_header (output, BROADWAY_OP_NEW_SURFACE);
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_disconnected (BroadwayOutput *output)
{
write_header (output, BROADWAY_OP_DISCONNECTED);
}
void
broadway_output_show_surface(BroadwayOutput *output, int id)
{
write_header (output, BROADWAY_OP_SHOW_SURFACE);
append_uint16 (output, id);
}
void
broadway_output_hide_surface(BroadwayOutput *output, int id)
{
write_header (output, BROADWAY_OP_HIDE_SURFACE);
append_uint16 (output, id);
}
void
broadway_output_destroy_surface(BroadwayOutput *output, int id)
{
write_header (output, BROADWAY_OP_DESTROY_SURFACE);
append_uint16 (output, id);
}
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;
write_header (output, BROADWAY_OP_MOVE_RESIZE);
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_set_transient_for (BroadwayOutput *output,
int id,
int parent_id)
{
write_header (output, BROADWAY_OP_SET_TRANSIENT_FOR);
append_uint16 (output, id);
append_uint16 (output, parent_id);
}
void
broadway_output_put_buffer (BroadwayOutput *output,
int id,
BroadwayBuffer *prev_buffer,
BroadwayBuffer *buffer)
{
gsize len;
int w, h;
GZlibCompressor *compressor;
GOutputStream *out, *out_mem;
GString *encoded;
write_header (output, BROADWAY_OP_PUT_BUFFER);
w = broadway_buffer_get_width (buffer);
h = broadway_buffer_get_height (buffer);
append_uint16 (output, id);
append_uint16 (output, w);
append_uint16 (output, h);
encoded = g_string_new ("");
broadway_buffer_encode (buffer, prev_buffer, encoded);
compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_RAW, -1);
out_mem = g_memory_output_stream_new_resizable ();
out = g_converter_output_stream_new (out_mem, G_CONVERTER (compressor));
g_object_unref (compressor);
if (!g_output_stream_write_all (out, encoded->str, encoded->len,
NULL, NULL, NULL) ||
!g_output_stream_close (out, NULL, NULL))
g_warning ("compression failed\n");
len = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (out_mem));
append_uint32 (output, len);
g_string_append_len (output->buf, g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (out_mem)), len);
g_string_free (encoded, TRUE);
g_object_unref (out);
g_object_unref (out_mem);
}