--- /dev/null
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <tizen-surface-server-protocol.h>
+#include <wtz-blender-server-protocol.h>
+#include <libds/log.h>
+#include <libds/util/addon.h>
+#include <libds/types/ds_surface.h>
+
+#include "util.h"
+#include "libds-tizen/blender.h"
+
+#define TIZEN_BLENDER_VERSION 1
+
+struct ds_tizen_blender
+{
+ struct wl_global *global;
+
+ struct wl_list clients;
+
+ struct wl_listener destroy;
+
+ struct {
+ struct wl_signal destroy;
+ struct wl_signal new_blend;
+ } events;
+};
+
+struct ds_tizen_blender_client
+{
+ struct wl_list link; //clients
+
+ struct ds_tizen_blender *blender;
+
+ struct wl_resource *resource;
+ struct wl_client *wl_client;
+
+ struct {
+ struct wl_signal destroy;
+ } events;
+};
+
+struct ds_tizen_blend
+{
+ struct wl_resource *resource;
+ struct wl_client *wl_client;
+
+ struct ds_tizen_blend_state current, pending;
+
+ struct ds_surface *surface;
+ struct ds_addon surface_addon;
+
+ struct {
+ struct wl_listener surface_commit;
+ } listener;
+
+ struct {
+ struct wl_signal commit;
+ struct wl_signal destroy;
+ } events;
+};
+
+static void
+blender_handle_display_destroy(struct wl_listener *listener, void *data);
+static void
+blender_bind(struct wl_client *wl_client, void *data, uint32_t version,
+ uint32_t id);
+
+WL_EXPORT struct ds_tizen_blender *
+ds_tizen_blender_create(struct wl_display *display)
+{
+ struct ds_tizen_blender *blender;
+
+ blender = calloc(1, sizeof *blender);
+ if (!blender) {
+ ds_err("calloc() failed.");
+ return NULL;
+ }
+
+ blender->global = wl_global_create(display, &wtz_blender_interface,
+ TIZEN_BLENDER_VERSION, blender, blender_bind);
+ if (!blender->global) {
+ ds_err("wl_global_create() failed. tizen_surface_shm_interface");
+ free(blender);
+ return NULL;
+ }
+
+ wl_list_init(&blender->clients);
+
+ blender->destroy.notify = blender_handle_display_destroy;
+ wl_display_add_destroy_listener(display, &blender->destroy);
+
+ wl_signal_init(&blender->events.destroy);
+ wl_signal_init(&blender->events.new_blend);
+
+ ds_inf("Global created: tizen_blender(%p)", blender);
+
+ return blender;
+}
+
+WL_EXPORT void
+ds_tizen_blender_add_destroy_listener(struct ds_tizen_blender *blender,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&blender->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_tizen_blender_add_new_blend_listener(struct ds_tizen_blender *blender,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&blender->events.new_blend, listener);
+}
+
+WL_EXPORT void
+ds_tizen_blend_add_destroy_listener(struct ds_tizen_blend *blend,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&blend->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_tizen_blend_add_commit_listener(struct ds_tizen_blend *blend,
+ struct wl_listener *listener)
+{
+ wl_signal_add(&blend->events.commit, listener);
+}
+
+WL_EXPORT struct ds_surface *
+ds_tizen_blend_get_surface(struct ds_tizen_blend *blend)
+{
+ return blend->surface;
+}
+
+WL_EXPORT struct ds_tizen_blend_state *
+ds_tizen_blend_get_state(struct ds_tizen_blend *blend)
+{
+ return &blend->current;
+}
+
+static void
+blender_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_tizen_blender *blender;
+
+ blender = wl_container_of(listener, blender, destroy);
+
+ ds_inf("Global destroy: blender(%p)", blender);
+
+ wl_signal_emit_mutable(&blender->events.destroy, blender);
+ wl_list_remove(&blender->destroy.link);
+ wl_global_destroy(blender->global);
+ free(blender);
+}
+
+static void
+blend_handle_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+blend_handle_set_alpha(struct wl_client *client, struct wl_resource *resource,
+ uint32_t alpha)
+{
+ struct ds_tizen_blend *blend;
+
+ blend = wl_resource_get_user_data(resource);
+
+ blend->pending.alpha = alpha;
+ blend->pending.committed |= DS_TIZEN_BLEND_STATE_ALPHA;
+}
+
+static void
+blend_handle_set_equation(struct wl_client *client, struct wl_resource *resource,
+ uint32_t equation)
+{
+ struct ds_tizen_blend *blend;
+
+ blend = wl_resource_get_user_data(resource);
+
+ blend->pending.equation = equation;
+ blend->pending.committed |= DS_TIZEN_BLEND_STATE_EQUATION;
+}
+
+static const struct wtz_blend_interface blend_impl = {
+ blend_handle_destroy,
+ blend_handle_set_alpha,
+ blend_handle_set_equation,
+};
+
+static void
+blend_destroy(struct ds_tizen_blend *blend)
+{
+ ds_inf("blend_destroy (blend:%p)", blend);
+
+ wl_signal_emit_mutable(&blend->events.destroy, blend);
+
+ if (blend->listener.surface_commit.notify)
+ wl_list_remove(&blend->listener.surface_commit.link);
+
+ if (blend->surface)
+ ds_addon_finish(&blend->surface_addon);
+
+ free(blend);
+}
+
+static void
+blend_handle_resource_destroy(struct wl_resource *resource)
+{
+ struct ds_tizen_blend *blend;
+
+ blend = wl_resource_get_user_data(resource);
+
+ ds_inf("blend_handle_resource_destroy (blend:%p)", blend);
+
+ if (blend->surface) {
+ blend->resource = NULL;
+ return;
+ }
+
+ blend_destroy(blend);
+}
+
+static void
+blend_handle_surface_commit(struct wl_listener *listener, void *data)
+{
+ struct ds_tizen_blend *blend;
+
+ blend = wl_container_of(listener, blend, listener.surface_commit);
+
+ if (!blend->resource) {
+ blend_destroy(blend);
+ return;
+ }
+
+ if (blend->pending.committed == DS_TIZEN_BLEND_STATE_NONE)
+ return;
+
+ if (blend->pending.committed & DS_TIZEN_BLEND_STATE_ALPHA)
+ blend->current.alpha = blend->pending.alpha;
+
+ if (blend->pending.committed & DS_TIZEN_BLEND_STATE_EQUATION)
+ blend->current.equation = blend->pending.equation;
+
+ blend->current.committed = blend->pending.committed;
+ blend->pending.committed = DS_TIZEN_BLEND_STATE_NONE;
+
+ wl_signal_emit_mutable(&blend->events.commit, blend);
+}
+
+static void
+blend_handle_surface_destroy(struct ds_addon *addon)
+{
+ struct ds_tizen_blend *blend;
+
+ blend = wl_container_of(addon, blend, surface_addon);
+
+ if (blend->listener.surface_commit.notify) {
+ wl_list_remove(&blend->listener.surface_commit.link);
+ blend->listener.surface_commit.notify = NULL;
+ }
+
+ ds_addon_finish(&blend->surface_addon);
+ blend->surface = NULL;
+
+ if (!blend->resource)
+ blend_destroy(blend);
+}
+
+static struct ds_addon_interface blend_addon_impl = {
+ .name = "ds_tizen_blend",
+ .destroy = blend_handle_surface_destroy,
+};
+
+static struct ds_tizen_blend *
+blend_client_get_from_surface(struct ds_tizen_blender_client *client, struct ds_surface *surface)
+{
+ struct ds_addon *addon;
+ struct ds_tizen_blend *blend;
+
+ addon = ds_addon_find(&surface->addons, client, &blend_addon_impl);
+ if (!addon) return NULL;
+
+ blend = wl_container_of(addon, blend, surface_addon);
+
+ return blend;
+}
+
+static void
+blender_handle_get_blend(struct wl_client *wl_client,
+ struct wl_resource *resource,
+ uint32_t id, struct wl_resource *surface_resource)
+{
+ struct ds_tizen_blender_client *client;
+ struct ds_surface *surface;
+ struct ds_tizen_blend *blend;
+
+ ds_inf("tizen_blender: get_blend");
+
+ client = wl_resource_get_user_data(resource);
+
+ surface = ds_surface_from_resource(surface_resource);
+ if (!surface) {
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "invalid wl_surface resource:%u",
+ (unsigned int)wl_resource_get_id(surface_resource));
+ return;
+ }
+
+ blend = blend_client_get_from_surface(client, surface);
+ if (blend) {
+ wl_resource_post_error(resource, WTZ_BLENDER_ERROR_BLEND_EXISTS,
+ "blend object already exists");
+ return;
+ }
+
+ blend = calloc(1, sizeof *blend);
+ if (blend == NULL) {
+ ds_err("calloc() failed. tizen_blend");
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ blend->resource = wl_resource_create(wl_client, &wtz_blend_interface,
+ wl_resource_get_version(resource), id);
+ if (blend->resource == NULL) {
+ ds_err("tizen_blend : wl_resource_create() failed.");
+ free(blend);
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ wl_resource_set_implementation(blend->resource, &blend_impl, blend,
+ blend_handle_resource_destroy);
+
+ ds_addon_init(&blend->surface_addon, &surface->addons, client, &blend_addon_impl);
+
+ blend->listener.surface_commit.notify = blend_handle_surface_commit;
+ ds_surface_add_commit_listener(surface, &blend->listener.surface_commit);
+
+ blend->surface = surface;
+
+ wl_signal_init(&blend->events.destroy);
+ wl_signal_init(&blend->events.commit);
+
+ wl_signal_emit_mutable(&client->blender->events.new_blend, blend);
+}
+
+static void
+blender_handle_destroy(struct wl_client *wl_client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static const struct wtz_blender_interface blender_impl =
+{
+ blender_handle_destroy,
+ blender_handle_get_blend,
+};
+
+static void
+blender_client_handle_destroy(struct wl_resource *resource)
+{
+ struct ds_tizen_blender_client *client;
+
+ client = wl_resource_get_user_data(resource);
+
+ ds_inf("blender_client_handle_destroy (client:%p)", client);
+
+ wl_list_remove(&client->link);
+ free(client);
+}
+
+static void
+blender_bind(struct wl_client *wl_client, void *data, uint32_t version,
+ uint32_t id)
+{
+ struct ds_tizen_blender *blender = data;
+ struct ds_tizen_blender_client *client;
+
+ client = calloc(1, sizeof *client);
+ if (client == NULL) {
+ ds_err("calloc() failed. tizen_blender");
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ ds_inf("tizen_blender_client binds. (client:%p)", client);
+
+ client->blender = blender;
+ client->wl_client = wl_client;
+
+ client->resource = wl_resource_create(wl_client, &wtz_blender_interface,
+ MIN(version, TIZEN_BLENDER_VERSION), id);
+ if (client->resource == NULL) {
+ ds_err("tizen_blender : wl_resource_create() failed.");
+ free(client);
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ wl_resource_set_implementation(client->resource, &blender_impl, client,
+ blender_client_handle_destroy);
+
+ wl_signal_init(&client->events.destroy);
+
+ wl_list_insert(&blender->clients, &client->link);
+}