diff options
Diffstat (limited to 'transmitter-plugin')
-rw-r--r-- | transmitter-plugin/input.c | 1333 | ||||
-rw-r--r-- | transmitter-plugin/meson.build | 24 | ||||
-rw-r--r-- | transmitter-plugin/output.c | 57 | ||||
-rw-r--r-- | transmitter-plugin/plugin.c | 1024 | ||||
-rw-r--r-- | transmitter-plugin/weston.ini.transmitter | 13 |
5 files changed, 2451 insertions, 0 deletions
diff --git a/transmitter-plugin/input.c b/transmitter-plugin/input.c new file mode 100644 index 0000000..8fb543c --- /dev/null +++ b/transmitter-plugin/input.c @@ -0,0 +1,1333 @@ +/* + * Copyright (C) 2017 Advanced Driver Information Technology Joint Venture GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +/* for fake stuff */ +#include <math.h> + +#include <wayland-client.h> +#include <libweston/libweston.h> + +#include <weston.h> +#include "plugin.h" +#include "transmitter_api.h" + +/** @file + * + * This is an implementation of a remote input. + * + * Request wl_data_device_manager.get_data_device would need to be blocked, + * except maybe it's not necessary, we just "forget" to forward data to/from + * the remote wl_seat. It might still work inside the local compositor. + * + * weston_compositor_set_default_pointer_grab() will break our pointer + * implementation, but no in-tree code is calling it. + */ + +/* XXX: all functions and variables with a name, and things marked with a + * comment, containing the word "fake" are mockups that need to be + * removed from the final implementation. + */ + +static void +pointer_focus_grab_handler(struct weston_pointer_grab *grab) +{ + /* No-op: + * + * Weston internal events do not change the focus. + */ +} + +static void +pointer_motion_grab_handler(struct weston_pointer_grab *grab, + const struct timespec *time, + struct weston_pointer_motion_event *event) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_button_grab_handler(struct weston_pointer_grab *grab, + const struct timespec *time, + uint32_t button, + uint32_t state) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_axis_grab_handler(struct weston_pointer_grab *grab, + const struct timespec *time, + struct weston_pointer_axis_event *event) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_axis_source_grab_handler(struct weston_pointer_grab *grab, + uint32_t source) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_frame_grab_handler(struct weston_pointer_grab *grab) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_cancel_grab_handler(struct weston_pointer_grab *grab) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +/* These handlers would be called from the notify_*() functions in src/input.c. + * However, as we do not use the low level input notify_*() functions that + * backends drive, these are mostly uncalled, except the focus handler which + * weston core generates internally. + */ +static const struct weston_pointer_grab_interface pointer_grab_impl = { + pointer_focus_grab_handler, + pointer_motion_grab_handler, + pointer_button_grab_handler, + pointer_axis_grab_handler, + pointer_axis_source_grab_handler, + pointer_frame_grab_handler, + pointer_cancel_grab_handler, +}; + +static void +keyboard_grab_key(struct weston_keyboard_grab *grab, + const struct timespec *time, + uint32_t key, + uint32_t state) +{ +} + +static void +keyboard_grab_modifiers(struct weston_keyboard_grab *grab, + uint32_t serial, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) +{ +} + +static void +keyboard_grab_cancel(struct weston_keyboard_grab *grab) +{ +} + +static const struct weston_keyboard_grab_interface keyborad_grab_impl = { + keyboard_grab_key, + keyboard_grab_modifiers, + keyboard_grab_cancel +}; + +static void +touch_grab_down_handler(struct weston_touch_grab *grab, + const struct timespec *time, + int touch_id, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void +touch_grab_up_handler(struct weston_touch_grab *grab, + const struct timespec *time, + int touch_id) +{ +} + +static void +touch_grab_motion_handler(struct weston_touch_grab *grab, + const struct timespec *time, + int touch_id, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void +touch_grab_frame_handler(struct weston_touch_grab *grab) +{ +} + +static void +touch_grab_cancel_handler(struct weston_touch_grab *grab) +{ +} + +static const struct weston_touch_grab_interface touch_grab_impl = { + touch_grab_down_handler, + touch_grab_up_handler, + touch_grab_motion_handler, + touch_grab_frame_handler, + touch_grab_cancel_handler, +}; + +static struct weston_pointer_client * +weston_pointer_get_pointer_client(struct weston_pointer *pointer, + struct wl_client *client) +{ + struct weston_pointer_client *pointer_client; + + wl_list_for_each(pointer_client, &pointer->pointer_clients, link) { + if (pointer_client->client == client) + return pointer_client; + } + + return NULL; +} + +/* The different ways to get pointer focus on a remoted surface: + * + * 1. Transmitter seat has pointer. The client has wl_pointer. Transmitter + * receives pointer.enter. (transmitter_seat_pointer_enter()) + * + * 2. Transmitter seat has pointer. Transmitter has received pointer.enter. + * The client calls wl_seat.get_pointer. => send enter only on the new + * wl_pointer. (seat_get_pointer_handler()) + * + * 3. Client has wl_pointer. Transmitter seat adds pointer capability. + * Transmitter receives pointer.enter. wl_pointer MUST NOT enter, + * specified by wl_seat.capabilities. + * + * By definition, Transmitter cannot receive pointer.enter without having + * pointer capability in the seat, so no other combinations are possible. + * + * The same applies to wl_keyboard and wl_touch. + */ + +/* Implementor notes: + * + * The handling of all of wl_pointer, wl_keyboard and wl_touch should be + * similar. To make it work, we need to add a signal to each of the + * wl_seat.get_pointer, wl_seat.get_keyboard, and wl_seat.get_touch request + * handlers in Weston core. Otherwise we cannot implement the case 2 of gaining + * input device focus. + * + * However, weston_keyboard::focus is a weston_surface, not a weston_view, so + * we may be able to leverage more of the core implementation and maybe do + * without the wl_seat.get_keyboard signal. Weston_touch uses a weston_view, so + * that is similar to weston_pointer. + * + * It might be useful to convert weston_keyboard and weston_touch to use a + * similar thing as weston_pointer_client, in case it makes things more + * consistent. It might also fix issues when a client has multiple copies of a + * wl_keyboard or a wl_touch, but that is getting off-topic. + * + * This file shows which part of the Weston input path we skip and where we + * hook in. We skip everything starting from the notify_*() API used by + * backends, and stub out the grab handlers. Instead of actual grab handlers, + * we have our own network protocol events handlers. They do much of the same + * as normal grab handlers would do, except focus is pre-given, and we do not + * have weston_view for the focus surfaces, so we need to bypass core code + * dealing with those. + * + * Our remote seat implementation will leave many struct members unused and + * replicate some from weston_pointer, weston_keyboard, and weston_touch. + * Weston core must be kept out from the focus handling business, because we + * will send enter/leave events ourselves, and focus assignments are given + * to us from the remote, they cannot be changed at will by the local Weston. + */ + +/** Callback from the protocol request handler for wl_seat.get_pointer + * + * The Weston core handler never sees focus set on the weston_pointer, + * so it won't send wl_pointer.enter nor set focus_client. It does call + * weston_pointer_ensure_pointer_client() though. + */ +void +seat_get_pointer_handler(struct wl_listener *listener, void *data) +{ + struct wl_resource *new_pointer = data; + struct weston_transmitter_seat *seat; + struct wl_resource *surface; + struct weston_pointer_client *pointer_client; + struct wl_client *client; + struct weston_pointer *pointer; + + seat = wl_container_of(listener, seat, get_pointer_listener); + if (!seat->pointer_focus) + return; + + client = wl_resource_get_client(new_pointer); + surface = seat->pointer_focus->surface->resource; + + if (wl_resource_get_client(surface) != client) + return; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); /* guaranteed by having pointer_focus */ + pointer_client = weston_pointer_get_pointer_client(pointer, client); + + if (!pointer->focus_client) + pointer->focus_client = pointer_client; + else + assert(pointer->focus_client == pointer_client); + + wl_pointer_send_enter(new_pointer, pointer->focus_serial, surface, + seat->pointer_surface_x, seat->pointer_surface_y); + + if (wl_resource_get_version(new_pointer) >= + WL_POINTER_FRAME_SINCE_VERSION) + wl_pointer_send_frame(new_pointer); +} + +static void +transmitter_seat_create_pointer(struct weston_transmitter_seat *seat) +{ + struct weston_pointer *pointer; + + seat->pointer_phase = 0.0; + seat->pointer_surface_x = wl_fixed_from_int(-1000000); + seat->pointer_surface_y = wl_fixed_from_int(-1000000); + seat->pointer_focus = NULL; + wl_list_init(&seat->pointer_focus_destroy_listener.link); + + weston_seat_init_pointer(seat->base); + + pointer = weston_seat_get_pointer(seat->base); + + /* not exported: + * weston_pointer_set_default_grab(pointer, &pointer_grab_impl); */ + pointer->default_grab.interface = &pointer_grab_impl; + + /* Changes to local outputs are irrelevant. */ + wl_list_remove(&pointer->output_destroy_listener.link); + wl_list_init(&pointer->output_destroy_listener.link); + + weston_log("Transmitter created pointer=%p for seat %p\n", + pointer, seat->base); +} + +static void +seat_pointer_focus_destroy_handler(struct wl_listener *listener, void *data) +{ + struct weston_transmitter_surface *txs = data; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(listener, seat, pointer_focus_destroy_listener); + assert(seat->pointer_focus == txs); + + seat->pointer_focus = NULL; +} + +void +transmitter_seat_pointer_enter(struct weston_transmitter_seat *seat, + uint32_t serial, + struct weston_transmitter_surface *txs, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *resource; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + + assert(txs->surface); + //struct wl_client *client = wl_resource_get_client(txs->surface->resource); + + seat->pointer_focus = txs; + seat->pointer_focus_destroy_listener.notify = + seat_pointer_focus_destroy_handler; + wl_signal_add(&txs->destroy_signal, + &seat->pointer_focus_destroy_listener); + + /* If pointer-focus gets destroyed, txs will get destroyed, the + * remote surface object is destroyed, and the remote will send a + * leave and a frame. + */ + + seat->pointer_surface_x = surface_x; + seat->pointer_surface_y = surface_y; + + pointer->focus_serial = serial; + + /* pointer->focus is not used, because it is a weston_view, while + * remoted surfaces have no views. + * + * pointer->x,y are not used because they are in global coordinates. + * Remoted surfaces are not in the global space at all, so there are + * no such coordinates. + */ + + if (!pointer->focus_client) + return; + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) { + wl_pointer_send_enter(resource, + serial, + txs->surface->resource, + surface_x, surface_y); + } +} + +void +transmitter_seat_pointer_leave(struct weston_transmitter_seat *seat, + uint32_t serial, + struct weston_transmitter_surface *txs) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *surface_resource; + struct wl_resource *resource; + + if (txs != seat->pointer_focus) { + weston_log("Transmitter Warning: pointer leave for %p, expected %p\n", + txs, seat->pointer_focus); + } + + seat->pointer_focus = NULL; + wl_list_remove(&seat->pointer_focus_destroy_listener.link); + wl_list_init(&seat->pointer_focus_destroy_listener.link); + + if (!txs) + return; + assert(txs->surface); + surface_resource = txs->surface->resource; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + if (!pointer->focus_client) + return; + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) + wl_pointer_send_leave(resource, serial, surface_resource); + + /* Do not reset pointer->focus_client, because we need to be able + * to send a following 'frame' event in + * transmitter_seat_pointer_frame(). + */ +} + +void +transmitter_seat_pointer_motion(struct weston_transmitter_seat *seat, + uint32_t time, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *resource; + struct weston_transmitter_surface *txs; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + + seat->pointer_surface_x = surface_x; + seat->pointer_surface_y = surface_y; + + if (!pointer->focus_client) + return; + + txs = seat->pointer_focus; + if (txs) + assert(wl_resource_get_client(txs->surface->resource) == + pointer->focus_client->client); + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) { + wl_pointer_send_motion(resource, time, + surface_x, surface_y); + } +} + +void +transmitter_seat_pointer_button(struct weston_transmitter_seat *seat, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *resource; + struct weston_transmitter_surface *txs; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + + if (!pointer->focus_client) + return; + + txs = seat->pointer_focus; + if (txs) + assert(wl_resource_get_client(txs->surface->resource) == + pointer->focus_client->client); + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) { + wl_pointer_send_button(resource, serial, time, + button, state); + } +} + +void +transmitter_seat_pointer_axis(struct weston_transmitter_seat *seat, + uint32_t time, + uint32_t axis, + wl_fixed_t value) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *resource; + struct weston_transmitter_surface *txs; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + + if (!pointer->focus_client) + return; + + txs = seat->pointer_focus; + if (txs) + assert(wl_resource_get_client(txs->surface->resource) == + pointer->focus_client->client); + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) { + wl_pointer_send_axis(resource, time, + axis, value); + } +} + +void +transmitter_seat_pointer_frame(struct weston_transmitter_seat *seat) +{ + struct weston_pointer *pointer; + + pointer = weston_seat_get_pointer(seat->base); + if (pointer) + weston_pointer_send_frame(pointer); +} + +void +transmitter_seat_pointer_axis_source(struct weston_transmitter_seat *seat, + uint32_t axis_source) +{ + /* ToDo : implement axis event handling */ +} + +void +transmitter_seat_pointer_axis_stop(struct weston_transmitter_seat *seat, + uint32_t time, + uint32_t axis) +{ + /* ToDo : implement axis event handling */ +} + +void +transmitter_seat_pointer_axis_discrete(struct weston_transmitter_seat *seat, + uint32_t axis, + int32_t discrete) +{ + /* ToDo : implement axis event handling */ +} + +static void +transmitter_seat_create_keyboard(struct weston_transmitter_seat *seat) +{ + struct weston_keyboard *keyboard; + + seat->keyboard_focus = NULL; + weston_seat_init_keyboard(seat->base, NULL); + + keyboard = weston_seat_get_keyboard(seat->base); + + keyboard->default_grab.interface = &keyborad_grab_impl; + + weston_log("Transmitter created keyboard=%p for seat %p\n", + keyboard, seat->base); +} + +static void +transmitter_seat_keyboard_enter(struct weston_transmitter_seat *seat, + uint32_t serial, + struct weston_transmitter_surface *txs, + struct wl_array *keys) +{ + struct weston_keyboard *keyboard; + struct wl_resource *resource = NULL; + struct wl_resource *surface_resource; + + keyboard = weston_seat_get_keyboard(seat->base); + assert(keyboard); + + assert(txs->surface); + surface_resource = txs->surface->resource; + + seat->keyboard_focus = txs; + wl_array_copy(&keyboard->keys, keys); + + wl_resource_for_each(resource, &keyboard->resource_list) { + if (wl_resource_get_client(resource) == wl_resource_get_client(surface_resource)) { + wl_keyboard_send_enter(resource, + serial, + surface_resource, + &keyboard->keys); + } + } +} + +static void +transmitter_seat_keyboard_leave(struct weston_transmitter_seat *seat, + uint32_t serial, + struct weston_transmitter_surface *txs) +{ + struct weston_keyboard *keyboard; + struct wl_resource *resource = NULL; + struct wl_resource *surface_resource; + + keyboard = weston_seat_get_keyboard(seat->base); + assert(keyboard); + + assert(txs->surface); + surface_resource = txs->surface->resource; + + wl_resource_for_each(resource, &keyboard->resource_list) { + if (wl_resource_get_client(resource) == wl_resource_get_client(surface_resource)) { + wl_keyboard_send_leave(resource, + serial, + surface_resource); + } + } +} + +static void +transmitter_seat_keyboard_key(struct weston_transmitter_seat *seat, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state) +{ + struct weston_keyboard *keyboard; + struct wl_resource *resource = NULL; + + keyboard = weston_seat_get_keyboard(seat->base); + assert(keyboard); + + wl_resource_for_each(resource, &keyboard->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->keyboard_focus->surface->resource)) { + wl_keyboard_send_key(resource, + serial, + time, + key, + state); + } + } +} + +static void +transmitter_seat_create_touch(struct weston_transmitter_seat *seat) +{ + struct weston_touch *touch; + + seat->touch_focus = NULL; + weston_seat_init_touch(seat->base); + + touch = weston_seat_get_touch(seat->base); + + touch->default_grab.interface = &touch_grab_impl; + + weston_log("Transmitter created touch=%p for seat %p\n", + touch, seat->base); +} + +static void +transmitter_seat_touch_down (struct weston_transmitter_seat *seat, + uint32_t serial, + uint32_t time, + struct weston_transmitter_surface *txs, + int32_t touch_id, + wl_fixed_t x, + wl_fixed_t y) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + struct wl_resource *surface_resource; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + assert(txs->surface); + surface_resource = txs->surface->resource; + + seat->touch_focus = txs; + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == wl_resource_get_client(surface_resource)) { + wl_touch_send_down(resource, serial, time, + surface_resource, + touch_id, x, y); + } + } +} + +static void +transmitter_seat_touch_up (struct weston_transmitter_seat *seat, + uint32_t serial, + uint32_t time, + int32_t touch_id) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->touch_focus->surface->resource)) { + wl_touch_send_up(resource, serial, time, touch_id); + } + } +} + +static void +transmitter_seat_touch_motion (struct weston_transmitter_seat *seat, + uint32_t time, + int32_t touch_id, + wl_fixed_t x, + wl_fixed_t y) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->touch_focus->surface->resource)) { + wl_touch_send_motion(resource, time, touch_id, x, y); + } + } +} + +static void +transmitter_seat_touch_frame (struct weston_transmitter_seat *seat) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->touch_focus->surface->resource)) { + wl_touch_send_frame(resource); + } + } +} + +static void +transmitter_seat_touch_cancel (struct weston_transmitter_seat *seat) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->touch_focus->surface->resource)) { + wl_touch_send_cancel(resource); + } + } +} + +static char * +make_seat_name(struct weston_transmitter_remote *remote, const char *name) +{ + char *str; + + if (asprintf(&str, "transmitter-%s-%s", remote->addr, name) < 0) + return NULL; + + return str; +} + +void +transmitter_seat_destroy(struct weston_transmitter_seat *seat) +{ + wl_list_remove(&seat->link); + + weston_log("Transmitter destroy seat=%p\n", seat->base); + + wl_list_remove(&seat->get_pointer_listener.link); + wl_list_remove(&seat->pointer_focus_destroy_listener.link); + + if (seat->pointer_timer) + wl_event_source_remove(seat->pointer_timer); + + free(seat); +} + +static void +pointer_handle_enter(struct wthp_pointer *wthp_pointer, + uint32_t serial, + struct wthp_surface *surface, + wth_fixed_t surface_x, + wth_fixed_t surface_y) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + if (txs != seat->pointer_focus) + transmitter_seat_pointer_leave(seat, serial, seat->pointer_focus); + transmitter_seat_pointer_enter(seat, serial, txs, + surface_x, surface_y); + } + } +} + +static void +pointer_handle_leave(struct wthp_pointer *wthp_pointer, + uint32_t serial, + struct wthp_surface *surface) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + transmitter_seat_pointer_leave(seat, serial, txs); + } + } +} + +static void +pointer_handle_motion(struct wthp_pointer *wthp_pointer, + uint32_t time, + wth_fixed_t surface_x, + wth_fixed_t surface_y) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_pointer_motion(seat, time, + surface_x, + surface_y); +} + +static void +pointer_handle_button(struct wthp_pointer *wthp_pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_pointer_button(seat, serial, + time, button, + state); +} + +static void +pointer_handle_axis(struct wthp_pointer *wthp_pointer, + uint32_t time, + uint32_t axis, wth_fixed_t value) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_pointer_axis(seat, time, + axis, value); +} + +static void +pointer_handle_frame(struct wthp_pointer *wthp_pointer) +{ + /* ToDo : implement pointer handle frame */ +} + +static void +pointer_handle_axis_source(struct wthp_pointer *wthp_pointer, + uint32_t axis_source) +{ + /* ToDo : implement pointer handle axis source */ +} + +static void +pointer_handle_axis_stop(struct wthp_pointer *wthp_pointer, + uint32_t time, + uint32_t axis) +{ + /* ToDo : implement pointer handle axis stop */ +} + +static void +pointer_handle_axis_discrete(struct wthp_pointer *wthp_pointer, + uint32_t axis, + int32_t discrete) +{ + /* ToDo : implement pointer handle axis discrete */ +} + +static const struct wthp_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, + pointer_handle_frame, + pointer_handle_axis_source, + pointer_handle_axis_stop, + pointer_handle_axis_discrete +}; + +static void +keyboard_handle_keymap(struct wthp_keyboard * wthp_keyboard, + uint32_t format, + uint32_t keymap_sz, + void * keymap) +{ + /* ToDo : implement keyboard handle keymap */ +} + +static void +keyboard_handle_enter(struct wthp_keyboard *wthp_keyboard, + uint32_t serial, + struct wthp_surface *surface, + struct wth_array *keys) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_keyboard); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + struct wl_array *wl_key = (struct wl_array *)malloc(sizeof(struct wl_array)); + + wl_key->size = keys->size; + wl_key->alloc = keys->alloc; + wl_key->data = keys->data; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + //transmitter_seat_keyboard_enter(seat, serial, txs, keys); + transmitter_seat_keyboard_enter(seat, serial, txs, wl_key); + } + } + free(wl_key); +} + +static void +keyboard_handle_leave(struct wthp_keyboard *wthp_keyboard, + uint32_t serial, + struct wthp_surface *surface) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_keyboard); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + transmitter_seat_keyboard_leave(seat, serial, txs); + } + } +} + +static void +keyboard_handle_key(struct wthp_keyboard *wthp_keyboard, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_keyboard); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_keyboard_key(seat, serial, time, key, state); +} + +static void +keyboard_handle_modifiers(struct wthp_keyboard *wthp_keyboard, + uint32_t serial, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) +{ + weston_log("keyboard_handle_modifiers\n"); +} + +static void +keyboard_handle_repeat_info(struct wthp_keyboard *wthp_keyboard, + int32_t rate, + int32_t delay) +{ + weston_log("keyboard_handle_repeat_info\n"); +} + +static const struct wthp_keyboard_listener keyboard_listener = { + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, + keyboard_handle_repeat_info +}; + +static void +touch_handle_down (struct wthp_touch * wthp_touch, + uint32_t serial, + uint32_t time, + struct wthp_surface * surface, + int32_t id, + wth_fixed_t x, + wth_fixed_t y) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + transmitter_seat_touch_down(seat, serial, time, + txs, id, x, y); + } + } +} + +static void +touch_handle_up (struct wthp_touch * wthp_touch, + uint32_t serial, + uint32_t time, + int32_t id) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_touch_up(seat, serial, time, id); +} + +static void +touch_handle_motion (struct wthp_touch * wthp_touch, + uint32_t time, + int32_t id, + wth_fixed_t x, + wth_fixed_t y) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_touch_motion(seat, time, id, x, y); +} + + +static void +touch_handle_frame (struct wthp_touch * wthp_touch) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_touch_frame(seat); +} + +static void +touch_handle_cancel (struct wthp_touch * wthp_touch) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_touch_cancel(seat); +} + + +static const struct wthp_touch_listener touch_listener = { + touch_handle_down, + touch_handle_up, + touch_handle_motion, + touch_handle_frame, + touch_handle_cancel +}; + +void +seat_capabilities(struct wthp_seat *wthp_seat, + enum wthp_seat_capability caps) +{ + struct waltham_display *dpy = wth_object_get_user_data((struct wth_object *)wthp_seat); + + weston_log("seat_capabilities\n"); + + if ((caps & WTHP_SEAT_CAPABILITY_POINTER) && !dpy->pointer) + { + weston_log("WTHP_SEAT_CAPABILITY_POINTER\n"); + dpy->pointer = wthp_seat_get_pointer(dpy->seat); + wthp_pointer_set_listener(dpy->pointer, &pointer_listener, dpy); + } + if ((caps & WTHP_SEAT_CAPABILITY_KEYBOARD) && !dpy->keyboard) + { + weston_log("WTHP_SEAT_CAPABILITY_KEYBOARD\n"); + dpy->keyboard = wthp_seat_get_keyboard(dpy->seat); + wthp_keyboard_set_listener(dpy->keyboard, &keyboard_listener, dpy); + } + if ((caps & WTHP_SEAT_CAPABILITY_TOUCH) && !dpy->touch) + { + weston_log("WTHP_SEAT_CAPABILITY_TOUCH\n"); + dpy->touch = wthp_seat_get_touch(dpy->seat); + wthp_touch_set_listener(dpy->touch, &touch_listener, dpy); + } +} + +int +transmitter_remote_create_seat(struct weston_transmitter_remote *remote) +{ + struct weston_transmitter_seat *seat = NULL; + char *name = NULL; + struct weston_seat *weston_seat = NULL; + + seat = zalloc(sizeof *seat); + if (!seat) + goto fail; + + wl_list_init(&seat->get_pointer_listener.link); + wl_list_init(&seat->pointer_focus_destroy_listener.link); + + /* XXX: get the name from remote */ + name = make_seat_name(remote, "default"); + if (!name) + goto fail; + + + if (wl_list_empty(&remote->transmitter->compositor->seat_list)) { + weston_seat = zalloc(sizeof *weston_seat); + if (!weston_seat) + goto fail; + + weston_seat_init(weston_seat, remote->transmitter->compositor, name); + seat->base = weston_seat; + weston_log("Transmitter created seat=%p \n", &seat->base); + } else { + wl_list_for_each(weston_seat, &remote->transmitter->compositor->seat_list, link) { + weston_log("Transmitter weston_seat %p\n", weston_seat); + seat->base = weston_seat; + } + } + + free(name); +#if DEBUG + weston_seat_init(&seat->base, remote->transmitter->compositor, name); + free(name); + + /* Hide the weston_seat from the rest of Weston, there are too many + * things making assumptions: + * - backends assume they control all seats + * - shells assume they control all input foci + * We do not want either to mess with our seat. + */ + wl_list_remove(&seat->base.link); + wl_list_init(&seat->base.link); + + /* The weston_compositor::seat_created_signal has already been + * emitted. Shells use it to subscribe to focus changes, but we should + * never handle focus with weston core... except maybe with keyboard. + * text-backend.c will also act on the new seat. + * It is possible weston_seat_init() needs to be split to fix this + * properly. + */ + + weston_log("Transmitter created seat=%p '%s'\n", + &seat->base, seat->base.seat_name); +#endif + + /* XXX: mirror remote capabilities */ + transmitter_seat_create_pointer(seat); + transmitter_seat_create_keyboard(seat); + transmitter_seat_create_touch(seat); + + wl_list_insert(&remote->seat_list, &seat->link); + + return 0; + +fail: + free(seat); + free(name); + + return -1; +} + +static void +fake_pointer_get_position(struct weston_transmitter_seat *seat, double step, + wl_fixed_t *x, wl_fixed_t *y) +{ + double s, c; + + seat->pointer_phase += step; + while (seat->pointer_phase > 2.0 * M_PI) + seat->pointer_phase -= 2.0 * M_PI; + + sincos(seat->pointer_phase, &s, &c); + *x = wl_fixed_from_double(100.0 + 50.0 * c); + *y = wl_fixed_from_double(100.0 + 50.0 * s); +} + +static int +fake_pointer_timer_handler(void *data) +{ + struct weston_transmitter_seat *seat = data; + wl_fixed_t x, y; + uint32_t time; + struct timespec timespec; + + weston_compositor_get_time(×pec); + time=timespec.tv_sec * 1000 + timespec.tv_nsec / 1000000; + + fake_pointer_get_position(seat, 18.0 / 180.0 * M_PI, &x, &y); + transmitter_seat_pointer_motion(seat, time, x, y); + transmitter_seat_pointer_frame(seat); + + wl_event_source_timer_update(seat->pointer_timer, 100); + + return 0; +} + +int +transmitter_seat_fake_pointer_input(struct weston_transmitter_seat *seat, + struct weston_transmitter_surface *txs) +{ + struct wl_event_loop *loop; + wl_fixed_t x, y; + uint32_t serial = 5; + + /* remove focus from earlier surface */ + transmitter_seat_pointer_leave(seat, serial++, seat->pointer_focus); + transmitter_seat_pointer_frame(seat); + + /* set pointer focus to surface */ + fake_pointer_get_position(seat, 0.0, &x, &y); + transmitter_seat_pointer_enter(seat, serial++, txs, x, y); + transmitter_seat_pointer_frame(seat); + + if (!seat->pointer_timer) { + /* schedule timer for motion */ + loop = wl_display_get_event_loop(seat->base->compositor->wl_display); + seat->pointer_timer = wl_event_loop_add_timer(loop, + fake_pointer_timer_handler, seat); + wl_event_source_timer_update(seat->pointer_timer, 100); + } + + /* XXX: if the now focused surface disappears, we should call + * transmitter_seat_pointer_leave() as part of the mockup. Otherwise + * you get a "Transmitter Warning: no pointer->focus_client?". + */ + + return 0; +} diff --git a/transmitter-plugin/meson.build b/transmitter-plugin/meson.build new file mode 100644 index 0000000..7efd1ce --- /dev/null +++ b/transmitter-plugin/meson.build @@ -0,0 +1,24 @@ + +libpixman_dep = dependency('pixman-1') + +deps_transmiter_plugin = [ + libwayland_dep, + libpixman_dep, + libweston_dep, + libwaltham_dep, + weston_dep, + cc.find_library('m') +] + +plugin_transmitter = shared_library( + 'waltham-transmitter', + 'input.c', + 'output.c', + 'plugin.c', + include_directories: common_inc, + dependencies: deps_transmiter_plugin, + name_prefix: '', + install: true, + install_dir: plugin_install_dir, +) +env_modmap += 'transmitter-plugin.so=@0@;'.format(plugin_transmitter.full_path()) diff --git a/transmitter-plugin/output.c b/transmitter-plugin/output.c new file mode 100644 index 0000000..8aff75d --- /dev/null +++ b/transmitter-plugin/output.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 Advanced Driver Information Technology Joint Venture GmbH + * Copyright © 2020 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include <libweston/libweston.h> + +#include "plugin.h" +#include "transmitter_api.h" + +void +transmitter_output_destroy(struct weston_transmitter_output *output) +{ + wl_list_remove(&output->link); + free(output); +} + +int +transmitter_remote_create_output_with_name(struct weston_transmitter_remote *remote, char *name) +{ + struct weston_transmitter_output *output; + + output = zalloc(sizeof(*output)); + if (!output) + return -1; + + output->remote = remote; + output->name = name; + wl_list_insert(&remote->output_list, &output->link); + + return 0; +} diff --git a/transmitter-plugin/plugin.c b/transmitter-plugin/plugin.c new file mode 100644 index 0000000..2fb60b6 --- /dev/null +++ b/transmitter-plugin/plugin.c @@ -0,0 +1,1024 @@ +/* + * Copyright (C) 2017 Advanced Driver Information Technology Joint Venture GmbH + * Copyright © 2020 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <linux/input.h> + +#include <libweston/libweston.h> +#include <libweston/plugin-registry.h> + +#include <weston.h> +#include <ivi-layout-export.h> + +#include "plugin.h" +#include "transmitter_api.h" + +/* waltham */ +#include <errno.h> +#include <sys/epoll.h> +#include <sys/timerfd.h> +#include <waltham-object.h> +#include <waltham-client.h> +#include <waltham-connection.h> + +#define MAX_EPOLL_WATCHES 2 +#define ESTABLISH_CONNECTION_PERIOD 2000 +#define RETRY_CONNECTION_PERIOD 5000 + +static const struct wthp_seat_listener seat_listener = { + seat_capabilities, + NULL +}; + +/* XXX: all functions and variables with a name, and things marked with a + * comment, containing the word "fake" are mockups that need to be + * removed from the final implementation. + */ + +/** Send configure event through ivi-shell. + * + * \param txs The Transmitter surface. + * \param width Suggestion for surface width. + * \param height Suggestion for surface height. + * + * When the networking code receives a ivi_surface.configure event, it calls + * this function to relay it to the application. + * + * \c txs cannot be a zombie, because transmitter_surface_zombify() must + * tear down the network link, so a zombie cannot receive events. + */ +void +transmitter_surface_ivi_resize(struct weston_transmitter_surface *txs, + int32_t width, int32_t height) +{ + assert(txs->resize_handler); + if (!txs->resize_handler) + return; + + assert(txs->surface); + if (!txs->surface) + return; + + txs->resize_handler(txs->resize_handler_data, width, height); +} + +static void +transmitter_surface_configure(struct weston_transmitter_surface *txs, + int32_t dx, int32_t dy) +{ + assert(txs->surface); + if (!txs->surface) + return; + + txs->attach_dx += dx; + txs->attach_dy += dy; +} + +static void +buffer_send_complete(struct wthp_buffer *b, uint32_t serial) +{ + if (b) + wthp_buffer_destroy(b); +} + +static const struct wthp_buffer_listener buffer_listener = { + buffer_send_complete +}; + +/* + * FIXME: note, at this point, no one is using this. + */ +static void +transmitter_surface_gather_state(struct weston_transmitter_surface *txs) +{ + struct weston_transmitter_remote *remote = txs->remote; + struct waltham_display *dpy = remote->display; + + if (!dpy->running) { + weston_log("transmitter_surface_gather_state() -- dpy not running\n"); + if (remote->status != WESTON_TRANSMITTER_CONNECTION_DISCONNECTED) { + weston_log("transmitter_surface_gather_state() -- disconnected\n"); + + remote->status = + WESTON_TRANSMITTER_CONNECTION_DISCONNECTED; + + wth_connection_destroy(remote->display->connection); + wl_event_source_remove(remote->source); + wl_event_source_timer_update(remote->retry_timer, 1); + } + } else { + /* TODO: transmit surface state to remote */ + /* The buffer must be transmitted to remote side */ + + /* waltham */ + struct weston_surface *surf = txs->surface; + struct weston_compositor *comp = surf->compositor; + int32_t stride, data_sz, width, height; + void *data; + + width = 1; + height = 1; + stride = width * (PIXMAN_FORMAT_BPP(comp->read_format) / 8); + + data = zalloc(stride * height); + data_sz = stride * height; + + /* fake sending buffer */ + txs->wthp_buf = wthp_blob_factory_create_buffer(remote->display->blob_factory, + data_sz, data, surf->width, + surf->height, stride, + PIXMAN_FORMAT_BPP(comp->read_format)); + + wthp_buffer_set_listener(txs->wthp_buf, &buffer_listener, txs); + + wthp_surface_attach(txs->wthp_surf, txs->wthp_buf, + txs->attach_dx, txs->attach_dy); + wthp_surface_damage(txs->wthp_surf, txs->attach_dx, + txs->attach_dy, surf->width, surf->height); + wthp_surface_commit(txs->wthp_surf); + + wth_connection_flush(remote->display->connection); + free(data); + data = NULL; + txs->attach_dx = 0; + txs->attach_dy = 0; + } +} + +/** Mark the weston_transmitter_surface dead. + * + * Stop all remoting actions on this surface. + * + * Still keeps the pointer stored by a shell valid, so it can be freed later. + */ +static void +transmitter_surface_zombify(struct weston_transmitter_surface *txs) +{ + struct weston_transmitter_remote *remote; + /* may be called multiple times */ + if (!txs->surface) + return; + + wl_signal_emit(&txs->destroy_signal, txs); + + wl_list_remove(&txs->surface_destroy_listener.link); + txs->surface = NULL; + + wl_list_remove(&txs->sync_output_destroy_listener.link); + + remote = txs->remote; + + if (!remote->display->compositor) + weston_log("remote->compositor is NULL\n"); + + if (txs->wthp_surf) + wthp_surface_destroy(txs->wthp_surf); + + if (txs->wthp_ivi_surface) + wthp_ivi_surface_destroy(txs->wthp_ivi_surface); + + /* In case called from destroy_transmitter() */ + txs->remote = NULL; +} + +static void +transmitter_surface_destroy(struct weston_transmitter_surface *txs) +{ + transmitter_surface_zombify(txs); + + wl_list_remove(&txs->link); + free(txs); +} + +/** weston_surface destroy signal handler */ +static void +transmitter_surface_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_transmitter_surface *txs = + wl_container_of(listener, txs, surface_destroy_listener); + + assert(data == txs->surface); + + transmitter_surface_zombify(txs); +} + +void +sync_output_destroy_handler(struct wl_listener *listener, void *data) +{ + struct weston_transmitter_surface *txs; + + txs = wl_container_of(listener, txs, sync_output_destroy_listener); + + wl_list_remove(&txs->sync_output_destroy_listener.link); + wl_list_init(&txs->sync_output_destroy_listener.link); + + //weston_surface_force_output(txs->surface, NULL); + weston_surface_unmap(txs->surface); +} + +static void +transmitter_surface_set_ivi_id(struct weston_transmitter_surface *txs, + const char *app_id) +{ + struct weston_transmitter_remote *remote = txs->remote; + struct waltham_display *dpy = remote->display; + + assert(txs->surface); + + if (!dpy) + return; + + if (!dpy->compositor) + return; + + + if (!dpy->seat) + return; + + if (!dpy->application_id) + return; + + txs->wthp_ivi_surface = + wthp_ivi_app_id_surface_create(dpy->application_id, app_id, txs->wthp_surf); + + wth_connection_flush(remote->display->connection); + if (!txs->wthp_ivi_surface) { + weston_log("Failed to create txs->ivi_surf\n"); + return; + } + + weston_log("Created wthp_ivi_surface %p\n", txs->wthp_ivi_surface); +} + +static struct weston_transmitter_surface * +transmitter_surface_push_to_remote(struct weston_surface *ws, const char *app_id, + struct weston_transmitter_remote *remote, + struct wl_listener *stream_status) +{ + struct weston_transmitter *txr = remote->transmitter; + struct weston_transmitter_surface *txs; + bool found = false; + + if (remote->status != WESTON_TRANSMITTER_CONNECTION_READY) { + return NULL; + } + + wl_list_for_each(txs, &remote->surface_list, link) { + if (txs->surface == ws) { + found = true; + break; + } + } + + if (!found) { + txs = NULL; + txs = zalloc(sizeof(*txs)); + + if (!txs) { + weston_log("No memory to create weston_transmitter_surface\n"); + return NULL; + } + + txs->remote = remote; + wl_signal_init(&txs->destroy_signal); + wl_list_insert(&remote->surface_list, &txs->link); + + txs->status = WESTON_TRANSMITTER_STREAM_INITIALIZING; + wl_signal_init(&txs->stream_status_signal); + if (stream_status) + wl_signal_add(&txs->stream_status_signal, stream_status); + + txs->surface = ws; + txs->surface_destroy_listener.notify = transmitter_surface_destroyed; + wl_signal_add(&ws->destroy_signal, &txs->surface_destroy_listener); + + wl_list_init(&txs->sync_output_destroy_listener.link); + + wl_list_init(&txs->frame_callback_list); + wl_list_init(&txs->feedback_list); + + txs->lyt = weston_plugin_api_get(txr->compositor, + IVI_LAYOUT_API_NAME, sizeof(txs->lyt)); + } + + /* TODO: create the content stream connection... */ + if (!remote->display->compositor) + weston_log("remote->compositor is NULL\n"); + + if (!txs->wthp_surf) { + txs->wthp_surf = wthp_compositor_create_surface(remote->display->compositor); + wth_connection_flush(remote->display->connection); + + transmitter_surface_set_ivi_id(txs, app_id); + } + + return txs; +} + +static enum weston_transmitter_stream_status +transmitter_surface_get_stream_status(struct weston_transmitter_surface *txs) +{ + return txs->status; +} + +/* waltham */ + +/* The server advertises a global interface. We can store the ad for later + * and/or bind to it immediately if we want to. We also need to keep track of + * the globals we bind to, so that global_remove can be handled properly (not + * implemented). + */ +static void +registry_handle_global(struct wthp_registry *registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + struct waltham_display *dpy = wth_object_get_user_data((struct wth_object *)registry); + + if (strcmp(interface, "wthp_compositor") == 0) { + assert(!dpy->compositor); + dpy->compositor = (struct wthp_compositor *) + wthp_registry_bind(registry, name, interface, 1); + /* has no events to handle */ + } else if (strcmp(interface, "wthp_blob_factory") == 0) { + assert(!dpy->blob_factory); + dpy->blob_factory = (struct wthp_blob_factory *) + wthp_registry_bind(registry, name, interface, 1); + /* has no events to handle */ + } else if (strcmp(interface, "wthp_seat") == 0) { + assert(!dpy->seat); + dpy->seat = (struct wthp_seat *) + wthp_registry_bind(registry, name, interface, 1); + wthp_seat_set_listener(dpy->seat, &seat_listener, dpy); + } else if (strcmp(interface, "wthp_ivi_application") == 0) { + assert(!dpy->application); + dpy->application = (struct wthp_ivi_application *) + wthp_registry_bind(registry, name, interface, 1); + } else if (strcmp(interface, "wthp_ivi_app_id") == 0) { + assert(!dpy->application_id); + dpy->application_id = (struct wthp_ivi_app_id *) + wthp_registry_bind(registry, name, interface, 1); + } +} + +/* notify connection ready */ +static void +conn_ready_notify(struct wl_listener *l, void *data) +{ + struct weston_transmitter_remote *remote = + wl_container_of(l, remote, establish_listener); + + weston_log("conn_ready_notify()\n"); + + /* Outputs and seats are dynamic, do not guarantee they are all + * present when signalling connection status. + */ + transmitter_remote_create_output_with_name(remote, strdup(remote->model)); + transmitter_remote_create_seat(remote); +} + +/* waltham */ +/* The server removed a global. + * We should destroy everything we created through that global, + * and destroy the objects we created by binding to it. + * The identification happens by global's name, so we need to keep + * track what names we bound. + * (not implemented) + */ +static void +registry_handle_global_remove(struct wthp_registry *wthp_registry, + uint32_t name) +{ + if (wthp_registry) + wthp_registry_free(wthp_registry); +} + +static const struct wthp_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static void +connection_handle_data(struct watch *w, uint32_t events) +{ + struct waltham_display *dpy = wl_container_of(w, dpy, conn_watch); + struct weston_transmitter_remote *remote = dpy->remote; + int ret; + + + if (!dpy->running) { + weston_log("This server is not running yet. %s:%s\n", remote->addr, remote->port); + return; + } + + if (events & EPOLLERR) { + weston_log("Connection errored out.\n"); + dpy->running = false; + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + return; + } + + if (events & EPOLLOUT) { + /* Flush out again. If the flush completes, stop + * polling for writable as everything has been written. + */ + ret = wth_connection_flush(dpy->connection); + } + + if (events & EPOLLIN) { + /* Do not ignore EPROTO */ + ret = wth_connection_read(dpy->connection); + + if (ret < 0) { + weston_log("Connection read error %s:%s\n", remote->addr, remote->port); + perror("Connection read error\n"); + dpy->running = false; + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + perror("EPOLL_CTL_DEL\n"); + return; + } + } + + if (events & EPOLLHUP) { + weston_log("Connection hung up.\n"); + dpy->running = false; + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + + return; + } +} + +static int +waltham_mainloop(int fd, uint32_t mask, void *data) +{ + struct weston_transmitter_remote *remote = data; + struct watch *w; + int ret; + int running_display; + running_display = 0; + + struct waltham_display *dpy = remote->display; + w = &dpy->conn_watch; + if (!dpy) + return -1; + + if (!dpy->connection) + dpy->running = false; + + if (!dpy->running) + return -1; + + running_display++; + /* Dispatch queued events. */ + ret = wth_connection_dispatch(dpy->connection); + if (ret < 0) { + dpy->running = false; + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + } + + if (!dpy->running) + return -1; + + /* Run any application idle tasks at this point. */ + /* (nothing to run so far) */ + + /* Flush out buffered requests. If the Waltham socket is + * full, poll it for writable too, and continue flushing then. + */ + ret = wth_connection_flush(dpy->connection); + + if (0 < running_display) { + /* Waltham events only read in the callback, not dispatched, + * if the Waltham socket signalled readable. If it signalled + * writable, flush more. See connection_handle_data(). + */ + w->cb(w, mask); + } + + return 0; +} + +static int +waltham_client_init(struct waltham_display *dpy) +{ + if (!dpy) + return -1; + /* + * get server_address from controller (adrress is set to weston.ini) + */ + dpy->connection = wth_connect_to_server(dpy->remote->addr, dpy->remote->port); + if(!dpy->connection) { + return -2; + } + else { + dpy->remote->status = WESTON_TRANSMITTER_CONNECTION_READY; + wl_signal_emit(&dpy->remote->connection_status_signal, dpy->remote); + } + + dpy->conn_watch.display = dpy; + dpy->conn_watch.cb = connection_handle_data; + dpy->conn_watch.fd = wth_connection_get_fd(dpy->connection); + dpy->remote->source = wl_event_loop_add_fd(dpy->remote->transmitter->loop, + dpy->conn_watch.fd, + WL_EVENT_READABLE, + waltham_mainloop, dpy->remote); + + dpy->display = wth_connection_get_display(dpy->connection); + /* wth_display_set_listener() is already done by waltham, as + * all the events are just control messaging. + */ + + /* Create a registry so that we will get advertisements of the + * interfaces implemented by the server. + */ + dpy->registry = wth_display_get_registry(dpy->display); + wthp_registry_set_listener(dpy->registry, ®istry_listener, dpy); + + /* Roundtrip ensures all globals' ads have been received. */ + if (wth_connection_roundtrip(dpy->connection) < 0) { + weston_log("Roundtrip failed.\n"); + return -1; + } + + if (!dpy->compositor) { + weston_log("Did not find wthp_compositor, quitting.\n"); + return -1; + } + + dpy->running = true; + weston_log("waltham_client_init()\n"); + + return 0; +} + +static int +establish_timer_handler(void *data) +{ + struct weston_transmitter_remote *remote = data; + int ret; + + ret = waltham_client_init(remote->display); + if (ret == -2) { + wl_event_source_timer_update(remote->establish_timer, + ESTABLISH_CONNECTION_PERIOD); + return 0; + } + + weston_log("establish_timer_handler()\n"); + + remote->status = WESTON_TRANSMITTER_CONNECTION_READY; + wl_signal_emit(&remote->connection_status_signal, remote); + return 0; +} + +static void +init_globals(struct waltham_display *dpy) +{ + dpy->compositor = NULL; + dpy->blob_factory = NULL; + dpy->seat = NULL; + dpy->application = NULL; + dpy->pointer = NULL; + dpy->keyboard = NULL; + dpy->touch = NULL; +} + +static void +disconnect_surface(struct weston_transmitter_remote *remote) +{ + struct weston_transmitter_surface *txs; + + wl_list_for_each(txs, &remote->surface_list, link) { + free(txs->wthp_ivi_surface); + txs->wthp_ivi_surface = NULL; + free(txs->wthp_surf); + txs->wthp_surf = NULL; + } +} + +static int +retry_timer_handler(void *data) +{ + struct weston_transmitter_remote *remote = data; + struct waltham_display *dpy = remote->display; + + weston_log("retry_timer_handler()\n"); + + if (!dpy->running) { + registry_handle_global_remove(dpy->registry, 1); + init_globals(dpy); + disconnect_surface(remote); + wl_event_source_timer_update(remote->establish_timer, + ESTABLISH_CONNECTION_PERIOD); + return 0; + } else { + wl_event_source_timer_update(remote->retry_timer, + RETRY_CONNECTION_PERIOD); + } + + return 0; +} + +static struct weston_transmitter_remote * +transmitter_connect_to_remote(struct weston_transmitter *txr) +{ + struct weston_transmitter_remote *remote; + struct wl_event_loop *loop_est, *loop_retry; + + wl_list_for_each_reverse(remote, &txr->remote_list, link) { + /* XXX: actually start connecting */ + /* waltham */ + remote->display = zalloc(sizeof *remote->display); + if (!remote->display) { + weston_log("Failed to allocate remote display\n"); + return NULL; + } + + remote->display->remote = remote; + + weston_log("transmitter_connect_to_remote() for remote %p\n", remote); + + /* set connection establish timer */ + loop_est = wl_display_get_event_loop(txr->compositor->wl_display); + remote->establish_timer = + wl_event_loop_add_timer(loop_est, establish_timer_handler, remote); + wl_event_source_timer_update(remote->establish_timer, 1); + + /* set connection retry timer */ + loop_retry = wl_display_get_event_loop(txr->compositor->wl_display); + remote->retry_timer = + wl_event_loop_add_timer(loop_retry, retry_timer_handler, remote); + wl_signal_emit(&remote->conn_establish_signal, NULL); + } + + return remote; +} + +static enum weston_transmitter_connection_status +transmitter_remote_get_status(struct weston_transmitter_remote *remote) +{ + return remote->status; +} + +static void +transmitter_remote_destroy(struct weston_transmitter_remote *remote) +{ + struct weston_transmitter_surface *txs; + struct weston_transmitter_output *output, *otmp; + struct weston_transmitter_seat *seat, *stmp; + + /* Do not emit connection_status_signal. */ + + /* + * Must not touch remote->transmitter as it may be stale: the + * destruction order between the shell and Transmitter is undefined. + */ + + if (!wl_list_empty(&remote->surface_list)) + weston_log("Transmitter warning: surfaces remain in %s.\n", __func__); + + wl_list_for_each(txs, &remote->surface_list, link) + txs->remote = NULL; + + wl_list_remove(&remote->surface_list); + + wl_list_for_each_safe(seat, stmp, &remote->seat_list, link) + transmitter_seat_destroy(seat); + + wl_list_for_each_safe(output, otmp, &remote->output_list, link) + transmitter_output_destroy(output); + + free(remote->addr); + wl_list_remove(&remote->link); + + wl_event_source_remove(remote->source); + + free(remote); +} + +/** Transmitter is destroyed on compositor shutdown. */ +static void +transmitter_compositor_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_transmitter_remote *remote; + struct weston_transmitter_surface *txs; + struct weston_transmitter *txr = + wl_container_of(listener, txr, compositor_destroy_listener); + + assert(data == txr->compositor); + + /* may be called before or after shell cleans up */ + wl_list_for_each(remote, &txr->remote_list, link) { + wl_list_for_each(txs, &remote->surface_list, link) { + transmitter_surface_zombify(txs); + } + } + + /* + * Remove the head in case the list is not empty, to avoid + * transmitter_remote_destroy() accessing freed memory if the shell + * cleans up after Transmitter. + */ + wl_list_remove(&txr->remote_list); + + free(txr); +} + +static struct weston_transmitter * +transmitter_get(struct weston_compositor *compositor) +{ + struct wl_listener *listener; + struct weston_transmitter *txr; + + listener = wl_signal_get(&compositor->destroy_signal, + transmitter_compositor_destroyed); + if (!listener) + return NULL; + + txr = wl_container_of(listener, txr, compositor_destroy_listener); + assert(compositor == txr->compositor); + + return txr; +} + +static void +transmitter_register_connection_status(struct weston_transmitter *txr, + struct wl_listener *connected_listener) +{ + wl_signal_add(&txr->connected_signal, connected_listener); +} + +static struct weston_surface * +transmitter_get_weston_surface(struct weston_transmitter_surface *txs) +{ + return txs->surface; +} + +static struct weston_transmitter_remote * +transmitter_get_transmitter_remote(const char *output_transmitter_name, struct weston_transmitter *transmitter) +{ + + struct weston_transmitter_remote *trans_remote; + + if (!output_transmitter_name) + return NULL; + + wl_list_for_each(trans_remote, &transmitter->remote_list, link) { + struct weston_transmitter_output *trans_output; + wl_list_for_each(trans_output, &trans_remote->output_list, link) { + const char *name = trans_output->base.name; + + if (name && !strcmp(output_transmitter_name, name)) + return trans_remote; + + name = trans_output->name; + if (name && !strcmp(output_transmitter_name, name)) + return trans_remote; + } + } + + return NULL; +} + +static const struct weston_transmitter_api transmitter_api_impl = { + transmitter_get, + transmitter_connect_to_remote, + transmitter_remote_get_status, + transmitter_remote_destroy, + transmitter_surface_push_to_remote, + transmitter_surface_get_stream_status, + transmitter_surface_destroy, + transmitter_surface_configure, + transmitter_surface_gather_state, + transmitter_register_connection_status, + transmitter_get_weston_surface, + transmitter_get_transmitter_remote, +}; + +static void +transmitter_surface_set_resize_callback( + struct weston_transmitter_surface *txs, + weston_transmitter_ivi_resize_handler_t cb, + void *data) +{ + txs->resize_handler = cb; + txs->resize_handler_data = data; +} + +static void +_transmitter_surface_set_ivi_id(struct weston_transmitter_surface *txs, uint32_t ivi_id) +{ + +} + +static const struct weston_transmitter_ivi_api transmitter_ivi_api_impl = { + _transmitter_surface_set_ivi_id, + transmitter_surface_set_resize_callback, +}; + +static int +transmitter_create_remote(struct weston_transmitter *txr, + const char *model, + const char *addr, + const char *port, + const char *width, + const char *height) +{ + struct weston_transmitter_remote *remote; + + remote = zalloc(sizeof (*remote)); + if (!remote) + return -1; + + remote->transmitter = txr; + wl_list_insert(&txr->remote_list, &remote->link); + + remote->model = strdup(model); + remote->addr = strdup(addr); + remote->port = strdup(port); + if (remote->width) + remote->width = atoi(width); + if (remote->height) + remote->height = atoi(height); + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + + weston_log("Creating transmitter remote output model %s, addr %s, " + "port %s, width %d, height %d, status %d\n", + remote->model, remote->addr, remote->port, remote->width, + remote->height, remote->status); + + wl_signal_init(&remote->connection_status_signal); + + wl_list_init(&remote->output_list); + wl_list_init(&remote->surface_list); + wl_list_init(&remote->seat_list); + + wl_signal_init(&remote->conn_establish_signal); + remote->establish_listener.notify = conn_ready_notify; + wl_signal_add(&remote->conn_establish_signal, &remote->establish_listener); + + return 0; +} + +struct wet_compositor { + struct weston_config *config; + struct wet_output_config *parsed_options; + struct wl_listener pending_output_listener; + bool drm_use_current_mode; +}; + +static int +load_config(struct weston_config **config, bool no_config, + const char *config_file) +{ + const char *file = "agl-compositor.ini"; + const char *full_path; + + if (config_file) + file = config_file; + + if (!no_config) + *config = weston_config_parse(file); + + if (*config) { + full_path = weston_config_get_full_path(*config); + + weston_log("Using config file '%s'.\n", full_path); + setenv(WESTON_CONFIG_FILE_ENV_VAR, full_path, 1); + + return 0; + } + + if (config_file && !no_config) { + weston_log("fatal: error opening or reading config file '%s'.\n", + config_file); + + return -1; + } + + weston_log("Starting with no config file.\n"); + setenv(WESTON_CONFIG_FILE_ENV_VAR, "", 1); + + return 0; +} + + +static void +transmitter_get_server_config(struct weston_transmitter *txr) +{ + struct weston_config *config = NULL; + struct weston_config_section *section; + const char *name = NULL; + char *model = NULL; + char *addr = NULL; + char *port = NULL; + char *width = NULL; + char *height = NULL; + int ret; + + /* FIXME: this assume as hard-coded agl-compositor */ + if (load_config(&config, 0, "agl-compositor.ini") < 0) + return; + + section = weston_config_get_section(config, "remote", NULL, NULL); + + while (weston_config_next_section(config, §ion, &name)) { + if (0 == strcmp(name, "transmitter-output")) { + if (0 != weston_config_section_get_string(section, "name", + &model, 0)) + continue; + + if (0 != weston_config_section_get_string(section, "host", + &addr, 0)) + continue; + + if (0 != weston_config_section_get_string(section, "port", + &port, 0)) + continue; + + ret = transmitter_create_remote(txr, model, addr, + port, width, height); + if (ret < 0) { + weston_log("Fatal: Transmitter create_remote failed.\n"); + } + } + } +} + +WL_EXPORT int +wet_module_init(struct weston_compositor *compositor, int *argc, char *argv[]) +{ + struct weston_transmitter *txr; + int ret; + + txr = zalloc(sizeof *txr); + if (!txr){ + weston_log("Transmitter disabled\n"); + return -1; + } + wl_list_init(&txr->remote_list); + + txr->compositor = compositor; + txr->compositor_destroy_listener.notify = + transmitter_compositor_destroyed; + wl_signal_add(&compositor->destroy_signal, + &txr->compositor_destroy_listener); + + ret = weston_plugin_api_register(compositor, + WESTON_TRANSMITTER_API_NAME, + &transmitter_api_impl, + sizeof(transmitter_api_impl)); + if (ret < 0) { + weston_log("Fatal: Transmitter API registration failed.\n"); + goto fail; + } + + ret = weston_plugin_api_register(compositor, + WESTON_TRANSMITTER_IVI_API_NAME, + &transmitter_ivi_api_impl, + sizeof(transmitter_ivi_api_impl)); + if (ret < 0) { + weston_log("Fatal: Transmitter IVI API registration failed.\n"); + goto fail; + } + + weston_log("Transmitter initialized.\n"); + + txr->loop = wl_display_get_event_loop(compositor->wl_display); + transmitter_get_server_config(txr); + transmitter_connect_to_remote(txr); + + return 0; + +fail: + wl_list_remove(&txr->compositor_destroy_listener.link); + free(txr); + + return -1; +} diff --git a/transmitter-plugin/weston.ini.transmitter b/transmitter-plugin/weston.ini.transmitter new file mode 100644 index 0000000..0d5efe9 --- /dev/null +++ b/transmitter-plugin/weston.ini.transmitter @@ -0,0 +1,13 @@ +[transmitter-output] +name=transmitter_1 +mode=800x600@60 +host=192.168.100.9 +port=34400 +agl-shell-app-id=weston-simple-egl + +[transmitter-output] +name=transmitter_2 +mode=800x600@60 +host=192.168.100.9 +port=34401 +agl-shell-app-id=weston-simple-shm |