summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@bitplanet.net>2013-03-25 21:36:50 -0400
committerKristian Høgsberg <krh@bitplanet.net>2013-05-20 21:32:41 -0400
commit7adffe7be46f4cb31c27066332356e4a19b609d5 (patch)
tree9d727e18ea596b07d830e0e18a9a96d6e0cd1ad8
parentb0a19671c2329019fc03e8581bf9fbe6c3fbd8de (diff)
Add remote display prototyperemote
-rw-r--r--configure.ac6
-rw-r--r--protocol/remote.xml37
-rw-r--r--src/Makefile.am32
-rw-r--r--src/remote-proxy.c1761
-rw-r--r--src/remote-server.c580
-rw-r--r--src/remote-stream.c143
-rw-r--r--src/remote-stream.h48
7 files changed, 2606 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index 93a67203..6378e776 100644
--- a/configure.ac
+++ b/configure.ac
@@ -142,6 +142,12 @@ AM_CONDITIONAL(ENABLE_HEADLESS_COMPOSITOR,
test x$enable_headless_compositor = xyes)
+AC_ARG_ENABLE(remote-display, [ --enable-remote-display],,
+ enable_remote_display=yes)
+AM_CONDITIONAL(ENABLE_REMOTE_DISPLAY,
+ test x$enable_remote_display = xyes)
+
+
AC_ARG_ENABLE(rpi-compositor,
AS_HELP_STRING([--disable-rpi-compositor],
[do not build the Raspberry Pi backend]),,
diff --git a/protocol/remote.xml b/protocol/remote.xml
new file mode 100644
index 00000000..0facd236
--- /dev/null
+++ b/protocol/remote.xml
@@ -0,0 +1,37 @@
+<protocol name="remote">
+
+ <interface name="remote" version="1">
+ <enum name="format">
+ <entry name="argb8888" value="0"/>
+ <entry name="xrgb8888" value="1"/>
+ </enum>
+
+ <request name="create_buffer">
+ <description summary="create wl_buffer from pool">
+ Create a wl_buffer remote buffer. Content for the buffer is
+ provided using the update_buffer request, which uploads data
+ to the buffer from the out-of-band block with the given tag.
+ </description>
+
+ <arg name="id" type="new_id" interface="wl_buffer"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ <arg name="format" type="uint"/>
+ </request>
+
+ <request name="update_buffer">
+ <arg name="buffer" type="object" interface="wl_buffer"/>
+ <arg name="tag" type="uint"
+ summary="reference to out of band block carrying data"/>
+ <arg name="region" type="object" interface="wl_region"/>
+ </request>
+
+ <event name="keymap">
+ <arg name="keyboard" type="object" interface="wl_keyboard"/>
+ <arg name="format" type="uint"/>
+ <arg name="tag" type="uint"/>
+ <arg name="size" type="uint"/>
+ </event>
+
+ </interface>
+</protocol>
diff --git a/src/Makefile.am b/src/Makefile.am
index 859f5834..612fc737 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -105,7 +105,8 @@ module_LTLIBRARIES = \
$(wayland_backend) \
$(headless_backend) \
$(fbdev_backend) \
- $(rdp_backend)
+ $(rdp_backend) \
+ $(remote_display)
noinst_LTLIBRARIES =
@@ -276,6 +277,32 @@ cms_colord_la_SOURCES = \
cms-helper.c \
cms-helper.h
endif
+
+if ENABLE_REMOTE_DISPLAY
+remote_display = remote-proxy.la remote-server.la
+remote_proxy_la_LDFLAGS = -module -avoid-version
+remote_proxy_la_LIBADD = $(COMPOSITOR_LIBS) -lz
+remote_proxy_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+remote_proxy_la_SOURCES = \
+ remote-proxy.c \
+ remote-stream.c \
+ remote-stream.h \
+ remote-protocol.c \
+ remote-client-protocol.h \
+ ../shared/os-compatibility.c \
+ ../shared/os-compatibility.h
+
+remote_server_la_LDFLAGS = -module -avoid-version
+remote_server_la_LIBADD = $(COMPOSITOR_LIBS) -lz
+remote_server_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+remote_server_la_SOURCES = \
+ remote-server.c \
+ remote-stream.c \
+ remote-stream.h \
+ remote-protocol.c \
+ remote-server-protocol.h \
+ ../shared/os-compatibility.c \
+ ../shared/os-compatibility.h
endif
BUILT_SOURCES = \
@@ -295,6 +322,9 @@ BUILT_SOURCES = \
workspaces-protocol.c \
subsurface-server-protocol.h \
subsurface-protocol.c \
+ remote-protocol.c \
+ remote-client-protocol.h \
+ remote-server-protocol.h \
git-version.h
CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/remote-proxy.c b/src/remote-proxy.c
new file mode 100644
index 00000000..78cf216c
--- /dev/null
+++ b/src/remote-proxy.c
@@ -0,0 +1,1761 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <linux/input.h>
+#include <sys/uio.h>
+#include <assert.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <zlib.h>
+
+#include <wayland-server.h>
+#include <wayland-client.h>
+#include "compositor.h"
+
+#include "../shared/os-compatibility.h"
+
+#include "remote-stream.h"
+#include "remote-client-protocol.h"
+
+/*
+ * - Don't abuse weston_seat, weston_output etc.
+ *
+ * - Encapsulate fd users in abstract objects? wl_buffer,
+ * wl_keyboard_map... idea is to keep most of core protocol the
+ * same, but allow the out of band usage to be encapsulated.
+ *
+ * - Minimize shm update regions.
+ *
+ * - Surface enter/leave.
+ *
+ */
+
+struct remote {
+ struct weston_compositor *compositor;
+ struct wl_listener destroy_listener;
+ struct wl_display *display;
+ struct wl_registry *registry;
+ struct wl_list surface_list;
+ struct wl_compositor *remote_compositor;
+ struct wl_shell *shell;
+ struct wl_event_source *source;
+ uint32_t event_mask;
+
+ int protocol_fd[2];
+ char protocol_buffer[4096];
+ struct wl_event_source *protocol_source;
+ struct remote_stream *stream;
+ struct remote *proxy_remote;
+
+ struct remote_stream_block protocol_stream_block;
+};
+
+struct remote_output {
+ struct weston_output output;
+ struct remote *remote;
+ struct wl_output *proxy;
+};
+
+struct remote_seat {
+ struct wl_seat seat;
+ struct remote *remote;
+ struct wl_seat *proxy;
+ struct wl_pointer *proxy_pointer;
+ struct wl_keyboard *proxy_keyboard;
+ uint32_t enter_serial;
+ struct wl_pointer pointer;
+ struct wl_keyboard keyboard;
+
+ int keymap_fd;
+ void *keymap_area;
+ size_t keymap_size;
+};
+
+struct shell_surface {
+ struct wl_resource resource;
+ struct weston_surface *surface;
+};
+
+struct remote_surface {
+ struct weston_surface *surface;
+ struct wl_surface *proxy;
+ struct wl_shell_surface *proxy_shell_surface;
+
+ struct shell_surface *shell_surface;
+
+ struct remote *remote;
+ struct wl_buffer *proxy_buffer;
+ struct buffer_hash *buffer;
+ void *map, *zipped;
+ int width, height;
+ int encoded_length;
+
+ uint32_t remote_ping_serial;
+ uint32_t subst_ping_serial;
+
+ struct wl_listener destroy_listener;
+
+ const struct wl_surface_interface *interface;
+ const struct wl_shell_surface_interface *shell_surface_interface;
+
+ struct remote_stream_block stream_block;
+};
+
+static struct remote_seat *
+is_remote_seat(struct wl_resource *resource);
+
+static void
+destroy_remote(struct wl_listener *listener, void *data);
+
+static int
+keymap_tag_handler(struct remote_stream *stream,
+ size_t remain, void *data)
+{
+ struct remote_seat *seat = data;
+ int len;
+
+ if (stream->len == 0) {
+ seat->keymap_size = stream->total;
+ seat->keymap_fd =
+ os_create_anonymous_file(seat->keymap_size);
+ seat->keymap_area = mmap(NULL, seat->keymap_size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ seat->keymap_fd, 0);
+ }
+
+ len = read(stream->fd, seat->keymap_area + stream->len, remain);
+ if (len < 0)
+ return -1;
+
+ stream->len += len;
+
+ return len;
+}
+
+static int
+stream_data(int fd, uint32_t mask, void *data)
+{
+ struct remote *remote = data;
+ int ret;
+
+ if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
+ fprintf(stderr, "stream_data: hangup\n");
+ destroy_remote(&remote->destroy_listener, NULL);
+ return 1;
+ }
+
+ if (mask & WL_EVENT_WRITABLE) {
+ ret = remote_stream_write(remote->stream);
+ if (ret == 0) {
+ wl_event_source_fd_update(remote->source,
+ WL_EVENT_READABLE);
+ wl_event_source_fd_update(remote->stream->source,
+ WL_EVENT_READABLE);
+ } else if (ret == -1 && errno != EAGAIN) {
+ fprintf(stderr, "other error: %m\n");
+ }
+ }
+
+ if (mask & WL_EVENT_READABLE)
+ remote_stream_read(remote->stream);
+
+ return 1;
+}
+
+static int
+protocol_data(int fd, uint32_t mask, void *data)
+{
+ struct remote *remote = data;
+ struct remote_stream_block *block;
+ int len, ret;
+
+ if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
+ fprintf(stderr, "stream_data: hup/err in protocol_data\n");
+ return -1;
+ }
+
+ len = read(fd, remote->protocol_buffer,
+ sizeof remote->protocol_buffer);
+ if (len == -1 && errno == EAGAIN) {
+ return 1;
+ } else if (len == -1) {
+ fprintf(stderr, "read error: %m\n");
+ return -1;
+ }
+
+ block = &remote->protocol_stream_block;
+ wl_list_insert(&remote->stream->block_queue, &block->link);
+ block->tag = 0;
+ block->written = 0;
+ block->size = len;
+ block->data = remote->protocol_buffer;
+
+ ret = remote_stream_write(remote->stream);
+ if (ret == -1 && errno == EAGAIN) {
+ wl_event_source_fd_update(remote->source, 0);
+ wl_event_source_fd_update(remote->stream->source,
+ WL_EVENT_READABLE |
+ WL_EVENT_WRITABLE);
+ } else if (ret == -1) {
+ fprintf(stderr, "other error: %m\n");
+ }
+
+ return 1;
+}
+
+static int
+protocol_tag_handler(struct remote_stream *stream, size_t remain, void *data)
+{
+ struct remote *remote = data;
+ char buffer[4096];
+ int len;
+
+ if (remain > sizeof buffer)
+ len = sizeof buffer;
+ else
+ len = remain;
+
+ len = read(stream->fd, buffer, len);
+ stream->len += len;
+ write(remote->protocol_fd[0], buffer, len);
+
+ return 1;
+}
+
+static struct remote_stream *
+remote_stream_connect(struct remote *remote,
+ const char *hostname, int port)
+{
+ struct wl_event_loop *loop =
+ wl_display_get_event_loop(remote->compositor->wl_display);
+ struct remote_stream *stream;
+ struct sockaddr_in name;
+ struct hostent *hostinfo;
+
+ stream = malloc(sizeof *stream);
+ if (stream == NULL)
+ return NULL;
+
+ memset(stream, 0, sizeof *stream);
+ stream->tag_handler[0].func = protocol_tag_handler;
+ stream->tag_handler[0].data = remote;
+ stream->tag = 1;
+ stream->tag_handler_count = 1;
+ wl_list_init(&stream->block_queue);
+
+ hostinfo = gethostbyname(hostname);
+ if (hostinfo == NULL) {
+ weston_log("Unknown host %s.\n", hostname);
+ free(stream);
+ return NULL;
+ }
+
+ name.sin_family = AF_INET;
+ name.sin_port = htons(port);
+ name.sin_addr = *(struct in_addr *) hostinfo->h_addr;
+
+ stream->fd = socket(PF_INET,
+ SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
+ if (stream->fd < 0) {
+ weston_log("Failed to create socket for remote stream: %m.\n");
+ free(stream);
+ return NULL;
+ }
+
+ if (connect(stream->fd, (struct sockaddr *) &name, sizeof name) < 0 &&
+ errno != EINPROGRESS) {
+ weston_log("Could not connect to remote server: %m.\n");
+ return NULL;
+ }
+
+ /* FIXME: Poll socket for writability, check for error with
+ * getsockopt for SO_ERROR, level SOL_SOCKET. */
+
+ stream->source =
+ wl_event_loop_add_fd(loop, stream->fd,
+ WL_EVENT_READABLE, stream_data, remote);
+
+ return stream;
+}
+
+static void
+surface_enter(void *data,
+ struct wl_surface *wl_surface, struct wl_output *wl_output)
+{
+}
+
+static void
+surface_leave(void *data,
+ struct wl_surface *wl_surface, struct wl_output *output)
+{
+}
+
+static const struct wl_surface_listener surface_listener = {
+ surface_enter,
+ surface_leave
+};
+
+static void
+shell_surface_handle_ping(void *data, struct wl_shell_surface *shell_surface,
+ uint32_t serial)
+{
+ struct remote_surface *surface = data;
+ struct weston_compositor *compositor = surface->remote->compositor;
+
+ surface->remote_ping_serial = serial;
+ surface->subst_ping_serial =
+ wl_display_get_serial(compositor->wl_display);
+ wl_shell_surface_send_ping(&surface->shell_surface->resource,
+ surface->subst_ping_serial);
+}
+
+static void
+shell_surface_handle_configure(void *data,
+ struct wl_shell_surface *shell_surface,
+ uint32_t edges, int32_t width, int32_t height)
+{
+ struct remote_surface *surface = data;
+
+ wl_shell_surface_send_configure(&surface->shell_surface->resource,
+ edges, width, height);
+}
+
+static void
+shell_surface_handle_popup_done(void *data,
+ struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+ shell_surface_handle_ping,
+ shell_surface_handle_configure,
+ shell_surface_handle_popup_done
+};
+
+static void
+resize_buffer(struct remote_surface *surface)
+{
+ struct remote *remote = surface->remote;
+ struct weston_surface *es = surface->surface;
+ struct wl_buffer *buffer = es->pending.buffer;
+ int stride, size;
+ uint32_t format;
+
+ if (surface->proxy_buffer != NULL &&
+ buffer != NULL &&
+ surface->width == buffer->width &&
+ surface->height == buffer->height)
+ return;
+
+ if (surface->proxy_buffer != NULL)
+ wl_buffer_destroy(surface->proxy_buffer);
+
+ if (buffer == NULL) {
+ wl_surface_attach(surface->proxy, NULL, 0, 0);
+ return;
+ }
+
+ stride = buffer->width * 4;
+ size = stride * buffer->height;
+
+ /* Assume ARGB for now */
+ format = REMOTE_FORMAT_ARGB8888;
+
+ surface->width = buffer->width;
+ surface->height = buffer->height;
+ surface->map = malloc(size);
+
+ surface->zipped = malloc(size);
+
+ surface->proxy_buffer =
+ remote_create_buffer(remote->proxy_remote,
+ buffer->width, buffer->height, format);
+
+ wl_surface_attach(surface->proxy, surface->proxy_buffer,
+ es->pending.sx, es->pending.sy);
+}
+
+struct entry {
+ int count;
+ int matches;
+ uint32_t hash;
+ int x, y;
+ int index;
+};
+
+struct buffer_hash {
+ void *data;
+ struct entry *table;
+ int width, height, stride;
+ int block_stride, length, block_count, shift;
+ int stats[5];
+ int clashes;
+};
+
+static const uint32_t prime = 0x1f821e2d;
+static const uint32_t end_prime = 0xf907ec81; /* prime^size */
+#if 0
+static const uint32_t vprime = 0x0137b89d;
+static const uint32_t end_vprime = 0xaea9a281; /* vprime^size */
+#else
+static const uint32_t vprime = 0xf907ec81;
+static const uint32_t end_vprime = 0xcdb99001; /* vprime^size */
+#endif
+static const uint32_t step = 0x0ac93019;
+static const int size = 32, mask = 31;
+
+static int
+verify_match(struct buffer_hash *bh, int x, int y,
+ struct buffer_hash *prev, struct entry *entry)
+{
+ int i;
+ void *old, *match;
+
+ if (x + size > bh->width || y + size > bh->height)
+ return 0;
+
+ for (i = 0; i < size; i++) {
+ match = bh->data + (y + i) * bh->stride + x * 4;
+ old = prev->data + (entry->y + i) * prev->stride + entry->x * 4;
+ if (memcmp(match, old, size * 4) != 0) {
+ bh->clashes++;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void
+insert_block(struct buffer_hash *bh, uint32_t h, int x, int y)
+{
+ struct entry *entry;
+ int i;
+ uint32_t collision = 0;
+
+ entry = &bh->table[h >> bh->shift];
+ for (i = step; entry->count > 0 && entry->hash != h; i += step) {
+ entry = &bh->table[(h + i) >> bh->shift];
+ collision++;
+ }
+
+ entry->hash = h;
+ entry->count++;
+ entry->x = x;
+ entry->y = y;
+ entry->index = (bh->block_stride * y + x) / size;
+
+ if (collision > ARRAY_LENGTH(bh->stats) - 1)
+ collision = ARRAY_LENGTH(bh->stats) - 1;
+ bh->stats[collision]++;
+}
+
+static struct entry *
+lookup_block(struct buffer_hash *prev, uint32_t h)
+{
+ uint32_t i;
+ struct entry *entry;
+ int shift = prev->shift;
+
+ for (i = h;
+ entry = &prev->table[i >> shift], entry->count > 0;
+ i += step)
+ if (entry->hash == h)
+ return entry;
+
+ return NULL;
+}
+
+struct encoder {
+ uint32_t color;
+ uint32_t color_run;
+ uint32_t delta;
+ uint32_t delta_run;
+ void *data;
+ int bytes;
+};
+
+/* Encoding:
+ *
+ * - all 1 pixel colors are encoded literally
+ *
+ * - using premultiplied alpha lets us use colors with alpha 0 and
+ * non-zero color components as special codes:
+ *
+ * - 0x00 00 00 00 : one alpha 0 pixel
+ * - 0xaa rr gg bb : one color pixel, alpha > 0
+ * - 0x00 1x xx xx : delta 0 run, x is length, (20 bits)
+ * - 0x00 2x xx xx : block ref, block number x (20 bits)
+ * - 0x00 3x xx xx 0xaarrggbb : solid color run, length x
+ * - 0x00 4x xx xx 0xaarrggbb : delta run, length x
+ *
+ */
+
+static void
+emit(struct encoder *encoder, uint32_t symbol)
+{
+ *(uint32_t *) (encoder->data + encoder->bytes) = symbol;
+ encoder->bytes += 4;
+}
+
+static void
+encode_pixel(struct encoder *encoder, uint32_t color, uint32_t delta)
+{
+ if ((encoder->color != color &&
+ encoder->color_run > encoder->delta_run) ||
+
+ (encoder->delta != delta &&
+ encoder->delta_run > encoder->color_run) ||
+
+ (encoder->delta != delta && encoder->color != color)) {
+
+ if (encoder->color_run >= encoder->delta_run) {
+ if (encoder->color_run == 1) {
+ emit(encoder, encoder->color);
+ } else {
+ emit(encoder, 0x00300000 | encoder->color_run);
+ emit(encoder, encoder->color);
+ }
+ } else {
+ if (encoder->delta == 0) {
+ emit(encoder, 0x00100000 | encoder->delta_run);
+ } else {
+ emit(encoder, 0x00400000 | encoder->delta_run);
+ emit(encoder, encoder->delta);
+ }
+ }
+
+ encoder->color_run = 1;
+ encoder->color = color;
+ encoder->delta_run = 1;
+ encoder->delta = delta;
+ return;
+ }
+
+ if (encoder->color == color) {
+ encoder->color_run++;
+ } else {
+ encoder->color_run = 1;
+ encoder->color = color;
+ }
+
+ if (encoder->delta == delta) {
+ encoder->delta_run++;
+ } else {
+ encoder->delta_run = 1;
+ encoder->delta = delta;
+ }
+}
+
+static void
+encode_block(struct encoder *encoder, struct entry *entry, int x, int y)
+{
+ /* 0x00 2x xx xx 0x xxxx yyyy:
+ * block ref, block number x (20 bits) at x, y */
+
+ /* FIXME: Maybe don't encode pixels under blocks and just emit
+ * blocks at their position within the stream. */
+
+ emit(encoder, 0x00200000 | entry->index);
+ emit(encoder, (x << 16) | y);
+}
+
+static void
+destroy_buffer(struct buffer_hash *bh)
+{
+ free(bh->data);
+ free(bh->table);
+ free(bh);
+}
+
+static struct buffer_hash *
+create_buffer(int width, int height)
+{
+ struct buffer_hash *bh;
+
+ bh = malloc(sizeof *bh);
+ bh->width = width;
+ bh->stride = width * 4;
+ bh->height = height;
+
+ bh->block_stride = (width + size - 1) / size;
+ bh->block_count =
+ bh->block_stride * ((height + size - 1) / size);
+ bh->length = 1 << (31 - __builtin_clz(bh->block_count * 4));
+ bh->shift = __builtin_clz(bh->length) + 1;
+
+ bh->table = malloc(bh->length * sizeof bh->table[0]);
+ memset(bh->table, 0, bh->length * sizeof bh->table[0]);
+
+ memset(bh->stats, 0, sizeof bh->stats);
+ bh->clashes = 0;
+
+ bh->data = malloc(bh->stride * height);
+
+ return bh;
+}
+
+static int
+encode_buffer(struct buffer_hash *bh, struct buffer_hash *prev, void *data)
+{
+ struct entry *entry;
+ int i, j, k;
+ int x0, x1, y0, y1;
+ uint32_t *block_hashes;
+ uint32_t hash, bottom_hash, h, *line, *bottom, *prev_line;
+ int width, height;
+ struct encoder encoder;
+ int *skyline, skyline_pixels;
+ int matches;
+
+ width = bh->width;
+ height = bh->height;
+ x0 = 0;
+ x1 = width;
+ y0 = 0;
+ y1 = height;
+
+ skyline = malloc((width + size) * sizeof skyline[0]);
+ memset(skyline, 0, width * sizeof skyline[0]);
+
+ block_hashes = malloc(width * sizeof block_hashes[0]);
+ memset(block_hashes, 0, width * sizeof block_hashes[0]);
+
+ matches = 0;
+ memset(&encoder, 0, sizeof encoder);
+ encoder.data = data;
+
+ for (i = y0; i < y0 + size; i++) {
+ line = bh->data + i * bh->stride;
+ hash = 0;
+ for (j = x0; j < x0 + size; j++)
+ hash = hash * prime + line[j];
+ for (j = x0; j < x1; j++) {
+ block_hashes[j] = block_hashes[j] * vprime + hash;
+ hash = hash * prime - line[j] * end_prime;
+ if (j + size < width)
+ hash += line[j + size];
+ }
+ }
+
+ for (i = y0; i < y1; i++) {
+ bottom = bh->data + (i + size) * bh->stride;
+ line = bh->data + i * bh->stride;
+ bottom_hash = 0;
+ hash = 0;
+ skyline_pixels = 0;
+
+ if (prev)
+ prev_line = prev->data + i * prev->stride;
+
+ for (j = x0; j < x0 + size; j++) {
+ hash = hash * prime + line[j];
+ if (i + size < height)
+ bottom_hash = bottom_hash * prime + bottom[j];
+ if (i < skyline[j])
+ skyline_pixels = 0;
+ else
+ skyline_pixels++;
+ }
+
+ for (j = x0; j < x1; j++) {
+ if (i < skyline[j])
+ encode_pixel(&encoder, line[j], 0);
+ else if (prev) {
+ /* FIXME: Add back overlap exception
+ * for consecutive blocks */
+
+ h = block_hashes[j];
+ entry = lookup_block(prev, h);
+ if (entry && entry->count < 2 &&
+ skyline_pixels >= size &&
+ verify_match(bh, j, i, prev, entry) &&
+ (entry->x != j || entry->y != i)) {
+ matches++;
+ encode_block(&encoder, entry, j, i);
+
+ for (k = 0; k < size; k++)
+ skyline[j + k] = i + size;
+
+ encode_pixel(&encoder, line[j], 0);
+ } else {
+ encode_pixel(&encoder, line[j],
+ line[j] - prev_line[j]);
+ }
+ } else
+ encode_pixel(&encoder, line[j], line[j]);
+
+ if (i < skyline[j + size])
+ skyline_pixels = 0;
+ else
+ skyline_pixels++;
+
+ /* Insert block in hash table if we're on a
+ * grid point. */
+ if (((i | j) & mask) == 0 && i + size <= width &&
+ j + size <= height)
+ insert_block(bh, block_hashes[j], j, i);
+
+ /* Update sliding block hash */
+ block_hashes[j] =
+ block_hashes[j] * vprime + bottom_hash -
+ hash * end_vprime;
+
+ if (i + size < height) {
+ bottom_hash = bottom_hash * prime -
+ bottom[j] * end_prime;
+ if (j + size < width)
+ bottom_hash += bottom[j + size];
+ }
+ hash = hash * prime + line[j + size] -
+ line[j] * end_prime;
+ }
+ }
+
+#if 0
+ fprintf(stderr, "collision stats:");
+ for (i = 0; i < (int) ARRAY_LENGTH(bh->stats); i++)
+ fprintf(stderr, "%c%d", i == 0 ? ' ' : '/', bh->stats[i]);
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "%d / %d blocks (%d%%) matched, %d clashes\n",
+ matches, bh->block_count,
+ 100 * matches / bh->block_count, bh->clashes);
+
+ fprintf(stderr, "output stream %d bytes, raw buffer %d bytes (%d%%)\n",
+ encoder.bytes, height * bh->stride,
+ 100 * encoder.bytes / (height * bh->stride));
+#endif
+
+ free(skyline);
+ free(block_hashes);
+
+ return encoder.bytes;
+}
+
+static void
+remote_surface_commit(struct remote_surface *surface)
+{
+ struct remote *remote = surface->remote;
+ struct weston_renderer *renderer;
+ struct wl_region *region;
+ struct buffer_hash *bh;
+ size_t length;
+
+ resize_buffer(surface);
+
+ bh = create_buffer(surface->width, surface->height);
+ renderer = remote->compositor->renderer;
+ renderer->read_surface_pixels(surface->surface,
+ remote->compositor->read_format,
+ bh->data,
+ 0, 0, bh->width, bh->height);
+
+ surface->encoded_length =
+ encode_buffer(bh, surface->buffer, surface->map);
+
+ compress2(surface->zipped, &length,
+ surface->map, surface->encoded_length, 1);
+
+ if (surface->buffer)
+ destroy_buffer(surface->buffer);
+ surface->buffer = bh;
+
+#if 0
+ if (surface->encoded_length > 0)
+ fprintf(stderr, "zip %d -> %zu bytes (%zu%%)\n",
+ surface->encoded_length, length,
+ length * 100 / surface->encoded_length);
+#endif
+
+ if (surface->encoded_length > 0) {
+ remote_stream_queue_block(remote->stream,
+ &surface->stream_block,
+ surface->zipped, length);
+ region = wl_compositor_create_region(remote->remote_compositor);
+ wl_region_add(region, 0, 0, surface->width, surface->height);
+ remote_update_buffer(remote->proxy_remote, surface->proxy_buffer,
+ surface->stream_block.tag, region);
+ wl_region_destroy(region);
+
+ wl_surface_damage(surface->proxy,
+ 0, 0, surface->width, surface->height);
+
+ /* We need to flush the surface data before the
+ * commit, but we need something more subtle than this
+ * big, blocking hammer. We'll need to read the
+ * protocol data into a buffer and then queue that.
+ * We also have to make the socketpair non-blocking so
+ * that we don't end up in protocol_data after this
+ * with nothing to read. */
+ wl_display_flush(remote->display);
+ protocol_data(remote->protocol_fd[0], WL_EVENT_READABLE, remote);
+
+ while (!wl_list_empty(&remote->stream->block_queue)) {
+ fprintf(stderr, "more flushing\n");
+ remote_stream_write(remote->stream);
+ }
+ }
+
+ wl_surface_commit(surface->proxy);
+}
+
+static void
+remote_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct remote_surface *surface =
+ container_of(listener,
+ struct remote_surface, destroy_listener);
+
+ wl_surface_destroy(surface->proxy);
+ if (surface->proxy_shell_surface)
+ wl_shell_surface_destroy(surface->proxy_shell_surface);
+
+ free(surface);
+}
+
+static struct remote_surface *
+get_remote_surface(struct wl_resource *resource)
+{
+ struct wl_listener *listener;
+
+ listener = wl_signal_get(&resource->destroy_signal,
+ remote_surface_destroy);
+
+ if (listener == NULL)
+ return NULL;
+
+ return container_of(listener, struct remote_surface, destroy_listener);
+}
+
+static void
+surface_handle_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ struct remote_surface *surface = get_remote_surface(resource);
+
+ surface->interface->destroy(client, resource);
+}
+
+static void
+surface_handle_attach(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *buffer_resource,
+ int32_t sx, int32_t sy)
+{
+ struct remote_surface *surface = get_remote_surface(resource);
+
+ surface->interface->attach(client, resource, buffer_resource, sx, sy);
+}
+
+static void
+surface_handle_damage(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ struct remote_surface *surface = get_remote_surface(resource);
+
+ surface->interface->damage(client, resource, x, y, width, height);
+}
+
+static void
+surface_handle_frame(struct wl_client *client,
+ struct wl_resource *resource, uint32_t callback)
+{
+ struct remote_surface *surface = get_remote_surface(resource);
+
+ surface->interface->frame(client, resource, callback);
+}
+
+static struct wl_region *
+get_remote_region(struct remote *remote, pixman_region32_t *region)
+{
+ struct wl_region *remote_region;
+ pixman_box32_t *rectangles;
+ int i, n;
+
+ remote_region = wl_compositor_create_region(remote->remote_compositor);
+ rectangles = pixman_region32_rectangles(region, &n);
+ for (i = 0; i < n; i++)
+ wl_region_add(remote_region,
+ rectangles[i].x1, rectangles[i].y1,
+ rectangles[i].x2 - rectangles[i].x1,
+ rectangles[i].y2 - rectangles[i].y1);
+
+ return remote_region;
+}
+
+static void
+surface_handle_set_opaque_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ struct remote_surface *surface = get_remote_surface(resource);
+ struct weston_region *region;
+ struct wl_region *proxy;
+
+ surface->interface->set_opaque_region(client,
+ resource, region_resource);
+
+ if (region_resource) {
+ region = region_resource->data;
+ proxy = get_remote_region(surface->remote, &region->region);
+ wl_surface_set_opaque_region(surface->proxy, proxy);
+ wl_region_destroy(proxy);
+ } else {
+ wl_surface_set_opaque_region(surface->proxy, NULL);
+ }
+}
+
+static void
+surface_handle_set_input_region(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *region_resource)
+{
+ struct remote_surface *surface = get_remote_surface(resource);
+ struct weston_region *region;
+ struct wl_region *proxy;
+
+ surface->interface->set_input_region(client,
+ resource, region_resource);
+
+ if (region_resource) {
+ region = region_resource->data;
+ proxy = get_remote_region(surface->remote, &region->region);
+ wl_surface_set_input_region(surface->proxy, proxy);
+ wl_region_destroy(proxy);
+ } else {
+ wl_surface_set_input_region(surface->proxy, NULL);
+ }
+}
+
+static void
+surface_handle_commit(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct remote_surface *surface = get_remote_surface(resource);
+
+ surface->interface->commit(client, resource);
+
+ remote_surface_commit(surface);
+}
+
+static const struct wl_surface_interface surface_interface = {
+ surface_handle_destroy,
+ surface_handle_attach,
+ surface_handle_damage,
+ surface_handle_frame,
+ surface_handle_set_opaque_region,
+ surface_handle_set_input_region,
+ surface_handle_commit
+};
+
+
+static void
+shell_surface_handle_pong(struct wl_client *client,
+ struct wl_resource *resource, uint32_t serial)
+{
+ struct shell_surface *shell_surface = resource->data;
+ struct remote_surface *surface =
+ get_remote_surface(&shell_surface->surface->surface.resource);
+
+ if (serial == surface->subst_ping_serial)
+ wl_shell_surface_pong(surface->proxy_shell_surface,
+ surface->remote_ping_serial);
+ else
+ surface->shell_surface_interface->pong(client,
+ resource, serial);
+}
+
+static void
+shell_surface_handle_move(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *seat_resource, uint32_t serial)
+{
+ struct remote_seat *seat;
+ struct shell_surface *shell_surface = resource->data;
+ struct remote_surface *surface =
+ get_remote_surface(&shell_surface->surface->surface.resource);
+
+ seat = is_remote_seat(seat_resource);
+ if (seat) {
+ wl_shell_surface_move(surface->proxy_shell_surface,
+ seat->proxy, serial);
+ } else {
+ surface->shell_surface_interface->move(client, resource,
+ seat_resource, serial);
+ }
+}
+
+static void
+shell_surface_handle_resize(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *seat_resource,
+ uint32_t serial, uint32_t edges)
+{
+ struct remote_seat *seat;
+ struct shell_surface *shell_surface = resource->data;
+ struct remote_surface *surface =
+ get_remote_surface(&shell_surface->surface->surface.resource);
+
+ seat = is_remote_seat(seat_resource);
+ if (seat) {
+ wl_shell_surface_resize(surface->proxy_shell_surface,
+ seat->proxy, serial, edges);
+ } else {
+ surface->shell_surface_interface->resize(client, resource,
+ seat_resource,
+ serial, edges);
+ }
+}
+
+static void
+shell_surface_handle_set_toplevel(struct wl_client *client,
+ struct wl_resource *resource)
+{
+}
+
+static void
+shell_surface_handle_set_transient(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *parent_resource,
+ int x, int y, uint32_t flags)
+{
+}
+
+static void
+shell_surface_handle_set_fullscreen(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t method,
+ uint32_t framerate,
+ struct wl_resource *output_resource)
+{
+ /* FIXME: How could this ever work with different size outputs */
+}
+
+static void
+shell_surface_handle_set_popup(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *seat_resource,
+ uint32_t serial,
+ struct wl_resource *parent_resource,
+ int32_t x, int32_t y, uint32_t flags)
+{
+}
+
+static void
+shell_surface_handle_set_maximized(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource)
+{
+}
+
+static void
+shell_surface_handle_set_title(struct wl_client *client,
+ struct wl_resource *resource, const char *title)
+{
+}
+
+static void
+shell_surface_handle_set_class(struct wl_client *client,
+ struct wl_resource *resource, const char *class)
+{
+}
+
+static const struct wl_shell_surface_interface shell_surface_implementation = {
+ shell_surface_handle_pong,
+ shell_surface_handle_move,
+ shell_surface_handle_resize,
+ shell_surface_handle_set_toplevel,
+ shell_surface_handle_set_transient,
+ shell_surface_handle_set_fullscreen,
+ shell_surface_handle_set_popup,
+ shell_surface_handle_set_maximized,
+ shell_surface_handle_set_title,
+ shell_surface_handle_set_class
+};
+
+static struct remote_surface *
+remote_surface_create(struct remote *remote, struct weston_surface *surface,
+ struct shell_surface *shell_surface)
+{
+ struct remote_surface *rs;
+ struct wl_region *proxy;
+
+ weston_log("creating remote surface for %p\n", surface);
+
+ rs = malloc(sizeof *rs);
+ if (rs == NULL)
+ return NULL;
+
+ memset(rs, 0, sizeof *rs);
+ rs->remote = remote;
+ rs->surface = surface;
+ wl_list_init(&rs->stream_block.link);
+ rs->proxy = wl_compositor_create_surface(remote->remote_compositor);
+ wl_surface_add_listener(rs->proxy, &surface_listener, rs);
+
+ rs->destroy_listener.notify = remote_surface_destroy;
+ wl_signal_add(&surface->surface.resource.destroy_signal,
+ &rs->destroy_listener);
+
+ rs->interface = (const struct wl_surface_interface *)
+ surface->surface.resource.object.implementation;
+ surface->surface.resource.object.implementation =
+ (void *) &surface_interface;
+
+ rs->shell_surface = shell_surface;
+ if (shell_surface) {
+ rs->proxy_shell_surface =
+ wl_shell_get_shell_surface(remote->shell, rs->proxy);
+ wl_shell_surface_add_listener(rs->proxy_shell_surface,
+ &shell_surface_listener, rs);
+
+ wl_shell_surface_set_toplevel(rs->proxy_shell_surface);
+
+ rs->shell_surface_interface =
+ (const struct wl_shell_surface_interface *)
+ rs->shell_surface->resource.object.implementation;
+ rs->shell_surface->resource.object.implementation =
+ (void *) &shell_surface_implementation;
+ }
+
+ if (surface->buffer_ref.buffer)
+ remote_surface_commit(rs);
+
+ if (pixman_region32_not_empty(&surface->opaque)) {
+ proxy = get_remote_region(remote, &surface->opaque);
+ wl_surface_set_opaque_region(rs->proxy, proxy);
+ wl_region_destroy(proxy);
+ }
+
+ if (pixman_region32_not_empty(&surface->input)) {
+ proxy = get_remote_region(remote, &surface->input);
+ wl_surface_set_input_region(rs->proxy, proxy);
+ wl_region_destroy(proxy);
+ }
+
+ wl_surface_commit(rs->proxy);
+ wl_display_flush(remote->display);
+
+ return rs;
+}
+
+static void
+remote_binding(struct wl_seat *seat, uint32_t time, uint32_t key, void *data)
+{
+ struct remote *remote = data;
+ struct weston_surface *surface;
+
+ if (seat->keyboard->focus == NULL)
+ return;
+
+ /* FIXME: Verify this is a shell surface, handle other surface
+ * types (eg cursor) */
+
+ surface = (struct weston_surface *) seat->keyboard->focus;
+ remote_surface_create(remote, surface, surface->private);
+
+#if 0
+ wl_list_remove(&surface->layer_link);
+ wl_list_insert(&remote->surface_list, &surface->layer_link);
+#endif
+ weston_surface_schedule_repaint(surface);
+}
+
+static void
+pointer_handle_set_cursor(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial,
+ struct wl_resource *surface_resource,
+ int32_t x, int32_t y)
+{
+ struct remote_seat *seat = resource->data;
+ struct weston_surface *surface;
+ struct remote_surface *remote_surface;
+
+ if (surface_resource == NULL) {
+ wl_pointer_set_cursor(seat->proxy_pointer,
+ seat->enter_serial, NULL, x, y);
+ return;
+ }
+
+ surface = surface_resource->data;
+ remote_surface = get_remote_surface(surface_resource);
+ if (remote_surface == NULL)
+ remote_surface = remote_surface_create(seat->remote,
+ surface, NULL);
+
+ wl_pointer_set_cursor(seat->proxy_pointer, seat->enter_serial,
+ remote_surface->proxy, x, y);
+}
+
+static const struct wl_pointer_interface pointer_implementation = {
+ pointer_handle_set_cursor
+};
+
+static void
+pointer_handle_enter(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *wl_surface,
+ wl_fixed_t sx, wl_fixed_t sy)
+{
+ struct remote_seat *seat = data;
+ struct remote_surface *surface = wl_surface_get_user_data(wl_surface);
+
+ wl_pointer_set_focus(&seat->pointer,
+ &surface->surface->surface, sx, sy);
+}
+
+static void
+pointer_handle_leave(void *data, struct wl_pointer *pointer,
+ uint32_t serial, struct wl_surface *surface)
+{
+ struct remote_seat *seat = data;
+
+ wl_pointer_set_focus(&seat->pointer, NULL, 0, 0);
+}
+
+static void
+pointer_handle_motion(void *data, struct wl_pointer *pointer,
+ uint32_t time, wl_fixed_t x, wl_fixed_t y)
+{
+ struct remote_seat *seat = data;
+ struct wl_resource *resource = seat->pointer.focus_resource;
+
+ if (resource)
+ wl_pointer_send_motion(resource, time, x, y);
+}
+
+static void
+pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
+ uint32_t time, uint32_t button, uint32_t state)
+{
+ struct remote_seat *seat = data;
+ struct wl_resource *resource = seat->pointer.focus_resource;
+
+ /* FIXME: translate serials? */
+
+ if (resource)
+ wl_pointer_send_button(resource, serial, time, button, state);
+}
+
+static void
+pointer_handle_axis(void *data, struct wl_pointer *pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+ struct remote_seat *seat = data;
+ struct wl_resource *resource = seat->pointer.focus_resource;
+
+ if (resource)
+ wl_pointer_send_axis(resource, time, axis, value);
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ pointer_handle_enter,
+ pointer_handle_leave,
+ pointer_handle_motion,
+ pointer_handle_button,
+ pointer_handle_axis,
+};
+
+static void
+keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
+ uint32_t format, int fd, uint32_t size)
+{
+ /* We'll never get this event. */
+}
+
+static void
+keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *wl_surface,
+ struct wl_array *keys)
+{
+ struct remote_seat *seat = data;
+ struct remote_surface *surface = wl_surface_get_user_data(wl_surface);
+
+ wl_keyboard_set_focus(&seat->keyboard,
+ &surface->surface->surface);
+}
+
+static void
+keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface)
+{
+ struct remote_seat *seat = data;
+
+ wl_keyboard_set_focus(&seat->keyboard, NULL);
+}
+
+static void
+keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state)
+{
+ struct remote_seat *seat = data;
+ struct wl_resource *resource = seat->keyboard.focus_resource;
+
+ if (resource)
+ wl_keyboard_send_key(resource, serial, time, key, state);
+}
+
+static void
+keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t mods_depressed,
+ uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group)
+{
+ struct remote_seat *seat = data;
+ struct wl_resource *resource = seat->keyboard.focus_resource;
+
+ if (resource)
+ wl_keyboard_send_modifiers(resource, serial, mods_depressed,
+ mods_latched, mods_locked, group);
+}
+
+static const struct wl_keyboard_listener keyboard_listener = {
+ keyboard_handle_keymap,
+ keyboard_handle_enter,
+ keyboard_handle_leave,
+ keyboard_handle_key,
+ keyboard_handle_modifiers,
+};
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
+ enum wl_seat_capability capabilities)
+{
+ struct remote_seat *seat = data;
+
+ if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
+ seat->proxy_pointer = wl_seat_get_pointer(seat->proxy);
+ wl_pointer_add_listener(seat->proxy_pointer,
+ &pointer_listener, seat);
+
+ wl_pointer_init(&seat->pointer);
+ wl_seat_set_pointer(&seat->seat, &seat->pointer);
+
+ } else {
+ wl_pointer_destroy(seat->proxy_pointer);
+ seat->proxy_pointer = NULL;
+ }
+
+ if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
+ seat->proxy_keyboard = wl_seat_get_keyboard(seat->proxy);
+ wl_keyboard_add_listener(seat->proxy_keyboard,
+ &keyboard_listener, seat);
+
+ wl_keyboard_init(&seat->keyboard);
+ wl_seat_set_keyboard(&seat->seat, &seat->keyboard);
+ } else {
+ wl_keyboard_destroy(seat->proxy_keyboard);
+ seat->proxy_keyboard = NULL;
+ }
+}
+
+static const struct wl_seat_listener seat_listener = {
+ seat_handle_capabilities,
+};
+
+static void unbind_resource(struct wl_resource *resource)
+{
+ wl_list_remove(&resource->link);
+ free(resource);
+}
+
+static void
+remote_seat_get_pointer(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct remote_seat *seat = resource->data;
+ struct wl_resource *cr;
+
+ if (!seat->proxy_pointer)
+ return;
+
+ cr = wl_client_add_object(client, &wl_pointer_interface,
+ &pointer_implementation, id, seat);
+ wl_list_insert(&seat->pointer.resource_list, &cr->link);
+ cr->destroy = unbind_resource;
+}
+
+static void
+remote_seat_get_keyboard(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct remote_seat *seat = resource->data;
+ struct wl_resource *cr;
+
+ if (!seat->proxy_keyboard)
+ return;
+
+ cr = wl_client_add_object(client, &wl_keyboard_interface,
+ NULL, id, seat);
+ wl_list_insert(&seat->keyboard.resource_list, &cr->link);
+ cr->destroy = unbind_resource;
+
+ wl_keyboard_send_keymap(cr, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+ seat->keymap_fd,
+ seat->keymap_size);
+
+ if (seat->seat.keyboard->focus &&
+ seat->seat.keyboard->focus->resource.client == client) {
+ wl_keyboard_set_focus(seat->seat.keyboard,
+ seat->seat.keyboard->focus);
+ }
+}
+
+static void
+remote_seat_get_touch(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+}
+
+static const struct wl_seat_interface remote_seat_interface = {
+ remote_seat_get_pointer,
+ remote_seat_get_keyboard,
+ remote_seat_get_touch,
+};
+
+static struct remote_seat *
+is_remote_seat(struct wl_resource *resource)
+{
+ if (resource->object.implementation == (void *) &remote_seat_interface)
+ return resource->data;
+ else
+ return NULL;
+}
+
+static void
+bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct remote_seat *seat = data;
+ struct wl_resource *resource;
+ enum wl_seat_capability caps = 0;
+
+ resource = wl_client_add_object(client, &wl_seat_interface,
+ &remote_seat_interface, id, seat);
+ wl_list_insert(&seat->seat.base_resource_list, &resource->link);
+ resource->destroy = unbind_resource;
+
+ if (seat->proxy_pointer)
+ caps |= WL_SEAT_CAPABILITY_POINTER;
+ if (seat->proxy_keyboard)
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+
+ wl_seat_send_capabilities(resource, caps);
+}
+
+static void
+remote_add_seat(struct remote *remote, uint32_t id)
+{
+ struct remote_seat *seat;
+
+ seat = malloc(sizeof *seat);
+ if (seat == NULL)
+ return;
+
+ memset(seat, 0, sizeof *seat);
+
+ seat->remote = remote;
+ seat->proxy = wl_registry_bind(remote->registry, id,
+ &wl_seat_interface, 1);
+ wl_seat_add_listener(seat->proxy, &seat_listener, seat);
+
+ wl_seat_init(&seat->seat);
+ wl_display_add_global(seat->remote->compositor->wl_display,
+ &wl_seat_interface, seat, bind_seat);
+}
+
+static void
+output_handle_geometry(void *data,
+ struct wl_output *wl_output,
+ int32_t x, int32_t y,
+ int32_t physical_width,
+ int32_t physical_height,
+ int subpixel,
+ const char *make,
+ const char *model,
+ int transform)
+{
+ struct remote_output *output = data;
+
+ /* Discard x and y. */
+ output->output.make = strdup(make);
+ output->output.model = strdup(model);
+ output->output.mm_width = physical_width;
+ output->output.mm_height = physical_height;
+ output->output.subpixel = subpixel;
+ output->output.transform = transform;
+ wl_list_init(&output->output.mode_list);
+}
+
+struct remote_mode {
+ struct weston_mode base;
+};
+
+static void
+output_handle_mode(void *data,
+ struct wl_output *wl_output,
+ uint32_t flags,
+ int width,
+ int height,
+ int refresh)
+{
+ struct remote_output *output = data;
+ struct remote_mode *mode;
+
+ mode = malloc(sizeof *mode);
+
+ mode->base.flags = flags;
+ mode->base.width = width;
+ mode->base.height = height;
+ mode->base.refresh = refresh;
+
+ if (flags & WL_OUTPUT_MODE_CURRENT)
+ output->output.current = &mode->base;
+
+ wl_list_insert(output->output.mode_list.prev, &mode->base.link);
+}
+
+static const struct wl_output_listener output_listener = {
+ output_handle_geometry,
+ output_handle_mode
+};
+
+static void
+output_callback_done(void *data, struct wl_callback *callback, uint32_t time)
+{
+ struct remote_output *output = data;
+ int x = 0, y = 0, width = 800, height = 600;
+
+ wl_callback_destroy(callback);
+ weston_output_init(&output->output, output->remote->compositor,
+ x, y, width, height, WL_OUTPUT_TRANSFORM_NORMAL);
+}
+
+static const struct wl_callback_listener output_callback_listener = {
+ output_callback_done
+};
+
+static void
+remote_output_destroy(struct weston_output *output)
+{
+ free(output->make);
+ free(output->model);
+ weston_output_destroy(output);
+ wl_list_remove(&output->link);
+}
+
+static void
+remote_output_repaint(struct weston_output *output_base,
+ pixman_region32_t *damage)
+{
+}
+
+static void
+remote_add_output(struct remote *remote, uint32_t id)
+{
+ struct remote_output *output;
+ struct wl_callback *callback;
+
+ output = malloc(sizeof *output);
+ if (output == NULL)
+ return;
+
+ memset(output, 0, sizeof *output);
+ output->remote = remote;
+ output->proxy = wl_registry_bind(remote->registry, id,
+ &wl_output_interface, 1);
+
+ output->output.destroy = remote_output_destroy;
+ output->output.repaint = remote_output_repaint;
+
+ wl_output_add_listener(output->proxy, &output_listener, output);
+
+ callback = wl_display_sync(remote->display);
+ wl_callback_add_listener(callback, &output_callback_listener, output);
+}
+
+static void
+remote_handle_keymap(void *data, struct remote *proxy,
+ struct wl_keyboard *keyboard, uint32_t format,
+ uint32_t tag, uint32_t size)
+{
+ struct remote *remote = data;
+ struct remote_seat *seat = wl_keyboard_get_user_data(keyboard);
+
+ remote_stream_handle_tag(remote->stream, tag,
+ keymap_tag_handler, seat);
+}
+
+static const struct remote_listener remote_listener = {
+ remote_handle_keymap
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
+ const char *interface, uint32_t version)
+{
+ struct remote *remote = data;
+
+ if (strcmp(interface, "wl_compositor") == 0) {
+ remote->remote_compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, "wl_output") == 0) {
+ remote_add_output(remote, name);
+ } else if (strcmp(interface, "wl_seat") == 0) {
+ remote_add_seat(remote, name);
+ } else if (strcmp(interface, "wl_shell") == 0) {
+ remote->shell = wl_registry_bind(registry, name,
+ &wl_shell_interface, 1);
+ } else if (strcmp(interface, "remote") == 0) {
+ remote->proxy_remote =
+ wl_registry_bind(registry, name, &remote_interface, 1);
+ remote_add_listener(remote->proxy_remote,
+ &remote_listener, remote);
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ registry_handle_global
+};
+
+static void
+destroy_remote(struct wl_listener *listener, void *data)
+{
+ struct remote *remote =
+ container_of(listener, struct remote, destroy_listener);
+
+ /* FIXME: More clean up needed, unhook all forwarded surfaces,
+ * for example. */
+
+ wl_registry_destroy(remote->registry);
+ wl_event_source_remove(remote->stream->source);
+ close(remote->stream->fd);
+ wl_event_source_remove(remote->source);
+ wl_event_source_remove(remote->protocol_source);
+ close(remote->protocol_fd[0]);
+ wl_display_disconnect(remote->display);
+ wl_list_remove(&remote->destroy_listener.link);
+ free(remote);
+}
+
+static int
+remote_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct remote *remote = data;
+ int count = 0;
+
+ if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
+ destroy_remote(&remote->destroy_listener, NULL);
+ return 0;
+ }
+
+ if (mask & WL_EVENT_READABLE)
+ count = wl_display_dispatch(remote->display);
+ if (mask & WL_EVENT_WRITABLE)
+ wl_display_flush(remote->display);
+
+ if (mask == 0) {
+ count = wl_display_dispatch_pending(remote->display);
+ wl_display_flush(remote->display);
+ }
+
+ return count;
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *compositor,
+ int *argc, char *argv[], const char *config_file)
+{
+ struct remote *remote;
+ struct wl_event_loop *loop =
+ wl_display_get_event_loop(compositor->wl_display);
+ int port = 44888;
+ static const char *host;
+
+ host = getenv("WESTON_REMOTE_HOST");
+ if (host == NULL)
+ host = "localhost";
+
+ remote = malloc(sizeof *remote);
+ if (remote == NULL)
+ return -1;
+
+ memset(remote, 0, sizeof *remote);
+ remote->compositor = compositor;
+ remote->stream = remote_stream_connect(remote, host, port);
+ if (remote->stream == NULL)
+ return -1;
+
+ if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
+ remote->protocol_fd) < 0)
+ return -1;
+
+ remote->protocol_source =
+ wl_event_loop_add_fd(loop, remote->protocol_fd[0],
+ WL_EVENT_READABLE,
+ protocol_data, remote);
+
+ remote->display =
+ wl_display_connect_to_fd(remote->protocol_fd[1]);
+
+ if (remote->display == NULL) {
+ weston_log("failed to connect to remote display on %s:%d\n",
+ host, port);
+ free(remote);
+ return -1;
+ }
+
+ remote->source = wl_event_loop_add_fd(loop, remote->protocol_fd[1],
+ WL_EVENT_READABLE,
+ remote_handle_event, remote);
+ if (remote->source == NULL) {
+ wl_display_disconnect(remote->display);
+ free(remote);
+ return -1;
+ }
+
+ wl_event_source_check(remote->source);
+
+ remote->registry = wl_display_get_registry(remote->display);
+ wl_registry_add_listener(remote->registry, &registry_listener, remote);
+
+ weston_compositor_add_key_binding(compositor, KEY_F, MODIFIER_SUPER,
+ remote_binding, remote);
+
+ wl_list_init(&remote->surface_list);
+ remote->destroy_listener.notify = destroy_remote;
+ wl_signal_add(&compositor->destroy_signal, &remote->destroy_listener);
+
+ weston_log("connected to remote host %s:%d\n", host, port);
+
+ wl_display_flush(remote->display);
+
+ return 0;
+}
diff --git a/src/remote-server.c b/src/remote-server.c
new file mode 100644
index 00000000..bacba7ec
--- /dev/null
+++ b/src/remote-server.c
@@ -0,0 +1,580 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/uio.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include "compositor.h"
+#include "../shared/os-compatibility.h"
+
+#include "remote-stream.h"
+#include "remote-server-protocol.h"
+
+
+struct remote_server {
+ struct weston_compositor *compositor;
+ int sock;
+ struct wl_event_source *sock_source;
+ struct wl_global *global;
+ struct wl_listener seat_created_listener;
+};
+
+struct remote_client {
+ struct remote_server *server;
+ int protocol_fd[2];
+ struct wl_event_source *protocol_source;
+ struct wl_client *client;
+ struct wl_listener destroy_listener;
+ struct wl_resource *remote_resource;
+
+ struct remote_stream *stream;
+ struct remote_stream_block protocol_stream_block;
+ struct remote_stream_block keymap_stream_block;
+};
+
+struct remote_buffer
+{
+ struct wl_buffer *buffer;
+ struct wl_listener destroy_listener;
+ void *encoded_buffer;
+ void *copy;
+};
+
+static void
+decode_buffer(struct remote_buffer *b, int length)
+{
+ uint32_t *p, *end, *d;
+ int i, l, x, y, w, h;
+ int32_t width, height, stride, block_stride;
+ uint32_t *copy;
+ char *src, *dest;
+ div_t qr;
+
+ void *unzipped;
+ size_t uncompressed_length;
+
+ const int size = 32;
+
+ d = wl_shm_buffer_get_data(b->buffer);
+
+ width = wl_shm_buffer_get_width(b->buffer);
+ height = wl_shm_buffer_get_height(b->buffer);
+ stride = wl_shm_buffer_get_stride(b->buffer);
+ block_stride = (width + size - 1) / size;
+ copy = malloc(stride * height);
+ memcpy(copy, d, stride * height);
+
+ unzipped = malloc(height * stride);
+ uncompress(unzipped, &uncompressed_length, b->encoded_buffer, length);
+
+ p = unzipped;
+ end = unzipped + uncompressed_length;
+ while (p < end) {
+ l = *p & 0x000fffff;
+ switch (*p >> 20) {
+ case 0x001:
+ /* delta 0 run */
+ d += l;
+ break;
+ case 0x002:
+ /* block ref */
+ qr = div(l, block_stride);
+
+ x = qr.rem * 32;
+ y = qr.quot * 32;
+
+ w = size;
+ h = size;
+
+ if (x + w > width)
+ w = width - x;
+ if (y + h > height)
+ h = height - y;
+
+ src = (char *) copy + y * stride + x * 4;
+
+ p++;
+ x = *p >> 16;
+ y = *p & 0xffff;
+
+ if (x + w > width)
+ w = width - x;
+ if (y + h > height)
+ h = height - y;
+
+ dest = (char *)
+ wl_shm_buffer_get_data(b->buffer) +
+ y * stride + x * 4;
+
+ for (i = 0; i < h; i++)
+ memcpy(dest + i * stride,
+ src + i * stride, w * 4);
+ break;
+ case 0x003:
+ /* solid color run */
+ p++;
+ for (i = 0; i < l; i++)
+ *d++ = *p;
+ break;
+ case 0x004:
+ /* delta run */
+ p++;
+ for (i = 0; i < l; i++)
+ *d++ += *p;
+ break;
+ default:
+ /* regular pixel */
+ *d++ = *p;
+ break;
+ }
+ p++;
+ }
+
+ free(copy);
+ free(unzipped);
+}
+
+static int
+buffer_data_tag_handler(struct remote_stream *stream,
+ size_t remain, void *data)
+{
+ struct remote_buffer *b = data;
+ int len;
+
+ len = read(stream->fd, b->encoded_buffer + stream->len, remain);
+ if (len < 0) {
+ weston_log("buffer data read failed: %m\n");
+ return -1;
+ }
+ stream->len += len;
+
+ if (stream->len == stream->total)
+ decode_buffer(b, stream->total);
+
+ return len;
+}
+
+static int
+stream_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct remote_client *client = data;
+
+ if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
+ fprintf(stderr, "stream_handle_event: hangup\n");
+ wl_client_destroy(client->client);
+ return 1;
+ }
+
+ remote_stream_read(client->stream);
+
+ return 1;
+}
+
+static int
+protocol_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct remote_client *client = data;
+ struct remote_stream_block *block;
+ char buffer[4096];
+ int len, ret;
+
+ if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR))
+ fprintf(stderr, "protocol_handle_event: hangup\n");
+
+ len = read(fd, buffer, sizeof buffer);
+
+ block = &client->protocol_stream_block;
+ wl_list_insert(&client->stream->block_queue, &block->link);
+ block->tag = 0;
+ block->written = 0;
+ block->size = len;
+ block->data = buffer;
+
+ ret = remote_stream_write(client->stream);
+ if (ret == -1)
+ fprintf(stderr, "remote_stream_write(): %m\n");
+
+ return 1;
+}
+
+static int
+protocol_tag_handler(struct remote_stream *stream, size_t remain, void *data)
+{
+ struct remote_client *client = data;
+ char buffer[4096];
+ int len;
+
+ if (remain > sizeof buffer)
+ len = sizeof buffer;
+ else
+ len = remain;
+
+ len = read(stream->fd, buffer, len);
+ if (len < 0)
+ return -1;
+ stream->len += len;
+ write(client->protocol_fd[0], buffer, len);
+
+ return len;
+}
+
+static struct remote_stream *
+remote_stream_accept(struct remote_server *server,
+ struct remote_client *client)
+{
+ struct weston_compositor *compositor = server->compositor;
+ struct wl_event_loop *loop =
+ wl_display_get_event_loop(compositor->wl_display);
+ struct remote_stream *stream;
+ struct sockaddr_in name;
+ socklen_t length;
+ int fd;
+
+ length = sizeof name;
+ fd = accept4(server->sock,
+ (struct sockaddr *) &name, &length, SOCK_CLOEXEC);
+ if (fd < 0) {
+ weston_log("failed to accept: %m\n");
+ return NULL;
+ }
+
+ stream = malloc(sizeof *stream);
+ if (stream == NULL)
+ return NULL;
+
+ memset(stream, 0, sizeof *stream);
+ stream->tag_handler[0].func = protocol_tag_handler;
+ stream->tag_handler[0].data = client;
+ stream->tag = 1;
+ stream->tag_handler_count = 1;
+ stream->fd = fd;
+ wl_list_init(&stream->block_queue);
+
+ stream->source =
+ wl_event_loop_add_fd(loop, stream->fd, WL_EVENT_READABLE,
+ stream_handle_event, client);
+ if (stream->source == NULL) {
+ weston_log("failed to create remote client: %m\n");
+ close(fd);
+ free(stream);
+ return NULL;
+ }
+
+ return stream;
+}
+
+static void
+remote_stream_destroy(struct remote_stream *stream)
+{
+ wl_event_source_remove(stream->source);
+ close(stream->fd);
+ free(stream);
+}
+
+static void
+remote_client_destroy(struct wl_listener *listener, void *data)
+{
+ struct remote_client *client =
+ container_of(listener, struct remote_client, destroy_listener);
+
+ /* FIXME: Clean up objects */
+
+ remote_stream_destroy(client->stream);
+
+ wl_event_source_remove(client->protocol_source);
+ close(client->protocol_fd[0]);
+ close(client->protocol_fd[1]);
+
+ free(client);
+}
+
+static int
+sock_handle_event(int fd, uint32_t mask, void *data)
+{
+ struct remote_server *server = data;
+ struct weston_compositor *compositor = server->compositor;
+ struct remote_client *client;
+ struct wl_event_loop *loop =
+ wl_display_get_event_loop(compositor->wl_display);
+
+ client = malloc(sizeof *client);
+ if (client == NULL) {
+ weston_log("failed to create remote-server client: %m\n");
+ return 1;
+ }
+
+ memset(client, 0, sizeof *client);
+ client->server = server;
+ client->stream = remote_stream_accept(server, client);
+ if (client->stream == NULL) {
+ free(client);
+ return 1;
+ }
+
+ if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0,
+ client->protocol_fd) < 0) {
+ weston_log("failed to create socketpair: %m\n");
+ return 1;
+ }
+
+ client->protocol_source =
+ wl_event_loop_add_fd(loop, client->protocol_fd[0],
+ WL_EVENT_READABLE,
+ protocol_handle_event, client);
+ if (client->protocol_source == NULL) {
+ weston_log("failed to create remote client source: %m\n");
+ free(client);
+ return 1;
+ }
+
+ client->client = wl_client_create(compositor->wl_display,
+ client->protocol_fd[1]);
+
+ client->destroy_listener.notify = remote_client_destroy;
+ wl_client_add_destroy_listener(client->client,
+ &client->destroy_listener);
+
+ weston_log("remote client connected (%p)\n", client->client);
+
+ return 1;
+}
+
+static void
+try_send_keymap(struct weston_seat *seat, struct wl_resource *resource)
+{
+ struct wl_listener *listener;
+ struct wl_client *client = resource->client;
+ struct remote_client *rc;
+
+ listener = wl_client_get_destroy_listener(client,
+ remote_client_destroy);
+ if (listener == NULL) {
+ wl_keyboard_send_keymap(resource,
+ WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+ seat->xkb_info.keymap_fd,
+ seat->xkb_info.keymap_size);
+ return;
+ }
+
+ rc = container_of(listener, struct remote_client, destroy_listener);
+
+ remote_stream_queue_block(rc->stream, &rc->keymap_stream_block,
+ seat->xkb_info.keymap_area,
+ seat->xkb_info.keymap_size);
+ remote_send_keymap(rc->remote_resource, resource,
+ WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+ rc->keymap_stream_block.tag,
+ seat->xkb_info.keymap_size);
+}
+
+static void
+seat_created(struct wl_listener *listener, void *data)
+{
+ struct weston_seat *seat = data;
+
+ seat->send_keymap = try_send_keymap;
+}
+
+static int
+make_socket(uint16_t port)
+{
+ int sock, one = 1;
+ struct sockaddr_in name;
+
+ sock = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (sock < 0) {
+ weston_log("failed to create remote-server socket: %m\n");
+ return -1;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
+
+ name.sin_family = AF_INET;
+ name.sin_port = htons(port);
+ name.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(sock, (struct sockaddr *) &name, sizeof name) < 0) {
+ weston_log("failed to bind remote-server socket: %m\n");
+ close(sock);
+ return -1;
+ }
+
+ if (listen(sock, 1) < 0) {
+ weston_log("failed to listen on remote-server socket: %m\n");
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+static void
+remote_buffer_destroy(struct wl_listener *listener, void *data)
+{
+ struct remote_buffer *rb =
+ container_of(listener, struct remote_buffer, destroy_listener);
+
+ free(rb->copy);
+ free(rb->encoded_buffer);
+ free(rb);
+}
+
+static void
+remote_handle_create_buffer(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id,
+ int32_t width, int32_t height, uint32_t format)
+{
+ struct remote_buffer *rb;
+
+ switch (format) {
+ case REMOTE_FORMAT_ARGB8888:
+ case REMOTE_FORMAT_XRGB8888:
+ break;
+ default:
+ wl_resource_post_error(resource,
+ WL_SHM_ERROR_INVALID_FORMAT,
+ "invalid format");
+ return;
+ }
+
+ /* FIXME: check integer overflow */
+ if (width <= 0 || height <= 0) {
+ wl_resource_post_error(resource,
+ WL_SHM_ERROR_INVALID_STRIDE,
+ "invalid width, height or stride (%dx%d)",
+ width, height);
+ return;
+ }
+
+ /* FIXME: This is going to send spurious buffer.release events
+ * to the remote side... or is it just right? */
+
+ rb = malloc(sizeof *rb);
+ rb->buffer = wl_shm_buffer_create(client, id,
+ width, height, width * 4, format);
+ rb->copy = malloc(width * height * 4);
+ rb->destroy_listener.notify = remote_buffer_destroy;
+ wl_signal_add(&rb->buffer->resource.destroy_signal,
+ &rb->destroy_listener);
+ rb->encoded_buffer = malloc(2 * 1024 * 1024);
+}
+
+static void
+remote_handle_update_buffer(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *buffer_resource,
+ uint32_t tag,
+ struct wl_resource *region)
+{
+ struct wl_listener *listener;
+ struct remote_client *rc;
+ struct remote_buffer *rb;
+
+ listener = wl_client_get_destroy_listener(client,
+ remote_client_destroy);
+ rc = container_of(listener, struct remote_client, destroy_listener);
+
+ listener = wl_signal_get(&buffer_resource->destroy_signal,
+ remote_buffer_destroy);
+ rb = container_of(listener, struct remote_buffer, destroy_listener);
+
+ remote_stream_handle_tag(rc->stream, tag,
+ buffer_data_tag_handler, rb);
+}
+
+static const struct remote_interface remote_implementation = {
+ remote_handle_create_buffer,
+ remote_handle_update_buffer
+};
+
+static void
+bind_remote(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct remove_server *server = data;
+ struct wl_listener *listener;
+ struct remote_client *rc;
+
+ /* Reject non-remote clients. This interface only makes sense
+ * for remote clients that can refer to out-of-band objects. */
+ listener = wl_client_get_destroy_listener(client,
+ remote_client_destroy);
+ if (!listener)
+ return;
+
+ rc = container_of(listener, struct remote_client, destroy_listener);
+ rc->remote_resource =
+ wl_client_add_object(client, &remote_interface,
+ &remote_implementation, id, server);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *compositor,
+ int *argc, char *argv[], const char *config_file)
+{
+ struct remote_server *server;
+ struct wl_event_loop *loop =
+ wl_display_get_event_loop(compositor->wl_display);
+ int port = 44888;
+
+ server = malloc(sizeof *server);
+ if (server == NULL)
+ return -1;
+
+ server->compositor = compositor;
+ server->sock = make_socket(port);
+ if (server->sock < 0) {
+ free(server);
+ return -1;
+ }
+
+ server->sock_source =
+ wl_event_loop_add_fd(loop, server->sock, WL_EVENT_READABLE,
+ sock_handle_event, server);
+ if (server->sock_source == NULL) {
+ weston_log("failed to set up remote-server socket source\n");
+ free(server);
+ return -1;
+ }
+
+ server->global = wl_display_add_global(compositor->wl_display,
+ &remote_interface,
+ server, bind_remote);
+
+
+ server->seat_created_listener.notify = seat_created;
+ wl_signal_add(&compositor->seat_created_signal,
+ &server->seat_created_listener);
+
+ weston_log("remote server listening on port %d\n", port);
+
+ return 0;
+}
diff --git a/src/remote-stream.c b/src/remote-stream.c
new file mode 100644
index 00000000..69fd568f
--- /dev/null
+++ b/src/remote-stream.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/uio.h>
+
+#include "compositor.h"
+#include "remote-stream.h"
+
+int
+remote_stream_write(struct remote_stream *stream)
+{
+ struct remote_stream_block *block, *next;
+ uint32_t header;
+ struct iovec iov[2];
+ int len;
+
+ wl_list_for_each_safe(block, next, &stream->block_queue, link) {
+ /* FIXME: Coalesce all this into one writev? */
+ if (block->written == 0) {
+ header = (block->tag << 24) | block->size;
+ iov[0].iov_base = &header;
+ iov[0].iov_len = sizeof header;
+ iov[1].iov_base = block->data;
+ iov[1].iov_len = block->size;
+
+ /* FIXME: async write this... */
+ len = writev(stream->fd, iov, 2);
+ if (len == -1)
+ return len;
+
+ assert(len >= (int) sizeof header);
+ len -= sizeof header;
+ } else {
+ len = write(stream->fd, block->data + block->written,
+ block->size - block->written);
+ }
+
+ if (len < 0) {
+ weston_log("write error: %m\n");
+ return -1;
+ }
+
+ block->written += len;
+
+ if (block->written < block->size) {
+ /* FIXME: handle EAGAIN in the caller */
+ weston_log("partial write: %m\n");
+ return -1;
+ }
+
+ wl_list_remove(&block->link);
+ block->tag = 0;
+ }
+
+ stream->tag = 1;
+
+ return 0;
+}
+
+int
+remote_stream_read(struct remote_stream *stream)
+{
+ size_t remain;
+ uint32_t tag;
+
+ if (stream->handler == NULL) {
+ if (stream->header == 0)
+ /* FIXME: Check this read... */
+ read(stream->fd,
+ &stream->header, sizeof stream->header);
+ tag = stream->header >> 24;
+ if (tag == 0)
+ stream->tag_handler_count = 1;
+ if (tag >= stream->tag_handler_count)
+ /* If there's no handler for this tag yet,
+ * return to the main loop to process protocol
+ * events first. */
+ return 1;
+
+ stream->len = 0;
+ stream->total = stream->header & 0xffffff;
+ stream->handler = &stream->tag_handler[tag];
+ stream->header = 0;
+ } else {
+ remain = stream->total - stream->len;
+ stream->handler->func(stream, remain, stream->handler->data);
+ if (stream->len == stream->total)
+ stream->handler = NULL;
+ }
+
+ return 1;
+}
+
+void
+remote_stream_handle_tag(struct remote_stream *stream, uint32_t tag,
+ remote_stream_handler_func_t func, void *data)
+{
+ /* FIXME: Bad assert */
+ assert(tag < ARRAY_LENGTH(stream->tag_handler));
+
+ if (tag != stream->tag_handler_count)
+ assert("tags out of sync");
+ stream->tag_handler[tag].func = func;
+ stream->tag_handler[tag].data = data;
+ stream->tag_handler_count++;
+}
+
+void
+remote_stream_queue_block(struct remote_stream *stream,
+ struct remote_stream_block *block,
+ void *data, size_t size)
+{
+ if (block->tag > 0)
+ return;
+
+ wl_list_insert(stream->block_queue.prev, &block->link);
+ block->tag = stream->tag++;
+ block->written = 0;
+ block->data = data;
+ block->size = size;
+}
diff --git a/src/remote-stream.h b/src/remote-stream.h
new file mode 100644
index 00000000..438c4a47
--- /dev/null
+++ b/src/remote-stream.h
@@ -0,0 +1,48 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <wayland-util.h>
+
+struct remote_stream;
+
+typedef int (*remote_stream_handler_func_t)(struct remote_stream *stream,
+ size_t remain, void *data);
+
+struct remote_stream_handler {
+ remote_stream_handler_func_t func;
+ void *data;
+};
+
+struct remote_stream {
+ uint32_t tag, header;
+ int len, total;
+ int fd, protocol_fd;
+ uint32_t tag_handler_count;
+ struct remote_stream_handler tag_handler[10];
+ struct remote_stream_handler *handler;
+ struct wl_event_source *source;
+ struct wl_list block_queue;
+};
+
+struct remote_stream_block {
+ struct wl_list link;
+ void *data;
+ size_t size;
+ size_t written;
+ uint32_t tag;
+};
+
+int
+remote_stream_write(struct remote_stream *stream);
+
+int
+remote_stream_read(struct remote_stream *stream);
+
+void
+remote_stream_handle_tag(struct remote_stream *stream, uint32_t tag,
+ remote_stream_handler_func_t func, void *data);
+
+void
+remote_stream_queue_block(struct remote_stream *stream,
+ struct remote_stream_block *block,
+ void *data, size_t size);