/* * 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); } } static int transmitter_output_disable(struct weston_output *base) { struct weston_transmitter_output *output = wl_container_of(base, output, base); wl_event_source_remove(output->finish_frame_timer); return 0; } void transmitter_output_destroy(struct weston_transmitter_output *output) { wl_list_remove(&output->link); struct weston_head *head=weston_output_get_first_head(&output->base); free_mode_list(&output->base.mode_list); weston_head_release(head); free(head); transmitter_output_disable(&output->base); weston_output_release(&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 int transmitter_output_finish_frame_handler(void *data) { struct weston_transmitter_output *output = data; struct timespec now; weston_compositor_read_presentation_clock(output->base.compositor, &now); weston_output_finish_frame(&output->base, &now, 0); return 0; } static void transmitter_start_repaint_loop(struct weston_output *base) { struct weston_transmitter_output *output = wl_container_of(base, output, base); weston_output_finish_frame(&output->base,NULL, WP_PRESENTATION_FEEDBACK_INVALID); } static int transmitter_output_repaint(struct weston_output *base, pixman_region32_t *damage,void *repaint_data) { 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_READY) goto out; wl_list_for_each_reverse(view, &compositor->view_list, link) { bool found_surface = false; if (view->output == &output->base && (view->surface->width >= 64 && view->surface->height >= 64)) { 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){ txs = 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; } 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_output) goto out; wl_event_source_timer_update(output->finish_frame_timer,1); return 0; out: wl_event_source_timer_update(output->finish_frame_timer,1); return 0; } static void transmitter_assign_planes(struct weston_output *base,void *repaint_data) { /* * 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 && (view->surface->width >= 64 && view->surface->height >= 64)) { 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); struct wl_event_loop *loop; 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; loop = wl_display_get_event_loop(base->compositor->wl_display); output->finish_frame_timer = wl_event_loop_add_timer(loop, transmitter_output_finish_frame_handler, output); return 0; } /* FIXME:This is a dummy call we just return 0(success)*/ int transmitter_output_attach_head(struct weston_output *output, struct weston_head *head) { weston_log("%s is called\n",__func__); return 0; } /* FIXME:This is a dummy call we just return 0(success)*/ int transmitter_output_detach_head(struct weston_output *output, struct weston_head *head) { weston_log("%s is called\n",__func__); 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_head *head; const char *make = strdup(WESTON_TRANSMITTER_OUTPUT_MAKE); const char *model = make_model(remote, 1); const char *serial_number = strdup("0"); const char *connector_name = make_model(remote, 1); head=zalloc(sizeof *head); if (!head){ weston_log("allocation failed for head\n"); return -1; } output = zalloc(sizeof *output); if (!output) return -1; output->parent.draw_initial_frame = true; weston_head_init(head,connector_name); weston_head_set_subpixel(head, info->subpixel); weston_head_set_monitor_strings(head, make, model, serial_number); head->compositor=remote->transmitter->compositor; /* x and y is fake value */ wl_list_init(&output->base.mode_list); output->base.name = make_model(remote, 1); /* WL_OUTPUT_MODE_CURRENT already set */ weston_output_init(&output->base, remote->transmitter->compositor,output->base.name); 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; 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.disable = transmitter_output_disable; 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->base.attach_head = transmitter_output_attach_head; output->base.detach_head = transmitter_output_detach_head; 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; } if(!weston_output_attach_head(&output->base,head)){ weston_log("Weston head attached successfully to output\n"); } if(weston_output_enable(&output->base)<0){ weston_log("Failed to enable weston output\n"); } return 0; fail: free_mode_list(&output->base.mode_list); free(head); free(output); return -1; }