diff options
Diffstat (limited to 'weston-ivi-shell/src/ivi-share.c')
-rw-r--r-- | weston-ivi-shell/src/ivi-share.c | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/weston-ivi-shell/src/ivi-share.c b/weston-ivi-shell/src/ivi-share.c new file mode 100644 index 0000000..dedbf45 --- /dev/null +++ b/weston-ivi-shell/src/ivi-share.c @@ -0,0 +1,733 @@ +/************************************************************************** + * + * Copyright 2015 Codethink Ltd + * Copyright (C) 2015 Advanced Driver Information Technology Joint Venture GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************/ +#include <stdio.h> + +#include <fcntl.h> +#include <string.h> +#include <assert.h> + +#include "ivi-share.h" +#include "ivi-share-server-protocol.h" + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +struct ivi_share_nativesurface_client_link +{ + struct wl_resource *resource; + struct wl_client *client; + bool firstSendConfigureComp; + struct wl_list link; /* ivi_share_nativesurface link */ + struct ivi_share_nativesurface *parent; +}; + +struct shell_surface +{ + struct wl_resource *resource; + struct wl_listener surface_destroy_listener; + struct weston_surface *surface; + uint32_t surface_id; + struct wl_list link; /* ivi_shell_share_ext link */ +}; + +struct redirect_target { + struct wl_client *client; + struct wl_resource *resource; + struct wl_resource *target_resource; + uint32_t id; + struct wl_list link; +}; + +struct update_sharing_surface_content { + struct wl_listener listener; + struct ivi_shell_share_ext *shell_ext; +}; + +static void +free_nativesurface(struct ivi_share_nativesurface *nativesurf) +{ + if (NULL == nativesurf) { + return; + } + + nativesurf->surface_destroy_listener.notify = NULL; + wl_list_remove(&nativesurf->surface_destroy_listener.link); + free(nativesurf); +} + +static void +share_surface_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + struct ivi_share_nativesurface_client_link *client_link = + wl_resource_get_user_data(resource); + if (NULL == client_link) { + weston_log("share_surface does not have user data\n"); + return; + } + + wl_resource_destroy(resource); +} + +static struct weston_seat * +get_weston_seat(struct weston_compositor *compositor, + struct ivi_share_nativesurface_client_link *client_link) +{ + struct weston_seat *link = NULL; + struct wl_client *target_client = + wl_resource_get_client(client_link->parent->surface->resource); + + wl_list_for_each(link, &compositor->seat_list, link) { + struct wl_resource *res; + wl_list_for_each(res, &link->base_resource_list, link) { + struct wl_client *client = wl_resource_get_client(res); + if (target_client == client && link->touch_state != NULL) { + return link; + } + } + } + + return NULL; +} + +static uint32_t +get_event_time() +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return (tv.tv_sec * 1000 + tv.tv_usec / 1000); +} + +static void +share_surface_redirect_touch_down(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial, + int32_t id, + wl_fixed_t x, + wl_fixed_t y) +{ + struct ivi_share_nativesurface_client_link *client_link = wl_resource_get_user_data(resource); + struct weston_seat *seat = NULL; + struct wl_resource *target_resource = NULL; + uint32_t time = get_event_time(); + struct redirect_target *redirect_target = NULL; + struct wl_resource *surface_resource; + + if (client_link->parent == NULL) { + weston_log("share_surface_redirect_touch_down: No parent surface\n"); + return; + } + surface_resource = client_link->parent->surface->resource; + + seat = get_weston_seat(client_link->parent->shell_ext->wc, client_link); + if (seat == NULL || seat->touch_state == NULL) { + return; + } + + wl_list_for_each(target_resource, &seat->touch_state->resource_list, link) { + if (wl_resource_get_client(target_resource) == wl_resource_get_client(surface_resource)) { + uint32_t new_serial = + wl_display_next_serial(client_link->parent->shell_ext->wc->wl_display); + wl_touch_send_down(target_resource, new_serial, time, surface_resource, id, x, y); + break; + } + } + + redirect_target = malloc(sizeof *redirect_target); + redirect_target->client = client; + redirect_target->resource = resource; + redirect_target->target_resource = target_resource; + redirect_target->id = id; + wl_list_insert(&client_link->parent->shell_ext->list_redirect_target, &redirect_target->link); +} + +static void +share_surface_redirect_touch_up(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial, + int32_t id) +{ + struct ivi_share_nativesurface_client_link *client_link = wl_resource_get_user_data(resource); + struct redirect_target *redirect_target = NULL; + struct redirect_target *next = NULL; + uint32_t new_serial = wl_display_next_serial(client_link->parent->shell_ext->wc->wl_display); + uint32_t time = get_event_time(); + + wl_list_for_each_safe(redirect_target, next, &client_link->parent->shell_ext->list_redirect_target, link) { + if (client == redirect_target->client && + resource == redirect_target->resource && + id == redirect_target->id) { + wl_touch_send_up(redirect_target->target_resource, new_serial, time, id); + wl_list_remove(&redirect_target->link); + free(redirect_target); + break; + } + } +} + +static void +share_surface_redirect_touch_motion(struct wl_client *client, + struct wl_resource *resource, + int32_t id, + wl_fixed_t x, + wl_fixed_t y) +{ + struct ivi_share_nativesurface_client_link *client_link = wl_resource_get_user_data(resource); + struct weston_seat *seat = NULL; + struct wl_resource *target_resource = NULL; + uint32_t time = get_event_time(); + struct wl_resource *surface_resource; + + if (client_link->parent == NULL) { + weston_log("share_surface_redirect_touch_motion: No parent surface\n"); + return; + } + surface_resource = client_link->parent->surface->resource; + + seat = get_weston_seat(client_link->parent->shell_ext->wc, client_link); + if (seat == NULL || seat->touch_state == NULL) { + return; + } + + wl_list_for_each(target_resource, &seat->touch_state->resource_list, link) { + if (wl_resource_get_client(target_resource) == wl_resource_get_client(surface_resource)) { + wl_touch_send_motion(target_resource, time, id, x, y); + break; + } + } +} + +static void +share_surface_redirect_touch_frame(struct wl_client *client, + struct wl_resource *resource) +{ + struct ivi_share_nativesurface_client_link *client_link = wl_resource_get_user_data(resource); + struct weston_seat *seat = NULL; + struct wl_resource *target_resource = NULL; + struct wl_resource *surface_resource = client_link->parent->surface->resource; + + seat = get_weston_seat(client_link->parent->shell_ext->wc, client_link); + if (seat == NULL || seat->touch_state == NULL) { + return; + } + + wl_list_for_each(target_resource, &seat->touch_state->resource_list, link) { + if (wl_resource_get_client(target_resource) == wl_resource_get_client(surface_resource)) { + wl_touch_send_frame(target_resource); + break; + } + } +} + +static void +share_surface_redirect_touch_cancel(struct wl_client *client, + struct wl_resource *resource) +{ + struct ivi_share_nativesurface_client_link *client_link = wl_resource_get_user_data(resource); + struct weston_seat *seat = NULL; + struct wl_resource *target_resource = NULL; + uint32_t time = get_event_time(); + struct wl_resource *surface_resource = client_link->parent->surface->resource; + + seat = get_weston_seat(client_link->parent->shell_ext->wc, client_link); + if (seat == NULL || seat->touch_state == NULL) { + return; + } + + wl_list_for_each(target_resource, &seat->touch_state->resource_list, link) { + if (wl_resource_get_client(target_resource) == wl_resource_get_client(surface_resource)) { + wl_touch_send_cancel(target_resource); + break; + } + } +} + +static const +struct ivi_share_surface_interface share_surface_implementation = { + share_surface_destroy, + share_surface_redirect_touch_down, + share_surface_redirect_touch_up, + share_surface_redirect_touch_motion, + share_surface_redirect_touch_frame, + share_surface_redirect_touch_cancel +}; + +static struct shell_surface * +get_shell_surface_from_weston_surface(struct weston_surface *surface, + struct ivi_shell_share_ext *shell_ext) +{ + struct shell_surface *shsurf; + + wl_list_for_each(shsurf, &shell_ext->list_shell_surface, link) { + if (shsurf->surface == surface) { + return shsurf; + } + } + + return NULL; +} + +static void +send_share_surface_state(struct ivi_share_nativesurface_client_link *client_link, + uint32_t share_surface_state); + +static void +remove_nativesurface(struct ivi_share_nativesurface *nativesurf) +{ + if (NULL == nativesurf) { + return; + } + + struct ivi_share_nativesurface_client_link *p_link = NULL; + struct ivi_share_nativesurface_client_link *p_next = NULL; + wl_list_for_each_safe(p_link, p_next, &nativesurf->client_list, link) { + /* Notify the shared surface has been destroyed */ + send_share_surface_state(p_link, + IVI_SHARE_SURFACE_SHARE_SURFACE_STATE_DESTROYED); + + /* Initialize information about shared surface */ + p_link->parent = NULL; + p_link->firstSendConfigureComp = false; + wl_list_remove(&p_link->link); + wl_list_init(&p_link->link); + } + + wl_list_remove(&nativesurf->link); + free_nativesurface(nativesurf); +} + +static void +nativesurface_destroy(struct wl_listener *listener, void *data) +{ + struct weston_surface *surface = data; + struct ivi_share_nativesurface *nativesurf = + container_of(listener, struct ivi_share_nativesurface, + surface_destroy_listener); + + if (nativesurf->surface == surface) { + remove_nativesurface(nativesurf); + } +} + +struct ivi_share_nativesurface* +alloc_share_nativesurface(struct weston_surface *surface, uint32_t id, uint32_t surface_id, + uint32_t bufferType, uint32_t format, + struct ivi_shell_share_ext *shell_ext) +{ + struct ivi_share_nativesurface *nativesurface = + malloc(sizeof(struct ivi_share_nativesurface)); + if (NULL == nativesurface) { + return NULL; + } + + nativesurface->id = id; + nativesurface->surface = surface; + nativesurface->bufferType = bufferType; + nativesurface->name = 0; + nativesurface->width = 0; + nativesurface->height = 0; + nativesurface->stride = 0; + nativesurface->format = format; + nativesurface->surface_id = surface_id; + nativesurface->shell_ext = shell_ext; + wl_list_init(&nativesurface->client_list); + + if (NULL != surface) { + nativesurface->surface_destroy_listener.notify = nativesurface_destroy; + wl_signal_add(&surface->destroy_signal, &nativesurface->surface_destroy_listener); + } + + return nativesurface; +} + +static void +destroy_client_link(struct wl_resource *resource) +{ + struct ivi_share_nativesurface_client_link *client_link = wl_resource_get_user_data(resource); + + wl_list_remove(&client_link->link); + free(client_link); +} + +static struct ivi_share_nativesurface_client_link * +add_nativesurface_client(struct ivi_share_nativesurface *nativesurface, + uint32_t id, struct wl_client *client, uint32_t version) +{ + struct ivi_share_nativesurface_client_link *link = malloc(sizeof(*link)); + if (NULL == link) { + return NULL; + } + + link->resource = wl_resource_create(client, &ivi_share_surface_interface, version, id); + link->client = client; + link->firstSendConfigureComp = false; + link->parent = nativesurface; + + wl_resource_set_implementation(link->resource, &share_surface_implementation, + link, destroy_client_link); + wl_list_insert(&nativesurface->client_list, &link->link); + + return link; +} + +static struct ivi_share_nativesurface_client_link * +create_empty_nativesurface_client(struct wl_client *client, + uint32_t id, uint32_t version, + struct ivi_shell_share_ext *shell_ext) +{ + struct ivi_share_nativesurface *nativesurf; + + nativesurf = alloc_share_nativesurface(NULL, 0, 0, + IVI_SHARE_SURFACE_TYPE_GBM, + IVI_SHARE_SURFACE_FORMAT_ARGB8888, + shell_ext); + if (NULL == nativesurf) { + return NULL; + } + + return add_nativesurface_client(nativesurf, id, client, version); +} + +static struct ivi_share_nativesurface* +find_nativesurface(struct shell_surface *shsurf, struct ivi_shell_share_ext *shell_ext) +{ + struct ivi_share_nativesurface *nativesurf = NULL; + + wl_list_for_each(nativesurf, &shell_ext->list_nativesurface, link) { + if (shsurf->surface_id == nativesurf->surface_id) { + return nativesurf; + } + } + return NULL; +} + +static uint32_t +get_shared_client_input_caps(struct ivi_share_nativesurface_client_link *client_link, + struct ivi_shell_share_ext *shell_ext) +{ + uint32_t caps = 0; + struct weston_seat *seat = get_weston_seat(shell_ext->wc, client_link); + struct wl_client *creator = wl_resource_get_client(client_link->parent->surface->resource); + + if ((seat != NULL) && (seat->touch_state != NULL)) { + struct wl_resource *resource = NULL; + wl_list_for_each(resource, &seat->touch_state->focus_resource_list, link) { + if (wl_resource_get_client(resource) == creator) { + caps |= (uint32_t)IVI_SHARE_SURFACE_INPUT_CAPS_TOUCH; + break; + } + } + + wl_list_for_each(resource, &seat->touch_state->resource_list, link) { + if (wl_resource_get_client(resource) == creator) { + caps |= (uint32_t)IVI_SHARE_SURFACE_INPUT_CAPS_TOUCH; + break; + } + } + } + + return caps; +} + +static void +send_share_surface_state(struct ivi_share_nativesurface_client_link *client_link, + uint32_t share_surface_state) +{ + if (NULL != client_link->resource) { + ivi_share_surface_send_share_surface_state(client_link->resource, + share_surface_state); + } +} + +static void +share_get_ivi_share_surface(struct wl_client *client, struct wl_resource *resource, + uint32_t id, uint32_t surface_id) +{ + struct ivi_shell_share_ext *shell_ext = wl_resource_get_user_data(resource); + struct ivi_layout_surface *layout_surface; + struct weston_surface *surface; + struct ivi_share_nativesurface *nativesurf; + struct shell_surface *shsurf; + struct ivi_share_nativesurface_client_link *client_link = NULL; + uint32_t caps = 0; + uint32_t version = wl_resource_get_version(resource); + + layout_surface = shell_ext->controller_interface->get_surface_from_id(surface_id); + surface = shell_ext->controller_interface->surface_get_weston_surface(layout_surface); + + shsurf = get_shell_surface_from_weston_surface(surface, shell_ext); + if (shsurf == NULL) { + client_link = create_empty_nativesurface_client(client, id, version, shell_ext); + if (NULL == client_link) { + wl_client_post_no_memory(client); + return; + } + send_share_surface_state(client_link, + IVI_SHARE_SURFACE_SHARE_SURFACE_STATE_NOT_EXIST); + return; + } + + if (shsurf->surface_id != surface_id) { + shsurf->surface_id = surface_id; + } + + nativesurf = find_nativesurface(shsurf, shell_ext); + if (nativesurf == NULL) { + nativesurf = alloc_share_nativesurface(shsurf->surface, id, 0, + (int32_t)IVI_SHARE_SURFACE_TYPE_GBM, + (int32_t)IVI_SHARE_SURFACE_FORMAT_ARGB8888, + shell_ext); + + if (NULL == nativesurf) { + weston_log("Buffer Sharing Insufficient memory\n"); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "Insufficient memory"); + return; + } + + wl_list_insert(&shell_ext->list_nativesurface, &nativesurf->link); + } + + client_link = add_nativesurface_client(nativesurf, id, client, version); + + caps = get_shared_client_input_caps(client_link, shell_ext); + ivi_share_surface_send_input_capabilities(client_link->resource, caps); +} + +static struct ivi_share_interface g_share_implementation = { + share_get_ivi_share_surface +}; + +static struct ivi_shell_share_ext* +init_ivi_shell_share_ext(struct weston_compositor *wc, + const struct ivi_controller_interface *controller_interface, + struct ivi_shell_share_ext *shell_ext) +{ + shell_ext = calloc(1, sizeof(*shell_ext)); + if (shell_ext == NULL) { + weston_log("no memory to allocate ivi_shell_share_ext\n"); + return NULL; + } + + shell_ext->wc = wc; + shell_ext->controller_interface = controller_interface; + wl_list_init(&shell_ext->list_shell_surface); + wl_list_init(&shell_ext->list_nativesurface); + wl_list_init(&shell_ext->list_redirect_target); + + return shell_ext; +} + +static void +send_configure(struct wl_resource *p_resource, uint32_t id, + struct ivi_share_nativesurface *p_nativesurface) +{ + if ((NULL == p_resource) || (NULL == p_nativesurface)) { + return; + } + + ivi_share_surface_send_configure(p_resource, + p_nativesurface->bufferType, + p_nativesurface->width, + p_nativesurface->height, + p_nativesurface->stride / 4, + p_nativesurface->format); +} + +static void +send_damage(struct wl_resource *p_resource, uint32_t id, uint32_t name) +{ + if (!p_resource) + return; + + ivi_share_surface_send_damage(p_resource, name); +} + +static void +bind_share_interface(struct wl_client *p_client, void *p_data, + uint32_t version, uint32_t id) +{ + struct ivi_shell_share_ext *shell_ext = p_data; + + weston_log("Buffer Sharing Request bind: version(%d)\n", version); + + if (NULL == p_client) { + return; + } + + shell_ext->resource = wl_resource_create(p_client, &ivi_share_interface, 1, id); + wl_resource_set_implementation(shell_ext->resource, + &g_share_implementation, + shell_ext, NULL); +} + +static void +send_to_client(struct ivi_share_nativesurface *p_nativesurface, uint32_t send_flag, + struct ivi_shell_share_ext *shell_ext) +{ + struct ivi_share_nativesurface_client_link *p_link = NULL; + + if ((IVI_SHAREBUFFER_NOT_AVAILABLE & send_flag) == IVI_SHAREBUFFER_NOT_AVAILABLE) { + return; + } + + if (IVI_SHAREBUFFER_INVALID & send_flag) { + /* Notify the shared surface is invalid */ + wl_list_for_each(p_link, &p_nativesurface->client_list, link) { + send_share_surface_state(p_link, + IVI_SHARE_SURFACE_SHARE_SURFACE_STATE_INVALID_SURFACE); + } + return; + } + + wl_list_for_each(p_link, &p_nativesurface->client_list, link) + { + if ((!p_link->firstSendConfigureComp) || + (IVI_SHAREBUFFER_CONFIGURE & send_flag) == IVI_SHAREBUFFER_CONFIGURE) { + send_configure(p_link->resource, p_nativesurface->id, + p_nativesurface); + p_link->firstSendConfigureComp = true; + } + if ((IVI_SHAREBUFFER_DAMAGE & send_flag) == IVI_SHAREBUFFER_DAMAGE) { + send_damage(p_link->resource, p_nativesurface->id, + get_buffer_name(p_nativesurface->surface, shell_ext)); + } + } +} + +static void +send_nativesurface_event(struct wl_listener *listener, void *data) +{ + struct update_sharing_surface_content *surface_content = + container_of(listener, struct update_sharing_surface_content, listener); + struct ivi_shell_share_ext *shell_ext = surface_content->shell_ext; + if (wl_list_empty(&shell_ext->list_nativesurface)) { + return; + } + + struct ivi_share_nativesurface *p_nativesurface = NULL; + struct ivi_share_nativesurface *p_next = NULL; + wl_list_for_each_safe(p_nativesurface, p_next, &shell_ext->list_nativesurface, link) + { + if (NULL == p_nativesurface) { + continue; + } + + if (wl_list_empty(&p_nativesurface->client_list) || + (0 == p_nativesurface->id)) { + weston_log("Buffer Sharing warning, Unnecessary nativesurface exists.\n"); + wl_list_remove(&p_nativesurface->link); + free_nativesurface(p_nativesurface); + continue; + } + + p_nativesurface->send_flag = update_buffer_nativesurface(p_nativesurface, shell_ext); + send_to_client(p_nativesurface, p_nativesurface->send_flag, shell_ext); + } +} + +static int32_t +buffer_sharing_init(struct weston_compositor *wc, + struct ivi_shell_share_ext *shell_ext) +{ + if (NULL == wc) { + weston_log("Can not execute buffer sharing.\n"); + return -1; + } + + if (NULL == wl_global_create(wc->wl_display, &ivi_share_interface, 1, + shell_ext, bind_share_interface)) { + weston_log("Buffer Sharing, Failed to global create\n"); + return -1; + } + + return 0; +} + +static void +add_weston_surf_data(struct wl_listener *listener, void *data) +{ + struct weston_surface *ws = data; + struct ivi_shell_share_ext *shell_ext = NULL; + struct shell_surface *s = NULL; + shell_ext = container_of(listener, struct ivi_shell_share_ext, surface_created_listener); + + wl_list_for_each(s, &shell_ext->list_shell_surface, link) { + if (s->surface == ws) { + return; + } + } + + struct shell_surface *shsurf = NULL; + shsurf = malloc(sizeof *shsurf); + if (NULL == shsurf || NULL == ws) { + return; + } + + shsurf->resource = ws->resource; + shsurf->surface = ws; + wl_list_init(&shsurf->link); + wl_list_insert(shell_ext->list_shell_surface.prev, &shsurf->link); +} + +int32_t +setup_buffer_sharing(struct weston_compositor *wc, + const struct ivi_controller_interface *interface) +{ + if (NULL == wc || NULL == interface) { + weston_log("Can not execute buffer sharing.\n"); + return -1; + } + + struct ivi_shell_share_ext *shell_ext = NULL; + + shell_ext = init_ivi_shell_share_ext(wc, interface, shell_ext); + if (shell_ext == NULL) { + weston_log("ivi_shell_share_ext initialize failed\n"); + return -1; + } + + int32_t init_ret = buffer_sharing_init(wc, shell_ext); + if (init_ret < 0) { + weston_log("Buffer Sharing initialize failed. init_ret = %d\n", init_ret); + return init_ret; + } + + struct weston_output *output = NULL; + + wl_list_for_each(output, &wc->output_list, link) { + struct update_sharing_surface_content *surf_content = malloc(sizeof *surf_content); + if (surf_content == NULL) { + weston_log("Failed to allocate surf_content. Buffer Sharing can't use.\n"); + return -1; + } + + surf_content->shell_ext = shell_ext; + surf_content->listener.notify = send_nativesurface_event; + wl_signal_add(&output->frame_signal, &surf_content->listener); + } + + shell_ext->surface_created_listener.notify = add_weston_surf_data; + wl_signal_add(&wc->create_surface_signal, &shell_ext->surface_created_listener); +} |