--- /dev/null
+#include <stdlib.h>
+
+#include <wayland-server.h>
+#include <scaler-server-protocol.h>
+
+#include <libds/log.h>
+#include <libds/surface.h>
+
+#define SCALER_VERSION 2
+
+struct ds_tizen_scaler
+{
+ struct wl_global *global;
+
+ struct wl_listener display_destroy;
+};
+
+struct viewport
+{
+ struct wl_resource *resource;
+ struct ds_surface_viewport *surface_viewport;
+
+ struct wl_listener surface_destroy;
+ struct wl_listener surface_commit;
+};
+
+static void scaler_bind(struct wl_client *client, void *data, uint32_t version,
+ uint32_t id);
+static void scaler_handle_display_destroy(struct wl_listener *listener,
+ void *data);
+static void viewport_set_source(struct viewport *viewport,
+ wl_fixed_t x_fixed, wl_fixed_t y_fixed,
+ wl_fixed_t width_fixed, wl_fixed_t height_fixed);
+static void viewport_set_destination(struct viewport *viewport,
+ int32_t width, int32_t height);
+
+struct ds_tizen_scaler *
+ds_tizen_scaler_create(struct wl_display *display)
+{
+ struct ds_tizen_scaler *scaler;
+
+ scaler = calloc(1, sizeof *scaler);
+ if (!scaler)
+ return NULL;
+
+ scaler->global = wl_global_create(display, &wl_scaler_interface,
+ SCALER_VERSION, NULL, scaler_bind);
+ if (!scaler->global) {
+ free(scaler);
+ return NULL;
+ }
+
+ scaler->display_destroy.notify = scaler_handle_display_destroy;
+ wl_display_add_destroy_listener(display, &scaler->display_destroy);
+
+ return scaler;
+}
+
+static void
+viewport_destroy(struct viewport *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_handle_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+viewport_handle_set(struct wl_client *client, struct wl_resource *resource,
+ wl_fixed_t src_x, wl_fixed_t src_y,
+ wl_fixed_t src_width, wl_fixed_t src_height,
+ int32_t dst_width, int32_t dst_height)
+{
+ struct viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ if (!viewport) {
+ ds_err("wl_viewport@%d.set sent after wl_surface has been destroyed",
+ wl_resource_get_id(resource));
+ return;
+ }
+
+ viewport_set_source(viewport, src_x, src_y, src_width, src_height);
+ viewport_set_destination(viewport, dst_width, dst_height);
+}
+
+static void
+viewport_handle_set_source(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 viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ if (!viewport) {
+ ds_err("wl_viewport@%d.set_source sent after wl_surface has been "
+ "destroyed", wl_resource_get_id(resource));
+ return;
+ }
+
+ viewport_set_source(viewport, x, y, width, height);
+}
+
+static void
+viewport_handle_set_destination(struct wl_client *client,
+ struct wl_resource *resource, int32_t width, int32_t height)
+{
+ struct viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ if (!viewport) {
+ ds_err("wl_viewport@%d.set_destination sent after wl_surface has been "
+ "destroyed", wl_resource_get_id(resource));
+ return;
+ }
+
+ viewport_set_destination(viewport, width, height);
+}
+
+static const struct wl_viewport_interface viewport_iface = {
+ .destroy = viewport_handle_destroy,
+ .set = viewport_handle_set,
+ .set_source = viewport_handle_set_source,
+ .set_destination = viewport_handle_set_destination,
+};
+
+static void
+viewport_handle_surface_destroy(struct wl_listener *listener, void *data)
+{
+ struct viewport *viewport;
+
+ viewport = wl_container_of(listener, viewport, surface_destroy);
+ viewport_destroy(viewport);
+}
+
+static void
+viewport_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct viewport *viewport;
+
+ viewport = wl_resource_get_user_data(resource);
+ if (viewport)
+ viewport_destroy(viewport);
+}
+
+static void
+viewport_handle_surface_commit(struct wl_listener *listener, void *data)
+{
+ struct viewport *viewport;
+
+ viewport = wl_container_of(listener, viewport, surface_commit);
+
+ // TODO
+}
+
+static void
+scaler_handle_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+scaler_handle_get_viewport(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id,
+ struct wl_resource *surface_resource)
+{
+ struct viewport *viewport;
+ struct ds_surface *surface;
+ struct ds_surface_viewport *surface_viewport;
+
+ surface = ds_surface_from_resource(surface_resource);
+ surface_viewport = ds_surface_take_viewport(surface);
+ if (!surface_viewport) {
+ wl_resource_post_error(resource, WL_SCALER_ERROR_VIEWPORT_EXISTS,
+ "a viewport for that surface already exists");
+ return;
+ }
+
+ viewport = calloc(1, sizeof *viewport);
+ if (!viewport) {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ viewport->resource = wl_resource_create(client, &wl_viewport_interface,
+ wl_resource_get_version(resource), id);
+ if (!viewport->resource) {
+ wl_resource_post_no_memory(resource);
+ free(viewport);
+ return;
+ }
+ wl_resource_set_implementation(viewport->resource, &viewport_iface,
+ viewport, viewport_handle_resource_destroy);
+
+ 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);
+}
+
+static const struct wl_scaler_interface scaler_iface = {
+ .destroy = scaler_handle_destroy,
+ .get_viewport = scaler_handle_get_viewport,
+};
+
+static void
+scaler_bind(struct wl_client *client, void *data, uint32_t version,
+ uint32_t id)
+{
+ struct wl_resource *resource;
+ uint32_t bind_version;
+
+ bind_version = version < SCALER_VERSION ? version : SCALER_VERSION;
+ resource = wl_resource_create(client, &wl_scaler_interface, bind_version,
+ id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &scaler_iface, NULL, NULL);
+}
+
+static void
+scaler_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_tizen_scaler *scaler;
+
+ scaler = wl_container_of(listener, scaler, display_destroy);
+ wl_list_remove(&scaler->display_destroy.link);
+ wl_global_destroy(scaler->global);
+ free(scaler);
+}
+
+static void
+viewport_set_source(struct viewport *viewport,
+ wl_fixed_t x_fixed, wl_fixed_t y_fixed,
+ wl_fixed_t width_fixed, wl_fixed_t height_fixed)
+{
+ double x, y, width, height;
+
+ x = wl_fixed_to_double(x_fixed);
+ y = wl_fixed_to_double(y_fixed);
+ width = wl_fixed_to_double(width_fixed);
+ height = wl_fixed_to_double(height_fixed);
+
+ if (width == -1.0 && height == -1.0) {
+ ds_surface_viewport_unset_source(viewport->surface_viewport);
+ }
+ else if (x < 0 || y < 0 || width <= 0 || height <= 0) {
+ ds_inf("wl_viewport.set_source sent with invalid values (%f %f %fx%f)",
+ x, y, width, height);
+ if (width <= 0 || height <= 0) {
+ wl_resource_post_error(viewport->resource,
+ WL_VIEWPORT_ERROR_BAD_VALUE,
+ "wl_viewport.set_source sent with invalid values");
+ }
+ }
+ else {
+ ds_surface_viewport_set_source(viewport->surface_viewport,
+ x, y, width, height);
+ }
+}
+
+static void
+viewport_set_destination(struct viewport *viewport,
+ int32_t width, int32_t height)
+{
+ if (width == -1 && height == -1) {
+ ds_surface_viewport_unset_destination(viewport->surface_viewport);
+ }
+ else if (width <= 0 || height <= 0) {
+ wl_resource_post_error(viewport->resource, WL_VIEWPORT_ERROR_BAD_VALUE,
+ "destination size must be positive (%dx%d)",
+ width, height);
+ }
+ else {
+ ds_surface_viewport_set_destination(viewport->surface_viewport,
+ width, height);
+ }
+}