diff options
author | Kristian Høgsberg <krh@bitplanet.net> | 2013-03-25 21:36:50 -0400 |
---|---|---|
committer | Kristian Høgsberg <krh@bitplanet.net> | 2013-05-20 21:32:41 -0400 |
commit | 7adffe7be46f4cb31c27066332356e4a19b609d5 (patch) | |
tree | 9d727e18ea596b07d830e0e18a9a96d6e0cd1ad8 | |
parent | b0a19671c2329019fc03e8581bf9fbe6c3fbd8de (diff) |
Add remote display prototyperemote
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | protocol/remote.xml | 37 | ||||
-rw-r--r-- | src/Makefile.am | 32 | ||||
-rw-r--r-- | src/remote-proxy.c | 1761 | ||||
-rw-r--r-- | src/remote-server.c | 580 | ||||
-rw-r--r-- | src/remote-stream.c | 143 | ||||
-rw-r--r-- | src/remote-stream.h | 48 |
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, ®ion->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, ®ion->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, ®istry_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); |