2 * Copyright (c) 2014 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "sclcoreui-efl.h"
19 #include "sclcoreimpl.h"
20 #include <Elementary.h>
24 #include <vconf-keys.h>
28 #include <Ecore_Wayland.h>
29 #include <input-method-client-protocol.h>
33 #include <X11/Xatom.h>
37 /* This websocket agent is for supporting Tizen 2.X legacy web IMEs that uses websocket */
38 CWebHelperAgentWebSocket g_websocket;
42 struct WaylandKeyboard
45 Ecore_Wl_Window *wl_win;
46 const char *ee_engine;
48 struct wl_surface *surface;
49 struct wl_input_panel *ip;
50 struct wl_output *output;
51 struct wl_input_panel_surface *ips;
54 struct WaylandKeyboard wlkb = {0};
56 #define RENDER_PRE_TIMEOUT 1.0f
57 static Ecore_Timer *_render_pre_timer = NULL;
58 static void delete_render_pre_timer()
60 if (_render_pre_timer) {
61 ecore_timer_del(_render_pre_timer);
62 _render_pre_timer = NULL;
69 CSCLCoreUIEFL::CSCLCoreUIEFL()
71 m_initialized = FALSE;
73 m_backend_identifier = "EFL";
75 m_rotation_degree = 0;
76 m_main_window = SCLWINDOW_INVALID;
79 CSCLCoreUIEFL::~CSCLCoreUIEFL()
83 sclboolean CSCLCoreUIEFL::init()
86 m_rotation_degree = -1;
88 for (int loop = 0;loop < OPTION_WINDOW_TYPE_MAX;loop++) {
89 m_option_window_info[loop].window = SCLWINDOW_INVALID;
99 void CSCLCoreUIEFL::fini()
101 m_initialized = FALSE;
108 sclwindow CSCLCoreUIEFL::get_main_window()
111 return m_main_window;
117 void CSCLCoreUIEFL::set_keyboard_size_hints(SclSize portrait, SclSize landscape)
119 Evas_Object *main_window = NATIVE_WINDOW_CAST(m_main_window);
122 ecore_wl_window_rotation_geometry_set(elm_win_wl_window_get(main_window), 0, 0, 0, portrait.width, portrait.height);
123 ecore_wl_window_rotation_geometry_set(elm_win_wl_window_get(main_window), 90, 0, 0, landscape.height, landscape.width);
124 ecore_wl_window_rotation_geometry_set(elm_win_wl_window_get(main_window), 180, 0, 0, portrait.width, portrait.height);
125 ecore_wl_window_rotation_geometry_set(elm_win_wl_window_get(main_window), 270, 0, 0, landscape.height, landscape.width);
128 ecore_x_e_window_rotation_geometry_set(elm_win_xwindow_get(main_window), 0, 0, 0, portrait.width, portrait.height);
129 ecore_x_e_window_rotation_geometry_set(elm_win_xwindow_get(main_window), 90, 0, 0, landscape.height, landscape.width);
130 ecore_x_e_window_rotation_geometry_set(elm_win_xwindow_get(main_window), 180, 0, 0, portrait.width, portrait.height);
131 ecore_x_e_window_rotation_geometry_set(elm_win_xwindow_get(main_window), 270, 0, 0, landscape.height, landscape.width);
136 static void language_changed_cb(keynode_t *key, void* data)
138 char clang[_POSIX_PATH_MAX] = {0};
139 char *vconf_str = vconf_get_str(VCONFKEY_LANGSET);
141 snprintf(clang, sizeof(clang), "%s", vconf_str);
144 LOGD("current language is %s\n", clang);
146 CSCLCoreImpl *impl = CSCLCoreImpl::get_instance();
148 ISCLCoreEventCallback *callback = impl->get_core_event_callback();
150 callback->on_set_display_language(clang);
155 static void accessibility_changed_cb(keynode_t *key, void* data)
158 if (vconf_get_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, &vconf_value) == 0) {
159 LOGD("accessibility state : %d\n", vconf_value);
161 CSCLCoreImpl *impl = CSCLCoreImpl::get_instance();
163 ISCLCoreEventCallback *callback = impl->get_core_event_callback();
165 callback->on_set_accessibility_state(vconf_value);
172 static void win_rotation_changed_cb(void *data, Evas_Object *obj, void *event)
174 int degree = elm_win_rotation_get(obj);
175 LOGD("rotation angle : %d\n", degree);
177 ISCLCoreEventCallback *callback = NULL;
178 CSCLCoreImpl *impl = CSCLCoreImpl::get_instance();
180 callback = impl->get_core_event_callback();
183 CSCLCoreUIEFL *coreui = static_cast<CSCLCoreUIEFL*>(data);
185 coreui->set_screen_rotation_degree(degree);
188 callback->on_set_rotation_degree(degree);
192 static Eina_Bool _client_message_cb(void *data, int type, void *event)
194 Ecore_X_Event_Client_Message *ev = (Ecore_X_Event_Client_Message *)event;
196 ISCLCoreEventCallback *callback = NULL;
197 CSCLCoreImpl *impl = CSCLCoreImpl::get_instance();
198 Evas_Object *main_window = NULL;
200 callback = impl->get_core_event_callback();
201 main_window = NATIVE_WINDOW_CAST(impl->get_main_window());
204 #ifndef APPLY_WINDOW_MANAGER_CHANGE
206 if (ev->message_type == ECORE_X_ATOM_E_ILLUME_ROTATE_WINDOW_ANGLE) {
207 LOGD("ECORE_X_ATOM_E_ILLUME_ROTATE_WINDOW_ANGLE , %d %d\n", ev->data.l[0], gFHiddenState);
208 angle = ev->data.l[0];
209 ise_set_screen_direction(angle);
210 if (!gFHiddenState) {
213 } else if (ev->message_type == ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_STATE) {
214 LOGD("ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_STATE , %d\n", ev->data.l[0]);
215 elm_win_keyboard_mode_set(main_window, (Elm_Win_Keyboard_Mode)(ev->data.l[0]));
216 gFHiddenState = !(ev->data.l[0]);
220 if (ev->message_type == ECORE_X_ATOM_E_WINDOW_ROTATION_CHANGE_REQUEST) {
221 if (ev->win == elm_win_xwindow_get(main_window)) {
222 int degree = ev->data.l[1];
223 CSCLCoreUIEFL *coreui = static_cast<CSCLCoreUIEFL*>(data);
225 coreui->set_screen_rotation_degree(degree);
227 LOGD("_ECORE_X_ATOM_E_WINDOW_ROTATION_REQUEST, %d\n", degree);
229 callback->on_set_rotation_degree(degree);
231 Ecore_X_Window control_window = 0;
232 Ecore_X_Atom atom = ecore_x_atom_get("_ISF_CONTROL_WINDOW");
233 Ecore_X_Window root = ecore_x_window_root_first_get();
234 if (ecore_x_window_prop_xid_get(root, atom, ECORE_X_ATOM_WINDOW, &control_window, 1) == 1) {
235 ecore_x_client_message32_send(control_window, ECORE_X_ATOM_E_WINDOW_ROTATION_CHANGE_REQUEST,
236 ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
237 ev->data.l[0], ev->data.l[1], ev->data.l[2], ev->data.l[3], ev->data.l[4]);
242 return ECORE_CALLBACK_RENEW;
246 int CSCLCoreUIEFL::get_screen_rotation_degree()
251 angle = elm_win_rotation_get(NATIVE_WINDOW_CAST(m_main_window));
253 if (m_rotation_degree == -1) {
257 unsigned long nitems_return;
258 unsigned long bytes_after_return;
259 unsigned char *data_window = NULL;
260 unsigned char *data_angle = NULL;
262 Ecore_X_Window app_window = 0;
264 Evas_Object *keypad_win = NATIVE_WINDOW_CAST(m_main_window);
266 LOGD("Trying to get app window degree for %p\n", keypad_win);
267 Ecore_X_Window win = elm_win_xwindow_get(NATIVE_WINDOW_CAST(keypad_win));
268 ret = XGetWindowProperty((Display *)ecore_x_display_get(),
269 ecore_x_window_root_get(win),
270 ecore_x_atom_get("_ISF_ACTIVE_WINDOW"),
271 0, G_MAXLONG, False, XA_WINDOW, &type_return,
272 &format_return, &nitems_return, &bytes_after_return,
275 if (ret == Success) {
276 if ((type_return == XA_WINDOW) && (format_return == 32) && (data_window)) {
277 app_window = *(Window *)data_window;
279 ret = XGetWindowProperty((Display *)ecore_x_display_get(), app_window,
280 ecore_x_atom_get("_E_ILLUME_ROTATE_WINDOW_ANGLE"),
281 0, G_MAXLONG, False, XA_CARDINAL, &type_return,
282 &format_return, &nitems_return, &bytes_after_return,
285 LOGD("app_window : %p, ret %d, %d, %p\n", app_window, ret, type_return, data_angle);
286 if (ret == Success) {
288 if (type_return == XA_CARDINAL) {
289 angle = *(unsigned int*)data_angle;
290 LOGD("current rotation angle is %p %d\n", app_window, angle);
300 angle = m_rotation_degree;
307 static void signal_handler(int sig) {
313 _wayland_setup(struct WaylandKeyboard *wlkb, Evas_Object *main_window)
315 Eina_Inlist *globals;
316 struct wl_registry *registry;
317 Ecore_Wl_Global *global;
319 if (!(registry = ecore_wl_registry_get()))
322 if (!(globals = ecore_wl_globals_get()))
325 EINA_INLIST_FOREACH(globals, global)
327 if (strcmp(global->interface, "wl_input_panel") == 0)
328 wlkb->ip = (wl_input_panel *)wl_registry_bind(registry, global->id, &wl_input_panel_interface, 1);
329 else if (strcmp(global->interface, "wl_output") == 0)
330 wlkb->output = (wl_output *)wl_registry_bind(registry, global->id, &wl_output_interface, 1);
334 LOGW("Can't get wayland input panel interface\n");
339 LOGW("Can't get wayland output interface\n");
343 wlkb->ee = ecore_evas_ecore_evas_get(evas_object_evas_get(main_window));
345 LOGW("ERROR: Unable to create Ecore_Evas object\n");
349 /* Set input panel surface */
350 LOGD("Setting up input panel\n");
351 wlkb->wl_win = ecore_evas_wayland_window_get(wlkb->ee);
353 LOGW("Couldn't get wayland window\n");
357 ecore_wl_window_type_set(wlkb->wl_win, ECORE_WL_WINDOW_TYPE_NONE);
358 wlkb->surface = ecore_wl_window_surface_create(wlkb->wl_win);
359 if (!wlkb->surface) {
360 LOGW("Couldn't create surface\n");
364 wlkb->ips = wl_input_panel_get_input_panel_surface(wlkb->ip, wlkb->surface);
366 LOGW("Couldn't get input panel surface\n");
370 wl_input_panel_surface_set_toplevel(wlkb->ips, wlkb->output, WL_INPUT_PANEL_SURFACE_POSITION_CENTER_BOTTOM);
376 check_evas_engine(struct WaylandKeyboard *wlkb)
378 Eina_Bool ret = EINA_FALSE;
379 const char *env = getenv("ECORE_EVAS_ENGINE");
382 if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_SHM)) {
384 } else if (ecore_evas_engine_type_supported_get(ECORE_EVAS_ENGINE_WAYLAND_EGL)) {
387 LOGW("ERROR: Ecore_Evas does must be compiled with support for Wayland engines\n");
390 } else if (strcmp(env, "wayland_shm") != 0 && strcmp(env, "wayland_egl") != 0) {
391 LOGW("ERROR: ECORE_EVAS_ENGINE must be set to either 'wayland_shm' or 'wayland_egl'\n");
395 wlkb->ee_engine = env;
403 void CSCLCoreUIEFL::run(const sclchar *display)
408 CSCLCoreImpl *impl = CSCLCoreImpl::get_instance();
410 const sclchar *uuid = impl->get_uuid();
414 argv[0] = const_cast<char *> (uuid);
415 argv[1] = (char *)"--display";
416 argv[2] = const_cast<char *> (display);
419 elm_init(argc, argv);
422 if (!check_evas_engine(&wlkb)) {
423 LOGW("_wlkb_check_evas_engine error!\n");
427 LOGD("Selected engine: '%s'\n", wlkb.ee_engine);
430 elm_config_accel_preference_set("3d");
431 elm_policy_set(ELM_POLICY_THROTTLE, ELM_POLICY_THROTTLE_NEVER);
433 Evas_Object *main_window = elm_win_add(NULL, uuid, ELM_WIN_UTILITY);
435 LOGE("Failed to create main window\n");
439 m_main_window = SCL_WINDOW_CAST(main_window);
442 if (!_wayland_setup(&wlkb, main_window)) {
443 LOGW("ERROR: Unable to setup input panel.\n");
449 elm_win_borderless_set(main_window, EINA_TRUE);
450 elm_win_keyboard_win_set(main_window, EINA_TRUE);
451 elm_win_autodel_set(main_window, EINA_TRUE);
452 elm_win_title_set(main_window, uuid);
453 elm_win_prop_focus_skip_set(main_window, EINA_TRUE);
454 int rots[] = { 0, 90, 180, 270 };
455 elm_win_wm_rotation_available_rotations_set(main_window, rots, (sizeof(rots) / sizeof(int)));
458 unsigned int set = 1;
459 ecore_x_window_prop_card32_set(elm_win_xwindow_get(main_window),
460 ECORE_X_ATOM_E_WINDOW_ROTATION_SUPPORTED,
463 ecore_x_icccm_name_class_set(elm_win_xwindow_get(main_window), "Virtual Keyboard", "ISF");
466 vconf_notify_key_changed(VCONFKEY_LANGSET, language_changed_cb, NULL);
467 vconf_notify_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, accessibility_changed_cb, NULL);
469 /* Should we call these callback functions here? */
470 language_changed_cb(NULL, NULL);
471 accessibility_changed_cb(NULL, NULL);
476 evas_object_smart_callback_add(main_window, "wm,rotation,changed", win_rotation_changed_cb, this);
478 Ecore_Event_Handler *XClientMsgHandler =
479 ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE, _client_message_cb, this);
482 signal(SIGQUIT, signal_handler);
483 signal(SIGTERM, signal_handler);
484 signal(SIGINT, signal_handler);
485 signal(SIGHUP, signal_handler);
488 evas_object_show(main_window);
495 vconf_ignore_key_changed(VCONFKEY_LANGSET, language_changed_cb);
496 vconf_ignore_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, accessibility_changed_cb);
499 if (XClientMsgHandler) {
500 ecore_event_handler_del(XClientMsgHandler);
501 XClientMsgHandler = NULL;
509 static void focus_out_cb(void *data, Evas *e, void *event)
511 OptionWindowInfo *info = static_cast<OptionWindowInfo*>(data);
514 CSCLCoreImpl *impl = CSCLCoreImpl::get_instance();
516 ISCLCoreEventCallback *callback = impl->get_core_event_callback();
518 callback->on_destroy_option_window(info->window);
522 evas_object_hide(NATIVE_WINDOW_CAST(info->window));
523 evas_object_del(NATIVE_WINDOW_CAST(info->window));
530 set_transient_for_app_window(Evas_Object *window)
532 /* Set a transient window for window stack */
533 /* Gets the current XID of the active window into the root window property */
536 unsigned long nitems_return;
537 unsigned long bytes_after_return;
539 unsigned char *data = NULL;
540 Ecore_X_Window xAppWindow;
541 Ecore_X_Window xWindow = elm_win_xwindow_get(window);
544 ret = XGetWindowProperty((Display *)ecore_x_display_get(), ecore_x_window_root_get(xWindow),
545 ecore_x_atom_get("_ISF_ACTIVE_WINDOW"),
546 0, G_MAXLONG, False, XA_WINDOW, &type_return,
547 &format_return, &nitems_return, &bytes_after_return,
550 if (ret == Success) {
552 if (type_return == XA_WINDOW) {
553 xAppWindow = *(Window *)data;
554 LOGD("TRANSIENT_FOR SET : %x , %x\n", xAppWindow, xWindow);
555 ecore_x_icccm_transient_for_set(xWindow, xAppWindow);
564 set_transient_for_isf_setting_window(Evas_Object *window)
566 /* Set a transient window for window stack */
567 /* Gets the current XID of the active window into the root window property */
570 unsigned long nitems_return;
571 unsigned long bytes_after_return;
573 unsigned char *data = NULL;
574 Ecore_X_Window xControlWindow, xSettingWindow;
575 Ecore_X_Window xWindow = elm_win_xwindow_get(window);
578 ret = XGetWindowProperty((Display *)ecore_x_display_get(), ecore_x_window_root_get(xWindow),
579 ecore_x_atom_get("_ISF_CONTROL_WINDOW"),
580 0, G_MAXLONG, False, XA_WINDOW, &type_return,
581 &format_return, &nitems_return, &bytes_after_return,
584 if (ret == Success) {
586 if (type_return == XA_WINDOW) {
587 xControlWindow = *(Window *)data;
589 ecore_x_window_prop_xid_get(xControlWindow, ecore_x_atom_get("ISF Setting window"),
590 ECORE_X_ATOM_WINDOW, &xSettingWindow, 1);
592 LOGD("TRANSIENT_FOR SET : %x , %x\n", xSettingWindow, xWindow);
593 ecore_x_icccm_transient_for_set(xWindow, xSettingWindow);
601 sclwindow CSCLCoreUIEFL::create_option_window(SCLOptionWindowType type)
603 if (type < 0 || type >= OPTION_WINDOW_TYPE_MAX) {
604 return SCLWINDOW_INVALID;
607 ISCLCoreEventCallback *callback = NULL;
608 CSCLCoreImpl *impl = CSCLCoreImpl::get_instance();
610 callback = impl->get_core_event_callback();
612 sclboolean ret = false;
613 callback->on_check_option_window_availability(&ret);
615 LOGW("on_create_option_window() is not available\n");
616 return SCLWINDOW_INVALID;
619 return SCLWINDOW_INVALID;
623 /* Just in case the previous option window for setting application exists */
624 if (type == OPTION_WINDOW_TYPE_SETTING_APPLICATION) {
625 if (m_option_window_info[type].window != SCLWINDOW_INVALID) {
626 destroy_option_window(m_option_window_info[type].window);
630 Evas_Object *window = elm_win_util_standard_add("Option window", "Option window");
633 Evas_Coord win_w = 0, win_h = 0;
634 elm_win_screen_size_get(window, NULL, NULL, &win_w, &win_h);
635 int degree = get_screen_rotation_degree();
636 if (degree == 90 || degree == 270) {
637 evas_object_resize(window, win_h, win_w);
639 evas_object_resize(window, win_w, win_h);
643 int rots[] = { 0, 90, 180, 270 };
644 elm_win_wm_rotation_available_rotations_set(window, rots, (sizeof(rots) / sizeof(int)));
646 elm_win_indicator_mode_set(window, ELM_WIN_INDICATOR_SHOW);
649 callback->on_create_option_window(window, type);
652 if (type == OPTION_WINDOW_TYPE_NORMAL) {
653 Evas *evas = evas_object_evas_get(window);
654 evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, focus_out_cb, &m_option_window_info[type]);
655 set_transient_for_app_window(window);
656 } else if (type == OPTION_WINDOW_TYPE_SETTING_APPLICATION) {
657 set_transient_for_isf_setting_window(window);
658 evas_object_show(window);
661 m_option_window_info[type].window = window;
666 void CSCLCoreUIEFL::destroy_option_window(sclwindow window)
668 CSCLCoreImpl *impl = CSCLCoreImpl::get_instance();
670 ISCLCoreEventCallback *callback = impl->get_core_event_callback();
672 callback->on_destroy_option_window(window);
676 for (int loop = 0;loop < OPTION_WINDOW_TYPE_MAX;loop++) {
677 if (m_option_window_info[loop].window == window) {
678 evas_object_del(NATIVE_WINDOW_CAST(window));
679 m_option_window_info[loop].window = SCLWINDOW_INVALID;
684 void CSCLCoreUIEFL::set_screen_rotation_degree(int degree)
686 m_rotation_degree = degree;
690 static void _render_pre_cb(void *data, Evas *e, void *event_info)
692 LOGD("_render_pre_cb() called, now invoking wl_input_panel_surface_set_ready()");
694 wl_input_panel_surface_set_ready(wlkb.ips, 1);
696 Evas_Object *main_window = NATIVE_WINDOW_CAST(data);
697 evas_event_callback_del_full(evas_object_evas_get(main_window),
698 EVAS_CALLBACK_RENDER_PRE, _render_pre_cb, main_window);
700 delete_render_pre_timer();
703 static Eina_Bool _render_pre_timeout(void *data)
705 LOGD("_render_pre_timer expired, forcing to call _render_pre_cb() callback");
707 _render_pre_cb(data, NULL, NULL);
709 return ECORE_CALLBACK_CANCEL;
713 void CSCLCoreUIEFL::process_keyboard_ui_state_change(KEYBOARD_UI_STATE state)
716 static Evas_Object *force_update_helper_obj = NULL;
717 static int force_update_num = 0;
719 if (state == KEYBOARD_UI_STATE_WILL_SHOW) {
720 evas_event_callback_add(evas_object_evas_get(NATIVE_WINDOW_CAST(m_main_window)),
721 EVAS_CALLBACK_RENDER_PRE, _render_pre_cb, (void*)m_main_window);
722 _render_pre_timer = ecore_timer_add(RENDER_PRE_TIMEOUT, _render_pre_timeout, (void*)m_main_window);
723 LOGD("Registered RENDER_PRE callback, _render_pre_cb() and a timer callback");
724 } else if (state == KEYBOARD_UI_STATE_DID_SHOW) {
725 LOGD("Forcing keyboard window to render : %d", force_update_num);
727 /* Since the ISE is waiting for RENDER_PRE event, we need to make sure the render event is
728 * occured on our ISE window. Since right now there is no proper way to trigger render event
729 * manually, we are creating a half transparent box above the keyboard window. Need to find
730 * more appropriate way to generate render event */
731 if (force_update_helper_obj) evas_object_del(force_update_helper_obj);
732 force_update_helper_obj = elm_bg_add(NATIVE_WINDOW_CAST(m_main_window));
733 evas_object_color_set(force_update_helper_obj, 255, 255, 255, 1);
734 evas_object_resize(force_update_helper_obj, 1, 1);
735 evas_object_move(force_update_helper_obj, force_update_num % 100, 0);
736 evas_object_layer_set(force_update_helper_obj, EVAS_LAYER_MAX);
737 evas_object_show(force_update_helper_obj);
739 } else if (state == KEYBOARD_UI_STATE_WILL_HIDE) {
740 if (force_update_helper_obj) evas_object_del(force_update_helper_obj);
741 force_update_helper_obj = NULL;