diff options
-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 |