aboutsummaryrefslogtreecommitdiffstats
path: root/transmitter-plugin/plugin.c
diff options
context:
space:
mode:
authorMarius Vlad <marius.vlad@collabora.com>2020-10-16 00:05:51 +0300
committerMarius Vlad <marius.vlad@collabora.com>2020-10-20 00:14:19 +0300
commit0e79ab59165bf925f1288476dad66109aa01b3fa (patch)
treecbabf8c3649c014fc996b97a753ec4e51e9f7949 /transmitter-plugin/plugin.c
parent13e791f0158ca79a0cfdb00613f69eaaf255da55 (diff)
Add waltham-transmitter-plugin
This adds the waltham-transmiter-plugin, which is a copy-pasta version of the wayland-ivi-plugins developed by ADIT-J. It has been split into a different repository. The major change from the initial version is the fact that remote output has been completely removed, and it only takes care of remote input. The renderer side has been moved/migrated into attic/ directory for further posterity. Bug-AGL: 3601 Signed-off-by: Marius Vlad <marius.vlad@collabora.com> Change-Id: Ifc1a6f58567d8b86cbe6e84dc1de79246dd95435
Diffstat (limited to 'transmitter-plugin/plugin.c')
-rw-r--r--transmitter-plugin/plugin.c1024
1 files changed, 1024 insertions, 0 deletions
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, &registry_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, &section, &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;
+}