+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <linux/limits.h>
+
+#include <Ecore_Wayland.h>
+#include <wayland-client.h>
+#include <wayland-tbm-client.h>
+#include <tizen-extension-client-protocol.h>
+
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <Ecore_Input_Evas.h>
+#include <Elementary.h>
+#include <glib-object.h>
+#include <malloc.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <stdbool.h>
+#include <aul.h>
+#include <aul_svc.h>
+#include <bundle_internal.h>
+#include <ttrace.h>
+
+#include "appcore_base.h"
+#include "appcore_ui_base.h"
+#include "appcore_ui_base_private.h"
+
+enum app_state {
+ AS_NONE,
+ AS_CREATED,
+ AS_RUNNING,
+ AS_PAUSED,
+ AS_DYING,
+};
+
+typedef struct _appcore_ui_base_context {
+ appcore_ui_base_ops ops;
+ void *data;
+ int argc;
+ char **argv;
+ unsigned int hint;
+ char *below_app;
+ char *appid;
+
+ int state;
+ Ecore_Event_Handler *hshow;
+ Ecore_Event_Handler *hhide;
+ Ecore_Event_Handler *hvchange;
+ Ecore_Event_Handler *hlower;
+} appcore_ui_base_context;
+
+
+static bool b_active = false;
+static bool first_launch = true;
+
+struct win_node {
+ unsigned int win;
+ unsigned int surf;
+ bool bfobscured;
+};
+
+static GSList *g_winnode_list;
+static appcore_ui_base_context __context;
+static struct wl_display *dsp;
+static struct wl_registry *reg;
+static struct tizen_policy *tz_policy;
+static bool bg_state = false;
+
+static void __wl_listener_cb(void *data, struct wl_registry *reg,
+ uint32_t id, const char *interface, uint32_t ver)
+{
+ if (interface && !strcmp(interface, "tizen_policy")) {
+ if (!tz_policy)
+ tz_policy = wl_registry_bind(reg, id,
+ &tizen_policy_interface, 1);
+ }
+}
+
+static void __wl_listener_remove_cb(void *data, struct wl_registry *reg,
+ unsigned int id)
+{
+ /* do nothing */
+}
+
+static const struct wl_registry_listener reg_listener = {
+ __wl_listener_cb,
+ __wl_listener_remove_cb
+};
+
+static Eina_Bool __stub_show_cb(void *data, int type, void *event)
+{
+ if (__context.ops.window.show)
+ __context.ops.window.show(type, event, __context.data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool __stub_hide_cb(void *data, int type, void *event)
+{
+ if (__context.ops.window.hide)
+ __context.ops.window.hide(type, event, __context.data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool __stub_visibility_cb(void *data, int type, void *event)
+{
+ if (__context.ops.window.visibility)
+ __context.ops.window.visibility(type, event, __context.data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool __stub_lower_cb(void *data, int type, void *event)
+{
+ if (__context.ops.window.lower)
+ __context.ops.window.lower(type, event, __context.data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void __prepare_to_suspend(void)
+{
+ int suspend = APPCORE_BASE_SUSPENDED_STATE_WILL_ENTER_SUSPEND;
+
+ if (appcore_base_is_bg_allowed() && !appcore_base_is_suspended()) {
+ appcore_base_raise_event((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
+ appcore_base_toggle_suspended_state();
+ }
+}
+
+static void __exit_from_suspend(void)
+{
+ int suspend = APPCORE_BASE_SUSPENDED_STATE_DID_EXIT_FROM_SUSPEND;
+
+ if (appcore_base_is_suspended()) {
+ appcore_base_raise_event((void *)&suspend, APPCORE_BASE_EVENT_SUSPENDED_STATE_CHANGE);
+ appcore_base_toggle_suspended_state();
+ }
+}
+
+static void __do_pause(void)
+{
+ if (__context.state == AS_RUNNING) {
+ if (__context.ops.pause)
+ __context.ops.pause(__context.data);
+
+ __context.state = AS_PAUSED;
+ __prepare_to_suspend();
+ }
+ aul_status_update(STATUS_BG);
+}
+
+static void __do_resume(void)
+{
+ if (__context.state == AS_PAUSED || __context.state == AS_CREATED) {
+ __exit_from_suspend();
+ if (__context.ops.resume) {
+ LOG(LOG_DEBUG, "LAUNCH", "[%s:Application:resume:start]", __context.appid);
+ __context.ops.resume(__context.data);
+ LOG(LOG_DEBUG, "LAUNCH", "[%s:Application:resume:done]", __context.appid);
+ LOG(LOG_DEBUG, "LAUNCH", "[%s:Application:Launching:done]", __context.appid);
+ }
+ if ((__context.hint & APPCORE_UI_BASE_HINT_WINDOW_STACK_CONTROL) &&
+ __context.below_app) {
+ aul_app_group_activate_below(__context.below_app);
+ free(__context.below_app);
+ __context.below_app = NULL;
+ }
+ __context.state = AS_RUNNING;
+ }
+
+ aul_status_update(STATUS_VISIBLE);
+}
+
+static GSList *__find_win(unsigned int win)
+{
+ GSList *iter;
+ struct win_node *t;
+
+ for (iter = g_winnode_list; iter; iter = g_slist_next(iter)) {
+ t = iter->data;
+ if (t && t->win == win)
+ return iter;
+ }
+
+ return NULL;
+}
+
+static int __get_main_window(void)
+{
+ struct win_node *entry = NULL;
+
+ if (g_winnode_list != NULL) {
+ entry = g_winnode_list->data;
+ return (unsigned int) entry->win;
+ }
+
+ return 0;
+}
+
+static int __get_main_surface(void)
+{
+ struct win_node *entry = NULL;
+
+ if (g_winnode_list != NULL) {
+ entry = g_winnode_list->data;
+ return (unsigned int) entry->surf;
+ }
+
+ return 0;
+}
+
+static bool __add_win(unsigned int win, unsigned int surf)
+{
+ struct win_node *t;
+ GSList *f;
+
+ _DBG("[EVENT_TEST][EVENT] __add_win WIN:%x\n", win);
+
+ f = __find_win(win);
+ if (f) {
+ errno = ENOENT;
+ _DBG("[EVENT_TEST][EVENT] ERROR There is already window : %x \n", win);
+ return FALSE;
+ }
+
+ t = calloc(1, sizeof(struct win_node));
+ if (t == NULL)
+ return FALSE;
+
+ t->win = win;
+ t->surf = surf;
+ t->bfobscured = FALSE;
+
+ g_winnode_list = g_slist_append(g_winnode_list, t);
+
+ return TRUE;
+}
+
+static bool __delete_win(unsigned int win)
+{
+ GSList *f;
+
+ f = __find_win(win);
+ if (!f) {
+ errno = ENOENT;
+ _DBG("[EVENT_TEST][EVENT] ERROR There is no window : %x \n",
+ win);
+ return FALSE;
+ }
+
+ free(f->data);
+ g_winnode_list = g_slist_delete_link(g_winnode_list, f);
+
+ return TRUE;
+}
+
+static bool __update_win(unsigned int win, unsigned int surf, bool bfobscured)
+{
+ GSList *f;
+ struct win_node *t;
+
+ _DBG("[EVENT_TEST][EVENT] __update_win WIN:%x fully_obscured %d\n", win,
+ bfobscured);
+
+ f = __find_win(win);
+ if (!f) {
+ errno = ENOENT;
+ _DBG("[EVENT_TEST][EVENT] ERROR There is no window : %x \n", win);
+ return FALSE;
+ }
+
+ t = (struct win_node *)f->data;
+ t->win = win;
+ if (surf != 0)
+ t->surf = surf;
+ t->bfobscured = bfobscured;
+
+ return TRUE;
+}
+
+static void __raise_win(void)
+{
+ Ecore_Wl_Window *win;
+ unsigned int win_id;
+
+ if (!(__context.hint & APPCORE_UI_BASE_HINT_WINDOW_STACK_CONTROL))
+ return;
+
+ win_id = __get_main_window();
+
+ _DBG("Raise window: %d", win_id);
+ win = ecore_wl_window_find(win_id);
+ ecore_wl_window_activate(win);
+}
+
+static void __pause_win(void)
+{
+ Ecore_Wl_Window *win;
+ GSList *wlist = g_winnode_list;
+ struct win_node *entry = NULL;
+
+ if (!(__context.hint & APPCORE_UI_BASE_HINT_WINDOW_STACK_CONTROL))
+ return;
+
+ _DBG("Pause window");
+
+ while (wlist) {
+ entry = wlist->data;
+
+ _DBG("Pause window: %d", entry->win);
+ win = ecore_wl_window_find(entry->win);
+ ecore_wl_window_iconified_set(win, EINA_TRUE);
+
+ wlist = wlist->next;
+ }
+}
+
+static int __init_wl(void)
+{
+ _DBG("initialize wayland");
+ dsp = wl_display_connect(NULL);
+ if (dsp == NULL) {
+ _ERR("Failed to connect wl display");
+ return -1;
+ }
+
+ reg = wl_display_get_registry(dsp);
+ if (reg == NULL) {
+ _ERR("Failed to get registry");
+ wl_display_disconnect(dsp);
+ return -1;
+ }
+
+ wl_registry_add_listener(reg, ®_listener, NULL);
+ wl_display_roundtrip(dsp);
+
+ if (!tz_policy) {
+ _ERR("Failed to get tizen policy interface");
+ wl_registry_destroy(reg);
+ wl_display_disconnect(dsp);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __finish_wl(void)
+{
+ if (tz_policy) {
+ tizen_policy_destroy(tz_policy);
+ tz_policy = NULL;
+ }
+
+ if (reg) {
+ wl_registry_destroy(reg);
+ reg = NULL;
+ }
+
+ if (dsp) {
+ wl_display_disconnect(dsp);
+ dsp = NULL;
+ }
+}
+
+static void __set_bg_state(void)
+{
+ if (__init_wl() < 0)
+ return;
+
+ tizen_policy_set_background_state(tz_policy, getpid());
+ wl_display_roundtrip(dsp);
+ bg_state = true;
+ _DBG("bg state: %d", bg_state);
+}
+
+static void __unset_bg_state(void)
+{
+ if (!tz_policy)
+ return;
+
+ tizen_policy_unset_background_state(tz_policy, getpid());
+ wl_display_roundtrip(dsp);
+ bg_state = false;
+ _DBG("bg state: %d", bg_state);
+ __finish_wl();
+}
+
+static void __do_start(bundle *b)
+{
+ const char *bg_launch;
+ const char *below_app;
+
+ if (__context.hint & APPCORE_UI_BASE_HINT_WINDOW_STACK_CONTROL) {
+ if (__context.below_app) {
+ free(__context.below_app);
+ __context.below_app = NULL;
+ }
+
+ below_app = bundle_get_val(b, AUL_SVC_K_RELOCATE_BELOW);
+ if (below_app)
+ __context.below_app = strdup(below_app);
+ }
+
+ if (first_launch) {
+ first_launch = FALSE;
+ return;
+ }
+
+ if ((__context.hint & APPCORE_UI_BASE_HINT_BG_LAUNCH_CONTROL) &&
+ bg_state) {
+ bg_launch = bundle_get_val(b, AUL_SVC_K_BG_LAUNCH);
+ if (!bg_launch || strcmp(bg_launch, "enable"))
+ __unset_bg_state();
+ }
+
+ __raise_win();
+}
+
+EXPORT_API int appcore_ui_base_on_receive(aul_type type, bundle *b)
+{
+ if (__context.state == AS_DYING) {
+ _ERR("Skip the event in dying state");
+ return 0;
+ }
+
+ if (type == AUL_TERMINATE_BGAPP && __context.state != AS_PAUSED)
+ return 0;
+
+ if (type == AUL_START)
+ __exit_from_suspend();
+
+ appcore_base_on_receive(type, b);
+
+ switch (type) {
+ case AUL_START:
+ __do_start(b);
+ break;
+ case AUL_RESUME:
+ __do_resume();
+ break;
+ case AUL_TERMINATE:
+ break;
+ case AUL_TERMINATE_BGAPP:
+ _DBG("[APP %d] is paused. TERMINATE", getpid());
+ __context.state = AS_DYING;
+ aul_status_update(STATUS_DYING);
+ if (__context.ops.base.exit)
+ __context.ops.base.exit(__context.data);
+ break;
+ case AUL_PAUSE:
+ __pause_win();
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+EXPORT_API int appcore_ui_base_on_create(void)
+{
+ appcore_base_on_create();
+
+ __context.hshow = ecore_event_handler_add(ECORE_WL_EVENT_WINDOW_SHOW, __stub_show_cb, NULL);
+ __context.hhide = ecore_event_handler_add(ECORE_WL_EVENT_WINDOW_HIDE, __stub_hide_cb, NULL);
+ __context.hvchange = ecore_event_handler_add(ECORE_WL_EVENT_WINDOW_VISIBILITY_CHANGE,
+ __stub_visibility_cb, NULL);
+ __context.hlower = ecore_event_handler_add(ECORE_WL_EVENT_WINDOW_LOWER, __stub_lower_cb, NULL);
+ __context.state = AS_CREATED;
+ LOG(LOG_DEBUG, "LAUNCH", "[%s:Application:create:done]", __context.appid);
+
+ return 0;
+}
+
+EXPORT_API int appcore_ui_base_on_terminate(void)
+{
+ if (__context.state == AS_RUNNING) {
+ if (__context.ops.pause)
+ __context.ops.pause(__context.data);
+ }
+
+ __context.state = AS_DYING;
+ if (__context.hshow)
+ ecore_event_handler_del(__context.hshow);
+ if (__context.hhide)
+ ecore_event_handler_del(__context.hhide);
+ if (__context.hvchange)
+ ecore_event_handler_del(__context.hvchange);
+ if (__context.hlower)
+ ecore_event_handler_del(__context.hlower);
+
+ elm_shutdown();
+
+ /* Check loader case */
+ if (getenv("AUL_LOADER_INIT")) {
+ unsetenv("AUL_LOADER_INIT");
+ elm_shutdown();
+ }
+
+ appcore_base_on_terminate();
+
+ return 0;
+}
+
+EXPORT_API int appcore_ui_base_on_pause(void)
+{
+ return 0;
+}
+
+EXPORT_API int appcore_ui_base_on_resume(void)
+{
+ return 0;
+}
+
+EXPORT_API int appcore_ui_base_on_control(bundle *b)
+{
+ LOG(LOG_DEBUG, "LAUNCH", "[%s:Application:reset:start]", __context.appid);
+ appcore_base_on_control(b);
+ LOG(LOG_DEBUG, "LAUNCH", "[%s:Application:reset:done]", __context.appid);
+
+ return 0;
+}
+
+static void __group_attach()
+{
+ static bool attached = false;
+
+ if (!(__context.hint & APPCORE_UI_BASE_HINT_WINDOW_GROUP_CONTROL))
+ return;
+
+ _DBG("__group_attach");
+ if (attached)
+ return;
+
+ int wid = __get_main_surface();
+ if (wid == 0) {
+ _ERR("window wasn't ready");
+ return;
+ }
+
+ aul_app_group_set_window(wid);
+ attached = true;
+}
+
+static void __group_lower()
+{
+ int exit = 0;
+
+ if (!(__context.hint & APPCORE_UI_BASE_HINT_WINDOW_GROUP_CONTROL))
+ return;
+
+ _DBG("__group_lower");
+ aul_app_group_lower(&exit);
+ if (exit) {
+ _DBG("__group_lower : sub-app!");
+ if (__context.ops.base.exit)
+ __context.ops.base.exit(__context.data);
+ }
+}
+
+EXPORT_API void appcore_ui_base_window_on_show(int type, void *event)
+{
+ Ecore_Wl_Event_Window_Show *ev;
+
+ ev = event;
+ if (ev->parent_win != 0) {
+ /* This is child window. Skip!!! */
+ return;
+ }
+
+ _DBG("[EVENT_TEST][EVENT] GET SHOW EVENT!!!. WIN:%x, %d\n", ev->win, ev->data[0]);
+
+ if (!__find_win((unsigned int)ev->win))
+ __add_win((unsigned int)ev->win, (unsigned int)ev->data[0]);
+ else
+ __update_win((unsigned int)ev->win, (unsigned int)ev->data[0], FALSE);
+ __group_attach();
+}
+
+static bool __check_visible(void)
+{
+ GSList *iter = NULL;
+ struct win_node *entry = NULL;
+
+ _DBG("[EVENT_TEST][EVENT] __check_visible\n");
+
+ for (iter = g_winnode_list; iter != NULL; iter = g_slist_next(iter)) {
+ entry = iter->data;
+ _DBG("win : %x obscured : %d\n", entry->win, entry->bfobscured);
+ if (entry->bfobscured == FALSE)
+ return true;
+ }
+
+ return false;
+}
+
+EXPORT_API void appcore_ui_base_window_on_hide(int type, void *event)
+{
+ Ecore_Wl_Event_Window_Hide *ev;
+ int bvisibility;
+
+ ev = event;
+ _DBG("[EVENT_TEST][EVENT] GET HIDE EVENT!!!. WIN:%x\n", ev->win);
+
+ if (__find_win((unsigned int)ev->win)) {
+ __delete_win((unsigned int)ev->win);
+ bvisibility = __check_visible();
+ if (!bvisibility && b_active == TRUE) {
+ _DBG(" Go to Pasue state \n");
+ b_active = FALSE;
+ __do_pause();
+ }
+ }
+}
+
+EXPORT_API void appcore_ui_base_window_on_lower(int type, void *event)
+{
+ Ecore_Wl_Event_Window_Lower *ev;
+
+ ev = event;
+ if (!ev)
+ return;
+ _DBG("ECORE_WL_EVENT_WINDOW_LOWER window id:%u\n", ev->win);
+ __group_lower();
+}
+
+EXPORT_API void appcore_ui_base_window_on_visibility(int type, void *event)
+{
+ Ecore_Wl_Event_Window_Visibility_Change *ev;
+ int bvisibility;
+
+ ev = event;
+ __update_win((unsigned int)ev->win, 0, ev->fully_obscured);
+ bvisibility = __check_visible();
+
+ _DBG("bvisibility %d, b_active %d", bvisibility, b_active);
+
+ if (bvisibility && b_active == FALSE) {
+ _DBG(" Go to Resume state\n");
+ b_active = TRUE;
+ __do_resume();
+ } else if (!bvisibility && b_active == TRUE) {
+ _DBG(" Go to Pasue state \n");
+ b_active = FALSE;
+ __do_pause();
+ } else {
+ _DBG(" No change state \n");
+ }
+
+}
+
+EXPORT_API int appcore_ui_base_init(appcore_ui_base_ops ops, int argc, char **argv,
+ void *data, unsigned int hint)
+{
+ char *hwacc;
+ const char *bg_launch;
+ bundle *b;
+ char appid[PATH_MAX] = {0, };
+
+ aul_app_get_appid_bypid(getpid(), appid, sizeof(appid));
+ __context.ops = ops;
+ __context.data = data;
+ __context.argc = argc;
+ __context.argv = argv;
+ __context.hint = hint;
+ __context.state = AS_NONE;
+ __context.appid = strdup(appid);
+
+ LOG(LOG_DEBUG, "LAUNCH", "[%s:Application:main:done]", appid);
+ elm_init(argc, argv);
+
+ if (__context.hint & APPCORE_UI_BASE_HINT_HW_ACC_CONTROL) {
+ hwacc = getenv("HWACC");
+
+ if (hwacc == NULL) {
+ _DBG("elm_config_accel_preference_set is not called");
+ } else if (strcmp(hwacc, "USE") == 0) {
+ elm_config_accel_preference_set("hw");
+ _DBG("elm_config_accel_preference_set : hw");
+ } else if (strcmp(hwacc, "NOT_USE") == 0) {
+ elm_config_accel_preference_set("none");
+ _DBG("elm_config_accel_preference_set : none");
+ } else {
+ _DBG("elm_config_accel_preference_set is not called");
+ }
+ }
+
+ if (__context.hint & APPCORE_UI_BASE_HINT_BG_LAUNCH_CONTROL) {
+ b = bundle_import_from_argv(argc, argv);
+ if (b) {
+ bg_launch = bundle_get_val(b, AUL_SVC_K_BG_LAUNCH);
+ if (bg_launch && strcmp(bg_launch, "enable") == 0)
+ __set_bg_state();
+
+ bundle_free(b);
+ }
+ }
+
+ return appcore_base_init(ops.base, argc, argv, data);
+}
+
+EXPORT_API void appcore_ui_base_fini(void)
+{
+ appcore_base_fini();
+ free(__context.appid);
+ __context.appid = NULL;
+}
+
+EXPORT_API void appcore_ui_base_pause(void)
+{
+ __do_pause();
+}
+
+EXPORT_API void appcore_ui_base_resume(void)
+{
+ __do_resume();
+}
+
+EXPORT_API bool appcore_ui_base_is_resumed(void)
+{
+ return __context.state == AS_RUNNING;
+}
+
+EXPORT_API void appcore_ui_base_exit(void)
+{
+ if (__context.ops.base.exit)
+ __context.ops.base.exit(__context.data);
+}
+
+static int __on_receive(aul_type type, bundle *b, void *data)
+{
+ return appcore_ui_base_on_receive(type, b);
+}
+
+static int __on_create(void *data)
+{
+ return appcore_ui_base_on_create();
+}
+
+static int __on_terminate(void *data)
+{
+ return appcore_ui_base_on_terminate();
+}
+
+static int __on_pause(void *data)
+{
+ return appcore_ui_base_on_pause();
+}
+
+static int __on_resume(void *data)
+{
+ return appcore_ui_base_on_resume();
+}
+
+static void __window_on_show(int type, void *event, void *data)
+{
+ appcore_ui_base_window_on_show(type, event);
+}
+
+static void __window_on_hide(int type, void *event, void *data)
+{
+ appcore_ui_base_window_on_hide(type, event);
+}
+
+static void __window_on_lower(int type, void *event, void *data)
+{
+ appcore_ui_base_window_on_lower(type, event);
+}
+
+static void __window_on_visibility(int type, void *event, void *data)
+{
+ appcore_ui_base_window_on_visibility(type, event);
+}
+
+static void __run(void *data)
+{
+ elm_run();
+}
+
+static void __exit(void *data)
+{
+ elm_exit();
+}
+
+EXPORT_API appcore_ui_base_ops appcore_ui_base_get_default_ops(void)
+{
+ appcore_ui_base_ops ops;
+
+ ops.base = appcore_base_get_default_ops();
+
+ /* override methods */
+ ops.base.create = __on_create;
+ ops.base.terminate = __on_terminate;
+ ops.base.receive = __on_receive;
+ ops.base.run = __run;
+ ops.base.exit = __exit;
+
+ ops.pause = __on_pause;
+ ops.resume = __on_resume;
+ ops.window.show = __window_on_show;
+ ops.window.hide = __window_on_hide;
+ ops.window.lower = __window_on_lower;
+ ops.window.visibility = __window_on_visibility;
+
+ return ops;
+}
+
+