diff options
author | Marius Vlad <marius.vlad@collabora.com> | 2020-10-16 00:05:51 +0300 |
---|---|---|
committer | Marius Vlad <marius.vlad@collabora.com> | 2020-10-20 00:14:19 +0300 |
commit | 0e79ab59165bf925f1288476dad66109aa01b3fa (patch) | |
tree | cbabf8c3649c014fc996b97a753ec4e51e9f7949 | |
parent | 13e791f0158ca79a0cfdb00613f69eaaf255da55 (diff) |
Add waltham-transmitter-plugin
This adds the waltham-transmiter-plugin, which is a copy-pasta version
of the wayland-ivi-plugins developed by ADIT-J. It has been split into a
different repository. The major change from the initial version is the
fact that remote output has been completely removed, and it only takes
care of remote input. The renderer side has been moved/migrated into
attic/ directory for further posterity.
Bug-AGL: 3601
Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
Change-Id: Ifc1a6f58567d8b86cbe6e84dc1de79246dd95435
-rw-r--r-- | COPYING | 30 | ||||
-rw-r--r-- | README.md | 125 | ||||
-rw-r--r-- | attic/waltham-renderer/meson.build | 27 | ||||
-rw-r--r-- | attic/waltham-renderer/pipeline_example_general.cfg | 1 | ||||
-rw-r--r-- | attic/waltham-renderer/pipeline_example_intel.cfg | 1 | ||||
-rw-r--r-- | attic/waltham-renderer/pipeline_example_rcar.cfg | 1 | ||||
-rw-r--r-- | attic/waltham-renderer/waltham-renderer.c | 281 | ||||
-rw-r--r-- | include/meson.build | 4 | ||||
-rw-r--r-- | include/plugin.h | 333 | ||||
-rw-r--r-- | include/transmitter_api.h | 283 | ||||
-rw-r--r-- | include/waltham-renderer.h | 43 | ||||
-rw-r--r-- | meson.build | 58 | ||||
-rw-r--r-- | transmitter-plugin/input.c | 1333 | ||||
-rw-r--r-- | transmitter-plugin/meson.build | 24 | ||||
-rw-r--r-- | transmitter-plugin/output.c | 57 | ||||
-rw-r--r-- | transmitter-plugin/plugin.c | 1024 | ||||
-rw-r--r-- | transmitter-plugin/weston.ini.transmitter | 13 |
17 files changed, 3638 insertions, 0 deletions
@@ -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 <mjg@redhat.com> + +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/README.md b/README.md new file mode 100644 index 0000000..25b54ad --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# Waltham Transmitter # + +Waltham transmitter is a weston plugin which uses waltham IPC library to +connect to remote and transmit input events from the remote client back +to the compositor. + +Transmitter plugin provides the API to create remote connections and push +surfaces over the network and handles remote input. The remote output creation +and handling is being done by the remoting plug-in. + + +### Architecture + +```` + ECU 1 ECU 2 + +-----------------------------------------------------+ +----------------------------------------------+ + | +-----------------+ | | | + | | IVI-Application | | | +-----------+-----------+ | + | +-----------------+ | | | Gstreamer | | | + | ^ | Buffer -----------------------> (Decode) | | | + | wayland | +----------------------/ | +-----------+ | | + | v | | (Ethernet) | | Waltham-receiver | | + | +----+---------------------+ | | ----------------------------> | | + | | | Transmitter Plugin |<-----------------------------/ | +-----------------------+ | + | | | | | | Waltham-Protocol | ^ | + | | |---------------------+ | | | wayland | | + | | | Remoting plug-in |------------+ | | v | + | | | | | | +---------------------+ | + | | +-+-------------------+ | | | | | + | | | | | | compositor | | + | | compositor | | | | | | + | +------+-------------------+ | | +----------------+----+ | + | | | | | | + | v | | v | + | +------------+ | | +----------+ | + | | Display | | | | Display | | + | | | | | | | | + | +------------+ | | +----------+ | + +-----------------------------------------------------+ +----------------------------------------------+ + +```` + +### How to build + +1. Prerequisite before building + + weston, wayland, gstreamer plugins and waltham should be built and + available. + +2. Get the source code from the repository. + + $ git clone https://gerrit.automotivelinux.org/gerrit/src/waltham-transmitter + +3. Create build folder + + $ cd waltham-transmitter + $ meson -Dprefix=$PREFIX_PATH build/ + +4. Run ninja + + $ ninja -C build/ install + +5. waltham-transmitter.so should be available in the + $PREFIX_PATH/lib/x86_64-linux-gnu/libweston-$MAJOR + +### How to configure the compositor and gstreamer pipeline + +1. weston.ini: + +The transmitter plugin will be loaded automatically is found in the plug-ins +directory of weston. + +The destination of remoting output is configured in weston.ini and it matches +the key entries from the remoting plug-in -- the output being actually created +by the remoting plug-in. Add output name, server address, mode ini entries +under '[transmitter-output]'. You can specify multiple [transmitter-output]. + +You can specify which application to be started/placed on the remote output +by adding agl-shell-app-id=app_id_name. + +2. gstreamer pipeline: + +You can customize the gstreamer pipeline as you want by configuring the +pipeline entry. + +Some pipeline gstreamer examples are in the following files: + +- pipeline_example_general.cfg : Does not use any HW encoder. +- pipeline_example_intel.cfg : Use Intel's HW encoder. +- pipeline_example_rcar.cfg : Use Rcar's HW encoder. + +### Connection Establishment + +1. Connect two board over ethernet. + +2. Assign IP to both the boards and check if the simple ping works. + + For example: if transmitter IP: 192.168.2.51 and Waltham-Receiver IP: + 192.168.2.52 then + + $ ping 192.168.2.52 (you can also ping vice versa) + +3. Make sure that IP address specified in the weston.ini under + [transmitter-output] matches the Waltham-Receiver IP. The section is + identical to that of the remoting plug-in, only the name of the section is + different. + +### How to test + +Start the compositor with modified weston.ini mention above. You can confirm +the transmitter is loaded properly from weston log as below. + +```` +[07:14:23.032] Transmitter weston_seat 0xaaaad0079e50 +[07:14:23.032] Transmitter created pointer=0xaaaad00977c0 for seat 0xaaaad0079e50 +[07:14:23.032] Transmitter created keyboard=0xaaaad0079fe0 for seat 0xaaaad0079e50 +[07:14:23.032] Transmitter created touch=0xaaaacffe1010 for seat 0xaaaad0079e50 +```` + +Start remoting: + +- Start the remote compositor and start the receiver application. +- Start the compositor on the transmitter side and the application mentioned in +'[transmitter-output]' section, under agl-shell-app-id in order to 'forward' +the application to that output. diff --git a/attic/waltham-renderer/meson.build b/attic/waltham-renderer/meson.build new file mode 100644 index 0000000..9754bc9 --- /dev/null +++ b/attic/waltham-renderer/meson.build @@ -0,0 +1,27 @@ + +libpixman_dep = dependency('pixman-1') +dep_gstreamer = dependency('gstreamer-1.0') +dep_gstreamer_app = dependency('gstreamer-app-1.0') +dep_gstreamer_video = dependency('gstreamer-video-1.0') +dep_gstreamer_alloc = dependency('gstreamer-allocators-1.0') + +deps_renderer_plugin = [ + libwayland_dep, + libpixman_dep, + libweston_dep, + libwaltham_dep, + weston_dep, + dep_gstreamer, dep_gstreamer_app, + dep_gstreamer_video, dep_gstreamer_alloc +] + +plugin_renderer = shared_library( + 'waltham-renderer', + 'waltham-renderer.c', + include_directories: common_inc, + dependencies: deps_renderer_plugin, + name_prefix: '', + install: true, + install_dir: plugin_install_dir, +) +env_modmap += 'renderer-plugin.so=@0@;'.format(plugin_renderer.full_path()) diff --git a/attic/waltham-renderer/pipeline_example_general.cfg b/attic/waltham-renderer/pipeline_example_general.cfg new file mode 100644 index 0000000..31db32e --- /dev/null +++ b/attic/waltham-renderer/pipeline_example_general.cfg @@ -0,0 +1 @@ +appsrc name=src ! videoconvert ! video/x-raw,format=I420 ! jpegenc ! rtpjpegpay ! udpsink name=sink host=YOUR_RECIEVER_IP port=YOUR_RECIEVER_PORT sync=false async=false
\ No newline at end of file diff --git a/attic/waltham-renderer/pipeline_example_intel.cfg b/attic/waltham-renderer/pipeline_example_intel.cfg new file mode 100644 index 0000000..8440bce --- /dev/null +++ b/attic/waltham-renderer/pipeline_example_intel.cfg @@ -0,0 +1 @@ +appsrc name=src ! videoconvert ! video/x-raw,format=I420 ! mfxh264enc bitrate=3000000 rate-control=1 ! rtph264pay config-interval=1 ! udpsink name=sink host=YOUR_RECIEVER_IP port=YOUR_RECIEVER_PORT sync=false async=false
\ No newline at end of file diff --git a/attic/waltham-renderer/pipeline_example_rcar.cfg b/attic/waltham-renderer/pipeline_example_rcar.cfg new file mode 100644 index 0000000..2993365 --- /dev/null +++ b/attic/waltham-renderer/pipeline_example_rcar.cfg @@ -0,0 +1 @@ +appsrc name=src ! videoconvert ! video/x-raw,format=I420 ! omxh264enc bitrate=3000000 control-rate=2 ! rtph264pay ! udpsink name=sink host=YOUR_RECIEVER_IP port=YOUR_RECIEVER_PORT sync=false async=false
\ No newline at end of file diff --git a/attic/waltham-renderer/waltham-renderer.c b/attic/waltham-renderer/waltham-renderer.c new file mode 100644 index 0000000..cda354e --- /dev/null +++ b/attic/waltham-renderer/waltham-renderer.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2017 Advanced Driver Information Technology GmbH, 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 <stdlib.h> +#include <assert.h> +#include <string.h> + +#include <gst/gst.h> +#include <gst/video/gstvideometa.h> +#include <gst/allocators/gstdmabuf.h> +#include <gst/app/gstappsrc.h> + +#include <libweston/libweston.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); + + return false; + break; + } + + case GST_MESSAGE_STATE_CHANGED: + { + GstState oldstate, newstate; + + gst_message_parse_state_changed(message, &oldstate, &newstate, NULL); + switch (newstate){ + case GST_STATE_PAUSED: + fprintf(stderr, "%s: state is paused\n", GST_MESSAGE_SRC_NAME(message)); + break; + case GST_STATE_READY: + fprintf(stderr, "%s: state is ready\n", GST_MESSAGE_SRC_NAME(message)); + break; + case GST_STATE_VOID_PENDING: + fprintf(stderr, "%s: state is pending\n", GST_MESSAGE_SRC_NAME(message)); + break; + case GST_STATE_NULL: + fprintf(stderr, "%s: state is NULL\n", GST_MESSAGE_SRC_NAME(message)); + break; + case GST_STATE_PLAYING: + fprintf(stderr, "%s: state is playing\n", GST_MESSAGE_SRC_NAME(message)); + break; + } + break; + } + default: + fprintf(stderr, "Unhandled message\n"); + break; + } + + return true; +} + +static int +gst_pipe_init(struct weston_transmitter_output *output, struct gst_settings *settings) +{ + struct GstAppContext *gstctx; + GstCaps *caps; + GError *gerror = NULL; + FILE * pFile; + size_t lSize; + char * pipe = NULL; + size_t res; + + gstctx=zalloc(sizeof (*gstctx)); + if(!gstctx){ + weston_log("Enable to allocate memory\n"); + return -1; + } + + /* create gstreamer pipeline */ + gst_init(NULL, NULL); + gstctx->loop = g_main_loop_new(NULL, FALSE); + + /* read pipeline from file */ + pFile = fopen("/etc/xdg/weston/transmitter_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 = 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"); + gstctx->appsrc = 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; + + weston_log("Gstreamer pipeline inited\n"); + + return 0; +} + +static int +recorder_enable(struct weston_transmitter_output *output) +{ + struct gst_settings *settings; + + 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); + + weston_log("Enabling renderer display\n"); + + return 0; +} + +static void waltham_renderer_repaint_output(struct weston_transmitter_output *output) +{ + GstBuffer *gstbuffer; + GstMemory *mem; + GstAllocator *allocator; + int stride = output->renderer->buf_stride; + gsize offset = 0; + + if (!output->renderer->recorder_enabled) { + recorder_enable(output); + 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(GST_APP_SRC(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; + + weston_log("Created renderer display\n"); + + return 0; +} + +WL_EXPORT struct waltham_renderer_interface waltham_renderer_interface = { + .display_create = waltham_renderer_display_create +}; diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 0000000..a8c2855 --- /dev/null +++ b/include/meson.build @@ -0,0 +1,4 @@ +install_headers( + 'transmitter_api.h', + subdir: dir_include_waltham_transmiter_install +) diff --git a/include/plugin.h b/include/plugin.h new file mode 100644 index 0000000..daa0fea --- /dev/null +++ b/include/plugin.h @@ -0,0 +1,333 @@ +/* + * 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 <stdint.h> +#include <wayland-client.h> + +#include <libweston/libweston.h> +#include <ivi-layout-export.h> + +#include "transmitter_api.h" + +#include <waltham-client.h> + + +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 wthp_ivi_app_id *application_id; + 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 waltham_renderer_interface *waltham_renderer; +}; + +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; /* not needed anymore, please remove */ + + 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; + + const char *name; + struct weston_transmitter_remote *remote; + struct wl_list link; /* weston_transmitter_remote::output_list */ + + struct frame *frame; + struct wl_event_source *finish_frame_timer; + 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); +int +transmitter_remote_create_output_with_name(struct weston_transmitter_remote *remote, char *name); + +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); + + + +#endif /* WESTON_TRANSMITTER_PLUGIN_H */ diff --git a/include/transmitter_api.h b/include/transmitter_api.h new file mode 100644 index 0000000..39b616a --- /dev/null +++ b/include/transmitter_api.h @@ -0,0 +1,283 @@ +/* + * 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 <libweston/plugin-registry.h> + +#include <stdint.h> + +/** \file + * + * This is the Transmitter API published via weston_plugin_api_register(). + */ + +struct weston_transmitter; +struct weston_transmitter_remote; +struct weston_transmitter_surface; +struct weston_transmitter_output; + +#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, const char *app_id, + 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); + + struct weston_transmitter_remote * + (*get_transmitter_remote)(const char *output_name, struct weston_transmitter *transmitter); +}; + +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_transmitter_output *output); + struct GstAppContext *ctx; + int32_t dmafd; /* dmafd received from compositor-drm */ + int buf_stride; + int surface_width; + int surface_height; + bool recorder_enabled; +}; + +#endif /* WESTON_TRANSMITTER_API_H */ diff --git a/include/waltham-renderer.h b/include/waltham-renderer.h new file mode 100644 index 0000000..268a002 --- /dev/null +++ b/include/waltham-renderer.h @@ -0,0 +1,43 @@ +/* + * 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 weston_transmitter_output; + +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_ */ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..43c8cf4 --- /dev/null +++ b/meson.build @@ -0,0 +1,58 @@ +project('waltham-transmitter', + 'c', + version: '0.0.1', + default_options: [ + 'warning_level=3', + 'c_std=gnu99', + ], + meson_version: '>= 0.50', + license: 'MIT/Expat', +) + +pkgconfig = import('pkgconfig') +cc = meson.get_compiler('c') + +add_project_arguments( + cc.get_supported_arguments([ + '-Wno-unused-parameter', + '-Wno-pedantic', + '-Wextra', + '-Werror' + ]), + language: 'c' +) + +add_project_arguments([ + '-DPACKAGE_STRING="waltham-transmitter @0@"'.format(meson.project_version()), + '-D_GNU_SOURCE', + '-D_ALL_SOURCE', + ], + language: 'c' +) + +optional_libc_funcs = [ 'memfd_create', 'strchrnul' ] +foreach func: optional_libc_funcs + if cc.has_function(func) + add_project_arguments('-DHAVE_@0@=1'.format(func.to_upper()), language: 'c') + endif +endforeach + + +env_modmap = '' +libweston_major_version = '8' +libweston_version = 'libweston-@0@'.format(libweston_major_version) +libweston_dep = dependency(libweston_version) + +libwayland_dep = dependency('wayland-server') +libwaltham_dep = dependency('waltham') +weston_dep = dependency('weston') + +prefix_path = get_option('prefix') +plugin_dir = join_paths(prefix_path, get_option('libdir')) +plugin_install_dir = join_paths(plugin_dir, libweston_version) +common_inc = include_directories('include') + +dir_include_waltham_transmiter_install = 'waltham-transmitter' + +subdir('transmitter-plugin') +subdir('include') diff --git a/transmitter-plugin/input.c b/transmitter-plugin/input.c new file mode 100644 index 0000000..8fb543c --- /dev/null +++ b/transmitter-plugin/input.c @@ -0,0 +1,1333 @@ +/* + * Copyright (C) 2017 Advanced Driver Information Technology Joint Venture GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +/* for fake stuff */ +#include <math.h> + +#include <wayland-client.h> +#include <libweston/libweston.h> + +#include <weston.h> +#include "plugin.h" +#include "transmitter_api.h" + +/** @file + * + * This is an implementation of a remote input. + * + * Request wl_data_device_manager.get_data_device would need to be blocked, + * except maybe it's not necessary, we just "forget" to forward data to/from + * the remote wl_seat. It might still work inside the local compositor. + * + * weston_compositor_set_default_pointer_grab() will break our pointer + * implementation, but no in-tree code is calling it. + */ + +/* XXX: all functions and variables with a name, and things marked with a + * comment, containing the word "fake" are mockups that need to be + * removed from the final implementation. + */ + +static void +pointer_focus_grab_handler(struct weston_pointer_grab *grab) +{ + /* No-op: + * + * Weston internal events do not change the focus. + */ +} + +static void +pointer_motion_grab_handler(struct weston_pointer_grab *grab, + const struct timespec *time, + struct weston_pointer_motion_event *event) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_button_grab_handler(struct weston_pointer_grab *grab, + const struct timespec *time, + uint32_t button, + uint32_t state) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_axis_grab_handler(struct weston_pointer_grab *grab, + const struct timespec *time, + struct weston_pointer_axis_event *event) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_axis_source_grab_handler(struct weston_pointer_grab *grab, + uint32_t source) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_frame_grab_handler(struct weston_pointer_grab *grab) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +static void +pointer_cancel_grab_handler(struct weston_pointer_grab *grab) +{ + weston_log("Unexpected! %s(pointer=%p, ...)\n", + __func__, grab->pointer); +} + +/* These handlers would be called from the notify_*() functions in src/input.c. + * However, as we do not use the low level input notify_*() functions that + * backends drive, these are mostly uncalled, except the focus handler which + * weston core generates internally. + */ +static const struct weston_pointer_grab_interface pointer_grab_impl = { + pointer_focus_grab_handler, + pointer_motion_grab_handler, + pointer_button_grab_handler, + pointer_axis_grab_handler, + pointer_axis_source_grab_handler, + pointer_frame_grab_handler, + pointer_cancel_grab_handler, +}; + +static void +keyboard_grab_key(struct weston_keyboard_grab *grab, + const struct timespec *time, + uint32_t key, + uint32_t state) +{ +} + +static void +keyboard_grab_modifiers(struct weston_keyboard_grab *grab, + uint32_t serial, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) +{ +} + +static void +keyboard_grab_cancel(struct weston_keyboard_grab *grab) +{ +} + +static const struct weston_keyboard_grab_interface keyborad_grab_impl = { + keyboard_grab_key, + keyboard_grab_modifiers, + keyboard_grab_cancel +}; + +static void +touch_grab_down_handler(struct weston_touch_grab *grab, + const struct timespec *time, + int touch_id, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void +touch_grab_up_handler(struct weston_touch_grab *grab, + const struct timespec *time, + int touch_id) +{ +} + +static void +touch_grab_motion_handler(struct weston_touch_grab *grab, + const struct timespec *time, + int touch_id, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void +touch_grab_frame_handler(struct weston_touch_grab *grab) +{ +} + +static void +touch_grab_cancel_handler(struct weston_touch_grab *grab) +{ +} + +static const struct weston_touch_grab_interface touch_grab_impl = { + touch_grab_down_handler, + touch_grab_up_handler, + touch_grab_motion_handler, + touch_grab_frame_handler, + touch_grab_cancel_handler, +}; + +static struct weston_pointer_client * +weston_pointer_get_pointer_client(struct weston_pointer *pointer, + struct wl_client *client) +{ + struct weston_pointer_client *pointer_client; + + wl_list_for_each(pointer_client, &pointer->pointer_clients, link) { + if (pointer_client->client == client) + return pointer_client; + } + + return NULL; +} + +/* The different ways to get pointer focus on a remoted surface: + * + * 1. Transmitter seat has pointer. The client has wl_pointer. Transmitter + * receives pointer.enter. (transmitter_seat_pointer_enter()) + * + * 2. Transmitter seat has pointer. Transmitter has received pointer.enter. + * The client calls wl_seat.get_pointer. => send enter only on the new + * wl_pointer. (seat_get_pointer_handler()) + * + * 3. Client has wl_pointer. Transmitter seat adds pointer capability. + * Transmitter receives pointer.enter. wl_pointer MUST NOT enter, + * specified by wl_seat.capabilities. + * + * By definition, Transmitter cannot receive pointer.enter without having + * pointer capability in the seat, so no other combinations are possible. + * + * The same applies to wl_keyboard and wl_touch. + */ + +/* Implementor notes: + * + * The handling of all of wl_pointer, wl_keyboard and wl_touch should be + * similar. To make it work, we need to add a signal to each of the + * wl_seat.get_pointer, wl_seat.get_keyboard, and wl_seat.get_touch request + * handlers in Weston core. Otherwise we cannot implement the case 2 of gaining + * input device focus. + * + * However, weston_keyboard::focus is a weston_surface, not a weston_view, so + * we may be able to leverage more of the core implementation and maybe do + * without the wl_seat.get_keyboard signal. Weston_touch uses a weston_view, so + * that is similar to weston_pointer. + * + * It might be useful to convert weston_keyboard and weston_touch to use a + * similar thing as weston_pointer_client, in case it makes things more + * consistent. It might also fix issues when a client has multiple copies of a + * wl_keyboard or a wl_touch, but that is getting off-topic. + * + * This file shows which part of the Weston input path we skip and where we + * hook in. We skip everything starting from the notify_*() API used by + * backends, and stub out the grab handlers. Instead of actual grab handlers, + * we have our own network protocol events handlers. They do much of the same + * as normal grab handlers would do, except focus is pre-given, and we do not + * have weston_view for the focus surfaces, so we need to bypass core code + * dealing with those. + * + * Our remote seat implementation will leave many struct members unused and + * replicate some from weston_pointer, weston_keyboard, and weston_touch. + * Weston core must be kept out from the focus handling business, because we + * will send enter/leave events ourselves, and focus assignments are given + * to us from the remote, they cannot be changed at will by the local Weston. + */ + +/** Callback from the protocol request handler for wl_seat.get_pointer + * + * The Weston core handler never sees focus set on the weston_pointer, + * so it won't send wl_pointer.enter nor set focus_client. It does call + * weston_pointer_ensure_pointer_client() though. + */ +void +seat_get_pointer_handler(struct wl_listener *listener, void *data) +{ + struct wl_resource *new_pointer = data; + struct weston_transmitter_seat *seat; + struct wl_resource *surface; + struct weston_pointer_client *pointer_client; + struct wl_client *client; + struct weston_pointer *pointer; + + seat = wl_container_of(listener, seat, get_pointer_listener); + if (!seat->pointer_focus) + return; + + client = wl_resource_get_client(new_pointer); + surface = seat->pointer_focus->surface->resource; + + if (wl_resource_get_client(surface) != client) + return; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); /* guaranteed by having pointer_focus */ + pointer_client = weston_pointer_get_pointer_client(pointer, client); + + if (!pointer->focus_client) + pointer->focus_client = pointer_client; + else + assert(pointer->focus_client == pointer_client); + + wl_pointer_send_enter(new_pointer, pointer->focus_serial, surface, + seat->pointer_surface_x, seat->pointer_surface_y); + + if (wl_resource_get_version(new_pointer) >= + WL_POINTER_FRAME_SINCE_VERSION) + wl_pointer_send_frame(new_pointer); +} + +static void +transmitter_seat_create_pointer(struct weston_transmitter_seat *seat) +{ + struct weston_pointer *pointer; + + seat->pointer_phase = 0.0; + seat->pointer_surface_x = wl_fixed_from_int(-1000000); + seat->pointer_surface_y = wl_fixed_from_int(-1000000); + seat->pointer_focus = NULL; + wl_list_init(&seat->pointer_focus_destroy_listener.link); + + weston_seat_init_pointer(seat->base); + + pointer = weston_seat_get_pointer(seat->base); + + /* not exported: + * weston_pointer_set_default_grab(pointer, &pointer_grab_impl); */ + pointer->default_grab.interface = &pointer_grab_impl; + + /* Changes to local outputs are irrelevant. */ + wl_list_remove(&pointer->output_destroy_listener.link); + wl_list_init(&pointer->output_destroy_listener.link); + + weston_log("Transmitter created pointer=%p for seat %p\n", + pointer, seat->base); +} + +static void +seat_pointer_focus_destroy_handler(struct wl_listener *listener, void *data) +{ + struct weston_transmitter_surface *txs = data; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(listener, seat, pointer_focus_destroy_listener); + assert(seat->pointer_focus == txs); + + seat->pointer_focus = NULL; +} + +void +transmitter_seat_pointer_enter(struct weston_transmitter_seat *seat, + uint32_t serial, + struct weston_transmitter_surface *txs, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *resource; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + + assert(txs->surface); + //struct wl_client *client = wl_resource_get_client(txs->surface->resource); + + seat->pointer_focus = txs; + seat->pointer_focus_destroy_listener.notify = + seat_pointer_focus_destroy_handler; + wl_signal_add(&txs->destroy_signal, + &seat->pointer_focus_destroy_listener); + + /* If pointer-focus gets destroyed, txs will get destroyed, the + * remote surface object is destroyed, and the remote will send a + * leave and a frame. + */ + + seat->pointer_surface_x = surface_x; + seat->pointer_surface_y = surface_y; + + pointer->focus_serial = serial; + + /* pointer->focus is not used, because it is a weston_view, while + * remoted surfaces have no views. + * + * pointer->x,y are not used because they are in global coordinates. + * Remoted surfaces are not in the global space at all, so there are + * no such coordinates. + */ + + if (!pointer->focus_client) + return; + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) { + wl_pointer_send_enter(resource, + serial, + txs->surface->resource, + surface_x, surface_y); + } +} + +void +transmitter_seat_pointer_leave(struct weston_transmitter_seat *seat, + uint32_t serial, + struct weston_transmitter_surface *txs) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *surface_resource; + struct wl_resource *resource; + + if (txs != seat->pointer_focus) { + weston_log("Transmitter Warning: pointer leave for %p, expected %p\n", + txs, seat->pointer_focus); + } + + seat->pointer_focus = NULL; + wl_list_remove(&seat->pointer_focus_destroy_listener.link); + wl_list_init(&seat->pointer_focus_destroy_listener.link); + + if (!txs) + return; + assert(txs->surface); + surface_resource = txs->surface->resource; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + if (!pointer->focus_client) + return; + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) + wl_pointer_send_leave(resource, serial, surface_resource); + + /* Do not reset pointer->focus_client, because we need to be able + * to send a following 'frame' event in + * transmitter_seat_pointer_frame(). + */ +} + +void +transmitter_seat_pointer_motion(struct weston_transmitter_seat *seat, + uint32_t time, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *resource; + struct weston_transmitter_surface *txs; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + + seat->pointer_surface_x = surface_x; + seat->pointer_surface_y = surface_y; + + if (!pointer->focus_client) + return; + + txs = seat->pointer_focus; + if (txs) + assert(wl_resource_get_client(txs->surface->resource) == + pointer->focus_client->client); + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) { + wl_pointer_send_motion(resource, time, + surface_x, surface_y); + } +} + +void +transmitter_seat_pointer_button(struct weston_transmitter_seat *seat, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *resource; + struct weston_transmitter_surface *txs; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + + if (!pointer->focus_client) + return; + + txs = seat->pointer_focus; + if (txs) + assert(wl_resource_get_client(txs->surface->resource) == + pointer->focus_client->client); + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) { + wl_pointer_send_button(resource, serial, time, + button, state); + } +} + +void +transmitter_seat_pointer_axis(struct weston_transmitter_seat *seat, + uint32_t time, + uint32_t axis, + wl_fixed_t value) +{ + struct weston_pointer *pointer; + struct wl_list *focus_resource_list; + struct wl_resource *resource; + struct weston_transmitter_surface *txs; + + pointer = weston_seat_get_pointer(seat->base); + assert(pointer); + + if (!pointer->focus_client) + return; + + txs = seat->pointer_focus; + if (txs) + assert(wl_resource_get_client(txs->surface->resource) == + pointer->focus_client->client); + + focus_resource_list = &pointer->focus_client->pointer_resources; + wl_resource_for_each(resource, focus_resource_list) { + wl_pointer_send_axis(resource, time, + axis, value); + } +} + +void +transmitter_seat_pointer_frame(struct weston_transmitter_seat *seat) +{ + struct weston_pointer *pointer; + + pointer = weston_seat_get_pointer(seat->base); + if (pointer) + weston_pointer_send_frame(pointer); +} + +void +transmitter_seat_pointer_axis_source(struct weston_transmitter_seat *seat, + uint32_t axis_source) +{ + /* ToDo : implement axis event handling */ +} + +void +transmitter_seat_pointer_axis_stop(struct weston_transmitter_seat *seat, + uint32_t time, + uint32_t axis) +{ + /* ToDo : implement axis event handling */ +} + +void +transmitter_seat_pointer_axis_discrete(struct weston_transmitter_seat *seat, + uint32_t axis, + int32_t discrete) +{ + /* ToDo : implement axis event handling */ +} + +static void +transmitter_seat_create_keyboard(struct weston_transmitter_seat *seat) +{ + struct weston_keyboard *keyboard; + + seat->keyboard_focus = NULL; + weston_seat_init_keyboard(seat->base, NULL); + + keyboard = weston_seat_get_keyboard(seat->base); + + keyboard->default_grab.interface = &keyborad_grab_impl; + + weston_log("Transmitter created keyboard=%p for seat %p\n", + keyboard, seat->base); +} + +static void +transmitter_seat_keyboard_enter(struct weston_transmitter_seat *seat, + uint32_t serial, + struct weston_transmitter_surface *txs, + struct wl_array *keys) +{ + struct weston_keyboard *keyboard; + struct wl_resource *resource = NULL; + struct wl_resource *surface_resource; + + keyboard = weston_seat_get_keyboard(seat->base); + assert(keyboard); + + assert(txs->surface); + surface_resource = txs->surface->resource; + + seat->keyboard_focus = txs; + wl_array_copy(&keyboard->keys, keys); + + wl_resource_for_each(resource, &keyboard->resource_list) { + if (wl_resource_get_client(resource) == wl_resource_get_client(surface_resource)) { + wl_keyboard_send_enter(resource, + serial, + surface_resource, + &keyboard->keys); + } + } +} + +static void +transmitter_seat_keyboard_leave(struct weston_transmitter_seat *seat, + uint32_t serial, + struct weston_transmitter_surface *txs) +{ + struct weston_keyboard *keyboard; + struct wl_resource *resource = NULL; + struct wl_resource *surface_resource; + + keyboard = weston_seat_get_keyboard(seat->base); + assert(keyboard); + + assert(txs->surface); + surface_resource = txs->surface->resource; + + wl_resource_for_each(resource, &keyboard->resource_list) { + if (wl_resource_get_client(resource) == wl_resource_get_client(surface_resource)) { + wl_keyboard_send_leave(resource, + serial, + surface_resource); + } + } +} + +static void +transmitter_seat_keyboard_key(struct weston_transmitter_seat *seat, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state) +{ + struct weston_keyboard *keyboard; + struct wl_resource *resource = NULL; + + keyboard = weston_seat_get_keyboard(seat->base); + assert(keyboard); + + wl_resource_for_each(resource, &keyboard->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->keyboard_focus->surface->resource)) { + wl_keyboard_send_key(resource, + serial, + time, + key, + state); + } + } +} + +static void +transmitter_seat_create_touch(struct weston_transmitter_seat *seat) +{ + struct weston_touch *touch; + + seat->touch_focus = NULL; + weston_seat_init_touch(seat->base); + + touch = weston_seat_get_touch(seat->base); + + touch->default_grab.interface = &touch_grab_impl; + + weston_log("Transmitter created touch=%p for seat %p\n", + touch, seat->base); +} + +static void +transmitter_seat_touch_down (struct weston_transmitter_seat *seat, + uint32_t serial, + uint32_t time, + struct weston_transmitter_surface *txs, + int32_t touch_id, + wl_fixed_t x, + wl_fixed_t y) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + struct wl_resource *surface_resource; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + assert(txs->surface); + surface_resource = txs->surface->resource; + + seat->touch_focus = txs; + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == wl_resource_get_client(surface_resource)) { + wl_touch_send_down(resource, serial, time, + surface_resource, + touch_id, x, y); + } + } +} + +static void +transmitter_seat_touch_up (struct weston_transmitter_seat *seat, + uint32_t serial, + uint32_t time, + int32_t touch_id) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->touch_focus->surface->resource)) { + wl_touch_send_up(resource, serial, time, touch_id); + } + } +} + +static void +transmitter_seat_touch_motion (struct weston_transmitter_seat *seat, + uint32_t time, + int32_t touch_id, + wl_fixed_t x, + wl_fixed_t y) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->touch_focus->surface->resource)) { + wl_touch_send_motion(resource, time, touch_id, x, y); + } + } +} + +static void +transmitter_seat_touch_frame (struct weston_transmitter_seat *seat) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->touch_focus->surface->resource)) { + wl_touch_send_frame(resource); + } + } +} + +static void +transmitter_seat_touch_cancel (struct weston_transmitter_seat *seat) +{ + struct weston_touch *touch; + struct wl_resource *resource = NULL; + + touch = weston_seat_get_touch(seat->base); + assert(touch); + + wl_resource_for_each(resource, &touch->resource_list) { + if (wl_resource_get_client(resource) == + wl_resource_get_client(seat->touch_focus->surface->resource)) { + wl_touch_send_cancel(resource); + } + } +} + +static char * +make_seat_name(struct weston_transmitter_remote *remote, const char *name) +{ + char *str; + + if (asprintf(&str, "transmitter-%s-%s", remote->addr, name) < 0) + return NULL; + + return str; +} + +void +transmitter_seat_destroy(struct weston_transmitter_seat *seat) +{ + wl_list_remove(&seat->link); + + weston_log("Transmitter destroy seat=%p\n", seat->base); + + wl_list_remove(&seat->get_pointer_listener.link); + wl_list_remove(&seat->pointer_focus_destroy_listener.link); + + if (seat->pointer_timer) + wl_event_source_remove(seat->pointer_timer); + + free(seat); +} + +static void +pointer_handle_enter(struct wthp_pointer *wthp_pointer, + uint32_t serial, + struct wthp_surface *surface, + wth_fixed_t surface_x, + wth_fixed_t surface_y) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + if (txs != seat->pointer_focus) + transmitter_seat_pointer_leave(seat, serial, seat->pointer_focus); + transmitter_seat_pointer_enter(seat, serial, txs, + surface_x, surface_y); + } + } +} + +static void +pointer_handle_leave(struct wthp_pointer *wthp_pointer, + uint32_t serial, + struct wthp_surface *surface) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + transmitter_seat_pointer_leave(seat, serial, txs); + } + } +} + +static void +pointer_handle_motion(struct wthp_pointer *wthp_pointer, + uint32_t time, + wth_fixed_t surface_x, + wth_fixed_t surface_y) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_pointer_motion(seat, time, + surface_x, + surface_y); +} + +static void +pointer_handle_button(struct wthp_pointer *wthp_pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_pointer_button(seat, serial, + time, button, + state); +} + +static void +pointer_handle_axis(struct wthp_pointer *wthp_pointer, + uint32_t time, + uint32_t axis, wth_fixed_t value) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_pointer); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_pointer_axis(seat, time, + axis, value); +} + +static void +pointer_handle_frame(struct wthp_pointer *wthp_pointer) +{ + /* ToDo : implement pointer handle frame */ +} + +static void +pointer_handle_axis_source(struct wthp_pointer *wthp_pointer, + uint32_t axis_source) +{ + /* ToDo : implement pointer handle axis source */ +} + +static void +pointer_handle_axis_stop(struct wthp_pointer *wthp_pointer, + uint32_t time, + uint32_t axis) +{ + /* ToDo : implement pointer handle axis stop */ +} + +static void +pointer_handle_axis_discrete(struct wthp_pointer *wthp_pointer, + uint32_t axis, + int32_t discrete) +{ + /* ToDo : implement pointer handle axis discrete */ +} + +static const struct wthp_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, + pointer_handle_frame, + pointer_handle_axis_source, + pointer_handle_axis_stop, + pointer_handle_axis_discrete +}; + +static void +keyboard_handle_keymap(struct wthp_keyboard * wthp_keyboard, + uint32_t format, + uint32_t keymap_sz, + void * keymap) +{ + /* ToDo : implement keyboard handle keymap */ +} + +static void +keyboard_handle_enter(struct wthp_keyboard *wthp_keyboard, + uint32_t serial, + struct wthp_surface *surface, + struct wth_array *keys) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_keyboard); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + struct wl_array *wl_key = (struct wl_array *)malloc(sizeof(struct wl_array)); + + wl_key->size = keys->size; + wl_key->alloc = keys->alloc; + wl_key->data = keys->data; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + //transmitter_seat_keyboard_enter(seat, serial, txs, keys); + transmitter_seat_keyboard_enter(seat, serial, txs, wl_key); + } + } + free(wl_key); +} + +static void +keyboard_handle_leave(struct wthp_keyboard *wthp_keyboard, + uint32_t serial, + struct wthp_surface *surface) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_keyboard); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + transmitter_seat_keyboard_leave(seat, serial, txs); + } + } +} + +static void +keyboard_handle_key(struct wthp_keyboard *wthp_keyboard, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_keyboard); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_keyboard_key(seat, serial, time, key, state); +} + +static void +keyboard_handle_modifiers(struct wthp_keyboard *wthp_keyboard, + uint32_t serial, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) +{ + weston_log("keyboard_handle_modifiers\n"); +} + +static void +keyboard_handle_repeat_info(struct wthp_keyboard *wthp_keyboard, + int32_t rate, + int32_t delay) +{ + weston_log("keyboard_handle_repeat_info\n"); +} + +static const struct wthp_keyboard_listener keyboard_listener = { + keyboard_handle_keymap, + keyboard_handle_enter, + keyboard_handle_leave, + keyboard_handle_key, + keyboard_handle_modifiers, + keyboard_handle_repeat_info +}; + +static void +touch_handle_down (struct wthp_touch * wthp_touch, + uint32_t serial, + uint32_t time, + struct wthp_surface * surface, + int32_t id, + wth_fixed_t x, + wth_fixed_t y) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + struct weston_transmitter_surface *txs; + + seat = wl_container_of(seat_list->next, seat, link); + + wl_list_for_each(txs, &remote->surface_list, link) + { + if (txs->wthp_surf == surface) { + transmitter_seat_touch_down(seat, serial, time, + txs, id, x, y); + } + } +} + +static void +touch_handle_up (struct wthp_touch * wthp_touch, + uint32_t serial, + uint32_t time, + int32_t id) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_touch_up(seat, serial, time, id); +} + +static void +touch_handle_motion (struct wthp_touch * wthp_touch, + uint32_t time, + int32_t id, + wth_fixed_t x, + wth_fixed_t y) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_touch_motion(seat, time, id, x, y); +} + + +static void +touch_handle_frame (struct wthp_touch * wthp_touch) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_touch_frame(seat); +} + +static void +touch_handle_cancel (struct wthp_touch * wthp_touch) +{ + struct waltham_display *dpy = + wth_object_get_user_data((struct wth_object *)wthp_touch); + struct weston_transmitter_remote *remote = dpy->remote; + struct wl_list *seat_list = &remote->seat_list; + struct weston_transmitter_seat *seat; + + seat = wl_container_of(seat_list->next, seat, link); + + transmitter_seat_touch_cancel(seat); +} + + +static const struct wthp_touch_listener touch_listener = { + touch_handle_down, + touch_handle_up, + touch_handle_motion, + touch_handle_frame, + touch_handle_cancel +}; + +void +seat_capabilities(struct wthp_seat *wthp_seat, + enum wthp_seat_capability caps) +{ + struct waltham_display *dpy = wth_object_get_user_data((struct wth_object *)wthp_seat); + + weston_log("seat_capabilities\n"); + + if ((caps & WTHP_SEAT_CAPABILITY_POINTER) && !dpy->pointer) + { + weston_log("WTHP_SEAT_CAPABILITY_POINTER\n"); + dpy->pointer = wthp_seat_get_pointer(dpy->seat); + wthp_pointer_set_listener(dpy->pointer, &pointer_listener, dpy); + } + if ((caps & WTHP_SEAT_CAPABILITY_KEYBOARD) && !dpy->keyboard) + { + weston_log("WTHP_SEAT_CAPABILITY_KEYBOARD\n"); + dpy->keyboard = wthp_seat_get_keyboard(dpy->seat); + wthp_keyboard_set_listener(dpy->keyboard, &keyboard_listener, dpy); + } + if ((caps & WTHP_SEAT_CAPABILITY_TOUCH) && !dpy->touch) + { + weston_log("WTHP_SEAT_CAPABILITY_TOUCH\n"); + dpy->touch = wthp_seat_get_touch(dpy->seat); + wthp_touch_set_listener(dpy->touch, &touch_listener, dpy); + } +} + +int +transmitter_remote_create_seat(struct weston_transmitter_remote *remote) +{ + struct weston_transmitter_seat *seat = NULL; + char *name = NULL; + struct weston_seat *weston_seat = NULL; + + seat = zalloc(sizeof *seat); + if (!seat) + goto fail; + + wl_list_init(&seat->get_pointer_listener.link); + wl_list_init(&seat->pointer_focus_destroy_listener.link); + + /* XXX: get the name from remote */ + name = make_seat_name(remote, "default"); + if (!name) + goto fail; + + + if (wl_list_empty(&remote->transmitter->compositor->seat_list)) { + weston_seat = zalloc(sizeof *weston_seat); + if (!weston_seat) + goto fail; + + weston_seat_init(weston_seat, remote->transmitter->compositor, name); + seat->base = weston_seat; + weston_log("Transmitter created seat=%p \n", &seat->base); + } else { + wl_list_for_each(weston_seat, &remote->transmitter->compositor->seat_list, link) { + weston_log("Transmitter weston_seat %p\n", weston_seat); + seat->base = weston_seat; + } + } + + free(name); +#if DEBUG + weston_seat_init(&seat->base, remote->transmitter->compositor, name); + free(name); + + /* Hide the weston_seat from the rest of Weston, there are too many + * things making assumptions: + * - backends assume they control all seats + * - shells assume they control all input foci + * We do not want either to mess with our seat. + */ + wl_list_remove(&seat->base.link); + wl_list_init(&seat->base.link); + + /* The weston_compositor::seat_created_signal has already been + * emitted. Shells use it to subscribe to focus changes, but we should + * never handle focus with weston core... except maybe with keyboard. + * text-backend.c will also act on the new seat. + * It is possible weston_seat_init() needs to be split to fix this + * properly. + */ + + weston_log("Transmitter created seat=%p '%s'\n", + &seat->base, seat->base.seat_name); +#endif + + /* XXX: mirror remote capabilities */ + transmitter_seat_create_pointer(seat); + transmitter_seat_create_keyboard(seat); + transmitter_seat_create_touch(seat); + + wl_list_insert(&remote->seat_list, &seat->link); + + return 0; + +fail: + free(seat); + free(name); + + return -1; +} + +static void +fake_pointer_get_position(struct weston_transmitter_seat *seat, double step, + wl_fixed_t *x, wl_fixed_t *y) +{ + double s, c; + + seat->pointer_phase += step; + while (seat->pointer_phase > 2.0 * M_PI) + seat->pointer_phase -= 2.0 * M_PI; + + sincos(seat->pointer_phase, &s, &c); + *x = wl_fixed_from_double(100.0 + 50.0 * c); + *y = wl_fixed_from_double(100.0 + 50.0 * s); +} + +static int +fake_pointer_timer_handler(void *data) +{ + struct weston_transmitter_seat *seat = data; + wl_fixed_t x, y; + uint32_t time; + struct timespec timespec; + + weston_compositor_get_time(×pec); + time=timespec.tv_sec * 1000 + timespec.tv_nsec / 1000000; + + fake_pointer_get_position(seat, 18.0 / 180.0 * M_PI, &x, &y); + transmitter_seat_pointer_motion(seat, time, x, y); + transmitter_seat_pointer_frame(seat); + + wl_event_source_timer_update(seat->pointer_timer, 100); + + return 0; +} + +int +transmitter_seat_fake_pointer_input(struct weston_transmitter_seat *seat, + struct weston_transmitter_surface *txs) +{ + struct wl_event_loop *loop; + wl_fixed_t x, y; + uint32_t serial = 5; + + /* remove focus from earlier surface */ + transmitter_seat_pointer_leave(seat, serial++, seat->pointer_focus); + transmitter_seat_pointer_frame(seat); + + /* set pointer focus to surface */ + fake_pointer_get_position(seat, 0.0, &x, &y); + transmitter_seat_pointer_enter(seat, serial++, txs, x, y); + transmitter_seat_pointer_frame(seat); + + if (!seat->pointer_timer) { + /* schedule timer for motion */ + loop = wl_display_get_event_loop(seat->base->compositor->wl_display); + seat->pointer_timer = wl_event_loop_add_timer(loop, + fake_pointer_timer_handler, seat); + wl_event_source_timer_update(seat->pointer_timer, 100); + } + + /* XXX: if the now focused surface disappears, we should call + * transmitter_seat_pointer_leave() as part of the mockup. Otherwise + * you get a "Transmitter Warning: no pointer->focus_client?". + */ + + return 0; +} diff --git a/transmitter-plugin/meson.build b/transmitter-plugin/meson.build new file mode 100644 index 0000000..7efd1ce --- /dev/null +++ b/transmitter-plugin/meson.build @@ -0,0 +1,24 @@ + +libpixman_dep = dependency('pixman-1') + +deps_transmiter_plugin = [ + libwayland_dep, + libpixman_dep, + libweston_dep, + libwaltham_dep, + weston_dep, + cc.find_library('m') +] + +plugin_transmitter = shared_library( + 'waltham-transmitter', + 'input.c', + 'output.c', + 'plugin.c', + include_directories: common_inc, + dependencies: deps_transmiter_plugin, + name_prefix: '', + install: true, + install_dir: plugin_install_dir, +) +env_modmap += 'transmitter-plugin.so=@0@;'.format(plugin_transmitter.full_path()) diff --git a/transmitter-plugin/output.c b/transmitter-plugin/output.c new file mode 100644 index 0000000..8aff75d --- /dev/null +++ b/transmitter-plugin/output.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 Advanced Driver Information Technology Joint Venture GmbH + * Copyright © 2020 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include <libweston/libweston.h> + +#include "plugin.h" +#include "transmitter_api.h" + +void +transmitter_output_destroy(struct weston_transmitter_output *output) +{ + wl_list_remove(&output->link); + free(output); +} + +int +transmitter_remote_create_output_with_name(struct weston_transmitter_remote *remote, char *name) +{ + struct weston_transmitter_output *output; + + output = zalloc(sizeof(*output)); + if (!output) + return -1; + + output->remote = remote; + output->name = name; + wl_list_insert(&remote->output_list, &output->link); + + return 0; +} diff --git a/transmitter-plugin/plugin.c b/transmitter-plugin/plugin.c new file mode 100644 index 0000000..2fb60b6 --- /dev/null +++ b/transmitter-plugin/plugin.c @@ -0,0 +1,1024 @@ +/* + * Copyright (C) 2017 Advanced Driver Information Technology Joint Venture GmbH + * Copyright © 2020 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <linux/input.h> + +#include <libweston/libweston.h> +#include <libweston/plugin-registry.h> + +#include <weston.h> +#include <ivi-layout-export.h> + +#include "plugin.h" +#include "transmitter_api.h" + +/* waltham */ +#include <errno.h> +#include <sys/epoll.h> +#include <sys/timerfd.h> +#include <waltham-object.h> +#include <waltham-client.h> +#include <waltham-connection.h> + +#define MAX_EPOLL_WATCHES 2 +#define ESTABLISH_CONNECTION_PERIOD 2000 +#define RETRY_CONNECTION_PERIOD 5000 + +static const struct wthp_seat_listener seat_listener = { + seat_capabilities, + NULL +}; + +/* XXX: all functions and variables with a name, and things marked with a + * comment, containing the word "fake" are mockups that need to be + * removed from the final implementation. + */ + +/** Send configure event through ivi-shell. + * + * \param txs The Transmitter surface. + * \param width Suggestion for surface width. + * \param height Suggestion for surface height. + * + * When the networking code receives a ivi_surface.configure event, it calls + * this function to relay it to the application. + * + * \c txs cannot be a zombie, because transmitter_surface_zombify() must + * tear down the network link, so a zombie cannot receive events. + */ +void +transmitter_surface_ivi_resize(struct weston_transmitter_surface *txs, + int32_t width, int32_t height) +{ + assert(txs->resize_handler); + if (!txs->resize_handler) + return; + + assert(txs->surface); + if (!txs->surface) + return; + + txs->resize_handler(txs->resize_handler_data, width, height); +} + +static void +transmitter_surface_configure(struct weston_transmitter_surface *txs, + int32_t dx, int32_t dy) +{ + assert(txs->surface); + if (!txs->surface) + return; + + txs->attach_dx += dx; + txs->attach_dy += dy; +} + +static void +buffer_send_complete(struct wthp_buffer *b, uint32_t serial) +{ + if (b) + wthp_buffer_destroy(b); +} + +static const struct wthp_buffer_listener buffer_listener = { + buffer_send_complete +}; + +/* + * FIXME: note, at this point, no one is using this. + */ +static void +transmitter_surface_gather_state(struct weston_transmitter_surface *txs) +{ + struct weston_transmitter_remote *remote = txs->remote; + struct waltham_display *dpy = remote->display; + + if (!dpy->running) { + weston_log("transmitter_surface_gather_state() -- dpy not running\n"); + if (remote->status != WESTON_TRANSMITTER_CONNECTION_DISCONNECTED) { + weston_log("transmitter_surface_gather_state() -- disconnected\n"); + + remote->status = + WESTON_TRANSMITTER_CONNECTION_DISCONNECTED; + + wth_connection_destroy(remote->display->connection); + wl_event_source_remove(remote->source); + wl_event_source_timer_update(remote->retry_timer, 1); + } + } else { + /* TODO: transmit surface state to remote */ + /* The buffer must be transmitted to remote side */ + + /* waltham */ + struct weston_surface *surf = txs->surface; + struct weston_compositor *comp = surf->compositor; + int32_t stride, data_sz, width, height; + void *data; + + width = 1; + height = 1; + stride = width * (PIXMAN_FORMAT_BPP(comp->read_format) / 8); + + data = zalloc(stride * height); + data_sz = stride * height; + + /* fake sending buffer */ + txs->wthp_buf = wthp_blob_factory_create_buffer(remote->display->blob_factory, + data_sz, data, surf->width, + surf->height, stride, + PIXMAN_FORMAT_BPP(comp->read_format)); + + wthp_buffer_set_listener(txs->wthp_buf, &buffer_listener, txs); + + wthp_surface_attach(txs->wthp_surf, txs->wthp_buf, + txs->attach_dx, txs->attach_dy); + wthp_surface_damage(txs->wthp_surf, txs->attach_dx, + txs->attach_dy, surf->width, surf->height); + wthp_surface_commit(txs->wthp_surf); + + wth_connection_flush(remote->display->connection); + free(data); + data = NULL; + txs->attach_dx = 0; + txs->attach_dy = 0; + } +} + +/** Mark the weston_transmitter_surface dead. + * + * Stop all remoting actions on this surface. + * + * Still keeps the pointer stored by a shell valid, so it can be freed later. + */ +static void +transmitter_surface_zombify(struct weston_transmitter_surface *txs) +{ + struct weston_transmitter_remote *remote; + /* may be called multiple times */ + if (!txs->surface) + return; + + wl_signal_emit(&txs->destroy_signal, txs); + + wl_list_remove(&txs->surface_destroy_listener.link); + txs->surface = NULL; + + wl_list_remove(&txs->sync_output_destroy_listener.link); + + remote = txs->remote; + + if (!remote->display->compositor) + weston_log("remote->compositor is NULL\n"); + + if (txs->wthp_surf) + wthp_surface_destroy(txs->wthp_surf); + + if (txs->wthp_ivi_surface) + wthp_ivi_surface_destroy(txs->wthp_ivi_surface); + + /* In case called from destroy_transmitter() */ + txs->remote = NULL; +} + +static void +transmitter_surface_destroy(struct weston_transmitter_surface *txs) +{ + transmitter_surface_zombify(txs); + + wl_list_remove(&txs->link); + free(txs); +} + +/** weston_surface destroy signal handler */ +static void +transmitter_surface_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_transmitter_surface *txs = + wl_container_of(listener, txs, surface_destroy_listener); + + assert(data == txs->surface); + + transmitter_surface_zombify(txs); +} + +void +sync_output_destroy_handler(struct wl_listener *listener, void *data) +{ + struct weston_transmitter_surface *txs; + + txs = wl_container_of(listener, txs, sync_output_destroy_listener); + + wl_list_remove(&txs->sync_output_destroy_listener.link); + wl_list_init(&txs->sync_output_destroy_listener.link); + + //weston_surface_force_output(txs->surface, NULL); + weston_surface_unmap(txs->surface); +} + +static void +transmitter_surface_set_ivi_id(struct weston_transmitter_surface *txs, + const char *app_id) +{ + struct weston_transmitter_remote *remote = txs->remote; + struct waltham_display *dpy = remote->display; + + assert(txs->surface); + + if (!dpy) + return; + + if (!dpy->compositor) + return; + + + if (!dpy->seat) + return; + + if (!dpy->application_id) + return; + + txs->wthp_ivi_surface = + wthp_ivi_app_id_surface_create(dpy->application_id, app_id, txs->wthp_surf); + + wth_connection_flush(remote->display->connection); + if (!txs->wthp_ivi_surface) { + weston_log("Failed to create txs->ivi_surf\n"); + return; + } + + weston_log("Created wthp_ivi_surface %p\n", txs->wthp_ivi_surface); +} + +static struct weston_transmitter_surface * +transmitter_surface_push_to_remote(struct weston_surface *ws, const char *app_id, + struct weston_transmitter_remote *remote, + struct wl_listener *stream_status) +{ + struct weston_transmitter *txr = remote->transmitter; + struct weston_transmitter_surface *txs; + bool found = false; + + if (remote->status != WESTON_TRANSMITTER_CONNECTION_READY) { + return NULL; + } + + wl_list_for_each(txs, &remote->surface_list, link) { + if (txs->surface == ws) { + found = true; + break; + } + } + + if (!found) { + txs = NULL; + txs = zalloc(sizeof(*txs)); + + if (!txs) { + weston_log("No memory to create weston_transmitter_surface\n"); + return NULL; + } + + txs->remote = remote; + wl_signal_init(&txs->destroy_signal); + wl_list_insert(&remote->surface_list, &txs->link); + + txs->status = WESTON_TRANSMITTER_STREAM_INITIALIZING; + wl_signal_init(&txs->stream_status_signal); + if (stream_status) + wl_signal_add(&txs->stream_status_signal, stream_status); + + txs->surface = ws; + txs->surface_destroy_listener.notify = transmitter_surface_destroyed; + wl_signal_add(&ws->destroy_signal, &txs->surface_destroy_listener); + + wl_list_init(&txs->sync_output_destroy_listener.link); + + wl_list_init(&txs->frame_callback_list); + wl_list_init(&txs->feedback_list); + + txs->lyt = weston_plugin_api_get(txr->compositor, + IVI_LAYOUT_API_NAME, sizeof(txs->lyt)); + } + + /* TODO: create the content stream connection... */ + if (!remote->display->compositor) + weston_log("remote->compositor is NULL\n"); + + if (!txs->wthp_surf) { + txs->wthp_surf = wthp_compositor_create_surface(remote->display->compositor); + wth_connection_flush(remote->display->connection); + + transmitter_surface_set_ivi_id(txs, app_id); + } + + return txs; +} + +static enum weston_transmitter_stream_status +transmitter_surface_get_stream_status(struct weston_transmitter_surface *txs) +{ + return txs->status; +} + +/* waltham */ + +/* The server advertises a global interface. We can store the ad for later + * and/or bind to it immediately if we want to. We also need to keep track of + * the globals we bind to, so that global_remove can be handled properly (not + * implemented). + */ +static void +registry_handle_global(struct wthp_registry *registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + struct waltham_display *dpy = wth_object_get_user_data((struct wth_object *)registry); + + if (strcmp(interface, "wthp_compositor") == 0) { + assert(!dpy->compositor); + dpy->compositor = (struct wthp_compositor *) + wthp_registry_bind(registry, name, interface, 1); + /* has no events to handle */ + } else if (strcmp(interface, "wthp_blob_factory") == 0) { + assert(!dpy->blob_factory); + dpy->blob_factory = (struct wthp_blob_factory *) + wthp_registry_bind(registry, name, interface, 1); + /* has no events to handle */ + } else if (strcmp(interface, "wthp_seat") == 0) { + assert(!dpy->seat); + dpy->seat = (struct wthp_seat *) + wthp_registry_bind(registry, name, interface, 1); + wthp_seat_set_listener(dpy->seat, &seat_listener, dpy); + } else if (strcmp(interface, "wthp_ivi_application") == 0) { + assert(!dpy->application); + dpy->application = (struct wthp_ivi_application *) + wthp_registry_bind(registry, name, interface, 1); + } else if (strcmp(interface, "wthp_ivi_app_id") == 0) { + assert(!dpy->application_id); + dpy->application_id = (struct wthp_ivi_app_id *) + wthp_registry_bind(registry, name, interface, 1); + } +} + +/* notify connection ready */ +static void +conn_ready_notify(struct wl_listener *l, void *data) +{ + struct weston_transmitter_remote *remote = + wl_container_of(l, remote, establish_listener); + + weston_log("conn_ready_notify()\n"); + + /* Outputs and seats are dynamic, do not guarantee they are all + * present when signalling connection status. + */ + transmitter_remote_create_output_with_name(remote, strdup(remote->model)); + transmitter_remote_create_seat(remote); +} + +/* waltham */ +/* The server removed a global. + * We should destroy everything we created through that global, + * and destroy the objects we created by binding to it. + * The identification happens by global's name, so we need to keep + * track what names we bound. + * (not implemented) + */ +static void +registry_handle_global_remove(struct wthp_registry *wthp_registry, + uint32_t name) +{ + if (wthp_registry) + wthp_registry_free(wthp_registry); +} + +static const struct wthp_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + +static void +connection_handle_data(struct watch *w, uint32_t events) +{ + struct waltham_display *dpy = wl_container_of(w, dpy, conn_watch); + struct weston_transmitter_remote *remote = dpy->remote; + int ret; + + + if (!dpy->running) { + weston_log("This server is not running yet. %s:%s\n", remote->addr, remote->port); + return; + } + + if (events & EPOLLERR) { + weston_log("Connection errored out.\n"); + dpy->running = false; + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + return; + } + + if (events & EPOLLOUT) { + /* Flush out again. If the flush completes, stop + * polling for writable as everything has been written. + */ + ret = wth_connection_flush(dpy->connection); + } + + if (events & EPOLLIN) { + /* Do not ignore EPROTO */ + ret = wth_connection_read(dpy->connection); + + if (ret < 0) { + weston_log("Connection read error %s:%s\n", remote->addr, remote->port); + perror("Connection read error\n"); + dpy->running = false; + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + perror("EPOLL_CTL_DEL\n"); + return; + } + } + + if (events & EPOLLHUP) { + weston_log("Connection hung up.\n"); + dpy->running = false; + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + + return; + } +} + +static int +waltham_mainloop(int fd, uint32_t mask, void *data) +{ + struct weston_transmitter_remote *remote = data; + struct watch *w; + int ret; + int running_display; + running_display = 0; + + struct waltham_display *dpy = remote->display; + w = &dpy->conn_watch; + if (!dpy) + return -1; + + if (!dpy->connection) + dpy->running = false; + + if (!dpy->running) + return -1; + + running_display++; + /* Dispatch queued events. */ + ret = wth_connection_dispatch(dpy->connection); + if (ret < 0) { + dpy->running = false; + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + } + + if (!dpy->running) + return -1; + + /* Run any application idle tasks at this point. */ + /* (nothing to run so far) */ + + /* Flush out buffered requests. If the Waltham socket is + * full, poll it for writable too, and continue flushing then. + */ + ret = wth_connection_flush(dpy->connection); + + if (0 < running_display) { + /* Waltham events only read in the callback, not dispatched, + * if the Waltham socket signalled readable. If it signalled + * writable, flush more. See connection_handle_data(). + */ + w->cb(w, mask); + } + + return 0; +} + +static int +waltham_client_init(struct waltham_display *dpy) +{ + if (!dpy) + return -1; + /* + * get server_address from controller (adrress is set to weston.ini) + */ + dpy->connection = wth_connect_to_server(dpy->remote->addr, dpy->remote->port); + if(!dpy->connection) { + return -2; + } + else { + dpy->remote->status = WESTON_TRANSMITTER_CONNECTION_READY; + wl_signal_emit(&dpy->remote->connection_status_signal, dpy->remote); + } + + dpy->conn_watch.display = dpy; + dpy->conn_watch.cb = connection_handle_data; + dpy->conn_watch.fd = wth_connection_get_fd(dpy->connection); + dpy->remote->source = wl_event_loop_add_fd(dpy->remote->transmitter->loop, + dpy->conn_watch.fd, + WL_EVENT_READABLE, + waltham_mainloop, dpy->remote); + + dpy->display = wth_connection_get_display(dpy->connection); + /* wth_display_set_listener() is already done by waltham, as + * all the events are just control messaging. + */ + + /* Create a registry so that we will get advertisements of the + * interfaces implemented by the server. + */ + dpy->registry = wth_display_get_registry(dpy->display); + wthp_registry_set_listener(dpy->registry, ®istry_listener, dpy); + + /* Roundtrip ensures all globals' ads have been received. */ + if (wth_connection_roundtrip(dpy->connection) < 0) { + weston_log("Roundtrip failed.\n"); + return -1; + } + + if (!dpy->compositor) { + weston_log("Did not find wthp_compositor, quitting.\n"); + return -1; + } + + dpy->running = true; + weston_log("waltham_client_init()\n"); + + return 0; +} + +static int +establish_timer_handler(void *data) +{ + struct weston_transmitter_remote *remote = data; + int ret; + + ret = waltham_client_init(remote->display); + if (ret == -2) { + wl_event_source_timer_update(remote->establish_timer, + ESTABLISH_CONNECTION_PERIOD); + return 0; + } + + weston_log("establish_timer_handler()\n"); + + remote->status = WESTON_TRANSMITTER_CONNECTION_READY; + wl_signal_emit(&remote->connection_status_signal, remote); + return 0; +} + +static void +init_globals(struct waltham_display *dpy) +{ + dpy->compositor = NULL; + dpy->blob_factory = NULL; + dpy->seat = NULL; + dpy->application = NULL; + dpy->pointer = NULL; + dpy->keyboard = NULL; + dpy->touch = NULL; +} + +static void +disconnect_surface(struct weston_transmitter_remote *remote) +{ + struct weston_transmitter_surface *txs; + + wl_list_for_each(txs, &remote->surface_list, link) { + free(txs->wthp_ivi_surface); + txs->wthp_ivi_surface = NULL; + free(txs->wthp_surf); + txs->wthp_surf = NULL; + } +} + +static int +retry_timer_handler(void *data) +{ + struct weston_transmitter_remote *remote = data; + struct waltham_display *dpy = remote->display; + + weston_log("retry_timer_handler()\n"); + + if (!dpy->running) { + registry_handle_global_remove(dpy->registry, 1); + init_globals(dpy); + disconnect_surface(remote); + wl_event_source_timer_update(remote->establish_timer, + ESTABLISH_CONNECTION_PERIOD); + return 0; + } else { + wl_event_source_timer_update(remote->retry_timer, + RETRY_CONNECTION_PERIOD); + } + + return 0; +} + +static struct weston_transmitter_remote * +transmitter_connect_to_remote(struct weston_transmitter *txr) +{ + struct weston_transmitter_remote *remote; + struct wl_event_loop *loop_est, *loop_retry; + + wl_list_for_each_reverse(remote, &txr->remote_list, link) { + /* XXX: actually start connecting */ + /* waltham */ + remote->display = zalloc(sizeof *remote->display); + if (!remote->display) { + weston_log("Failed to allocate remote display\n"); + return NULL; + } + + remote->display->remote = remote; + + weston_log("transmitter_connect_to_remote() for remote %p\n", remote); + + /* set connection establish timer */ + loop_est = wl_display_get_event_loop(txr->compositor->wl_display); + remote->establish_timer = + wl_event_loop_add_timer(loop_est, establish_timer_handler, remote); + wl_event_source_timer_update(remote->establish_timer, 1); + + /* set connection retry timer */ + loop_retry = wl_display_get_event_loop(txr->compositor->wl_display); + remote->retry_timer = + wl_event_loop_add_timer(loop_retry, retry_timer_handler, remote); + wl_signal_emit(&remote->conn_establish_signal, NULL); + } + + return remote; +} + +static enum weston_transmitter_connection_status +transmitter_remote_get_status(struct weston_transmitter_remote *remote) +{ + return remote->status; +} + +static void +transmitter_remote_destroy(struct weston_transmitter_remote *remote) +{ + struct weston_transmitter_surface *txs; + struct weston_transmitter_output *output, *otmp; + struct weston_transmitter_seat *seat, *stmp; + + /* Do not emit connection_status_signal. */ + + /* + * Must not touch remote->transmitter as it may be stale: the + * destruction order between the shell and Transmitter is undefined. + */ + + if (!wl_list_empty(&remote->surface_list)) + weston_log("Transmitter warning: surfaces remain in %s.\n", __func__); + + wl_list_for_each(txs, &remote->surface_list, link) + txs->remote = NULL; + + wl_list_remove(&remote->surface_list); + + wl_list_for_each_safe(seat, stmp, &remote->seat_list, link) + transmitter_seat_destroy(seat); + + wl_list_for_each_safe(output, otmp, &remote->output_list, link) + transmitter_output_destroy(output); + + free(remote->addr); + wl_list_remove(&remote->link); + + wl_event_source_remove(remote->source); + + free(remote); +} + +/** Transmitter is destroyed on compositor shutdown. */ +static void +transmitter_compositor_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_transmitter_remote *remote; + struct weston_transmitter_surface *txs; + struct weston_transmitter *txr = + wl_container_of(listener, txr, compositor_destroy_listener); + + assert(data == txr->compositor); + + /* may be called before or after shell cleans up */ + wl_list_for_each(remote, &txr->remote_list, link) { + wl_list_for_each(txs, &remote->surface_list, link) { + transmitter_surface_zombify(txs); + } + } + + /* + * Remove the head in case the list is not empty, to avoid + * transmitter_remote_destroy() accessing freed memory if the shell + * cleans up after Transmitter. + */ + wl_list_remove(&txr->remote_list); + + free(txr); +} + +static struct weston_transmitter * +transmitter_get(struct weston_compositor *compositor) +{ + struct wl_listener *listener; + struct weston_transmitter *txr; + + listener = wl_signal_get(&compositor->destroy_signal, + transmitter_compositor_destroyed); + if (!listener) + return NULL; + + txr = wl_container_of(listener, txr, compositor_destroy_listener); + assert(compositor == txr->compositor); + + return txr; +} + +static void +transmitter_register_connection_status(struct weston_transmitter *txr, + struct wl_listener *connected_listener) +{ + wl_signal_add(&txr->connected_signal, connected_listener); +} + +static struct weston_surface * +transmitter_get_weston_surface(struct weston_transmitter_surface *txs) +{ + return txs->surface; +} + +static struct weston_transmitter_remote * +transmitter_get_transmitter_remote(const char *output_transmitter_name, struct weston_transmitter *transmitter) +{ + + struct weston_transmitter_remote *trans_remote; + + if (!output_transmitter_name) + return NULL; + + wl_list_for_each(trans_remote, &transmitter->remote_list, link) { + struct weston_transmitter_output *trans_output; + wl_list_for_each(trans_output, &trans_remote->output_list, link) { + const char *name = trans_output->base.name; + + if (name && !strcmp(output_transmitter_name, name)) + return trans_remote; + + name = trans_output->name; + if (name && !strcmp(output_transmitter_name, name)) + return trans_remote; + } + } + + return NULL; +} + +static const struct weston_transmitter_api transmitter_api_impl = { + transmitter_get, + transmitter_connect_to_remote, + transmitter_remote_get_status, + transmitter_remote_destroy, + transmitter_surface_push_to_remote, + transmitter_surface_get_stream_status, + transmitter_surface_destroy, + transmitter_surface_configure, + transmitter_surface_gather_state, + transmitter_register_connection_status, + transmitter_get_weston_surface, + transmitter_get_transmitter_remote, +}; + +static void +transmitter_surface_set_resize_callback( + struct weston_transmitter_surface *txs, + weston_transmitter_ivi_resize_handler_t cb, + void *data) +{ + txs->resize_handler = cb; + txs->resize_handler_data = data; +} + +static void +_transmitter_surface_set_ivi_id(struct weston_transmitter_surface *txs, uint32_t ivi_id) +{ + +} + +static const struct weston_transmitter_ivi_api transmitter_ivi_api_impl = { + _transmitter_surface_set_ivi_id, + transmitter_surface_set_resize_callback, +}; + +static int +transmitter_create_remote(struct weston_transmitter *txr, + const char *model, + const char *addr, + const char *port, + const char *width, + const char *height) +{ + struct weston_transmitter_remote *remote; + + remote = zalloc(sizeof (*remote)); + if (!remote) + return -1; + + remote->transmitter = txr; + wl_list_insert(&txr->remote_list, &remote->link); + + remote->model = strdup(model); + remote->addr = strdup(addr); + remote->port = strdup(port); + if (remote->width) + remote->width = atoi(width); + if (remote->height) + remote->height = atoi(height); + remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING; + + weston_log("Creating transmitter remote output model %s, addr %s, " + "port %s, width %d, height %d, status %d\n", + remote->model, remote->addr, remote->port, remote->width, + remote->height, remote->status); + + wl_signal_init(&remote->connection_status_signal); + + wl_list_init(&remote->output_list); + wl_list_init(&remote->surface_list); + wl_list_init(&remote->seat_list); + + wl_signal_init(&remote->conn_establish_signal); + remote->establish_listener.notify = conn_ready_notify; + wl_signal_add(&remote->conn_establish_signal, &remote->establish_listener); + + return 0; +} + +struct wet_compositor { + struct weston_config *config; + struct wet_output_config *parsed_options; + struct wl_listener pending_output_listener; + bool drm_use_current_mode; +}; + +static int +load_config(struct weston_config **config, bool no_config, + const char *config_file) +{ + const char *file = "agl-compositor.ini"; + const char *full_path; + + if (config_file) + file = config_file; + + if (!no_config) + *config = weston_config_parse(file); + + if (*config) { + full_path = weston_config_get_full_path(*config); + + weston_log("Using config file '%s'.\n", full_path); + setenv(WESTON_CONFIG_FILE_ENV_VAR, full_path, 1); + + return 0; + } + + if (config_file && !no_config) { + weston_log("fatal: error opening or reading config file '%s'.\n", + config_file); + + return -1; + } + + weston_log("Starting with no config file.\n"); + setenv(WESTON_CONFIG_FILE_ENV_VAR, "", 1); + + return 0; +} + + +static void +transmitter_get_server_config(struct weston_transmitter *txr) +{ + struct weston_config *config = NULL; + struct weston_config_section *section; + const char *name = NULL; + char *model = NULL; + char *addr = NULL; + char *port = NULL; + char *width = NULL; + char *height = NULL; + int ret; + + /* FIXME: this assume as hard-coded agl-compositor */ + if (load_config(&config, 0, "agl-compositor.ini") < 0) + return; + + section = weston_config_get_section(config, "remote", NULL, NULL); + + while (weston_config_next_section(config, §ion, &name)) { + if (0 == strcmp(name, "transmitter-output")) { + if (0 != weston_config_section_get_string(section, "name", + &model, 0)) + continue; + + if (0 != weston_config_section_get_string(section, "host", + &addr, 0)) + continue; + + if (0 != weston_config_section_get_string(section, "port", + &port, 0)) + continue; + + ret = transmitter_create_remote(txr, model, addr, + port, width, height); + if (ret < 0) { + weston_log("Fatal: Transmitter create_remote failed.\n"); + } + } + } +} + +WL_EXPORT int +wet_module_init(struct weston_compositor *compositor, int *argc, char *argv[]) +{ + struct weston_transmitter *txr; + int ret; + + txr = zalloc(sizeof *txr); + if (!txr){ + weston_log("Transmitter disabled\n"); + return -1; + } + wl_list_init(&txr->remote_list); + + txr->compositor = compositor; + txr->compositor_destroy_listener.notify = + transmitter_compositor_destroyed; + wl_signal_add(&compositor->destroy_signal, + &txr->compositor_destroy_listener); + + ret = weston_plugin_api_register(compositor, + WESTON_TRANSMITTER_API_NAME, + &transmitter_api_impl, + sizeof(transmitter_api_impl)); + if (ret < 0) { + weston_log("Fatal: Transmitter API registration failed.\n"); + goto fail; + } + + ret = weston_plugin_api_register(compositor, + WESTON_TRANSMITTER_IVI_API_NAME, + &transmitter_ivi_api_impl, + sizeof(transmitter_ivi_api_impl)); + if (ret < 0) { + weston_log("Fatal: Transmitter IVI API registration failed.\n"); + goto fail; + } + + weston_log("Transmitter initialized.\n"); + + txr->loop = wl_display_get_event_loop(compositor->wl_display); + transmitter_get_server_config(txr); + transmitter_connect_to_remote(txr); + + return 0; + +fail: + wl_list_remove(&txr->compositor_destroy_listener.link); + free(txr); + + return -1; +} diff --git a/transmitter-plugin/weston.ini.transmitter b/transmitter-plugin/weston.ini.transmitter new file mode 100644 index 0000000..0d5efe9 --- /dev/null +++ b/transmitter-plugin/weston.ini.transmitter @@ -0,0 +1,13 @@ +[transmitter-output] +name=transmitter_1 +mode=800x600@60 +host=192.168.100.9 +port=34400 +agl-shell-app-id=weston-simple-egl + +[transmitter-output] +name=transmitter_2 +mode=800x600@60 +host=192.168.100.9 +port=34401 +agl-shell-app-id=weston-simple-shm |