+#include <stdlib.h>
+#include <wayland-server.h>
+#include <tizen-launch-server-protocol.h>
+#include <libds/log.h>
+
+#include "util.h"
+#include <libds-tizen/launch/appinfo.h>
+
+#define TIZEN_APPINFO_VERSION 1
+
+struct ds_tizen_appinfo_mgr
+{
+ struct wl_global *global;
+
+ struct wl_list clients;
+ struct wl_list infos;
+
+ struct wl_listener destroy;
+
+ struct {
+ struct wl_signal destroy;
+ struct wl_signal set_pid;
+ struct wl_signal set_appid;
+ struct wl_signal metadata_ready;
+ } events;
+
+ struct wl_resource *resource; //for gtest
+};
+
+struct ds_tizen_appinfo_client
+{
+ struct ds_tizen_appinfo_mgr *appinfo_mgr;
+
+ struct wl_resource *resource;
+ struct wl_client *wl_client;
+
+ struct wl_list link; // ds_tizen_appinfo_mgr::clients
+};
+
+enum ds_tizen_appinfo_owner
+{
+ DS_TIZEN_APPINFO_OWNER_SERVER,
+ DS_TIZEN_APPINFO_OWNER_CLIENT,
+};
+
+struct ds_tizen_appinfo
+{
+ struct ds_tizen_appinfo_mgr *appinfo_mgr;
+
+ pid_t pid;
+ char *appid;
+ bool base_output_available;
+ int base_output_width;
+ int base_output_height;
+ enum ds_tizen_appinfo_owner owner;
+
+ struct wl_list link; //ds_tizen_appinfo_mgr::infos;
+};
+
+static void appinfo_mgr_handle_display_destroy(struct wl_listener *listener,
+ void *data);
+
+static void appinfo_mgr_bind(struct wl_client *wl_client, void *data,
+ uint32_t version, uint32_t id);
+
+WL_EXPORT struct ds_tizen_appinfo_mgr *
+ds_tizen_appinfo_mgr_create(struct wl_display *display)
+{
+ struct ds_tizen_appinfo_mgr *appinfo_mgr;
+
+ appinfo_mgr = calloc(1, sizeof *appinfo_mgr);
+ if (!appinfo_mgr) {
+ ds_err("calloc() failed.");
+ return NULL;
+ }
+
+ appinfo_mgr->global = wl_global_create(display, &tizen_launch_appinfo_interface,
+ TIZEN_APPINFO_VERSION, appinfo_mgr, appinfo_mgr_bind);
+ if (!appinfo_mgr->global) {
+ ds_err("wl_global_create() failed. tizen_launch_appinfo_interface");
+ free(appinfo_mgr);
+ return NULL;
+ }
+
+ wl_list_init(&appinfo_mgr->clients);
+ wl_list_init(&appinfo_mgr->infos);
+
+ appinfo_mgr->destroy.notify = appinfo_mgr_handle_display_destroy;
+ wl_display_add_destroy_listener(display, &appinfo_mgr->destroy);
+
+ wl_signal_init(&appinfo_mgr->events.destroy);
+ wl_signal_init(&appinfo_mgr->events.set_pid);
+ wl_signal_init(&appinfo_mgr->events.set_appid);
+ wl_signal_init(&appinfo_mgr->events.metadata_ready);
+
+ ds_inf("Global create: tizen_launch_appinfo. appinfo_mgr(%p)", appinfo_mgr);
+
+ return appinfo_mgr;
+}
+
+WL_EXPORT void
+ds_tizen_appinfo_mgr_add_destroy_listener(
+ struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener)
+{
+ wl_signal_add(&appinfo_mgr->events.destroy, listener);
+}
+
+WL_EXPORT void
+ds_tizen_appinfo_mgr_add_set_pid_listener(
+ struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener)
+{
+ wl_signal_add(&appinfo_mgr->events.set_pid, listener);
+}
+
+WL_EXPORT void
+ds_tizen_appinfo_mgr_add_set_appid_listener(
+ struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener)
+{
+ wl_signal_add(&appinfo_mgr->events.set_appid, listener);
+}
+
+WL_EXPORT void
+ds_tizen_appinfo_mgr_add_metadata_ready_listener(
+ struct ds_tizen_appinfo_mgr *appinfo_mgr, struct wl_listener *listener)
+{
+ wl_signal_add(&appinfo_mgr->events.metadata_ready, listener);
+}
+
+static void
+appinfo_mgr_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_tizen_appinfo_mgr *appinfo_mgr;
+ struct ds_tizen_appinfo_client *client, *tmp_client;
+ struct ds_tizen_appinfo *info, *tmp_info;
+
+ appinfo_mgr = wl_container_of(listener, appinfo_mgr, destroy);
+
+ ds_inf("Global destroy: appinfo_mgr(%p)", appinfo_mgr);
+
+ wl_signal_emit(&appinfo_mgr->events.destroy, appinfo_mgr);
+ wl_list_remove(&appinfo_mgr->destroy.link);
+
+ wl_list_for_each_safe(client, tmp_client, &appinfo_mgr->clients, link) {
+ wl_list_remove(&client->link);
+ free(client);
+ }
+
+ wl_list_for_each_safe(info, tmp_info, &appinfo_mgr->infos, link) {
+ wl_list_remove(&info->link);
+ free(info->appid);
+ free(info);
+ }
+
+ wl_global_destroy(appinfo_mgr->global);
+ free(appinfo_mgr);
+}
+
+static void
+appinfo_handle_destroy(struct wl_client *wl_client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static struct ds_tizen_appinfo *
+appinfo_mgr_find_with_pid(struct ds_tizen_appinfo_mgr *appinfo_mgr,
+ pid_t pid)
+{
+ struct ds_tizen_appinfo *info;
+
+ wl_list_for_each(info, &appinfo_mgr->infos, link) {
+ if (pid == info->pid)
+ return info;
+ }
+
+ return NULL;
+}
+
+static struct ds_tizen_appinfo *
+appinfo_mgr_find_with_appid(struct ds_tizen_appinfo_mgr *appinfo_mgr,
+ const char *appid)
+{
+ struct ds_tizen_appinfo *info;
+
+ wl_list_for_each(info, &appinfo_mgr->infos, link) {
+ if (appid && !strcmp(appid, info->appid))
+ return info;
+ }
+
+ return NULL;
+}
+static struct ds_tizen_appinfo *
+appinfo_mgr_get_info(struct ds_tizen_appinfo_mgr *appinfo_mgr,
+ pid_t pid, const char *appid)
+{
+ struct ds_tizen_appinfo *info = NULL;
+
+ if (pid > 0)
+ info = appinfo_mgr_find_with_pid(appinfo_mgr, pid);
+ else if (appid)
+ info = appinfo_mgr_find_with_appid(appinfo_mgr, appid);
+
+ if (info) {
+ return info;
+ }
+
+ info = calloc(1, sizeof *info);
+ if (info == NULL) {
+ ds_err("calloc() failed. tizen_appinfo");
+ return NULL;
+ }
+
+ if (pid > 0)
+ info->pid = pid;
+ else if (appid)
+ info->appid = strdup(appid);
+
+ info->appinfo_mgr = appinfo_mgr;
+
+ wl_list_insert(&appinfo_mgr->infos, &info->link);
+
+ return info;
+}
+
+static void
+appinfo_destroy(struct ds_tizen_appinfo *info)
+{
+ wl_list_remove(&info->link);
+ free(info->appid);
+ free(info);
+}
+
+static bool
+appinfo_set_pid(struct ds_tizen_appinfo *info, pid_t pid)
+{
+ struct ds_tizen_appinfo_mgr *appinfo_mgr;
+ struct ds_tizen_appinfo *info2, *tmp;
+
+ if (!info) return false;
+ if (pid < 0) return false;
+
+ appinfo_mgr = info->appinfo_mgr;
+
+ wl_list_for_each_safe(info2, tmp, &appinfo_mgr->infos, link) {
+ if ((info2->pid == pid) && (info2 != info)) {
+ ds_inf("removed duplicated appinfo");
+
+ if (!info->appid) info->appid = strdup(info2->appid);
+ if (!info->base_output_available && info2->base_output_available) {
+ ds_inf("copy base_output variable into appinfo.");
+ info->base_output_available = true;
+ info->base_output_width = info2->base_output_width;
+ info->base_output_height = info2->base_output_height;
+ }
+ appinfo_destroy(info2);
+ }
+ }
+
+ info->pid = pid;
+ ds_inf("appinfo(%p) set pid(%u)", info, pid);
+
+ wl_signal_emit(&appinfo_mgr->events.set_pid, info);
+
+ return true;
+}
+
+static bool
+appinfo_set_appid(struct ds_tizen_appinfo *info, const char *appid)
+{
+ struct ds_tizen_appinfo_mgr *appinfo_mgr;
+ struct ds_tizen_appinfo *info2, *tmp;
+
+ if (!info) return false;
+ if (!appid) return false;
+
+ appinfo_mgr = info->appinfo_mgr;
+
+ wl_list_for_each_safe(info2, tmp, &appinfo_mgr->infos, link) {
+ if ((info2->appid && !strcmp(info2->appid, appid)) &&
+ (info2 != info)) {
+ ds_inf("removed duplicated appinfo");
+
+ if (info->pid < 0) info->pid = info2->pid;
+ if (!info->base_output_available && info2->base_output_available)
+ {
+ ds_inf("copy base_output variable into appinfo.");
+ info->base_output_available = true;
+ info->base_output_width = info2->base_output_width;
+ info->base_output_height = info2->base_output_height;
+ }
+ appinfo_destroy(info2);
+ }
+ }
+
+ free(info->appid);
+ info->appid = strdup(appid);
+ ds_inf("appinfo(%p) set appid(%u)", info, appid);
+
+ wl_signal_emit(&appinfo_mgr->events.set_appid, info);
+
+ return true;
+}
+
+static bool
+appinfo_get_base_output_resolution(struct ds_tizen_appinfo *info, int *width, int *height)
+{
+ if (!info) return false;
+ if (!width) return false;
+ if (!height) return false;
+
+ if (!info->base_output_available) {
+ *width = 0;
+ *height = 0;
+ return false;
+ }
+ *width = info->base_output_width;
+ *height = info->base_output_height;
+
+ return true;
+}
+
+static void
+appinfo_set_owner(struct ds_tizen_appinfo *info, enum ds_tizen_appinfo_owner owner)
+{
+ if (!info) return;
+
+ info->owner = owner;
+}
+
+static void
+appinfo_handle_register_pid(struct wl_client *wl_client,
+ struct wl_resource *resource, uint32_t pid)
+{
+ struct ds_tizen_appinfo_client *client;
+ struct ds_tizen_appinfo *info;
+
+ client = wl_resource_get_user_data(resource);
+
+ if (pid <= 0) {
+ ds_err("tizen_appinfo: pid is invalid. pid:%u", pid);
+ return;
+ }
+
+ info = appinfo_mgr_get_info(client->appinfo_mgr, pid, NULL);
+ if (info == NULL) {
+ ds_err("appinfo_mgr_get_info() failed. tizen_appinfo");
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ appinfo_set_pid(info, pid);
+ appinfo_set_owner(info, DS_TIZEN_APPINFO_OWNER_CLIENT);
+}
+
+
+static void
+appinfo_handle_deregister_pid(struct wl_client *wl_client,
+ struct wl_resource *resource, uint32_t pid)
+{
+ struct ds_tizen_appinfo_client *client;
+ struct ds_tizen_appinfo *info;
+
+ client = wl_resource_get_user_data(resource);
+
+ info = appinfo_mgr_find_with_pid(client->appinfo_mgr, pid);
+ if (info == NULL) {
+ ds_err("tizen_appinfo: no appinfo found by pid(%u)", pid);
+ return;
+ }
+
+ appinfo_destroy(info);
+}
+
+static void
+appinfo_handle_set_appid(struct wl_client *wl_client,
+ struct wl_resource *resource, uint32_t pid, const char *appid)
+{
+ struct ds_tizen_appinfo_client *client;
+ struct ds_tizen_appinfo *info;
+
+ client = wl_resource_get_user_data(resource);
+
+ if (pid <= 0) {
+ ds_err("tizen_appinfo: pid is invalid. pid:%u", pid);
+ return;
+ }
+
+ info = appinfo_mgr_find_with_pid(client->appinfo_mgr, pid);
+ if (info == NULL) {
+ ds_err("tizen_appinfo: no appinfo found by pid(%u)", pid);
+ return;
+ }
+
+ appinfo_set_appid(info, appid);
+
+ /* TODO: base output resolution */
+}
+
+//for gtest
+WL_EXPORT struct wl_resource *
+ds_tizen_appinfo_mgr_get_appinfo_resource(
+ struct ds_tizen_appinfo_mgr *appinfo_mgr)
+{
+ return appinfo_mgr->resource;
+}
+
+static void
+appinfo_handle_get_base_output_resolution(struct wl_client *wl_client,
+ struct wl_resource *resource, uint32_t pid)
+{
+ struct ds_tizen_appinfo_client *client;
+ struct ds_tizen_appinfo *info;
+ int width = 0, height = 0;
+
+ client = wl_resource_get_user_data(resource);
+
+ if (pid <= 0) {
+ ds_err("tizen_appinfo: pid is invalid. pid:%u", pid);
+ goto finish;
+ }
+
+ info = appinfo_mgr_find_with_pid(client->appinfo_mgr, pid);
+ if (info == NULL) {
+ ds_err("tizen_appinfo: no appinfo found by pid(%u)", pid);
+ goto finish;
+ }
+
+ appinfo_get_base_output_resolution(info, &width, &height);
+
+ client->appinfo_mgr->resource = resource;
+
+finish:
+ if (width == 0 && height == 0) {
+ width = 1080; //e_config->configured_output_resolution.w;
+ height = 1920; //e_config->configured_output_resolution.h;
+ }
+
+ ds_inf("send base_output_resolution. size(%d x %d). pid(%u)", width, height, pid);
+ tizen_launch_appinfo_send_base_output_resolution_done(resource, pid, width, height);
+}
+
+static void
+appinfo_handle_register_appid(struct wl_client *wl_client, struct wl_resource *resource,
+ const char *appid)
+{
+ struct ds_tizen_appinfo_client *client;
+ struct ds_tizen_appinfo *info;
+
+ client = wl_resource_get_user_data(resource);
+
+ info = appinfo_mgr_get_info(client->appinfo_mgr, -1, appid);
+ if (info == NULL) {
+ ds_err("appinfo_mgr_get_info() failed. tizen_appinfo");
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ appinfo_set_appid(info, appid);
+ appinfo_set_owner(info, DS_TIZEN_APPINFO_OWNER_CLIENT);
+}
+
+static void
+appinfo_handle_deregister_appid(struct wl_client *wl_client,
+ struct wl_resource *resource, const char *appid)
+{
+ struct ds_tizen_appinfo_client *client;
+ struct ds_tizen_appinfo *info;
+
+ client = wl_resource_get_user_data(resource);
+
+ info = appinfo_mgr_find_with_appid(client->appinfo_mgr, appid);
+ if (info == NULL) {
+ ds_err("tizen_appinfo: no appinfo found by appid(%u)", appid);
+ return;
+ }
+
+ appinfo_destroy(info);
+}
+
+static void
+appinfo_handle_set_pid(struct wl_client *wl_client,
+ struct wl_resource *resource, const char *appid, uint32_t pid)
+{
+ struct ds_tizen_appinfo_client *client;
+ struct ds_tizen_appinfo *info;
+
+ client = wl_resource_get_user_data(resource);
+
+ info = appinfo_mgr_find_with_appid(client->appinfo_mgr, appid);
+ if (info == NULL) {
+ ds_err("tizen_appinfo: no appinfo found by appid(%u)", appid);
+ return;
+ }
+
+ appinfo_set_pid(info, pid);
+}
+
+static void
+appinfo_handle_ready_metadata(struct wl_client *wl_client,
+ struct wl_resource *resource, const char *appid, uint32_t pid)
+{
+ struct ds_tizen_appinfo_client *client;
+ struct ds_tizen_appinfo *info;
+
+ client = wl_resource_get_user_data(resource);
+
+ info = appinfo_mgr_find_with_appid(client->appinfo_mgr, appid);
+ if (info == NULL) {
+ ds_err("tizen_appinfo: no appinfo found by appid(%u)", appid);
+ return;
+ }
+
+ if (info->pid != pid)
+ appinfo_set_pid(info, pid);
+
+ wl_signal_emit(&client->appinfo_mgr->events.metadata_ready, info);
+
+ /* TODO: base output resolution */
+}
+
+static const struct tizen_launch_appinfo_interface appinfo_impl =
+{
+ appinfo_handle_destroy,
+ appinfo_handle_register_pid,
+ appinfo_handle_deregister_pid,
+ appinfo_handle_set_appid,
+ appinfo_handle_get_base_output_resolution,
+ appinfo_handle_register_appid,
+ appinfo_handle_deregister_appid,
+ appinfo_handle_set_pid,
+ appinfo_handle_ready_metadata,
+};
+
+static void
+_tizen_appinfo_client_handle_destroy(struct wl_resource *resource)
+{
+ struct ds_tizen_appinfo_client *client;
+
+ client = wl_resource_get_user_data(resource);
+
+ ds_inf("_tizen_appinfo_client_handle_destroy (client:%p)", client);
+
+ wl_list_remove(&client->link);
+ free(client);
+}
+
+static void appinfo_mgr_bind(struct wl_client *wl_client, void *data,
+ uint32_t version, uint32_t id)
+{
+ struct ds_tizen_appinfo_mgr *appinfo_mgr = data;
+ struct ds_tizen_appinfo_client *client;
+
+ client = calloc(1, sizeof *client);
+ if (client == NULL) {
+ ds_err("calloc() failed. tizen_appinfo");
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ ds_inf("tizen_appinfo_client_binds. (client:%p)", client);
+
+ client->appinfo_mgr = appinfo_mgr;
+ client->wl_client = wl_client;
+
+ client->resource = wl_resource_create(wl_client,
+ &tizen_launch_appinfo_interface,
+ MIN(version, TIZEN_APPINFO_VERSION), id);
+
+ if (client->resource == NULL) {
+ ds_err("tizen_appinfo : wl_resource_create() failed.");
+ free(client);
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ wl_resource_set_implementation(client->resource, &appinfo_impl, client,
+ _tizen_appinfo_client_handle_destroy);
+
+ wl_list_insert(&appinfo_mgr->clients, &client->link);
+}