video: Add tizen_viewport implementation 71/283571/2
authorSeunghun Lee <shiin.lee@samsung.com>
Tue, 11 Oct 2022 09:29:20 +0000 (18:29 +0900)
committerSooChan Lim <sc1.lim@samsung.com>
Mon, 31 Oct 2022 01:16:34 +0000 (01:16 +0000)
Change-Id: I56840111bfa69fa4de99c8e12e45088712f6c6df

include/libds-tizen/video.h
src/video/meson.build
src/video/video-private.h [new file with mode: 0644]
src/video/video.c
src/video/viewport.c [new file with mode: 0644]

index 0cbf5d7..7358fc8 100644 (file)
@@ -12,12 +12,87 @@ struct ds_tizen_video;
 
 struct ds_tizen_video_object;
 
+struct ds_tizen_viewport;
+
+enum ds_tizen_viewport_state_field {
+    DS_TIZEN_VIEWPORT_STATE_NONE = 0,
+    DS_TIZEN_VIEWPORT_STATE_TRANSFORM = (1 << 0),
+    DS_TIZEN_VIEWPORT_STATE_SOURCE = (1 << 1),
+    DS_TIZEN_VIEWPORT_STATE_DESTINATION = (1 << 2),
+    DS_TIZEN_VIEWPORT_STATE_FOLLOW_PARENT_TRANSFORM = (1 << 3),
+};
+
+enum ds_tizen_viewport_destination_type {
+    DS_TIZEN_VIEWPORT_DESTINATION_TYPE_NONE,
+    DS_TIZEN_VIEWPORT_DESTINATION_TYPE_RECT,
+    DS_TIZEN_VIEWPORT_DESTINATION_TYPE_RATIO,
+    DS_TIZEN_VIEWPORT_DESTINATION_TYPE_MODE,
+};
+
+enum ds_tizen_viewport_destination_mode_type {
+    DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_NONE,
+    DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_LETTER_BOX,
+    DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_ORIGIN,
+    DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_FULL,
+    DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_CROPPED_FULL,
+    DS_TIZEN_VIEWPORT_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER,
+};
+
+struct ds_tizen_viewport_state {
+    enum ds_tizen_viewport_state_field committed;
+
+    struct {
+        uint32_t x, y, w, h;
+    } src;
+
+    struct {
+        enum ds_tizen_viewport_destination_type type;
+
+        struct {
+            int32_t x, y;
+            uint32_t w, h;
+        } rect;
+
+        struct {
+            double x, y, w, h;
+        } ratio;
+
+        struct {
+            enum ds_tizen_viewport_destination_mode_type type;
+
+            struct {
+                double h, v;
+            } ratio;
+
+            struct {
+                double h, v;
+            } scale;
+
+            struct {
+                double h, v;
+            } align;
+
+            struct {
+                int x, y, w, h;
+            } offset;
+        } mode;
+    } dst;
+
+    uint32_t transform;
+    bool follow_parent_transform;
+};
+
 struct ds_event_tizen_video_request_set_property {
     struct ds_tizen_video_object *object;
     const char *name;
     int32_t value;
 };
 
+struct ds_tizen_viewport_event_commit {
+    struct ds_tizen_viewport *viewport;
+    const struct ds_tizen_viewport_state *state;
+};
+
 struct ds_tizen_video *ds_tizen_video_create(struct wl_display *display);
 
 void ds_tizen_video_add_format(struct ds_tizen_video *video, uint32_t format);
@@ -35,6 +110,9 @@ void ds_tizen_video_add_destroy_listener(struct ds_tizen_video *video,
 void ds_tizen_video_add_new_object_listener(struct ds_tizen_video *video,
         struct wl_listener *listener);
 
+void ds_tizen_video_add_new_viewport_listener(struct ds_tizen_video *video,
+        struct wl_listener *listener);
+
 struct ds_surface *ds_tizen_video_object_get_surface(
         struct ds_tizen_video_object *object);
 
@@ -57,6 +135,22 @@ void ds_tizen_video_object_add_request_attribute_allowed_listener(
 void ds_tizen_video_object_add_request_attribute_disallowed_listener(
         struct ds_tizen_video_object *object, struct wl_listener *listener);
 
+struct ds_surface *ds_tizen_viewport_get_surface(
+        struct ds_tizen_viewport *viewport);
+
+void ds_tizen_viewport_add_destroy_listener(struct ds_tizen_viewport *viewport,
+        struct wl_listener *listener);
+
+/* @data: struct ds_tizen_viewport_event_commit */
+void ds_tizen_viewport_add_commit_listener(struct ds_tizen_viewport *viewport,
+        struct wl_listener *listener);
+
+void ds_tizen_viewport_add_query_parent_size_listener(
+        struct ds_tizen_viewport *viewport, struct wl_listener *listener);
+
+void ds_tizen_viewport_send_parent_size(struct ds_tizen_viewport *viewport,
+        uint32_t width, uint32_t height);
+
 #ifdef __cplusplus
 }
 #endif
index e091554..363926b 100644 (file)
@@ -1,5 +1,6 @@
 libds_tizen_video_files = [
   'video.c',
+  'viewport.c',
 ]
 
 libds_tizen_video_deps = [
diff --git a/src/video/video-private.h b/src/video/video-private.h
new file mode 100644 (file)
index 0000000..d399603
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef DS_TIZEN_VIDEO_PRIVATE_H
+#define DS_TIZEN_VIDEO_PRIVATE_H
+
+#include <wayland-server.h>
+
+struct ds_tizen_viewport *
+create_viewport(struct wl_client *client, struct wl_resource *resource,
+        uint32_t id, struct wl_resource *surface_resource);
+
+#endif
index db8bd26..0a258f4 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "libds-tizen/video.h"
 #include "shared/pixel_format.h"
+#include "video-private.h"
 
 #define TIZEN_VIDEO_VERSION 1
 
@@ -32,6 +33,7 @@ struct ds_tizen_video
     struct {
         struct wl_signal destroy;
         struct wl_signal new_object;
+        struct wl_signal new_viewport;
     } events;
 };
 
@@ -103,6 +105,7 @@ ds_tizen_video_create(struct wl_display *display)
 
     wl_signal_init(&video->events.destroy);
     wl_signal_init(&video->events.new_object);
+    wl_signal_init(&video->events.new_viewport);
 
     video->display_destroy.notify = video_handle_display_destroy;
     wl_display_add_destroy_listener(display, &video->display_destroy);
@@ -183,6 +186,13 @@ ds_tizen_video_add_new_object_listener(struct ds_tizen_video *video,
     wl_signal_add(&video->events.new_object, listener);
 }
 
+WL_EXPORT void
+ds_tizen_video_add_new_viewport_listener(struct ds_tizen_video *video,
+        struct wl_listener *listener)
+{
+    wl_signal_add(&video->events.new_viewport, listener);
+}
+
 WL_EXPORT struct ds_surface *
 ds_tizen_video_object_get_surface(struct ds_tizen_video_object *object)
 {
@@ -294,7 +304,15 @@ video_handle_get_viewport(struct wl_client *client,
         struct wl_resource *resource, uint32_t id,
         struct wl_resource *surface_resource)
 {
-    // TODO
+    struct ds_tizen_video *video;
+    struct ds_tizen_viewport *viewport;
+
+    viewport = create_viewport(client, resource, id, surface_resource);
+    if (!viewport)
+        return;
+
+    video = wl_resource_get_user_data(resource);
+    wl_signal_emit(&video->events.new_viewport, viewport);
 }
 
 static void
diff --git a/src/video/viewport.c b/src/video/viewport.c
new file mode 100644 (file)
index 0000000..69b3cd7
--- /dev/null
@@ -0,0 +1,606 @@
+#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");
+    }
+}