--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <tizen-extension-server-protocol.h>
+
+#include <libds/log.h>
+#include <libds/surface.h>
+
+#include "libds-tizen/video.h"
+
+struct ds_tizen_viewport {
+ struct wl_resource *resource;
+ struct wl_resource *mode_resource;
+ struct ds_surface *surface;
+ struct ds_surface_viewport *surface_viewport;
+
+ struct ds_tizen_viewport_state current, pending;
+
+ struct wl_listener surface_destroy;
+ struct wl_listener surface_commit;
+
+ struct {
+ struct wl_signal destroy;
+ struct wl_signal commit;
+ struct wl_signal query_parent_size;
+ } events;
+
+ bool query_parent_size;
+};
+
+static const struct tizen_viewport_interface viewport_impl;
+static const struct tizen_destination_mode_interface destination_mode_impl;
+
+static void viewport_handle_resource_destroy(struct wl_resource *resource);
+static void viewport_handle_surface_destroy(struct wl_listener *listener,
+ void *data);
+static void viewport_handle_surface_commit(struct wl_listener *listener,
+ void *data);
+static void viewport_destroy(struct ds_tizen_viewport *viewport);
+static void viewport_set_follow_parent_transform_pending(
+ struct ds_tizen_viewport *viewport, bool value);
+static void destination_mode_resource_destroy(struct wl_resource *resource);
+static enum ds_tizen_viewport_destination_mode_type
+destination_mode_type_to_ds_enum(uint32_t type);
+
+WL_EXPORT struct ds_surface *
+ds_tizen_viewport_get_surface(struct ds_tizen_viewport *viewport)
+{
+ return viewport->surface;
+}
+
+WL_EXPORT void
+ds_tizen_viewport_add_destroy_listener(struct ds_tizen_viewport *viewport,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&viewport->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_tizen_viewport_add_commit_listener(struct ds_tizen_viewport *viewport,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&viewport->events.commit, listener);
+}
+
+WL_EXPORT void
+ds_tizen_viewport_add_query_parent_size_listener(
+ struct ds_tizen_viewport *viewport, struct wl_listener *listener)
+{
+ wl_signal_add(&viewport->events.query_parent_size, listener);
+}
+
+WL_EXPORT void
+ds_tizen_viewport_send_parent_size(struct ds_tizen_viewport *viewport,
+ uint32_t width, uint32_t height)
+{
+ if (!viewport->query_parent_size)
+ return;
+
+ tizen_viewport_send_parent_size(viewport->resource, width, height);
+}
+
+struct ds_tizen_viewport *
+create_viewport(struct wl_client *client, struct wl_resource *resource,
+ uint32_t id, struct wl_resource *surface_resource)
+{
+ struct ds_tizen_viewport *viewport;
+ struct ds_surface *surface;
+ struct ds_surface_viewport *surface_viewport;
+
+ surface = ds_surface_from_resource(resource);
+ surface_viewport = ds_surface_take_viewport(surface);
+ if (!surface_viewport) {
+ wl_resource_post_error(resource, TIZEN_VIDEO_ERROR_VIEWPORT_EXISTS,
+ "a viewport for that surface already exists");
+ return NULL;
+ }
+
+ viewport = calloc(1, sizeof *viewport);
+ if (!viewport)
+ goto err_alloc;
+
+ viewport->resource = wl_resource_create(client, &tizen_viewport_interface,
+ wl_resource_get_version(resource), id);
+ if (!viewport->resource)
+ goto err_resource;
+
+ wl_resource_set_implementation(viewport->resource, &viewport_impl,
+ viewport, viewport_handle_resource_destroy);
+
+ wl_signal_init(&viewport->events.commit);
+ wl_signal_init(&viewport->events.query_parent_size);
+
+ viewport->surface_viewport = surface_viewport;
+
+ viewport->surface_destroy.notify = viewport_handle_surface_destroy;
+ ds_surface_add_destroy_listener(surface, &viewport->surface_destroy);
+
+ viewport->surface_commit.notify = viewport_handle_surface_commit;
+ ds_surface_add_commit_listener(surface, &viewport->surface_commit);
+
+ return viewport;
+
+err_resource:
+ free(viewport);
+err_alloc:
+ ds_surface_viewport_release(surface_viewport);
+ wl_resource_post_no_memory(resource);
+
+ return NULL;
+}
+
+static void
+viewport_handle_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+viewport_handle_set_transform(struct wl_client *client,
+ struct wl_resource *resource, uint32_t transform)
+{
+ struct ds_tizen_viewport *viewport;
+
+ if (transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) {
+ ds_err("Invalid param: transform(%d)", transform);
+ return;
+ }
+
+ viewport = wl_resource_get_user_data(resource);
+ if (viewport->pending.transform == transform)
+ return;
+
+ viewport->pending.transform = transform;
+ viewport->pending.committed |= DS_TIZEN_VIEWPORT_STATE_TRANSFORM;
+}
+
+static void
+viewport_handle_set_source(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t x, uint32_t y, uint32_t width, uint32_t height)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ if (viewport->pending.src.x == x &&
+ viewport->pending.src.y == y &&
+ viewport->pending.src.w == width &&
+ viewport->pending.src.h == height) {
+ return;
+ }
+
+ viewport->pending.src.x = x;
+ viewport->pending.src.y = y;
+ viewport->pending.src.w = width;
+ viewport->pending.src.h = height;
+ viewport->pending.committed |= DS_TIZEN_VIEWPORT_STATE_SOURCE;
+}
+
+static void
+viewport_handle_set_destination(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, uint32_t width, uint32_t height)
+{
+ struct ds_tizen_viewport *viewport;
+
+ if (width == 0 || height == 0) {
+ ds_err("Invalid param: destination.rect(%d,%d %dx%d)",
+ x, y, width, height);
+ return;
+ }
+
+ viewport = wl_resource_get_user_data(resource);
+ if (viewport->pending.dst.type == DS_TIZEN_VIEWPORT_DESTINATION_TYPE_RECT &&
+ viewport->pending.dst.rect.x == x &&
+ viewport->pending.dst.rect.y == y &&
+ viewport->pending.dst.rect.w == width &&
+ viewport->pending.dst.rect.h == height) {
+ return;
+ }
+
+ viewport->pending.dst.rect.x = x;
+ viewport->pending.dst.rect.y = y;
+ viewport->pending.dst.rect.w = width;
+ viewport->pending.dst.rect.h = height;
+ viewport->pending.dst.type = DS_TIZEN_VIEWPORT_DESTINATION_TYPE_RECT;
+ viewport->pending.committed |= DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+}
+
+static void
+viewport_handle_set_destination_ratio(struct wl_client *client,
+ struct wl_resource *resource,
+ wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height)
+{
+ struct ds_tizen_viewport *viewport;
+ double ratio_x, ratio_y, ratio_w, ratio_h;
+
+ viewport = wl_resource_get_user_data(resource);
+ if (viewport->pending.dst.type == DS_TIZEN_VIEWPORT_DESTINATION_TYPE_MODE) {
+ ds_err("Couldn't set viewport destination ratio. "
+ "tizen_viewport@%d has the mode",
+ wl_resource_get_id(resource));
+ return;
+ }
+
+ ratio_x = wl_fixed_to_double(x);
+ ratio_y = wl_fixed_to_double(y);
+ ratio_w = wl_fixed_to_double(width);
+ ratio_h = wl_fixed_to_double(height);
+ if (ratio_x < 0.0 || ratio_x >= 1.0 ||
+ ratio_y < 0.0 || ratio_y >= 1.0 ||
+ ratio_w <= 0.0 || ratio_h <= 0.0) {
+ ds_err("Invalid param: destination.ratio(%.2f,%.2f %.2fx%.2f)",
+ ratio_x, ratio_y, ratio_w, ratio_h);
+ return;
+ }
+
+ if (viewport->pending.dst.type == DS_TIZEN_VIEWPORT_DESTINATION_TYPE_RATIO &&
+ viewport->pending.dst.ratio.x == ratio_x &&
+ viewport->pending.dst.ratio.y == ratio_y &&
+ viewport->pending.dst.ratio.w == ratio_w &&
+ viewport->pending.dst.ratio.h == ratio_h) {
+ return;
+ }
+
+ viewport->pending.dst.ratio.x = ratio_x;
+ viewport->pending.dst.ratio.y = ratio_y;
+ viewport->pending.dst.ratio.w = ratio_w;
+ viewport->pending.dst.ratio.h = ratio_h;
+ viewport->pending.dst.type = DS_TIZEN_VIEWPORT_DESTINATION_TYPE_RATIO;
+ viewport->pending.committed |= DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+}
+
+static void
+viewport_handle_get_destination_mode(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ if (viewport->mode_resource) {
+ ds_err("Destination mode resource already exists.");
+ /* WORKAROUND
+ * Since there is no error protocol for this case,
+ * it calls wl_resource_post_no_memory() instead. */
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ viewport->mode_resource = wl_resource_create(client,
+ &tizen_destination_mode_interface,
+ wl_resource_get_version(resource), id);
+ if (!viewport->mode_resource) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+ wl_resource_set_implementation(viewport->mode_resource,
+ &destination_mode_impl, viewport,
+ destination_mode_resource_destroy);
+
+ viewport->pending.dst.mode.type = DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_NONE;
+ viewport->pending.dst.mode.ratio.h = -1.0;
+ viewport->pending.dst.mode.scale.h = -1.0;
+ viewport->pending.dst.mode.align.h = -1.0;
+}
+
+static void
+viewport_handle_query_parent_size(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ viewport->query_parent_size = true;
+ wl_signal_emit(&viewport->events.query_parent_size, viewport);
+}
+
+static void
+viewport_handle_follow_parent_transform(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ viewport_set_follow_parent_transform_pending(viewport, true);
+}
+
+static void
+viewport_handle_unfollow_parent_transform(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ viewport_set_follow_parent_transform_pending(viewport, false);
+}
+
+static const struct tizen_viewport_interface viewport_impl = {
+ .destroy = viewport_handle_destroy,
+ .set_transform = viewport_handle_set_transform,
+ .set_source = viewport_handle_set_source,
+ .set_destination = viewport_handle_set_destination,
+ .set_destination_ratio = viewport_handle_set_destination_ratio,
+ .get_destination_mode = viewport_handle_get_destination_mode,
+ .query_parent_size = viewport_handle_query_parent_size,
+ .follow_parent_transform = viewport_handle_follow_parent_transform,
+ .unfollow_parent_transform = viewport_handle_unfollow_parent_transform,
+};
+
+static void
+viewport_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ if (viewport)
+ viewport_destroy(viewport);
+}
+
+static void
+viewport_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_container_of(listener, viewport, surface_destroy);
+ viewport_destroy(viewport);
+}
+
+static void
+viewport_handle_surface_commit(struct wl_listener *listener, void *data)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_container_of(listener, viewport, surface_commit);
+ if (viewport->pending.committed == DS_TIZEN_VIEWPORT_STATE_NONE)
+ return;
+
+ viewport->current = viewport->pending;
+ viewport->pending.committed = DS_TIZEN_VIEWPORT_STATE_NONE;
+
+ wl_signal_emit(&viewport->events.commit, &viewport->current);
+}
+
+static void
+viewport_destroy(struct ds_tizen_viewport *viewport)
+{
+ wl_signal_emit(&viewport->events.destroy, viewport);
+
+ ds_surface_viewport_release(viewport->surface_viewport);
+
+ wl_resource_set_user_data(viewport->resource, NULL);
+ wl_list_remove(&viewport->surface_commit.link);
+ wl_list_remove(&viewport->surface_destroy.link);
+ free(viewport);
+}
+
+static void
+viewport_set_follow_parent_transform_pending(struct ds_tizen_viewport *viewport,
+ bool value)
+{
+ viewport->pending.follow_parent_transform = value;
+ viewport->pending.committed |=
+ DS_TIZEN_VIEWPORT_STATE_FOLLOW_PARENT_TRANSFORM;
+}
+
+static void
+destination_mode_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+destination_mode_handle_follow_parent_transform(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ viewport_set_follow_parent_transform_pending(viewport, true);
+}
+
+static void
+destination_mode_handle_unfollow_parent_transform(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ viewport_set_follow_parent_transform_pending(viewport, false);
+}
+
+static void
+destination_mode_handle_set(struct wl_client *client,
+ struct wl_resource *resource, uint32_t type)
+{
+ struct ds_tizen_viewport *viewport;
+ enum ds_tizen_viewport_destination_mode_type mode_type;
+
+ if (type > TIZEN_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER) {
+ ds_err("Invalid param: type(%d)", type);
+ return;
+ }
+
+ mode_type = destination_mode_type_to_ds_enum(type);
+ viewport = wl_resource_get_user_data(resource);
+ if (viewport->pending.dst.type == DS_TIZEN_VIEWPORT_DESTINATION_TYPE_MODE &&
+ viewport->pending.dst.mode.type == mode_type) {
+ return;
+ }
+
+ viewport->pending.dst.mode.type = mode_type;
+ viewport->pending.dst.type = DS_TIZEN_VIEWPORT_DESTINATION_TYPE_MODE;
+}
+
+static void
+destination_mode_handle_set_ratio(struct wl_client *client,
+ struct wl_resource *resource,
+ wl_fixed_t horizontal, wl_fixed_t vertical)
+{
+ struct ds_tizen_viewport *viewport;
+ double ratio_h, ratio_v;
+
+ viewport = wl_resource_get_user_data(resource);
+
+ ratio_h = wl_fixed_to_double(horizontal);
+ ratio_v = wl_fixed_to_double(vertical);
+ if (ratio_h <= 0.0 || ratio_v <= 0.0) {
+ if (ratio_h == -1.0) {
+ viewport->pending.dst.mode.ratio.h = ratio_h;
+ viewport->pending.committed |= DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+ }
+ else {
+ ds_err("Invalid param: ratio_h(%.2f) ratio_v(%.2f)",
+ ratio_h, ratio_v);
+ }
+
+ return;
+ }
+
+ if (viewport->pending.dst.mode.ratio.h == ratio_h &&
+ viewport->pending.dst.mode.ratio.v == ratio_v)
+ return;
+
+ viewport->pending.dst.mode.ratio.h = ratio_h;
+ viewport->pending.dst.mode.ratio.v = ratio_v;
+ viewport->pending.committed |= DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+}
+
+static void
+destination_mode_handle_set_scale(struct wl_client *client,
+ struct wl_resource *resource,
+ wl_fixed_t horizontal, wl_fixed_t vertical)
+{
+ struct ds_tizen_viewport *viewport;
+ double scale_h, scale_v;
+
+ viewport = wl_resource_get_user_data(resource);
+
+ scale_h = wl_fixed_to_double(horizontal);
+ scale_v = wl_fixed_to_double(vertical);
+ if (scale_h <= 0 || scale_v <= 0) {
+ if (scale_h == -1.0) {
+ viewport->pending.dst.mode.scale.h = scale_h;
+ viewport->pending.committed = DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+ }
+ else {
+ ds_err("Invalid param: scale_h(%.2f) scale_v(%.2f)",
+ scale_h, scale_v);
+ }
+
+ return;
+ }
+
+ if (viewport->pending.dst.mode.scale.h == scale_h &&
+ viewport->pending.dst.mode.scale.v == scale_v)
+ return;
+
+ viewport->pending.dst.mode.scale.h = scale_h;
+ viewport->pending.dst.mode.scale.v = scale_v;
+ viewport->pending.committed = DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+}
+
+static void
+destination_mode_handle_set_align(struct wl_client *client,
+ struct wl_resource *resource,
+ wl_fixed_t horizontal, wl_fixed_t vertical)
+{
+ struct ds_tizen_viewport *viewport;
+ double align_h, align_v;
+
+ viewport = wl_resource_get_user_data(resource);
+
+ align_h = wl_fixed_to_double(horizontal);
+ align_v = wl_fixed_to_double(vertical);
+ if (align_h == -1.0) {
+ viewport->pending.dst.mode.align.h = align_h;
+ viewport->pending.committed = DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+ return;
+ }
+
+ if (align_h < 0.0)
+ align_h = 0.0;
+ else if (align_h > 1.0)
+ align_h = 1.0;
+
+ if (align_v < 0.0)
+ align_v = 0.0;
+ else if (align_v > 1.0)
+ align_v = 1.0;
+
+ if (viewport->pending.dst.mode.align.h == align_h &&
+ viewport->pending.dst.mode.align.v == align_v)
+ return;
+
+ viewport->pending.dst.mode.align.h = align_h;
+ viewport->pending.dst.mode.align.v = align_v;
+ viewport->pending.committed = DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+}
+
+static void
+destination_mode_handle_set_offset(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x, int32_t y, int32_t w, int32_t h)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+
+ if (viewport->pending.dst.mode.offset.x == x &&
+ viewport->pending.dst.mode.offset.y == y &&
+ viewport->pending.dst.mode.offset.w == w &&
+ viewport->pending.dst.mode.offset.h == h)
+ return;
+
+ viewport->pending.dst.mode.offset.x = x;
+ viewport->pending.dst.mode.offset.y = y;
+ viewport->pending.dst.mode.offset.w = w;
+ viewport->pending.dst.mode.offset.h = h;
+ viewport->pending.committed = DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+}
+
+static const struct tizen_destination_mode_interface destination_mode_impl = {
+ .destroy = destination_mode_handle_destroy,
+ .follow_parent_transform = destination_mode_handle_follow_parent_transform,
+ .unfollow_parent_transform =
+ destination_mode_handle_unfollow_parent_transform,
+ .set = destination_mode_handle_set,
+ .set_ratio = destination_mode_handle_set_ratio,
+ .set_scale = destination_mode_handle_set_scale,
+ .set_align = destination_mode_handle_set_align,
+ .set_offset = destination_mode_handle_set_offset,
+};
+
+static void destination_mode_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_tizen_viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ viewport->mode_resource = NULL;
+
+ if (viewport->pending.dst.type == DS_TIZEN_VIEWPORT_DESTINATION_TYPE_MODE) {
+ viewport->pending.dst.type = DS_TIZEN_VIEWPORT_DESTINATION_TYPE_NONE;
+ viewport->pending.committed |= DS_TIZEN_VIEWPORT_STATE_DESTINATION;
+ }
+}
+
+static enum ds_tizen_viewport_destination_mode_type
+destination_mode_type_to_ds_enum(uint32_t type)
+{
+ switch (type) {
+ case TIZEN_DESTINATION_MODE_TYPE_NONE:
+ return DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_NONE;
+ case TIZEN_DESTINATION_MODE_TYPE_LETTER_BOX:
+ return DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_LETTER_BOX;
+ case TIZEN_DESTINATION_MODE_TYPE_ORIGIN:
+ return DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_ORIGIN;
+ case TIZEN_DESTINATION_MODE_TYPE_FULL:
+ return DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_FULL;
+ case TIZEN_DESTINATION_MODE_TYPE_CROPPED_FULL:
+ return DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_CROPPED_FULL;
+ case TIZEN_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER:
+ return DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER;
+ default:
+ assert(0 && "cannot reach here");
+ }
+}