From 18aa25385bcfc4c686f4700de2b2dd9fa9b38ee9 Mon Sep 17 00:00:00 2001 From: Wataru Mizuno Date: Mon, 9 Jul 2018 18:14:05 +0900 Subject: transmitter: initial commit for waltham-transmitter The waltham-transmitter is the plugin of weston to enable client applications to share surface between multiple domains. Change-Id: I691410f5b6e74fa981ae53201b728810373185a2 Signed-off-by: Wataru Mizuno --- waltham-transmitter/CMakeLists.txt | 4 + waltham-transmitter/COPYING | 30 + .../transmitter-plugin/CMakeLists.txt | 62 + waltham-transmitter/transmitter-plugin/README | 97 ++ waltham-transmitter/transmitter-plugin/input.c | 1317 ++++++++++++++++++++ waltham-transmitter/transmitter-plugin/output.c | 352 ++++++ waltham-transmitter/transmitter-plugin/plugin.c | 940 ++++++++++++++ waltham-transmitter/transmitter-plugin/plugin.h | 330 +++++ .../transmitter-plugin/transmitter_api.h | 278 +++++ .../transmitter-plugin/weston.ini.transmitter | 21 + .../waltham-renderer/CMakeLists.txt | 59 + .../waltham-renderer/waltham-renderer.c | 270 ++++ .../waltham-renderer/waltham-renderer.h | 41 + 13 files changed, 3801 insertions(+) create mode 100644 waltham-transmitter/CMakeLists.txt create mode 100644 waltham-transmitter/COPYING create mode 100644 waltham-transmitter/transmitter-plugin/CMakeLists.txt create mode 100644 waltham-transmitter/transmitter-plugin/README create mode 100644 waltham-transmitter/transmitter-plugin/input.c create mode 100644 waltham-transmitter/transmitter-plugin/output.c create mode 100644 waltham-transmitter/transmitter-plugin/plugin.c create mode 100644 waltham-transmitter/transmitter-plugin/plugin.h create mode 100644 waltham-transmitter/transmitter-plugin/transmitter_api.h create mode 100644 waltham-transmitter/transmitter-plugin/weston.ini.transmitter create mode 100644 waltham-transmitter/waltham-renderer/CMakeLists.txt create mode 100644 waltham-transmitter/waltham-renderer/waltham-renderer.c create mode 100644 waltham-transmitter/waltham-renderer/waltham-renderer.h (limited to 'waltham-transmitter') diff --git a/waltham-transmitter/CMakeLists.txt b/waltham-transmitter/CMakeLists.txt new file mode 100644 index 0000000..b4d1243 --- /dev/null +++ b/waltham-transmitter/CMakeLists.txt @@ -0,0 +1,4 @@ +project (transmitter) + +add_subdirectory(transmitter-plugin) +add_subdirectory(waltham-renderer) diff --git a/waltham-transmitter/COPYING b/waltham-transmitter/COPYING new file mode 100644 index 0000000..d65f5d3 --- /dev/null +++ b/waltham-transmitter/COPYING @@ -0,0 +1,30 @@ +Copyright © 2008-2012 Kristian Høgsberg +Copyright © 2010-2012 Intel Corporation +Copyright © 2010-2011 Benjamin Franzke +Copyright © 2011-2012 Collabora, Ltd. +Copyright © 2010 Red Hat + +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. + +--- + +The above is the version of the MIT "Expat" License used by X.org: + + http://cgit.freedesktop.org/xorg/xserver/tree/COPYING diff --git a/waltham-transmitter/transmitter-plugin/CMakeLists.txt b/waltham-transmitter/transmitter-plugin/CMakeLists.txt new file mode 100644 index 0000000..a900d3b --- /dev/null +++ b/waltham-transmitter/transmitter-plugin/CMakeLists.txt @@ -0,0 +1,62 @@ +project (transmitter) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(WAYLAND_SERVER wayland-server>=1.13.0 REQUIRED) +pkg_check_modules(WESTON weston>=2.0.0 REQUIRED) +pkg_check_modules(PIXMAN pixman-1 REQUIRED) +pkg_check_modules(WALTHAM waltham REQUIRED) + +include_directories( + include + ${CMAKE_SOURCE_DIR}/waltham-transmitter/waltham-renderer + ${CMAKE_CURRENT_BINARY_DIR} + ${WAYLAND_CLIENT_INCLUDE_DIRS} + ${WAYLAND_SERVER_INCLUDE_DIRS} + ${WESTON_INCLUDE_DIRS} + ${PIXMAN_INCLUDE_DIRS} + ${WALTHAM_INCLUDE_DIRS} +) + +link_directories( + ${WAYLAND_SERVER_LIBRARY_DIRS} + ${WESTON_LIBRARY_DIRS} + ${PIXMAN_LIBRARY_DIRS} + ${WALTHAM_LIBRARY_DIRS} +) + +add_library(${PROJECT_NAME} MODULE + plugin.c + output.c + input.c + plugin.h + transmitter_api.h +) + +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") + +set(LIBS + m + ${WAYLAND_SERVER_LIBRARIES} + ${WESTON_LIBRARIES} + ${PIXMAN_LIBRARIES} + ${WALTHAM_LIBRARIES} +) + +SET(SRC_FILES + plugin.c + output.c + input.c + plugin.h + transmitter_api.h +) + + + +add_dependencies(${PROJECT_NAME} ${LIBS}) + +target_link_libraries(${PROJECT_NAME} ${LIBS}) + +install ( + TARGETS ${PROJECT_NAME} + LIBRARY DESTINATION lib64/weston +) diff --git a/waltham-transmitter/transmitter-plugin/README b/waltham-transmitter/transmitter-plugin/README new file mode 100644 index 0000000..345142d --- /dev/null +++ b/waltham-transmitter/transmitter-plugin/README @@ -0,0 +1,97 @@ +Transmitter plugin + +The current implementation of Transmitter is a stub which interfaces to +other Weston parts appropriately, but all networking is just a mockup. + +Sections in this file describe: +- How to build +- How to write weston.ini +- How to test + +How to build +============ +Configure Weston with --enable-surface-remoting to build the Transmitter +plugin. + +How to write weston.ini +======================= +To load transmitter plugin to weston, add 'transmitter.so' to the 'modules' +key under '[core]', and make sure the 'shell' is 'ivi-shell.so'. + +The destination of remoting is configured in weston.ini. +Add output name, server address, port number, output's width and height key +under '[remote-output]'. +You can speficy multiple [remote-output]. + +In details, see 'weston.ini.transmitter'. + +How to test +=========== +You can use server side test application in waltham-server directory. + +If you set 'WALTHAM_DEBUG=1' to your environment valuable, you can +see the log like this: + + [13:24:08.345] Loading module '/usr/lib64/weston/transmitter.so' + [13:24:08.345] Registered plugin API 'transmitter_v1' of size 88 + [13:24:08.345] Registered plugin API 'transmitter_ivi_v1' of size 16 + [13:24:08.345] Transmitter initialized. + [13:24:08.345] Loading module '/usr/lib64/libweston-2/waltham-renderer.so' + [13:24:08.352] gst-setting are :--> + [13:24:08.352] ip = 192.168.2.52 + [13:24:08.352] port = 34400 + [13:24:08.352] bitrate = 3000000 + [13:24:08.352] crop = 384 x 368 + [13:24:08.352] width = 1920 + [13:24:08.352] width = 1080 + [13:24:08.531] open media device: platform:fe960000.vsp (fe960000.vsp) + [13:24:08.532] input pad setup ('fe960000.vsp rpf.0 input':'/dev/video0') + [13:24:08.533] output pad setup (fe960000.vsp wpf.0 output:/dev/video5) + [13:24:08.533] vsp-device '/dev/media0' created + [13:24:08.533] gst_recorder_create (1920x1080) crop 384x368 at 0,0 + [13:24:08.533] gst_pipeline: starting: appsrc name=src ! omxh264enc target-bitrate=3000000 control-rate=2 no-copy=k + [13:24:08.582] goot 1 pools + [13:24:08.583] pool settings size 211968, min 5, max 5 + [13:24:08.583] gst_recorder_create done + [13:24:08.583] [gst recorder] transmitter-192.168.2.52:34400-1: recorder initialized + [13:24:08.583] Transmitter weston_seat 0x15424630 + [13:24:08.583] Transmitter created pointer=0x15625df0 for seat 0x15424630 + [13:24:08.583] Transmitter created keyboard=0x154247c0 for seat 0x15424630 + [13:24:08.583] Transmitter created touch=0x15625f10 for seat 0x15424630 + +The connection is established, you can see following debug messages: + + debug: wth_connection_insert_new_object: new object id: 1 + debug: wth_connection_insert_new_object: new object id: 2 + 2018-01-09T13:24:22Z 00001000030000000100000002000000 wth_display_get_registry + debug: wth_connection_insert_new_object: new object id: 3 + 2018-01-09T13:24:22Z 00001000020000000100000003000000 wth_display_sync + debug: Message received on conn 0x15572730: (9) 40 bytes + debug: wthp_registry_send_global(2, 1, [variable type const char *], 4) (opcode 9) called. + debug: wth_connection_insert_new_object: new object id: 4 + 2018-01-09T13:24:22Z 00002c000800000002000000010000000400000010000000777468705f636f6d706f7369746f720001000000 wthpd + debug: Message received on conn 0x15572730: (9) 48 bytes + debug: wthp_registry_send_global(2, 1, [variable type const char *], 1) (opcode 9) called. + debug: wth_connection_insert_new_object: new object id: 5 + 2018-01-09T13:24:22Z 000034000800000002000000010000000500000015000000777468705f6976695f6170706c69636174696f6e00010d + debug: Message received on conn 0x15572730: (9) 44 bytes + debug: wthp_registry_send_global(2, 1, [variable type const char *], 4) (opcode 9) called. + debug: wth_connection_insert_new_object: new object id: 6 + 2018-01-09T13:24:22Z 000030000800000002000000010000000600000012000000777468705f626c6f625f666163746f72790001000000 d + debug: Message received on conn 0x15572730: (9) 36 bytes + debug: wthp_registry_send_global(2, 1, [variable type const char *], 4) (opcode 9) called. + debug: wth_connection_insert_new_object: new object id: 7 + 2018-01-09T13:24:22Z 00002800080000000200000001000000070000000a000000777468705f736561740001000000 wthp_registry_bid + debug: Message received on conn 0x15572730: (11) 16 bytes + debug: wthp_callback_send_done(3, 0) (opcode 11) called. + +Start remoting : +- Start an IVI application. +- Put surface on transmitter output + +Weston log will indicate remoting has started: + +[13:18:24.572] HMI transmitting surface 0x1c3dad0, ivi-id 0x9ff6 +[13:18:24.572] Transmitter: update surface 0x1c3dad0 (0, 0), 0 cb +[13:18:24.572] transmitter_surface_set_ivi_id(0x1c3dad0, 0x9ff6) +[13:18:24.972] Transmitter: surface 0x1c3dad0 entered output transmitter-0.0.0.0:66-1 \ No newline at end of file diff --git a/waltham-transmitter/transmitter-plugin/input.c b/waltham-transmitter/transmitter-plugin/input.c new file mode 100644 index 0000000..e00546b --- /dev/null +++ b/waltham-transmitter/transmitter-plugin/input.c @@ -0,0 +1,1317 @@ +/* + * 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 +#include +#include + +/* for fake stuff */ +#include + +#include "compositor.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, + uint32_t 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, + uint32_t 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, + uint32_t 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, + uint32_t 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, + uint32_t time, + int touch_id, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void +touch_grab_up_handler(struct weston_touch_grab *grab, + uint32_t time, + int touch_id) +{ +} + +static void +touch_grab_motion_handler(struct weston_touch_grab *grab, + uint32_t 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, +}; + + +/* 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. + */ +static 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 wl_client *client; + 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); + 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; + + time = weston_compositor_get_time(); + + 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/waltham-transmitter/transmitter-plugin/output.c b/waltham-transmitter/transmitter-plugin/output.c new file mode 100644 index 0000000..c379ce5 --- /dev/null +++ b/waltham-transmitter/transmitter-plugin/output.c @@ -0,0 +1,352 @@ +/* + * 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 +#include +#include + +#include "compositor.h" +#include "compositor-drm.h" +#include "plugin-registry.h" + +#include "plugin.h" +#include "transmitter_api.h" +#include "waltham-renderer.h" + +/** @file + * + * This is an implementation of a remote output. + * + * A remote output must not be accepted as an argument to: + * - wl_shell_surface.set_fullscreen + * - wl_shell_surface.set_maximized + * - zwp_fullscreen_shell_v1.present_surface + * - zwp_fullscreen_shell_v1.present_surface_for_mode + * - zwp_input_panel_surface_v1.set_toplevel + * - xdg_surface.set_fullscreen + * + * If a remote output is an argument to the above or similar requests, + * it should have the same effect as NULL if possible. + * + * @todo Should we instead accept the argument and have it start remoting + * automatically? That would be shell-specific. + * + * In ivi-shell's case, only zwp_input_panel_surface_v1.set_toplevel is + * reachable from keyboard.c. That just blindly uses whatever the first + * output happens to be, so there is no need to check for now. + * + * @todo Add weston_output_set_remote() which sets weston_output::is_remote + * to true and inits weston_output::link. This should be made mutually + * exclusive with weston_compositor_add_output(). + */ + +static struct waltham_renderer_interface *waltham_renderer; + +static char * +make_model(struct weston_transmitter_remote *remote, int name) +{ + char *str; + + if (asprintf(&str, "transmitter-%s:%s-%d", remote->addr, remote->port, name) < 0) + return NULL; + + return str; +} + +static int +make_mode_list(struct wl_list *list, + const struct weston_transmitter_output_info *info) +{ + struct weston_mode *mode; + + mode = zalloc(sizeof *mode); + if (!mode) + return -1; + + *mode = info->mode; + wl_list_insert(list->prev, &mode->link); + + return 0; +} + +static struct weston_mode * +get_current_mode(struct wl_list *mode_list) +{ + struct weston_mode *mode; + + wl_list_for_each(mode, mode_list, link) + if (mode->flags & WL_OUTPUT_MODE_CURRENT) + return mode; + + assert(0); + return NULL; +} + +static void +free_mode_list(struct wl_list *mode_list) +{ + struct weston_mode *mode; + + while (!wl_list_empty(mode_list)) { + mode = wl_container_of(mode_list->next, mode, link); + + wl_list_remove(&mode->link); + free(mode); + } +} + +void +transmitter_output_destroy(struct weston_transmitter_output *output) +{ + wl_list_remove(&output->link); + + free_mode_list(&output->base.mode_list); + free(output->base.serial_number); + free(output->base.model); + free(output->base.make); + + weston_output_destroy(&output->base); + free(output); +} + +static void +transmitter_output_destroy_(struct weston_output *base) +{ + struct weston_transmitter_output *output = wl_container_of(base, output, base); + + transmitter_output_destroy(output); +} + + +static void +transmitter_start_repaint_loop(struct weston_output *base) +{ + struct timespec ts; + struct weston_transmitter_output *output = wl_container_of(base, output, base); + + weston_compositor_read_presentation_clock(output->base.compositor, &ts); + weston_output_finish_frame(&output->base, &ts, 0); +} + +static int +transmitter_output_repaint(struct weston_output *base, + pixman_region32_t *damage) +{ + struct weston_transmitter_output* output = wl_container_of(base, output, base); + struct weston_transmitter_remote* remote = output->remote; + struct weston_transmitter* txr = remote->transmitter; + struct weston_transmitter_api* transmitter_api = + weston_get_transmitter_api(txr->compositor); + struct weston_transmitter_surface* txs; + struct weston_compositor *compositor = base->compositor; + struct weston_view *view; + bool found_output = false; + struct timespec ts; + + struct weston_drm_output_api *api = + weston_plugin_api_get(txr->compositor, WESTON_DRM_OUTPUT_API_NAME, sizeof(api)); + + /* + * Pick up weston_view in transmitter_output and check weston_view's surface + * If the surface hasn't been conbined to weston_transmitter_surface, + * then call push_to_remote. + * If the surface has already been combined, call gather_state. + */ + if (wl_list_empty(&compositor->view_list)) + goto out; + + if (remote->status == WESTON_TRANSMITTER_CONNECTION_DISCONNECTED) + goto out; + + wl_list_for_each_reverse(view, &compositor->view_list, link) { + bool found_surface = false; + if (view->output == &output->base) { + found_output = true; + wl_list_for_each(txs, &remote->surface_list, link) { + if (txs->surface == view->surface) { + found_surface = true; + if (!txs->wthp_surf) + transmitter_api->surface_push_to_remote + (view->surface, remote, NULL); + + output->renderer->dmafd = + api->get_dma_fd_from_view(&output->base, view); + if(!output->renderer->dmafd) { + weston_log("Failed to get dmafd\n"); + goto out; + } + + /* + * Updating the width x height + * from surface to gst-recorder + */ + output->renderer->surface_width + = view->surface->width; + output->renderer->surface_height + = view->surface->height; + + output->renderer->repaint_output(output); + output->renderer->dmafd = NULL; + transmitter_api->surface_gather_state(txs); + weston_buffer_reference(&view->surface->buffer_ref, NULL); + break; + } + } + if (!found_surface) + transmitter_api->surface_push_to_remote(view->surface, + remote, NULL); + } + } + if (!found_output) + goto out; + + weston_compositor_read_presentation_clock(output->base.compositor, &ts); + weston_output_finish_frame(&output->base, &ts, 0); + + return 0; + +out: + transmitter_start_repaint_loop(base); + + return 0; +} + +static void +transmitter_assign_planes(struct weston_output *base) { + /* + * This function prevents compositor releasing buffer early. + */ + struct weston_transmitter_output* output = wl_container_of(base, output, base); + struct weston_transmitter_remote* remote = output->remote; + struct weston_transmitter_surface* txs; + struct weston_compositor *compositor = base->compositor; + struct weston_view *view; + + wl_list_for_each_reverse(view, &compositor->view_list, link) { + if (view->output == &output->base) { + wl_list_for_each(txs, &remote->surface_list, link) { + if (txs->surface == view->surface) + view->surface->keep_buffer = true; + + } + } + } +} + +static void +transmitter_output_enable(struct weston_output *base) +{ + struct weston_transmitter_output *output = wl_container_of(base, output, base); + + output->base.assign_planes = transmitter_assign_planes; + output->base.set_backlight = NULL; + output->base.set_dpms = NULL; + output->base.switch_mode = NULL; +} + +int +transmitter_remote_create_output(struct weston_transmitter_remote *remote, + const struct weston_transmitter_output_info *info) +{ + struct weston_transmitter_output *output; + struct weston_transmitter *txr = remote->transmitter; + struct weston_output *def_output; + + output = zalloc(sizeof *output); + if (!output) + return -1; + + output->parent.draw_initial_frame = true; + + output->base.subpixel = info->subpixel; + + output->base.name = make_model(remote, 1); + output->base.make = strdup(WESTON_TRANSMITTER_OUTPUT_MAKE); + output->base.model = make_model(remote, 1); + output->base.serial_number = strdup("0"); + /* x and y is fake value */ + wl_list_init(&output->base.mode_list); + if (make_mode_list(&output->base.mode_list, info) < 0) + goto fail; + + output->base.current_mode = get_current_mode(&output->base.mode_list); + output->base.height = output->base.current_mode->height; + output->base.width = output->base.current_mode->width; + /* WL_OUTPUT_MODE_CURRENT already set */ + weston_output_init(&output->base, remote->transmitter->compositor); + + /* + * renderer_output_create skipped: + * no renderer awareness is needed for this output + */ + + /* + * weston_compositor_add_output() skipped: + * Most other code uses weston_compositor::output_list when traversing + * all outputs, we do not want any of that. + * Also weston_compositor::output_created_signal must not trigger + * for this output, since we must not involve input device management + * or color management or any kind of local management. + */ + output->base.enable = transmitter_output_enable; + output->base.start_repaint_loop = transmitter_start_repaint_loop; + output->base.repaint = transmitter_output_repaint; + output->base.destroy = transmitter_output_destroy_; + output->base.assign_planes = NULL; + output->base.set_dpms = NULL; + output->base.switch_mode = NULL; + output->base.gamma_size = 0; + output->base.set_gamma = NULL; + + output->base.native_mode = output->base.current_mode; + output->base.native_scale = output->base.current_scale; + output->base.scale = 1; + output->base.transform = WL_OUTPUT_TRANSFORM_NORMAL; + + output->remote = remote; + wl_list_insert(&remote->output_list, &output->link); + + /* Loading a waltham renderer library */ + waltham_renderer = weston_load_module("waltham-renderer.so","waltham_renderer_interface"); + + if (waltham_renderer->display_create(output) < 0) { + weston_log("Failed to create waltham renderer display \n"); + return -1; + } + + weston_output_enable(&output->base); + + return 0; + +fail: + free_mode_list(&output->base.mode_list); + free(output->base.serial_number); + free(output->base.model); + free(output->base.make); + free(output->base.name); + free(output); + + return -1; +} diff --git a/waltham-transmitter/transmitter-plugin/plugin.c b/waltham-transmitter/transmitter-plugin/plugin.c new file mode 100644 index 0000000..f643bd8 --- /dev/null +++ b/waltham-transmitter/transmitter-plugin/plugin.c @@ -0,0 +1,940 @@ +/* + * 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 +#include +#include +#include + +#include "compositor.h" + +#include "weston.h" +#include "plugin.h" +#include "transmitter_api.h" +#include "plugin-registry.h" +#include "ivi-layout-export.h" + +/* waltham */ +#include +#include +#include +#include +#include +#include + +#define MAX_EPOLL_WATCHES 2 +#define ESTABLISH_CONNECTION_PERIOD 2000 +#define RETRY_CONNECTION_PERIOD 5000 + +/* 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 +}; + +static void +transmitter_surface_gather_state(struct weston_transmitter_surface *txs) +{ + struct weston_transmitter_remote *remote = txs->remote; + struct waltham_display *dpy = remote->display; + int ret; + + if(!dpy->running) { + if(remote->status != WESTON_TRANSMITTER_CONNECTION_DISCONNECTED) { + 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 = malloc(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); + 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); +} + +static 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); +} + +static void +transmitter_surface_set_ivi_id(struct weston_transmitter_surface *txs) +{ + struct weston_transmitter_remote *remote = txs->remote; + struct waltham_display *dpy = remote->display; + struct weston_surface *ws; + struct ivi_layout_surface **pp_surface = NULL; + struct ivi_layout_surface *ivi_surf = NULL; + int32_t surface_length = 0; + int32_t ret = 0; + int32_t i = 0; + + ret = txs->lyt->get_surfaces(&surface_length, &pp_surface); + if(!ret) + weston_log("No ivi_surface\n"); + + ws = txs->surface; + + for(i = 0; i < surface_length; i++) { + ivi_surf = pp_surface[i]; + if (ivi_surf->surface == ws) { + assert(txs->surface); + if (!txs->surface) + return; + if(!dpy) + weston_log("no content in waltham_display\n"); + if(!dpy->compositor) + weston_log("no content in compositor object\n"); + if(!dpy->seat) + weston_log("no content in seat object\n"); + if(!dpy->application) + weston_log("no content in ivi-application object\n"); + + txs->wthp_ivi_surface = wthp_ivi_application_surface_create + (dpy->application, ivi_surf->id_surface, txs->wthp_surf); + weston_log("surface ID %d\n", ivi_surf->id_surface); + if(!txs->wthp_ivi_surface){ + weston_log("Failed to create txs->ivi_surf\n"); + } + } + } + free(pp_surface); + pp_surface = NULL; +} + +static struct weston_transmitter_surface * +transmitter_surface_push_to_remote(struct weston_surface *ws, + 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) + 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) { + weston_log("txs->wthp_surf is NULL\n"); + txs->wthp_surf = wthp_compositor_create_surface(remote->display->compositor); + transmitter_surface_set_ivi_id(txs); + } + + 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); + } +} + +/* 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); + struct weston_transmitter_output_info info = { + WL_OUTPUT_SUBPIXEL_NONE, + WL_OUTPUT_TRANSFORM_NORMAL, + 1, + 0, 0, + 300, 200, + strdup(remote->model), + { + WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED, + 800, 600, + 51519, + { NULL, NULL } + } + }; + if(remote->width != 0) { + if(remote->height != 0) { + info.mode.width = remote->width; + info.mode.height = remote->height; + } + } + /* Outputs and seats are dynamic, do not guarantee they are all + * present when signalling connection status. + */ + transmitter_remote_create_output(remote, &info); + 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 void +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) + goto not_running; + + if (!dpy->connection) + dpy->running = false; + + if (!dpy->running) + goto not_running; + + 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) + goto not_running; + + /* 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); + } + +not_running: + ; +} + +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; + + 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; + } + 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; + + 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; + int ret; + + wl_list_for_each_reverse(remote, &txr->remote_list, link) { + /* XXX: actually start connecting */ + /* waltham */ + remote->display = zalloc(sizeof *remote->display); + if (!remote->display) + return NULL; + remote->display->remote = 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); + if (ret < 0) { + weston_log("Fatal: Transmitter waltham connecting failed.\n"); + return NULL; + } + 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 desctruction 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 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, +}; + +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 const struct weston_transmitter_ivi_api transmitter_ivi_api_impl = { + 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); + remote->width = atoi(width); + remote->height = atoi(height); + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + 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 void +transmitter_get_server_config(struct weston_transmitter *txr) +{ + struct wet_compositor *compositor = + (struct wet_compositor *)weston_compositor_get_user_data(txr->compositor); + struct weston_config *config = wet_get_config(txr->compositor); + struct weston_config_section *section; + const char *name = NULL; + char *model = NULL; + char *addr = NULL; + char *port = NULL; + char *width = '0'; + char *height = '0'; + int ret; + + section = weston_config_get_section(config, "remote", NULL, NULL); + + while (weston_config_next_section(config, §ion, &name)) { + if (0 == strcmp(name, "remote-output")) { + if (0 != weston_config_section_get_string(section, "output-name", + &model, 0)) + continue; + + if (0 != weston_config_section_get_string(section, "server-address", + &addr, 0)) + continue; + + if (0 != weston_config_section_get_string(section, "port", + &port, 0)) + continue; + + if (0 != weston_config_section_get_string(section, "width", + &width, 0)) + continue; + + if (0 != weston_config_section_get_string(section, "height", + &height, 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/waltham-transmitter/transmitter-plugin/plugin.h b/waltham-transmitter/transmitter-plugin/plugin.h new file mode 100644 index 0000000..a473dab --- /dev/null +++ b/waltham-transmitter/transmitter-plugin/plugin.h @@ -0,0 +1,330 @@ +/* + * 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. + */ + +#ifndef WESTON_TRANSMITTER_PLUGIN_H +#define WESTON_TRANSMITTER_PLUGIN_H + +/* 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. + */ + +#include +#include + +#include "compositor.h" +#include "transmitter_api.h" +#include "ivi-layout-export.h" + +#include + + +struct waltham_display; + +enum wthp_seat_capability { + /** + * the seat has pointer devices + */ + WTHP_SEAT_CAPABILITY_POINTER = 1, + /** + * the seat has one or more keyboards + */ + WTHP_SEAT_CAPABILITY_KEYBOARD = 2, + /** + * the seat has touch devices + */ + WTHP_SEAT_CAPABILITY_TOUCH = 4, +}; + +/* epoll structure */ +struct watch { + struct waltham_display *display; + int fd; + void (*cb)(struct watch *w, uint32_t events); +}; + +struct waltham_display { + struct wth_connection *connection; + struct watch conn_watch; + struct wth_display *display; + + bool running; + + struct wthp_registry *registry; + + struct wthp_callback *bling; + + struct wthp_compositor *compositor; + struct wthp_blob_factory *blob_factory; + struct wthp_seat *seat; + struct wthp_pointer *pointer; + struct wthp_keyboard *keyboard; + struct wthp_touch *touch; + struct wthp_ivi_application *application; + struct wtimer *fiddle_timer; + + struct weston_transmitter_remote *remote; + char *addr; + char *port; +}; + +/* a timerfd based timer */ +struct wtimer { + struct watch watch; + void (*func)(struct wtimer *, void *); + void *data; +}; + +struct weston_transmitter { + struct weston_compositor *compositor; + struct wl_listener compositor_destroy_listener; + + struct wl_list remote_list; /* transmitter_remote::link */ + + struct wl_listener stream_listener; + struct wl_signal connected_signal; + struct wl_event_loop *loop; +}; + +struct weston_transmitter_remote { + struct weston_transmitter *transmitter; + struct wl_list link; + char *model; + char *addr; + char *port; + int32_t width; + int32_t height; + + enum weston_transmitter_connection_status status; + struct wl_signal connection_status_signal; + struct wl_signal conn_establish_signal; + + struct wl_list output_list; /* weston_transmitter_output::link */ + struct wl_list surface_list; /* weston_transmitter_surface::link */ + struct wl_list seat_list; /* weston_transmitter_seat::link */ + + struct wl_listener establish_listener; + + struct wl_event_source *establish_timer; /* for establish connection */ + struct wl_event_source *retry_timer; /* for retry connection */ + + struct waltham_display *display; /* waltham */ + struct wl_event_source *source; +}; + + +struct weston_transmitter_surface { + struct weston_transmitter_remote *remote; + struct wl_list link; /* weston_transmitter_remote::surface_list */ + struct wl_signal destroy_signal; /* data: weston_transmitter_surface */ + + enum weston_transmitter_stream_status status; + struct wl_signal stream_status_signal; + + struct weston_surface *surface; + struct wl_listener surface_destroy_listener; + const struct ivi_layout_interface *lyt; + + weston_transmitter_ivi_resize_handler_t resize_handler; + void *resize_handler_data; + + struct weston_output *sync_output; + struct wl_listener sync_output_destroy_listener; + + int32_t attach_dx; /**< wl_surface.attach(buffer, dx, dy) */ + int32_t attach_dy; /**< wl_surface.attach(buffer, dx, dy) */ + struct wl_list frame_callback_list; /* weston_frame_callback::link */ + struct wl_list feedback_list; /* weston_presentation_feedback::link */ + + /* waltham */ + struct wthp_surface *wthp_surf; + struct wthp_blob_factory *wthp_blob; + struct wthp_buffer *wthp_buf; + struct wthp_ivi_surface *wthp_ivi_surface; + struct wthp_ivi_application *wthp_ivi_application; +}; + +struct weston_transmitter_output_info { + uint32_t subpixel; /* enum wl_output_subpixel */ + uint32_t transform; /* enum wl_output_transform */ + int32_t scale; + int32_t x; + int32_t y; + int32_t width_mm; + int32_t height_mm; + /* char *make; is WESTON_TRANSMITTER_OUTPUT_MAKE */ + char *model; + + struct weston_mode mode; +}; + +struct weston_transmitter_output { + struct weston_output base; + + struct { + bool draw_initial_frame; + struct wl_surface *surface; + struct wl_output *output; + struct wl_display *display; + int configure_width, configure_height; + bool wait_for_configure; + } parent; + + struct weston_transmitter_remote *remote; + struct wl_list link; /* weston_transmitter_remote::output_list */ + + struct frame *frame; + + struct wl_callback *frame_cb; + struct renderer *renderer; +}; + +struct weston_transmitter_seat { + struct weston_seat *base; + struct wl_list link; + + /* pointer */ + wl_fixed_t pointer_surface_x; + wl_fixed_t pointer_surface_y; + + struct wl_listener get_pointer_listener; + struct weston_transmitter_surface *pointer_focus; + struct wl_listener pointer_focus_destroy_listener; + + struct wl_event_source *pointer_timer; /* fake */ + + double pointer_phase; /* fake */ + + /* keyboard */ + struct weston_transmitter_surface *keyboard_focus; + + /* touch */ + struct weston_transmitter_surface *touch_focus; +}; + +struct ivi_layout_surface { + struct wl_list link; /* ivi_layout::surface_list */ + struct wl_signal property_changed; + int32_t update_count; + uint32_t id_surface; + + struct ivi_layout *layout; + struct weston_surface *surface; + + struct ivi_layout_surface_properties prop; + + struct { + struct ivi_layout_surface_properties prop; + } pending; + + struct wl_list view_list; /* ivi_layout_view::surf_link */ +}; + +void +transmitter_surface_ivi_resize(struct weston_transmitter_surface *txs, + int32_t width, int32_t height); + +int +transmitter_remote_create_output(struct weston_transmitter_remote *remote, + const struct weston_transmitter_output_info *info); + +void +transmitter_output_destroy(struct weston_transmitter_output *output); + +int +transmitter_remote_create_seat(struct weston_transmitter_remote *remote); + +void +transmitter_seat_destroy(struct weston_transmitter_seat *seat); + +/* The below are the functions to be called from the network protocol + * input event handlers. + */ + +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); + +void +transmitter_seat_pointer_leave(struct weston_transmitter_seat *seat, + uint32_t serial, + struct weston_transmitter_surface *txs); + +void +transmitter_seat_pointer_motion(struct weston_transmitter_seat *seat, + uint32_t time, + wl_fixed_t surface_x, + wl_fixed_t surface_y); + +void +transmitter_seat_pointer_button(struct weston_transmitter_seat *seat, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state); + +void +transmitter_seat_pointer_axis(struct weston_transmitter_seat *seat, + uint32_t time, + uint32_t axis, + wl_fixed_t value); + +void +transmitter_seat_pointer_frame(struct weston_transmitter_seat *seat); + +void +transmitter_seat_pointer_axis_source(struct weston_transmitter_seat *seat, + uint32_t axis_source); + +void +transmitter_seat_pointer_axis_stop(struct weston_transmitter_seat *seat, + uint32_t time, + uint32_t axis); + +void +transmitter_seat_pointer_axis_discrete(struct weston_transmitter_seat *seat, + uint32_t axis, + int32_t discrete); + +/* Fake functions for mockup testing: */ + +int +transmitter_seat_fake_pointer_input(struct weston_transmitter_seat *seat, + struct weston_transmitter_surface *txs); + +void +seat_capabilities(struct wthp_seat *wthp_seat, + enum wthp_seat_capability caps); + +static const struct wthp_seat_listener seat_listener = { + seat_capabilities, + NULL +}; + + +#endif /* WESTON_TRANSMITTER_PLUGIN_H */ diff --git a/waltham-transmitter/transmitter-plugin/transmitter_api.h b/waltham-transmitter/transmitter-plugin/transmitter_api.h new file mode 100644 index 0000000..9b3e5fe --- /dev/null +++ b/waltham-transmitter/transmitter-plugin/transmitter_api.h @@ -0,0 +1,278 @@ +/* + * 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. + */ + +#ifndef WESTON_TRANSMITTER_API_H +#define WESTON_TRANSMITTER_API_H + +#include "plugin-registry.h" + +#include + +/** \file + * + * This is the Transmitter API published via weston_plugin_api_register(). + */ + +struct weston_transmitter; +struct weston_transmitter_remote; +struct weston_transmitter_surface; + +#define WESTON_TRANSMITTER_API_NAME "transmitter_v1" + +/** See weston_transmitter_api::remote_get_status */ +enum weston_transmitter_connection_status { + /** The connection hand-shake is not yet complete */ + WESTON_TRANSMITTER_CONNECTION_INITIALIZING, + + /** The connection is live and ready to be used. */ + WESTON_TRANSMITTER_CONNECTION_READY, + + /** The connection is dead. */ + WESTON_TRANSMITTER_CONNECTION_DISCONNECTED, +}; + +/** See weston_transmitter_api::surface_get_stream_status */ +enum weston_transmitter_stream_status { + /** The stream hand-shake is not yet complete. */ + WESTON_TRANSMITTER_STREAM_INITIALIZING, + + /** The stream is carrying surface content updates as needed. */ + WESTON_TRANSMITTER_STREAM_LIVE, + + /** The stream has failed and disconnected permanently. */ + WESTON_TRANSMITTER_STREAM_FAILED, +}; + +/** The Transmitter Base API + * + * Transmitter is a Weston plugin that provides remoting of weston_surfaces + * over the network. Shells use this API to create remote connections and + * push surfaces over the network. Shells are also responsible for relaying + * basic window state changes to Transmitter. + * + * In addition to the Transmitter Base API, shells also need to use a + * shell protocol specific Transmitter API to relay specific window state + * changes. + */ +struct weston_transmitter_api { + /** Fetch the Transmitter plugin context + * + * \param compositor The compositor instance. + * \return The weston_transmitter context, which is always the same + * for the given compositor instance. + */ + struct weston_transmitter * + (*transmitter_get)(struct weston_compositor *compositor); + + /** + * Connect to a remote server via Transmitter. + * + * \param txr The Transmitter context. + * \param status Listener to inform of connection status changes. + * \return A handle to the remote connection, or NULL on failure. + * + * This call attempts to open a connection asynchronously. The + * connection is not usable until the listener signals it is ready. + * The listener may also signal that the connection failed instead. + * + * The listener callback argument is the weston_transmitter_remote + * returned by this function. Use remote_get_status() to fetch the + * current status. + * + */ + struct weston_transmitter_remote * + (*connect_to_remote)(struct weston_transmitter *txr); + + /** + * Retrieve the connection status. + * + * If the status is WESTON_TRANSMITTER_CONNECTION_DISCONNECTED, + * you have to shut the remote down completely. There is no automatic + * reconnect. + */ + enum weston_transmitter_connection_status + (*remote_get_status)(struct weston_transmitter_remote *remote); + + /** + * Destroy/disconnect a remote connection. + * + * Disconnects if connected, and destroys the connection. + * The connection status handler is not called. + * + * The caller is responsible for destroying all + * weston_transmitter_surfaces before calling this. + */ + void + (*remote_destroy)(struct weston_transmitter_remote *remote); + + /** Push a weston_surface to be transmitted to a remote. + * + * \param ws The surface to push. + * \param remote The remote connection to use. + * \param stream_status Listener for stream status changes. + * \return The Transmitter surface handle. + * + * The surface cannot be visible on the remote until the stream + * status listener signals WESTON_TRANSMITTER_STREAM_LIVE. After that, + * surface updates made by the application will be automatically + * streamed to the remote, and input events from the remote will be + * delivered to the application. + * + * The listener callback argument is the weston_transmitter_surface + * returned by this function. Use surface_get_stream_status() to + * fetch the current status. + */ + struct weston_transmitter_surface * + (*surface_push_to_remote)(struct weston_surface *ws, + struct weston_transmitter_remote *remote, + struct wl_listener *stream_status); + + /** + * Retrieve the surface content stream status. + * + * If the status is WESTON_TRANSMITTER_STREAM_FAILED, remoting the + * surface has stopped. There is no automatic retry. + */ + enum weston_transmitter_stream_status + (*surface_get_stream_status)(struct weston_transmitter_surface *txs); + + /** Stop remoting a weston_surface + * + * \param txs Transmitter surface handle to be stopped and freed. + * + * The surface stream status handler is not called. + */ + void + (*surface_destroy)(struct weston_transmitter_surface *txs); + + /** Notify of weston_surface being configured + * + * \param txs The Transmitter surface handle. + * \param dx The x delta given in wl_surface.attach request. + * \param dy The y delta given in wl_surface.attach request. + * + * Notifies Transmitter of new surface confguration. Transmitter will + * forward the arguments, window state, and reference the buffer for + * image transmission. + * + * Shells are meant to call this function for remoted surfaces in + * the weston_surface::configure handler. + * + * XXX: Is this necessary if we have weston_surface::apply_state_signal? + * + * Essentially this is just an elaborate way to forward dx,dy. + */ + void + (*surface_configure)(struct weston_transmitter_surface *txs, + int32_t dx, int32_t dy); + + void + (*surface_gather_state)(struct weston_transmitter_surface *txs); + + /** Notify that surface is connected to receiver + * + * \param txr The Transmitter context. + * \param connected_listener Listener for connected_signal. + */ + void + (*register_connection_status)(struct weston_transmitter *txr, + struct wl_listener *connected_listener); + + /** get weston_surface from weston_transmitter_surface + * + * \param txs The Transmitter surface. + */ + struct weston_surface * + (*get_weston_surface)(struct weston_transmitter_surface *txs); +}; + +static inline const struct weston_transmitter_api * +weston_get_transmitter_api(struct weston_compositor *compositor) +{ + return weston_plugin_api_get(compositor, WESTON_TRANSMITTER_API_NAME, + sizeof(struct weston_transmitter_api)); +} + +#define WESTON_TRANSMITTER_IVI_API_NAME "transmitter_ivi_v1" + +/** For relaying configure events from Transmitter to shell. */ +typedef void (*weston_transmitter_ivi_resize_handler_t)(void *data, + int32_t width, + int32_t height); + +/** The Transmitter IVI-shell API + * + * Contains the IVI-shell specifics required to remote an ivi-surface. + */ +struct weston_transmitter_ivi_api { + /** Set IVI-id for a transmitter surface + * + * \param txs The transmitted surface. + * \param ivi_id The IVI-surface id as specified by the + * ivi_application.surface_create request. + */ + void + (*set_ivi_id)(struct weston_transmitter_surface *txs, uint32_t ivi_id); + + /** Set callback to relay configure events. + * + * \param txs The transmitted surface. + * \param cb The callback function pointer. + * \param data User data to be passed to the callback. + * + * The arguments to the callback function are user data, and width and + * height from the configure event from the remote compositor. The + * shell must relay this event to the application. + */ + void + (*set_resize_callback)(struct weston_transmitter_surface *txs, + weston_transmitter_ivi_resize_handler_t cb, + void *data); +}; + +static inline const struct weston_transmitter_ivi_api * +weston_get_transmitter_ivi_api(struct weston_compositor *compositor) +{ + return weston_plugin_api_get(compositor, + WESTON_TRANSMITTER_IVI_API_NAME, + sizeof(struct weston_transmitter_ivi_api)); +} + +/** Identifies outputs created by the Transmitter by make */ +#define WESTON_TRANSMITTER_OUTPUT_MAKE "Weston-Transmitter" + +/* Remote compositor/output are identified by model */ + + +struct renderer { + void (*repaint_output)(struct weston_output *base); + struct GstAppContext *ctx; + int32_t dmafd; /* dmafd received from compositor-drm */ + int surface_width; + int surface_height; + bool recorder_enabled; +}; + +#endif /* WESTON_TRANSMITTER_API_H */ diff --git a/waltham-transmitter/transmitter-plugin/weston.ini.transmitter b/waltham-transmitter/transmitter-plugin/weston.ini.transmitter new file mode 100644 index 0000000..1aff0b2 --- /dev/null +++ b/waltham-transmitter/transmitter-plugin/weston.ini.transmitter @@ -0,0 +1,21 @@ +[core] +shell=ivi-shell.so +modules=transmitter.so + +[ivi-shell] +ivi-module=ivi-controller.so +ivi-input-module=ivi-input-controller.so + +[remote-output] +output-name=transmitter_1 +server-address=192.168.2.11 +port=34400 +width=1920 +height=1080 + +[remote-output] +output-name=transmitter_2 +server-address=192.168.2.12 +port=34400 +width=1920 +height=1080 diff --git a/waltham-transmitter/waltham-renderer/CMakeLists.txt b/waltham-transmitter/waltham-renderer/CMakeLists.txt new file mode 100644 index 0000000..abf1a4c --- /dev/null +++ b/waltham-transmitter/waltham-renderer/CMakeLists.txt @@ -0,0 +1,59 @@ +project (waltham-renderer) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(WAYLAND_SERVER wayland-server>=1.13.0 REQUIRED) +pkg_check_modules(WESTON weston>=2.0.0 REQUIRED) +pkg_check_modules(PIXMAN pixman-1 REQUIRED) +pkg_check_modules(WALTHAM waltham REQUIRED) +pkg_search_module(GSTREAMER gstreamer-1.0 required) +pkg_search_module(GSTREAMERAPP gstreamer-app-1.0 required) + +include_directories( + ${CMAKE_SOURCE_DIR}/waltham-transmitter/transmitter-plugin + ${CMAKE_CURRENT_BINARY_DIR} + ${WAYLAND_CLIENT_INCLUDE_DIRS} + ${WAYLAND_SERVER_INCLUDE_DIRS} + ${WESTON_INCLUDE_DIRS} + ${PIXMAN_INCLUDE_DIRS} + ${WALTHAM_INCLUDE_DIRS} + ${GSTREAMER_INCLUDE_DIRS} + ${GSTREAMERAPP_INCLUDE_DIRS} +) + +link_directories( + ${WAYLAND_SERVER_LIBRARY_DIRS} + ${WESTON_LIBRARY_DIRS} + ${PIXMAN_LIBRARY_DIRS} + ${WALTHAM_LIBRARY_DIRS} + ${GSTREAMER_LIBRARY_DIRS} + ${GSTREAMERAPP_LIBRARY_DIRS} +) + +add_library(${PROJECT_NAME} MODULE + waltham-renderer.c + waltham-renderer.h +) + +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") + +set(LIBS + m + weston-2 + gstallocators-1.0 + gstvideo-1.0 + ${WAYLAND_SERVER_LIBRARIES} + ${WESTON_LIBRARIES} + ${PIXMAN_LIBRARIES} + ${WALTHAM_LIBRARIES} + ${GSTREAMER_LIBRARIES} + ${GSTREAMERAPP_LIBRARIES} +) + +add_dependencies(${PROJECT_NAME} ${LIBS}) + +target_link_libraries(${PROJECT_NAME} ${LIBS}) + +install ( + TARGETS ${PROJECT_NAME} + LIBRARY DESTINATION lib64/libweston-2 +) diff --git a/waltham-transmitter/waltham-renderer/waltham-renderer.c b/waltham-transmitter/waltham-renderer/waltham-renderer.c new file mode 100644 index 0000000..d274e3b --- /dev/null +++ b/waltham-transmitter/waltham-renderer/waltham-renderer.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2017 Advanced Driver Information Technology GmbH, Advanced Driver Information Technology Corporation, Robert Bosch GmbH, Robert Bosch Car Multimedia GmbH, DENSO Corporation + * + * 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 +#include +#include + +#include +#include +#include +#include + +#include "compositor.h" + +#include "transmitter_api.h" +#include "waltham-renderer.h" +#include "plugin.h" + +struct waltham_renderer { + struct renderer base; +}; + +struct GstAppContext +{ + GMainLoop *loop; + GstBus *bus; + GstElement *pipeline; + GstElement *appsrc; + GstBuffer *gstbuffer; +}; + +gboolean bus_message(GstBus *bus, GstMessage *message, gpointer p) +{ + struct GstAppContext *gstctx = p; + + switch( GST_MESSAGE_TYPE(message)) { + case GST_MESSAGE_ERROR: + { + GError *err; + gchar *debug; + + gst_message_parse_error(message, &err, &debug); + g_print("ERROR: %s\n", err->message); + + g_error_free(err); + g_free(debug); + g_main_loop_quit(gstctx->loop); + break; + } + + case GST_MESSAGE_STATE_CHANGED: + { + GstState oldstate, newstate; + + gst_message_parse_state_changed(message, &oldstate, &newstate, NULL); + switch (newstate){ + case GST_STATE_NULL: + fprintf(stderr, "%s: state is NULL\n", GST_MESSAGE_SRC_NAME(message)); + } + break; + } + default: + fprintf(stderr, "Unhandled message\n"); + break; + } +} + +static int +gst_pipe_init(struct weston_transmitter_output *output, struct gst_settings *settings) +{ + struct GstAppContext *gstctx; + gstctx=zalloc(sizeof (*gstctx)); + if(!gstctx){ + weston_log("Enable to allocate memory\n"); + return -1; + } + GstCaps *caps; + int ret = 0; + GError *gerror = NULL; + FILE * pFile; + long lSize; + char * pipe = NULL; + size_t res; + + /* create gstreamer pipeline */ + gst_init(NULL, NULL); + gstctx->loop = g_main_loop_new(NULL, FALSE); + + /* read pipeline from file */ + pFile = fopen ( "/etc/xdg/weston/pipeline.cfg" , "rb" ); + if (pFile==NULL) + { + weston_log("File open error\n"); + return -1; + } + + /* obtain file size */ + fseek (pFile , 0 , SEEK_END); + lSize = ftell (pFile); + rewind (pFile); + + /* allocate memory to contain the whole file: */ + pipe = (char*) zalloc (sizeof(char)*lSize); + if (pipe == NULL) + { + weston_log("Cannot allocate memory\n"); + return -1; + } + + /* copy the file into the buffer: */ + res = fread (pipe,1,lSize,pFile); + if (res != lSize) + { + weston_log("File read error\n"); + return -1; + } + + /* close file */ + fclose (pFile); + weston_log("Parsing GST pipeline:%s",pipe); + gstctx->pipeline = gst_parse_launch(pipe, &gerror); + free(pipe); + if(!gstctx->pipeline) + weston_log("Could not create gstreamer pipeline.\n"); + + gstctx->bus = gst_pipeline_get_bus((GstPipeline*)((void*)gstctx->pipeline)); + gst_bus_add_watch(gstctx->bus, bus_message, &gstctx); + + gstctx->appsrc = (GstAppSrc*) + gst_bin_get_by_name(GST_BIN(gstctx->pipeline), "src"); + if (!gstctx->appsrc) + return -1; + + caps = gst_caps_new_simple("video/x-raw", + "format", G_TYPE_STRING, "BGRx", + "width", G_TYPE_INT, settings->width, + "height", G_TYPE_INT, settings->height, + NULL); + if (!caps) + return -1; + + g_object_set(G_OBJECT(gstctx->appsrc), + "caps", caps, + "stream-type", 0, + "format", GST_FORMAT_TIME, + "is-live", TRUE, + NULL); + gst_caps_unref(caps); + + gst_element_set_state((GstElement*)((void*)gstctx->pipeline), GST_STATE_PLAYING); + output->renderer->ctx = gstctx; + + return 0; +} + +static int +recorder_enable(struct weston_transmitter_output *output) +{ + struct gst_settings *settings; + + struct weston_output* base = &output->base; + struct weston_compositor *compositor = base->compositor; + struct weston_transmitter_remote* remote = output->remote; + + /* + * Limitation: + * Hard coding bitrate and crop params. + * In case of gst-recorder case these were taken from weston.ini + */ + int32_t bitrate = 3000000; + + settings = malloc(sizeof(* settings)); + settings->ip = remote->addr; + + settings->port = atoi(remote->port); + + settings->bitrate = bitrate; + settings->width = output->renderer->surface_width; + settings->height = output->renderer->surface_height; + + weston_log("gst-setting are :-->\n"); + weston_log("ip = %s \n",settings->ip); + weston_log("port = %d \n",settings->port); + weston_log("bitrate = %d \n",settings->bitrate); + weston_log("width = %d \n",settings->width); + weston_log("height = %d \n",settings->height); + + gst_pipe_init(output, settings); + + return 0; +err: + weston_log("[gst recorder] %s:" + " invalid settings\n", + output->base.name); + free(settings); + return -1; +} + +static void waltham_renderer_repaint_output(struct weston_transmitter_output *output) +{ + GstBuffer *gstbuffer; + GstMemory *mem; + GstAllocator *allocator; + int stride = output->renderer->surface_width * 4; + gsize offset = 0; + + if(!output->renderer->recorder_enabled) + { + recorder_enable(&output->base); + output->renderer->recorder_enabled = 1; + } + + gstbuffer = gst_buffer_new(); + allocator = gst_dmabuf_allocator_new(); + mem = gst_dmabuf_allocator_alloc(allocator, output->renderer->dmafd, + stride * output->renderer->surface_height); + gst_buffer_append_memory(gstbuffer, mem); + gst_buffer_add_video_meta_full(gstbuffer, + GST_VIDEO_FRAME_FLAG_NONE, + GST_VIDEO_FORMAT_BGRx, + output->renderer->surface_width, + output->renderer->surface_height, + 1, + &offset, + &stride); + + gst_app_src_push_buffer(output->renderer->ctx->appsrc, gstbuffer); + gst_object_unref(allocator); +} + +static int +waltham_renderer_display_create(struct weston_transmitter_output *output) +{ + struct waltham_renderer *wth_renderer; + + wth_renderer = zalloc(sizeof *wth_renderer); + if (wth_renderer == NULL) + return -1; + wth_renderer->base.repaint_output = waltham_renderer_repaint_output; + + output->renderer = &wth_renderer->base; + + return 0; +} + +WL_EXPORT struct waltham_renderer_interface waltham_renderer_interface = { + .display_create = waltham_renderer_display_create +}; diff --git a/waltham-transmitter/waltham-renderer/waltham-renderer.h b/waltham-transmitter/waltham-renderer/waltham-renderer.h new file mode 100644 index 0000000..a9ac513 --- /dev/null +++ b/waltham-transmitter/waltham-renderer/waltham-renderer.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 Advanced Driver Information Technology GmbH, Advanced Driver Information Technology Corporation, Robert Bosch GmbH, Robert Bosch Car Multimedia GmbH, DENSO Corporation + * + * 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. + */ + +#ifndef TRANSMITTER_WALTHAM_RENDERER_H_ +#define TRANSMITTER_WALTHAM_RENDERER_H_ + +struct waltham_renderer_interface { + int (*display_create)(struct weston_transmitter_output *output); +}; + +struct gst_settings { + int width; + int height; + int bitrate; + char *ip; + int port; +}; + +#endif /* TRANSMITTER_WALTHAM_RENDERER_H_ */ -- cgit 1.2.3-korg