/* * Copyright (C) 2017 Advanced Driver Information Technology Joint Venture GmbH * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "compositor.h" #include "compositor-drm.h" #include "plugin-registry.h" #include "plugin.h" #include "transmitter_api.h" #include "waltham-renderer.h" /** @file * * This is an implementation of a remote output. * * A remote output must not be accepted as an argument to: * - wl_shell_surface.set_fullscreen * - wl_shell_surface.set_maximized * - zwp_fullscreen_shell_v1.present_surface * - zwp_fullscreen_shell_v1.present_surface_for_mode * - zwp_input_panel_surface_v1.set_toplevel * - xdg_surface.set_fullscreen * * If a remote output is an argument to the above or similar requests, * it should have the same effect as NULL if possible. * * @todo Should we instead accept the argument and have it start remoting * automatically? That would be shell-specific. * * In ivi-shell's case, only zwp_input_panel_surface_v1.set_toplevel is * reachable from keyboard.c. That just blindly uses whatever the first * output happens to be, so there is no need to check for now. * * @todo Add weston_output_set_remote() which sets weston_output::is_remote * to true and inits weston_output::link. This should be made mutually * exclusive with weston_compositor_add_output(). */ static struct waltham_renderer_interface *waltham_renderer; static char * make_model(struct weston_transmitter_remote *remote, int name) { char *str; if (asprintf(&str, "transmitter-%s:%s-%d", remote->addr, remote->port, name) < 0) return NULL; return str; } static int make_mode_list(struct wl_list *list, const struct weston_transmitter_output_info *info) { struct weston_mode *mode; mode = zalloc(sizeof *mode); if (!mode) return -1; *mode = info->mode; wl_list_insert(list->prev, &mode->link); return 0; } static struct weston_mode * get_current_mode(struct wl_list *mode_list) { struct weston_mode *mode; wl_list_for_each(mode, mode_list, link) if (mode->flags & WL_OUTPUT_MODE_CURRENT) return mode; assert(0); return NULL; } static void free_mode_list(struct wl_list *mode_list) { struct weston_mode *mode; while (!wl_list_empty(mode_list)) { mode = wl_container_of(mode_list->next, mode, link); wl_list_remove(&mode->link); free(mode); } } void transmitter_output_destroy(struct weston_transmitter_output *output) { wl_list_remove(&output->link); free_mode_list(&output->base.mode_list); free(output->base.serial_number); free(output->base.model); free(output->base.make); weston_output_destroy(&output->base); free(output); } static void transmitter_output_destroy_(struct weston_output *base) { struct weston_transmitter_output *output = wl_container_of(base, output, base); transmitter_output_destroy(output); } static void transmitter_start_repaint_loop(struct weston_output *base) { struct timespec ts; struct weston_transmitter_output *output = wl_container_of(base, output, base); weston_compositor_read_presentation_clock(output->base.compositor, &ts); weston_output_finish_frame(&output->base, &ts, 0); } static int transmitter_output_repaint(struct weston_output *base, pixman_region32_t *damage) { struct weston_transmitter_output* output = wl_container_of(base, output, base); struct weston_transmitter_remote* remote = output->remote; struct weston_transmitter* txr = remote->transmitter; struct weston_transmitter_api* transmitter_api = weston_get_transmitter_api(txr->compositor); struct weston_transmitter_surface* txs; struct weston_compositor *compositor = base->compositor; struct weston_view *view; bool found_output = false; struct timespec ts; struct weston_drm_output_api *api = weston_plugin_api_get(txr->compositor, WESTON_DRM_OUTPUT_API_NAME, sizeof(api)); /* * Pick up weston_view in transmitter_output and check weston_view's surface * If the surface hasn't been conbined to weston_transmitter_surface, * then call push_to_remote. * If the surface has already been combined, call gather_state. */ if (wl_list_empty(&compositor->view_list)) goto out; if (remote->status == WESTON_TRANSMITTER_CONNECTION_DISCONNECTED) goto out; wl_list_for_each_reverse(view, &compositor->view_list, link) { bool found_surface = false; if (view->output == &output->base) { found_output = true; wl_list_for_each(txs, &remote->surface_list, link) { if (txs->surface == view->surface) { found_surface = true; if (!txs->wthp_surf) transmitter_api->surface_push_to_remote (view->surface, remote, NULL); output->renderer->dmafd = api->get_dma_fd_from_view(&output->base, view, &output->renderer->buf_stride); if(output->renderer->dmafd < 0) { weston_log("Failed to get dmafd\n"); goto out; } /* * Updating the width x height * from surface to gst-recorder */ output->renderer->surface_width = view->surface->width; output->renderer->surface_height = view->surface->height; output->renderer->repaint_output(output); output->renderer->dmafd = NULL; transmitter_api->surface_gather_state(txs); weston_buffer_reference(&view->surface->buffer_ref, NULL); break; } } if (!found_surface) transmitter_api->surface_push_to_remote(view->surface, remote, NULL); } } if (!found_output) goto out; weston_compositor_read_presentation_clock(output->base.compositor, &ts); weston_output_finish_frame(&output->base, &ts, 0); return 0; out: transmitter_start_repaint_loop(base); return 0; } static void transmitter_assign_planes(struct weston_output *base) { /* * This function prevents compositor releasing buffer early. */ struct weston_transmitter_output* output = wl_container_of(base, output, base); struct weston_transmitter_remote* remote = output->remote; struct weston_transmitter_surface* txs; struct weston_compositor *compositor = base->compositor; struct weston_view *view; wl_list_for_each_reverse(view, &compositor->view_list, link) { if (view->output == &output->base) { wl_list_for_each(txs, &remote->surface_list, link) { if (txs->surface == view->surface) view->surface->keep_buffer = true; } } } } static int transmitter_output_enable(struct weston_output *base) { struct weston_transmitter_output *output = wl_container_of(base, output, base); if (!output) { weston_log("No weston output found\n"); return -1; } output->base.assign_planes = transmitter_assign_planes; output->base.set_backlight = NULL; output->base.set_dpms = NULL; output->base.switch_mode = NULL; return 0; } int transmitter_remote_create_output(struct weston_transmitter_remote *remote, const struct weston_transmitter_output_info *info) { struct weston_transmitter_output *output; struct weston_transmitter *txr = remote->transmitter; struct weston_output *def_output; output = zalloc(sizeof *output); if (!output) return -1; output->parent.draw_initial_frame = true; output->base.subpixel = info->subpixel; output->base.name = make_model(remote, 1); output->base.make = strdup(WESTON_TRANSMITTER_OUTPUT_MAKE); output->base.model = make_model(remote, 1); output->base.serial_number = strdup("0"); /* x and y is fake value */ wl_list_init(&output->base.mode_list); if (make_mode_list(&output->base.mode_list, info) < 0) goto fail; output->base.current_mode = get_current_mode(&output->base.mode_list); output->base.height = output->base.current_mode->height; output->base.width = output->base.current_mode->width; /* WL_OUTPUT_MODE_CURRENT already set */ weston_output_init(&output->base, remote->transmitter->compositor); /* * renderer_output_create skipped: * no renderer awareness is needed for this output */ /* * weston_compositor_add_output() skipped: * Most other code uses weston_compositor::output_list when traversing * all outputs, we do not want any of that. * Also weston_compositor::output_created_signal must not trigger * for this output, since we must not involve input device management * or color management or any kind of local management. */ output->base.enable = transmitter_output_enable; output->base.start_repaint_loop = transmitter_start_repaint_loop; output->base.repaint = transmitter_output_repaint; output->base.destroy = transmitter_output_destroy_; output->base.assign_planes = NULL; output->base.set_dpms = NULL; output->base.switch_mode = NULL; output->base.gamma_size = 0; output->base.set_gamma = NULL; output->base.native_mode = output->base.current_mode; output->base.native_scale = output->base.current_scale; output->base.scale = 1; output->base.transform = WL_OUTPUT_TRANSFORM_NORMAL; output->remote = remote; wl_list_insert(&remote->output_list, &output->link); if (txr->waltham_renderer->display_create(output) < 0) { weston_log("Failed to create waltham renderer display \n"); return -1; } weston_output_enable(&output->base); return 0; fail: free_mode_list(&output->base.mode_list); free(output->base.serial_number); free(output->base.model); free(output->base.make); free(output->base.name); free(output); return -1; }