Mostly just some spell checks. Reviewed-by: Bryce Harrington <[email protected]>
On Tue, Feb 25, 2014 at 07:26:47PM -0600, Jason Ekstrand wrote: > This adds a plugin called screen-share.so. If the screen-share.so module > is imported, it will add the CTRL+ALT+s keybinding to start a screen > sharing session. If you press CTRL+ALT+S, weston will spawn another copy > of weston, this time with the RDP backend, and mirrors the current screen > to it and adds any seats from RDP as aditional seats. The current screen > is defined as the one with the mouse pointer. Currently the CTRL+ALT+s > keybinding is hardcoded as the only way to activate screen sharing. If, at > some point, shells want more control over the screen sharing process, the > API's should be easy to update and export to make this possible. > > For security, the command and path to weston is currently hard-coded. It > would not take much aditional code to make this configurable or to allow a additional > shell to launch other screen-sharing programs. However, handling those > security issues is outside the scope of this patch so it is hard-coded for > now. > > Signed-off-by: Jason Ekstrand <[email protected]> > --- > Makefile.am | 22 ++ > configure.ac | 13 + > src/screen-share.c | 1082 > ++++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 1117 insertions(+) > create mode 100644 src/screen-share.c > > diff --git a/Makefile.am b/Makefile.am > index 838a051..f0fbec1 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -707,6 +707,28 @@ nodist_fullscreen_shell_la_SOURCES = > \ > protocol/fullscreen-shell-server-protocol.h > endif > > +if ENABLE_SCREEN_SHARING > + > +module_LTLIBRARIES += screen-share.la > + > +screen_share_la_CPPFLAGS = $(AM_CPPFLAGS) -DBINDIR='"$(bindir)"' > +screen_share_la_LDFLAGS = -module -avoid-version > +screen_share_la_LIBADD = \ > + $(COMPOSITOR_LIBS) \ > + $(SCREEN_SHARE_LIBS) \ > + libshared-cairo.la > +screen_share_la_CFLAGS = \ > + $(COMPOSITOR_CFLAGS) \ > + $(SCREEN_SHARE_CFLAGS) \ > + $(GCC_CFLAGS) > +screen_share_la_SOURCES = \ > + src/screen-share.c > +nodist_screen_share_la_SOURCES = \ > + protocol/fullscreen-shell-protocol.c \ > + protocol/fullscreen-shell-client-protocol.h > + > +endif > + > if ENABLE_XWAYLAND > > module_LTLIBRARIES += xwayland.la > diff --git a/configure.ac b/configure.ac > index 0809614..27fd536 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -212,6 +212,18 @@ if test x$enable_rdp_compositor = xyes; then > CPPFLAGS="$SAVED_CPPFLAGS" > fi > > +AC_ARG_ENABLE([screen-sharing], [--enable-screen-sharing],, > + enable_screen_sharing=no) > +AM_CONDITIONAL([ENABLE_SCREEN_SHARING], > + [test x$enable_screen_sharing = xyes]) > +if test x$enable_screen_sharing = xyes; then > + PKG_CHECK_MODULES(SCREEN_SHARE, [wayland-client]) > + > + if test x$enable_rdp_compositor != xyes; then > + AC_MSG_WARN([The screen-share.so module requires the RDP backend to work > properly.]) I think you can omit "to work properly", and it eliminates possible ambiguity. > + fi > +fi > + > AC_ARG_WITH(cairo, > AS_HELP_STRING([--with-cairo=@<:@image|gl|glesv2@:>@] > [Which Cairo renderer to use for the clients]), > @@ -513,6 +525,7 @@ AC_MSG_RESULT([ > RPI Compositor ${enable_rpi_compositor} > FBDEV Compositor ${enable_fbdev_compositor} > RDP Compositor ${enable_rdp_compositor} > + Screen Sharing ${enable_screen_sharing} > > Raspberry Pi BCM headers ${have_bcm_host} > > diff --git a/src/screen-share.c b/src/screen-share.c > new file mode 100644 > index 0000000..6cc6bd5 > --- /dev/null > +++ b/src/screen-share.c > @@ -0,0 +1,1082 @@ > +/* > + * Copyright © 2008-2011 Kristian Høgsberg > + * Copyright © 2014 Jason Ekstrand > + * > + * 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 <stdio.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/socket.h> > +#include <sys/mman.h> > +#include <signal.h> > +#include <linux/input.h> > +#include <errno.h> > + > +#include <wayland-client.h> > + > +#include "compositor.h" > +#include "../shared/os-compatibility.h" > +#include "fullscreen-shell-client-protocol.h" > + > +struct shared_output { > + struct weston_output *output; > + struct wl_listener output_destroyed; > + struct wl_list seat_list; > + > + struct { > + struct wl_display *display; > + struct wl_registry *registry; > + struct wl_compositor *compositor; > + struct wl_shm *shm; > + uint32_t shm_formats; > + struct wl_fullscreen_shell *fshell; > + struct wl_output *output; > + struct wl_surface *surface; > + struct wl_callback *frame_cb; > + struct wl_fullscreen_shell_mode_feedback *mode_feedback; > + } parent; > + > + struct wl_event_source *event_source; > + struct wl_listener frame_listener; > + > + struct { > + int32_t width, height; > + > + struct wl_list buffers; > + struct wl_list free_buffers; > + } shm; > + > + int cache_dirty; > + pixman_image_t *cache_image; > + uint32_t *tmp_data; > + size_t tmp_data_size; > +}; > + > +struct ss_seat { > + struct weston_seat base; > + struct shared_output *output; > + struct wl_list link; > + > + struct { > + struct wl_seat *seat; > + struct wl_pointer *pointer; > + struct wl_keyboard *keyboard; > + } parent; > + > + enum weston_key_state_update keyboard_state_update; > + uint32_t key_serial; > +}; > + > +struct ss_shm_buffer { > + struct shared_output *output; > + struct wl_list link; > + struct wl_list free_link; > + > + struct wl_buffer *buffer; > + void *data; > + size_t size; > + pixman_region32_t damage; > + > + pixman_image_t *pm_image; > +}; > + > +static void > +ss_seat_handle_pointer_enter(void *data, struct wl_pointer *pointer, > + uint32_t serial, struct wl_surface *surface, > + wl_fixed_t x, wl_fixed_t y) > +{ > + struct ss_seat *seat = data; > + > + /* We make the tacit assumption here that we are always recieving receiving > + * input in output coordinates. > + weston_output_transform_coordinate(&seat->output->base, x, y, &x, &y); > + */ Is that function call supposed to be commented out? If so, prefix with "*". > + > + notify_pointer_focus(&seat->base, NULL, 0, 0); > +} > + > +static void > +ss_seat_handle_pointer_leave(void *data, struct wl_pointer *pointer, > + uint32_t serial, struct wl_surface *surface) > +{ > + struct ss_seat *seat = data; > + > + notify_pointer_focus(&seat->base, NULL, 0, 0); > +} > + > +static void > +ss_seat_handle_motion(void *data, struct wl_pointer *pointer, > + uint32_t time, wl_fixed_t x, wl_fixed_t y) > +{ > + struct ss_seat *seat = data; > + > + /* We make the tacit assumption here that we are always recieving receiving > + * input in output coordinates. > + weston_output_transform_coordinate(&seat->output->base, x, y, &x, &y); > + */ Another oddly commented-out function call? > + > + notify_motion_absolute(&seat->base, time, x, y); > +} > + > +static void > +ss_seat_handle_button(void *data, struct wl_pointer *pointer, > + uint32_t serial, uint32_t time, uint32_t button, > + uint32_t state) > +{ > + struct ss_seat *seat = data; > + > + notify_button(&seat->base, time, button, state); > +} > + > +static void > +ss_seat_handle_axis(void *data, struct wl_pointer *pointer, > + uint32_t time, uint32_t axis, wl_fixed_t value) > +{ > + struct ss_seat *seat = data; > + > + notify_axis(&seat->base, time, axis, value); > +} > + > +static const struct wl_pointer_listener ss_seat_pointer_listener = { > + ss_seat_handle_pointer_enter, > + ss_seat_handle_pointer_leave, > + ss_seat_handle_motion, > + ss_seat_handle_button, > + ss_seat_handle_axis, > +}; > + > +static void > +ss_seat_handle_keymap(void *data, struct wl_keyboard *keyboard, > + uint32_t format, int fd, uint32_t size) > +{ > + struct ss_seat *seat = data; > + struct xkb_keymap *keymap; > + char *map_str; > + > + if (!data) > + goto error; > + > + if (format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { > + map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); > + if (map_str == MAP_FAILED) { > + weston_log("mmap failed: %m\n"); > + goto error; > + } > + > + keymap = > xkb_map_new_from_string(seat->base.compositor->xkb_context, > + map_str, > + XKB_KEYMAP_FORMAT_TEXT_V1, > + 0); > + munmap(map_str, size); > + > + if (!keymap) { > + weston_log("failed to compile keymap\n"); > + goto error; > + } > + > + seat->keyboard_state_update = STATE_UPDATE_NONE; > + } else if (format == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) { > + weston_log("No keymap provided; falling back to defalt\n"); default > + keymap = NULL; > + seat->keyboard_state_update = STATE_UPDATE_AUTOMATIC; > + } else { > + weston_log("Invalid keymap\n"); > + goto error; > + } > + > + close(fd); > + > + if (seat->base.keyboard) > + weston_seat_update_keymap(&seat->base, keymap); > + else > + weston_seat_init_keyboard(&seat->base, keymap); > + > + if (keymap) > + xkb_map_unref(keymap); > + > + return; > + > +error: > + wl_keyboard_release(seat->parent.keyboard); > + close(fd); > +} > + > +static void > +ss_seat_handle_keyboard_enter(void *data, struct wl_keyboard *keyboard, > + uint32_t serial, struct wl_surface *surface, > + struct wl_array *keys) > +{ > + struct ss_seat *seat = data; > + > + /* XXX: If we get a modifier event immediately before the focus, > + * we should try to keep the same serial. */ > + notify_keyboard_focus_in(&seat->base, keys, > + STATE_UPDATE_AUTOMATIC); > +} > + > +static void > +ss_seat_handle_keyboard_leave(void *data, struct wl_keyboard *keyboard, > + uint32_t serial, struct wl_surface *surface) > +{ > + struct ss_seat *seat = data; > + > + notify_keyboard_focus_out(&seat->base); > +} > + > +static void > +ss_seat_handle_key(void *data, struct wl_keyboard *keyboard, > + uint32_t serial, uint32_t time, > + uint32_t key, uint32_t state) > +{ > + struct ss_seat *seat = data; > + > + seat->key_serial = serial; > + notify_key(&seat->base, time, key, > + state ? WL_KEYBOARD_KEY_STATE_PRESSED : > + WL_KEYBOARD_KEY_STATE_RELEASED, > + seat->keyboard_state_update); > +} > + > +static void > +ss_seat_handle_modifiers(void *data, struct wl_keyboard *keyboard, > + uint32_t serial_in, uint32_t mods_depressed, > + uint32_t mods_latched, uint32_t mods_locked, > + uint32_t group) > +{ > + struct ss_seat *seat = data; > + struct weston_compositor *c = seat->output->output->compositor; > + uint32_t serial_out; > + > + /* If we get a key event followed by a modifier event with the > + * same serial number, then we try to preserve those semantics by > + * reusing the same serial number on the way out too. */ > + if (serial_in == seat->key_serial) > + serial_out = wl_display_get_serial(c->wl_display); > + else > + serial_out = wl_display_next_serial(c->wl_display); > + > + xkb_state_update_mask(seat->base.keyboard->xkb_state.state, > + mods_depressed, mods_latched, > + mods_locked, 0, 0, group); > + notify_modifiers(&seat->base, serial_out); > +} > + > +static const struct wl_keyboard_listener ss_seat_keyboard_listener = { > + ss_seat_handle_keymap, > + ss_seat_handle_keyboard_enter, > + ss_seat_handle_keyboard_leave, > + ss_seat_handle_key, > + ss_seat_handle_modifiers, > +}; > + > +static void > +ss_seat_handle_capabilities(void *data, struct wl_seat *seat, > + enum wl_seat_capability caps) > +{ > + struct ss_seat *ss_seat = data; > + > + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !ss_seat->parent.pointer) { > + ss_seat->parent.pointer = wl_seat_get_pointer(seat); > + wl_pointer_set_user_data(ss_seat->parent.pointer, ss_seat); > + wl_pointer_add_listener(ss_seat->parent.pointer, > + &ss_seat_pointer_listener, ss_seat); > + weston_seat_init_pointer(&ss_seat->base); > + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && > ss_seat->parent.pointer) { > + wl_pointer_destroy(ss_seat->parent.pointer); > + ss_seat->parent.pointer = NULL; > + } > + > + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !ss_seat->parent.keyboard) { > + ss_seat->parent.keyboard = wl_seat_get_keyboard(seat); > + wl_keyboard_set_user_data(ss_seat->parent.keyboard, ss_seat); > + wl_keyboard_add_listener(ss_seat->parent.keyboard, > + &ss_seat_keyboard_listener, ss_seat); > + } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && > ss_seat->parent.keyboard) { > + wl_keyboard_destroy(ss_seat->parent.keyboard); > + ss_seat->parent.keyboard = NULL; > + } > +} > + > +static const struct wl_seat_listener ss_seat_listener = { > + ss_seat_handle_capabilities, > +}; > + > +static struct ss_seat * > +ss_seat_create(struct shared_output *so, uint32_t id) > +{ > + struct ss_seat *seat; > + > + seat = zalloc(sizeof *seat); > + if (seat == NULL) > + return NULL; > + > + weston_seat_init(&seat->base, so->output->compositor, "default"); > + seat->output = so; > + seat->parent.seat = wl_registry_bind(so->parent.registry, id, > + &wl_seat_interface, 1); > + wl_list_insert(so->seat_list.prev, &seat->link); > + > + wl_seat_add_listener(seat->parent.seat, &ss_seat_listener, seat); > + wl_seat_set_user_data(seat->parent.seat, seat); > + > + return seat; > +} > + > +static void > +ss_seat_destroy(struct ss_seat *seat) > +{ > + if (seat->parent.pointer) > + wl_pointer_release(seat->parent.pointer); > + if (seat->parent.keyboard) > + wl_keyboard_release(seat->parent.keyboard); > + wl_seat_destroy(seat->parent.seat); > + > + wl_list_remove(&seat->link); > + > + weston_seat_release(&seat->base); > + > + free(seat); > +} > + > +static void > +ss_shm_buffer_destroy(struct ss_shm_buffer *buffer) > +{ > + pixman_image_unref(buffer->pm_image); > + > + wl_buffer_destroy(buffer->buffer); > + munmap(buffer->data, buffer->size); > + > + pixman_region32_fini(&buffer->damage); > + > + wl_list_remove(&buffer->link); > + wl_list_remove(&buffer->free_link); > + free(buffer); > +} > + > +static void > +buffer_release(void *data, struct wl_buffer *buffer) > +{ > + struct ss_shm_buffer *sb = data; > + > + if (sb->output) { > + wl_list_insert(&sb->output->shm.free_buffers, &sb->free_link); > + } else { > + ss_shm_buffer_destroy(sb); > + } > +} > + > +static const struct wl_buffer_listener buffer_listener = { > + buffer_release > +}; > + > +static struct ss_shm_buffer * > +shared_output_get_shm_buffer(struct shared_output *so) > +{ > + struct ss_shm_buffer *sb, *bnext; > + struct wl_shm_pool *pool; > + int width, height, stride; > + int fd; > + unsigned char *data; > + > + width = so->output->width; > + height = so->output->height; > + stride = width * 4; > + > + /* If the size of the output changed, we free the old buffers and > + * make new ones. */ > + if (so->shm.width != width || > + so->shm.height != height) { > + > + /* Destroy free buffers */ > + wl_list_for_each_safe(sb, bnext, &so->shm.free_buffers, link) > + ss_shm_buffer_destroy(sb); > + > + /* Orphan in-use buffers so they get destroyed */ > + wl_list_for_each(sb, &so->shm.buffers, link) > + sb->output = NULL; > + > + so->shm.width = width; > + so->shm.height = height; > + } > + > + if (!wl_list_empty(&so->shm.free_buffers)) { > + sb = container_of(so->shm.free_buffers.next, > + struct ss_shm_buffer, free_link); > + wl_list_remove(&sb->free_link); > + wl_list_init(&sb->free_link); > + > + return sb; > + } > + > + fd = os_create_anonymous_file(height * stride); > + if (fd < 0) { > + perror("os_create_anonymous_file"); > + return NULL; > + } > + > + data = mmap(NULL, height * stride, PROT_READ | PROT_WRITE, MAP_SHARED, > fd, 0); > + if (data == MAP_FAILED) { > + perror("mmap"); > + close(fd); > + return NULL; > + } I know this function is cribbed from an analogous routine from compositor-wayland.c, but I'm curious why perror() is used here, when weston_log() is used elsewhere? Also since there is some similarity, could any of this be refactored into shared code to avoid the redundancy? > + > + sb = zalloc(sizeof *sb); > + > + sb->output = so; > + wl_list_init(&sb->free_link); > + wl_list_insert(&so->shm.buffers, &sb->link); > + > + pixman_region32_init_rect(&sb->damage, 0, 0, width, height); > + > + sb->data = data; > + sb->size = height * stride; > + > + pool = wl_shm_create_pool(so->parent.shm, fd, sb->size); > + > + sb->buffer = wl_shm_pool_create_buffer(pool, 0, > + width, height, stride, > + WL_SHM_FORMAT_ARGB8888); > + wl_buffer_add_listener(sb->buffer, &buffer_listener, sb); > + wl_shm_pool_destroy(pool); > + close(fd); > + > + memset(data, 0, sb->size); > + > + sb->pm_image = > + pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, > + (uint32_t *)data, stride); > + > + return sb; > +} > + > +static void > +output_compute_transform(struct weston_output *output, > + pixman_transform_t *transform) > +{ > + pixman_fixed_t fw, fh; > + > + pixman_transform_init_identity(transform); > + > + fw = pixman_int_to_fixed(output->width); > + fh = pixman_int_to_fixed(output->height); > + > + switch (output->transform) { > + case WL_OUTPUT_TRANSFORM_FLIPPED: > + case WL_OUTPUT_TRANSFORM_FLIPPED_90: > + case WL_OUTPUT_TRANSFORM_FLIPPED_180: > + case WL_OUTPUT_TRANSFORM_FLIPPED_270: > + pixman_transform_scale(transform, NULL, > + pixman_int_to_fixed (-1), > + pixman_int_to_fixed (1)); > + pixman_transform_translate(transform, NULL, fw, 0); > + } > + > + switch (output->transform) { > + default: > + case WL_OUTPUT_TRANSFORM_NORMAL: > + case WL_OUTPUT_TRANSFORM_FLIPPED: > + break; > + case WL_OUTPUT_TRANSFORM_90: > + case WL_OUTPUT_TRANSFORM_FLIPPED_90: > + pixman_transform_rotate(transform, NULL, 0, pixman_fixed_1); > + pixman_transform_translate(transform, NULL, fh, 0); > + break; > + case WL_OUTPUT_TRANSFORM_180: > + case WL_OUTPUT_TRANSFORM_FLIPPED_180: > + pixman_transform_rotate(transform, NULL, -pixman_fixed_1, 0); > + pixman_transform_translate(transform, NULL, fw, fh); > + break; > + case WL_OUTPUT_TRANSFORM_270: > + case WL_OUTPUT_TRANSFORM_FLIPPED_270: > + pixman_transform_rotate(transform, NULL, 0, -pixman_fixed_1); > + pixman_transform_translate(transform, NULL, 0, fw); > + break; > + } > + > + pixman_transform_scale(transform, NULL, > + pixman_fixed_1 * output->current_scale, > + pixman_fixed_1 * output->current_scale); > +} > + > +static void > +shared_output_destroy(struct shared_output *so); > + > +static int > +shared_output_ensure_tmp_data(struct shared_output *so, > + pixman_region32_t *region) > +{ > + pixman_box32_t *ext; > + int32_t area; > + size_t size; > + > + if (pixman_region32_not_empty(region)) { > + ext = pixman_region32_extents(region); > + area = (ext->x2 - ext->x1) * (ext->y2 - ext->y1); > + } else { > + return 0; > + } > + > + /* Damage is in buffer coordinates */ > + area *= so->output->current_scale * so->output->current_scale; > + > + size = area * 4; The area temporary variable can be factored out, and the code simplified to: pixman_box32_t *ext; size_t size; if (!pixman_region32_not_empty(region)) return; ext = pixman_region32_extents(region); /* Damage is in buffer coordinates */ size = 4 * (ext->x2 - ext->x1) * (ext->y2 - ext->y1) * so->output->current_scale * so->output->current_scale; Might also be worth adding a comment as to why we're multiplying by 4. > + if (so->tmp_data != NULL && size <= so->tmp_data_size) > + return 0; > + > + free(so->tmp_data); > + so->tmp_data = malloc(size); > + if (so->tmp_data == NULL) { > + so->tmp_data_size = 0; > + errno = ENOMEM; > + return -1; > + } > + > + so->tmp_data_size = size; > + > + return 0; > +} > + > +static void > +shared_output_update(struct shared_output *so); > + > +static void > +shared_output_frame_callback(void *data, struct wl_callback *cb, uint32_t > time) > +{ > + struct shared_output *so = data; > + > + if (cb != so->parent.frame_cb) > + return; > + > + wl_callback_destroy(cb); > + so->parent.frame_cb = NULL; > + > + shared_output_update(so); > +} > + > +static const struct wl_callback_listener shared_output_frame_listener = { > + shared_output_frame_callback > +}; > + > +static void > +shared_output_update(struct shared_output *so) > +{ > + struct ss_shm_buffer *sb; > + pixman_box32_t *r; > + int i, nrects; > + pixman_transform_t transform; > + > + /* Only update if we need to */ > + if (!so->cache_dirty || so->parent.frame_cb) > + return; > + > + sb = shared_output_get_shm_buffer(so); > + if (sb == NULL) { > + shared_output_destroy(so); > + return; > + } > + > + output_compute_transform(so->output, &transform); > + pixman_image_set_transform(so->cache_image, &transform); > + > + pixman_image_set_clip_region32(sb->pm_image, &sb->damage); > + > + pixman_image_composite32(PIXMAN_OP_SRC, > + so->cache_image, /* src */ > + NULL, /* mask */ > + sb->pm_image, /* dest */ > + 0, 0, /* src_x, src_y */ > + 0, 0, /* mask_x, mask_y */ > + 0, 0, /* dest_x, dest_y */ > + so->output->width, /* width */ > + so->output->height /* height */); > + > + pixman_image_set_transform(sb->pm_image, NULL); > + pixman_image_set_clip_region32(sb->pm_image, NULL); > + > + r = pixman_region32_rectangles(&sb->damage, &nrects); > + for (i = 0; i < nrects; ++i) > + wl_surface_damage(so->parent.surface, r[i].x1, r[i].y1, > + r[i].x2 - r[i].x1, r[i].y2 - r[i].y1); > + > + wl_surface_attach(so->parent.surface, sb->buffer, 0, 0); > + > + so->parent.frame_cb = wl_surface_frame(so->parent.surface); > + wl_callback_add_listener(so->parent.frame_cb, > + &shared_output_frame_listener, so); > + > + wl_surface_commit(so->parent.surface); > + wl_callback_destroy(wl_display_sync(so->parent.display)); > + wl_display_flush(so->parent.display); > + > + /* Clear the buffer damage */ > + pixman_region32_fini(&sb->damage); > + pixman_region32_init(&sb->damage); > +} > + > +static void > +shm_handle_format(void *data, struct wl_shm *wl_shm, uint32_t format) > +{ > + struct shared_output *so = data; > + > + so->parent.shm_formats |= (1 << format); > +} > + > +struct wl_shm_listener shm_listener = { > + shm_handle_format > +}; > + > +static void > +registry_handle_global(void *data, struct wl_registry *registry, > + uint32_t id, const char *interface, uint32_t version) > +{ > + struct shared_output *so = data; > + > + if (strcmp(interface, "wl_compositor") == 0) { > + so->parent.compositor = > + wl_registry_bind(registry, > + id, &wl_compositor_interface, 1); > + } else if (strcmp(interface, "wl_output") == 0 && !so->parent.output) { > + so->parent.output = > + wl_registry_bind(registry, > + id, &wl_output_interface, 1); > + } else if (strcmp(interface, "wl_seat") == 0) { > + ss_seat_create(so, id); > + } else if (strcmp(interface, "wl_shm") == 0) { > + so->parent.shm = > + wl_registry_bind(registry, > + id, &wl_shm_interface, 1); > + wl_shm_add_listener(so->parent.shm, &shm_listener, so); > + } else if (strcmp(interface, "wl_fullscreen_shell") == 0) { > + so->parent.fshell = > + wl_registry_bind(registry, > + id, &wl_fullscreen_shell_interface, 1); > + } > +} > + > +static void > +registry_handle_global_remove(void *data, struct wl_registry *registry, > + uint32_t name) > +{ > +} > + > +static const struct wl_registry_listener registry_listener = { > + registry_handle_global, > + registry_handle_global_remove > +}; > + > +static int > +shared_output_handle_event(int fd, uint32_t mask, void *data) > +{ > + struct shared_output *so = data; > + int count = 0; > + > + if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { > + shared_output_destroy(so); > + return 0; > + } > + > + if (mask & WL_EVENT_READABLE) > + count = wl_display_dispatch(so->parent.display); > + if (mask & WL_EVENT_WRITABLE) > + wl_display_flush(so->parent.display); > + > + if (mask == 0) { > + count = wl_display_dispatch_pending(so->parent.display); > + wl_display_flush(so->parent.display); > + } > + > + return count; > +} > + > +static void > +output_destroyed(struct wl_listener *l, void *data) > +{ > + struct shared_output *so; > + > + so = container_of(l, struct shared_output, output_destroyed); > + > + shared_output_destroy(so); > +} > + > +static void > +mode_feedback_ok(void *data, struct wl_fullscreen_shell_mode_feedback *fb) > +{ > + struct shared_output *so = data; > + > + wl_fullscreen_shell_mode_feedback_destroy(so->parent.mode_feedback); > +} > + > +static void > +mode_feedback_failed(void *data, struct wl_fullscreen_shell_mode_feedback > *fb) > +{ > + struct shared_output *so = data; > + > + wl_fullscreen_shell_mode_feedback_destroy(so->parent.mode_feedback); > + > + weston_log("Screen share failed: present_surface_for_mode failed\n"); > + shared_output_destroy(so); > +} > + > +struct wl_fullscreen_shell_mode_feedback_listener mode_feedback_listener = { > + mode_feedback_ok, > + mode_feedback_failed, > + mode_feedback_ok, > +}; > + > +static void > +shared_output_repainted(struct wl_listener *listener, void *data) > +{ > + struct shared_output *so = > + container_of(listener, struct shared_output, frame_listener); > + pixman_region32_t damage; > + struct ss_shm_buffer *sb; > + int32_t x, y, width, height, stride; > + int i, nrects, do_yflip; > + pixman_box32_t *r; > + uint32_t *cache_data; > + > + /* Damage in output coordinates */ > + pixman_region32_init(&damage); > + pixman_region32_intersect(&damage, &so->output->region, > + &so->output->previous_damage); > + pixman_region32_translate(&damage, -so->output->x, -so->output->y); > + > + /* Apply damage to all buffers */ > + wl_list_for_each(sb, &so->shm.buffers, link) > + pixman_region32_union(&sb->damage, &sb->damage, &damage); > + > + /* Transform to buffer coordinates */ > + weston_transformed_region(so->output->width, so->output->height, > + so->output->transform, > + so->output->current_scale, > + &damage, &damage); > + > + width = so->output->current_mode->width; > + height = so->output->current_mode->height; > + stride = width; > + > + if (!so->cache_image || > + pixman_image_get_width(so->cache_image) != width || > + pixman_image_get_height(so->cache_image) != height) { > + if (so->cache_image) > + pixman_image_unref(so->cache_image); > + > + so->cache_image = > + pixman_image_create_bits(PIXMAN_a8r8g8b8, > + width, height, NULL, > + stride); > + if (!so->cache_image) { > + shared_output_destroy(so); > + return; > + } > + > + pixman_region32_fini(&damage); > + pixman_region32_init_rect(&damage, 0, 0, width, height); > + } > + > + if (shared_output_ensure_tmp_data(so, &damage) < 0) { > + shared_output_destroy(so); > + return; > + } > + > + do_yflip = !!(so->output->compositor->capabilities & > WESTON_CAP_CAPTURE_YFLIP); > + > + cache_data = pixman_image_get_data(so->cache_image); > + r = pixman_region32_rectangles(&damage, &nrects); > + for (i = 0; i < nrects; ++i) { > + x = r[i].x1; > + y = r[i].y1; > + width = r[i].x2 - r[i].x1; > + height = r[i].y2 - r[i].y1; > + > + if (do_yflip) { > + so->output->compositor->renderer->read_pixels( > + so->output, PIXMAN_a8r8g8b8, so->tmp_data, > + x, so->output->current_mode->height - r[i].y2, > + width, height); > + > + pixman_blt(so->tmp_data, cache_data, -width, stride, > + 32, 32, 0, 1 - height, x, y, width, height); > + } else { > + so->output->compositor->renderer->read_pixels( > + so->output, PIXMAN_a8r8g8b8, so->tmp_data, > + x, y, width, height); > + > + pixman_blt(so->tmp_data, cache_data, width, stride, > + 32, 32, 0, 0, x, y, width, height); > + } > + } > + > + pixman_region32_fini(&damage); > + > + so->cache_dirty = 1; > + > + shared_output_update(so); > +} > + > +static struct shared_output * > +shared_output_create(struct weston_output *output, int parent_fd) > +{ > + struct shared_output *so; > + struct wl_event_loop *loop; > + struct ss_seat *seat; > + int epoll_fd; > + > + so = zalloc(sizeof *so); > + if (so == NULL) > + goto err_close; > + > + wl_list_init(&so->seat_list); > + > + so->parent.display = wl_display_connect_to_fd(parent_fd); > + if (!so->parent.display) > + goto err_alloc; > + > + so->parent.registry = wl_display_get_registry(so->parent.display); > + if (!so->parent.registry) > + goto err_display; > + wl_registry_add_listener(so->parent.registry, > + ®istry_listener, so); > + wl_display_roundtrip(so->parent.display); > + if (so->parent.shm == NULL) { > + weston_log("Screen share failed: No wl_shm found\n"); > + goto err_display; > + } > + if (so->parent.fshell == NULL) { > + weston_log("Screen share failed: " > + "Parent does not support wl_fullscreen_shell\n"); > + goto err_display; > + } > + if (so->parent.compositor == NULL) { > + weston_log("Screen share failed: No wl_compositor found\n"); > + goto err_display; > + } > + > + /* Get SHM formats */ > + wl_display_roundtrip(so->parent.display); > + if (!(so->parent.shm_formats & (1 << WL_SHM_FORMAT_XRGB8888))) { > + weston_log("Screen share failed: " > + "WL_SHM_FORMAT_XRGB8888 not available\n"); > + goto err_display; > + } > + > + so->parent.surface = > + wl_compositor_create_surface(so->parent.compositor); > + if (!so->parent.surface) { > + weston_log("Screen share failed: %m"); > + goto err_display; > + } > + > + so->parent.mode_feedback = > + wl_fullscreen_shell_present_surface_for_mode(so->parent.fshell, > + so->parent.surface, > + so->parent.output, > + > output->current_mode->refresh); > + if (!so->parent.mode_feedback) { > + weston_log("Screen share failed: %m"); > + goto err_display; > + } > + wl_fullscreen_shell_mode_feedback_add_listener(so->parent.mode_feedback, > + &mode_feedback_listener, > + so); > + > + loop = wl_display_get_event_loop(output->compositor->wl_display); > + > + epoll_fd = wl_display_get_fd(so->parent.display); > + so->event_source = > + wl_event_loop_add_fd(loop, epoll_fd, WL_EVENT_READABLE, > + shared_output_handle_event, so); > + if (!so->event_source) { > + weston_log("Screen share failed: %m"); > + goto err_display; > + } > + > + /* Ok, everything's created. We should be good to go */ > + wl_list_init(&so->shm.buffers); > + wl_list_init(&so->shm.free_buffers); > + > + so->output = output; > + so->output_destroyed.notify = output_destroyed; > + wl_signal_add(&so->output->destroy_signal, &so->output_destroyed); > + > + so->frame_listener.notify = shared_output_repainted; > + wl_signal_add(&output->frame_signal, &so->frame_listener); > + output->disable_planes++; > + weston_output_damage(output); > + > + return so; > + > +err_display: > + wl_list_for_each(seat, &so->seat_list, link) > + ss_seat_destroy(seat); > + wl_display_disconnect(so->parent.display); > +err_alloc: > + free(so); > +err_close: > + close(parent_fd); > + return NULL; > +} > + > +static void > +shared_output_destroy(struct shared_output *so) > +{ > + struct ss_shm_buffer *buffer, *bnext; > + > + wl_list_for_each_safe(buffer, bnext, &so->shm.buffers, link) > + ss_shm_buffer_destroy(buffer); > + wl_list_for_each_safe(buffer, bnext, &so->shm.free_buffers, link) > + ss_shm_buffer_destroy(buffer); > + > + wl_display_disconnect(so->parent.display); > + wl_event_source_remove(so->event_source); > + > + wl_list_remove(&so->output_destroyed.link); > + wl_list_remove(&so->frame_listener.link); > + > + pixman_image_unref(so->cache_image); > + free(so->tmp_data); > + > + free(so); > +} > + > +static struct shared_output * > +weston_output_share(struct weston_output *output, > + const char *path, char *const argv[]) > +{ > + int sv[2]; > + char str[32]; > + pid_t pid; > + sigset_t allsigs; > + > + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { > + weston_log("weston_output_share: socketpair failed: %m\n"); > + return NULL; > + } > + > + pid = fork(); > + > + if (pid == -1) { > + close(sv[0]); > + close(sv[1]); > + weston_log("weston_output_share: fork failed: %m\n"); > + return NULL; > + } > + > + if (pid == 0) { > + /* We don't want anything circular */ > + unsetenv("WAYLAND_DISPLAY"); > + unsetenv("WAYLAND_SOCKET"); > + > + setenv("WAYLAND_DEBUG", "server", 1); > + > + /* do not give our signal mask to the new process */ > + sigfillset(&allsigs); > + sigprocmask(SIG_UNBLOCK, &allsigs, NULL); > + > + /* Launch clients as the user. Do not lauch clients with launch > + * wrong euid. */ > + if (seteuid(getuid()) == -1) { > + weston_log("weston_output_share: setuid failed: %m\n"); > + abort(); > + } > + > + sv[1] = dup(sv[1]); > + if (sv[1] == -1) { > + weston_log("weston_output_share: dup failed: %m\n"); > + abort(); > + } > + > + snprintf(str, sizeof str, "%d", sv[1]); > + setenv("WAYLAND_SERVER_SOCKET", str, 1); > + > + execv(path, argv); > + weston_log("weston_output_share: exec failed: %m\n"); > + abort(); > + } else { > + close(sv[1]); > + return shared_output_create(output, sv[0]); > + } > + > + return NULL; > +} > + > +static struct weston_output * > +weston_output_find(struct weston_compositor *c, int32_t x, int32_t y) > +{ > + struct weston_output *output; > + > + wl_list_for_each(output, &c->output_list, link) { > + if (x >= output->x && y >= output->y && > + x < output->x + output->width && > + y < output->y + output->height) > + return output; > + } > + > + return NULL; > +} > + > +static void > +share_output_binding(struct weston_seat *seat, uint32_t time, uint32_t key, > + void *data) > +{ > + struct weston_output *output; > + const char *path = BINDIR "/weston"; > + char width_arg[32], height_arg[32]; > + > + if (!seat->pointer) { > + weston_log("Cannot pick output: Seat does not have pointer\n"); > + return; > + } > + > + output = weston_output_find(seat->compositor, > + wl_fixed_to_int(seat->pointer->x), > + wl_fixed_to_int(seat->pointer->y)); > + if (!output) { > + weston_log("Cannot pick output: Pointer not on any output\n"); > + return; > + } > + > + snprintf(width_arg, sizeof width_arg, "--width=%d", output->width); > + snprintf(height_arg, sizeof height_arg, "--height=%d", output->height); > + > + char *const argv[] = { > + "weston", > + "--backend=rdp-backend.so", > + "--shell=fullscreen-shell.so", > + width_arg, > + height_arg, > + NULL > + }; > + > + weston_output_share(output, path, argv); > +} > + > +WL_EXPORT int > +module_init(struct weston_compositor *compositor, > + int *argc, char *argv[]) > +{ > + weston_compositor_add_key_binding(compositor, KEY_S, > + MODIFIER_CTRL | MODIFIER_ALT, > + share_output_binding, compositor); > + return 0; > +} > -- > 1.8.5.3 > > _______________________________________________ > wayland-devel mailing list > [email protected] > http://lists.freedesktop.org/mailman/listinfo/wayland-devel _______________________________________________ wayland-devel mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/wayland-devel
