2 * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
4 * Licensed under the Flora License, Version 1.1 (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://floralicense.org/license/
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.
22 #include <Ecore_Wayland.h>
26 #include <atspi/atspi.h>
28 #include "navigator.h"
29 #include "window_tracker.h"
30 #include "keyboard_tracker.h"
31 #include "flat_navi.h"
32 #include "app_tracker.h"
33 #include "smart_notification.h"
34 #include "screen_reader_system.h"
35 #include "screen_reader_haptic.h"
36 #include "screen_reader_tts.h"
37 #include "screen_reader_gestures.h"
38 #include "dbus_gesture_adapter.h"
39 #include "elm_access_adapter.h"
40 #include "lua_engine.h"
42 #define QUICKPANEL_DOWN TRUE
43 #define QUICKPANEL_UP FALSE
46 #define MENU_ITEM_TAB_INDEX_SIZE 16
47 #define HOVERSEL_TRAIT_SIZE 200
48 #define TTS_MAX_TEXT_SIZE 2000
49 #define GESTURE_LIMIT 10
51 //Timeout in ms which will be used as interval for handling ongoing
52 //hoved gesture updates. It is introduced to improve performance.
53 //Even if user makes many mouse move events within hover gesture
54 //only 5 highlight updates a second will be performed. Else we will
55 //highly pollute dbus bus and and decrease highlight performance.
56 #define ONGOING_HOVER_GESTURE_INTERPRETATION_INTERVAL 200
60 #define GERROR_CHECK(error)\
63 ERROR("Error_log:%s",error->message);\
68 static void on_window_activate(void *data, AtspiAccessible * window);
74 static last_focus_t gesture_start_p = { -1, -1 };
75 static last_focus_t last_focus = { -1, -1 };
77 static AtspiAccessible *current_obj;
78 static AtspiComponent *current_comp = NULL;
79 static AtspiAccessible *top_window;
80 static FlatNaviContext *context;
81 static bool prepared = false;
82 static int counter = 0;
83 int _last_hover_event_time = -1;
86 AtspiAccessible *focused_object;
89 .focused_object = NULL,.auto_review_on = false};
91 static bool _widget_has_state(AtspiAccessible * obj, AtspiStateType type)
93 Eina_Bool ret = EINA_FALSE;
94 AtspiStateSet *st = atspi_accessible_get_state_set(obj);
95 if (atspi_state_set_contains(st, type))
101 char *state_to_char(AtspiStateType state)
104 case ATSPI_STATE_INVALID:
105 return strdup("ATSPI_STATE_INVALID");
106 case ATSPI_STATE_ACTIVE:
107 return strdup("ATSPI_STATE_ACTIVE");
108 case ATSPI_STATE_ARMED:
109 return strdup("ATSPI_STATE_ARMED");
110 case ATSPI_STATE_BUSY:
111 return strdup("ATSPI_STATE_BUSY");
112 case ATSPI_STATE_CHECKED:
113 return strdup("ATSPI_STATE_CHECKED");
114 case ATSPI_STATE_COLLAPSED:
115 return strdup("ATSPI_STATE_COLLAPSED");
116 case ATSPI_STATE_DEFUNCT:
117 return strdup("ATSPI_STATE_DEFUNCT");
118 case ATSPI_STATE_EDITABLE:
119 return strdup("ATSPI_STATE_EDITABLE");
120 case ATSPI_STATE_ENABLED:
121 return strdup("ATSPI_STATE_ENABLED");
122 case ATSPI_STATE_EXPANDABLE:
123 return strdup("ATSPI_STATE_EXPANDABLE");
124 case ATSPI_STATE_EXPANDED:
125 return strdup("ATSPI_STATE_EXPANDED");
126 case ATSPI_STATE_FOCUSABLE:
127 return strdup("ATSPI_STATE_FOCUSABLE");
128 case ATSPI_STATE_FOCUSED:
129 return strdup("ATSPI_STATE_FOCUSED");
130 case ATSPI_STATE_HAS_TOOLTIP:
131 return strdup("ATSPI_STATE_HAS_TOOLTIP");
132 case ATSPI_STATE_HORIZONTAL:
133 return strdup("ATSPI_STATE_HORIZONTAL");
134 case ATSPI_STATE_ICONIFIED:
135 return strdup("ATSPI_STATE_ICONIFIED");
136 case ATSPI_STATE_MULTI_LINE:
137 return strdup("ATSPI_STATE_MULTI_LINE");
138 case ATSPI_STATE_MULTISELECTABLE:
139 return strdup("ATSPI_STATE_MULTISELECTABLE");
140 case ATSPI_STATE_OPAQUE:
141 return strdup("ATSPI_STATE_OPAQUE");
142 case ATSPI_STATE_PRESSED:
143 return strdup("ATSPI_STATE_PRESSED");
144 case ATSPI_STATE_RESIZABLE:
145 return strdup("ATSPI_STATE_RESIZABLE");
146 case ATSPI_STATE_SELECTABLE:
147 return strdup("ATSPI_STATE_SELECTABLE");
148 case ATSPI_STATE_SELECTED:
149 return strdup("ATSPI_STATE_SELECTED");
150 case ATSPI_STATE_SENSITIVE:
151 return strdup("ATSPI_STATE_SENSITIVE");
152 case ATSPI_STATE_SHOWING:
153 return strdup("ATSPI_STATE_SHOWING");
154 case ATSPI_STATE_SINGLE_LINE:
155 return strdup("ATSPI_STATE_SINGLE_LINE");
156 case ATSPI_STATE_STALE:
157 return strdup("ATSPI_STATE_STALE");
158 case ATSPI_STATE_TRANSIENT:
159 return strdup("ATSPI_STATE_TRANSIENT");
160 case ATSPI_STATE_VERTICAL:
161 return strdup("ATSPI_STATE_VERTICAL");
162 case ATSPI_STATE_VISIBLE:
163 return strdup("ATSPI_STATE_VISIBLE");
164 case ATSPI_STATE_MANAGES_DESCENDANTS:
165 return strdup("ATSPI_STATE_MANAGES_DESCENDANTS");
166 case ATSPI_STATE_INDETERMINATE:
167 return strdup("ATSPI_STATE_INDETERMINATE");
168 case ATSPI_STATE_REQUIRED:
169 return strdup("ATSPI_STATE_REQUIRED");
170 case ATSPI_STATE_TRUNCATED:
171 return strdup("ATSPI_STATE_TRUNCATED");
172 case ATSPI_STATE_ANIMATED:
173 return strdup("ATSPI_STATE_ANIMATED");
174 case ATSPI_STATE_INVALID_ENTRY:
175 return strdup("ATSPI_STATE_INVALID_ENTRY");
176 case ATSPI_STATE_SUPPORTS_AUTOCOMPLETION:
177 return strdup("ATSPI_STATE_SUPPORTS_AUTOCOMPLETION");
178 case ATSPI_STATE_SELECTABLE_TEXT:
179 return strdup("ATSPI_STATE_SELECTABLE_TEXT");
180 case ATSPI_STATE_IS_DEFAULT:
181 return strdup("ATSPI_STATE_IS_DEFAULT");
182 case ATSPI_STATE_VISITED:
183 return strdup("ATSPI_STATE_VISITED");
184 case ATSPI_STATE_CHECKABLE:
185 return strdup("ATSPI_STATE_CHECKABLE");
186 case ATSPI_STATE_HAS_POPUP:
187 return strdup("ATSPI_STATE_HAS_POPUP");
188 case ATSPI_STATE_READ_ONLY:
189 return strdup("ATSPI_STATE_READ_ONLY");
190 case ATSPI_STATE_LAST_DEFINED:
191 return strdup("ATSPI_STATE_LAST_DEFINED");
192 case ATSPI_STATE_MODAL:
193 return strdup("ATSPI_STATE_MODAL");
194 case ATSPI_STATE_HIGHLIGHTED:
195 return strdup("ATSPI_STATE_HIGHLIGHTED");
196 case ATSPI_STATE_HIGHLIGHTABLE:
197 return strdup("ATSPI_STATE_HIGHLIGHTABLE");
204 static void display_info_about_object(AtspiAccessible * obj, bool display_parent_info)
212 DEBUG("------------------------");
213 gchar *name = atspi_accessible_get_name(obj, NULL);
214 gchar *role = atspi_accessible_get_localized_role_name(obj, NULL);
215 gchar *description = atspi_accessible_get_description(obj, NULL);
216 char *state_name = NULL;
217 AtspiStateSet *st = atspi_accessible_get_state_set(obj);
218 GArray *states = atspi_state_set_get_states(st);
219 AtspiComponent *comp = atspi_accessible_get_component_iface(obj);
220 AtspiValue *value = atspi_accessible_get_value_iface(obj);
221 AtspiRect *rect_screen = atspi_component_get_extents(comp, ATSPI_COORD_TYPE_SCREEN, NULL);
222 AtspiRect *rect_win = atspi_component_get_extents(comp, ATSPI_COORD_TYPE_WINDOW, NULL);
224 if(display_parent_info) {
225 AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
226 display_info_about_object(parent, false);
227 g_object_unref(parent);
230 DEBUG("NAME:%s", name);
231 DEBUG("ROLE:%s", role);
232 DEBUG("DESCRIPTION:%s", description);
233 DEBUG("CHILDS:%d", atspi_accessible_get_child_count(obj, NULL));
234 DEBUG("HIGHLIGHT_INDEX:%d", atspi_component_get_highlight_index(comp, NULL));
235 DEBUG("INDEX IN PARENT:%d", atspi_accessible_get_index_in_parent(obj, NULL));
237 DEBUG("VALUE:%f", atspi_value_get_current_value(value, NULL));
238 DEBUG("VALUE MAX:%f", atspi_value_get_maximum_value(value, NULL));
239 DEBUG("VALUE MIN:%f", atspi_value_get_minimum_value(value, NULL));
240 g_object_unref(value);
245 for (a = 0; states && (a < states->len); ++a) {
246 stat = g_array_index(states, AtspiStateType, a);
247 state_name = state_to_char(stat);
248 DEBUG(" %s", state_name);
251 g_array_free(states, 0);
252 DEBUG("LOCALE:%s", atspi_accessible_get_object_locale(obj, NULL));
253 DEBUG("SIZE ON SCREEN, width:%d, height:%d", rect_screen->width, rect_screen->height);
254 DEBUG("POSITION ON SCREEN: x:%d y:%d", rect_screen->x, rect_screen->y);
255 DEBUG("SIZE ON WIN, width:%d, height:%d", rect_win->width, rect_win->height);
256 DEBUG("POSITION ON WIN: x:%d y:%d", rect_win->x, rect_win->y);
257 DEBUG("INTERFACES:");
258 GArray *ifaces = atspi_accessible_get_interfaces(obj);
259 for (a = 0; ifaces && (a < ifaces->len); ++a) {
260 gchar * interface_name = g_array_index(ifaces, gchar *, a);
261 DEBUG(" %s", interface_name);
262 g_free(interface_name);
265 g_array_free(ifaces, FALSE);
267 DEBUG("------------------------");
274 static char *generate_what_to_read(AtspiAccessible * obj)
276 return lua_engine_describe_object(obj);
279 static void _current_highlight_object_set(AtspiAccessible * obj)
286 DEBUG("Clearing highlight object");
289 atspi_component_clear_highlight(current_comp, &err);
290 g_object_ref(current_comp);
296 if (current_obj == obj) {
297 DEBUG("Object already highlighted");
298 DEBUG("Object name:%s", atspi_accessible_get_name(obj, NULL));
301 if (obj && ATSPI_IS_COMPONENT(obj)) {
302 DEBUG("OBJ WITH COMPONENT");
303 AtspiComponent *comp = atspi_accessible_get_component_iface(obj);
306 role = atspi_accessible_get_role_name(obj, &err);
307 ERROR("AtspiComponent *comp NULL, [%s]", role);
313 atspi_component_clear_highlight(current_comp, &err);
315 atspi_component_grab_highlight(comp, &err);
319 Eina_Bool is_paused = tts_pause_get();
322 tts_pause_set(EINA_FALSE);
325 char *text_to_speak = NULL;
326 text_to_speak = generate_what_to_read(obj);
327 DEBUG("SPEAK:%s", text_to_speak);
329 tts_speak(text_to_speak, EINA_TRUE);
330 g_free(text_to_speak);
332 DEBUG("Unable to highlight on object");
336 void test_debug(AtspiAccessible * current_widget)
342 AtspiAccessible *child_iter = NULL;
343 AtspiAccessible *parent = atspi_accessible_get_parent(current_widget, &err);
348 count_child = atspi_accessible_get_child_count(parent, &err);
350 DEBUG("Total childs in parent: %d\n", count_child);
353 g_object_unref(parent);
357 for (jdx = 0; jdx < count_child; jdx++) {
358 child_iter = atspi_accessible_get_child_at_index(parent, jdx, &err);
361 if (current_widget == child_iter) {
362 role = atspi_accessible_get_role_name(parent, &err);
363 DEBUG("Childen found in parent: %s at index: %d\n", role, jdx);
365 role = atspi_accessible_get_role_name(parent, &err);
366 DEBUG("Childen not found in parent: %s at index: %d\n", role, jdx);
371 g_object_unref(parent);
374 static void _focus_widget(Gesture_Info * info)
378 if ((last_focus.x == info->x_beg) && (last_focus.y == info->y_beg))
381 AtspiAccessible *obj = NULL;
382 if (flat_navi_context_current_at_x_y_set(context, info->x_beg, info->y_beg, &obj)) {
383 last_focus.x = info->x_beg;
384 last_focus.y = info->y_beg;
385 _current_highlight_object_set(obj);
391 static void _focus_next(void)
394 AtspiAccessible *obj;
396 ERROR("No navigation context created");
400 obj = flat_navi_context_next(context);
402 _current_highlight_object_set(obj);
404 DEBUG("Next widget not found. Abort");
408 static void _focus_next_visible(void)
411 AtspiAccessible *obj;
412 AtspiStateSet *ss = NULL;
413 Eina_Bool visible = EINA_FALSE;
415 ERROR("No navigation context created");
420 obj = flat_navi_context_next(context);
421 // try 'cycle' objects in context
423 ss = atspi_accessible_get_state_set(obj);
424 visible = atspi_state_set_contains(ss, ATSPI_STATE_SHOWING);
428 while (obj && !visible);
431 _current_highlight_object_set(obj);
433 DEBUG("Next widget not found. Abort");
437 static void _focus_prev_visible(void)
439 AtspiAccessible *obj;
440 AtspiStateSet *ss = NULL;
441 Eina_Bool visible = EINA_FALSE;
443 ERROR("No navigation context created");
447 obj = flat_navi_context_prev(context);
448 // try 'cycle' objects in context
450 ss = atspi_accessible_get_state_set(obj);
451 visible = atspi_state_set_contains(ss, ATSPI_STATE_SHOWING);
455 while (obj && !visible);
458 _current_highlight_object_set(obj);
460 DEBUG("Previous widget not found. Abort");
463 static void _focus_prev(void)
465 AtspiAccessible *obj;
467 ERROR("No navigation context created");
471 obj = flat_navi_context_prev(context);
473 _current_highlight_object_set(obj);
475 DEBUG("Previous widget not found. Abort");
478 static void _caret_move_beg(void)
480 AtspiAccessible *current_widget = NULL;
481 AtspiText *text_interface;
488 current_widget = current_obj;
490 text_interface = atspi_accessible_get_text_iface(current_widget);
491 if (text_interface) {
492 ret = atspi_text_set_caret_offset(text_interface, 0, &err);
495 DEBUG("Caret position increment done");
496 gchar *text = atspi_text_get_text(text_interface, 0, 1, NULL);
497 DEBUG("SPEAK:%s", text);
498 tts_speak(text, EINA_TRUE);
499 tts_speak(_("IDS_TEXT_BEGIN"), EINA_FALSE);
502 ERROR("Caret position increment error");
504 g_object_unref(text_interface);
506 ERROR("No text interface supported!");
509 static void _caret_move_end(void)
511 AtspiAccessible *current_widget = NULL;
512 AtspiText *text_interface;
519 current_widget = current_obj;
521 text_interface = atspi_accessible_get_text_iface(current_widget);
522 if (text_interface) {
523 int len = atspi_text_get_character_count(text_interface, NULL);
524 ret = atspi_text_set_caret_offset(text_interface, len, &err);
526 DEBUG("Caret position increment done");
527 DEBUG("SPEAK:%s", _("IDS_TEXT_END"));
528 tts_speak(_("IDS_TEXT_END"), EINA_TRUE);
530 ERROR("Caret position to end error");
531 g_object_unref(text_interface);
535 static void _caret_move_forward(void)
537 AtspiAccessible *current_widget = NULL;
538 AtspiText *text_interface;
547 current_widget = current_obj;
549 text_interface = atspi_accessible_get_text_iface(current_widget);
550 if (text_interface) {
551 current_offset = atspi_text_get_caret_offset(text_interface, &err);
553 ret = atspi_text_set_caret_offset(text_interface, current_offset + 1, &err);
556 offset_pos = atspi_text_get_caret_offset(text_interface, NULL);
557 text = atspi_text_get_text(text_interface, offset_pos, offset_pos + 1, NULL);
558 DEBUG("Caret position increment done");
559 DEBUG("Current caret position:%d", offset_pos);
560 DEBUG("Current caret offset:%d", current_offset);
561 if (offset_pos == atspi_text_get_character_count(text_interface, NULL)) {
562 DEBUG("SPEAK:%s", _("IDS_TEXT_END"));
563 tts_speak(_("IDS_TEXT_END"), EINA_FALSE);
565 DEBUG("SPEAK:%s", text);
566 tts_speak(text, EINA_TRUE);
570 ERROR("Caret position increment error");
572 g_object_unref(text_interface);
574 ERROR("No text interface supported!");
579 static void _caret_move_backward(void)
581 AtspiAccessible *current_widget = NULL;
582 AtspiText *text_interface;
592 current_widget = current_obj;
596 text_interface = atspi_accessible_get_text_iface(current_widget);
597 if (text_interface) {
598 current_offset = atspi_text_get_caret_offset(text_interface, &err);
600 ret = atspi_text_set_caret_offset(text_interface, current_offset - 1, &err);
603 offset_pos = atspi_text_get_caret_offset(text_interface, NULL);
604 text = atspi_text_get_text(text_interface, offset_pos, offset_pos + 1, NULL);
605 DEBUG("Caret position decrement done");
606 DEBUG("Current caret position:%d", offset_pos);
607 DEBUG("SPEAK:%s", text);
608 tts_speak(text, EINA_TRUE);
610 if (offset_pos == 0) {
611 DEBUG("SPEAK:%s", _("IDS_TEXT_BEGIN"));
612 tts_speak(_("IDS_TEXT_BEGIN"), EINA_FALSE);
615 ERROR("Caret position decrement error");
617 g_object_unref(text_interface);
619 ERROR("No text interface supported!");
623 static void _read_value(AtspiValue * value)
628 gdouble current_val = atspi_value_get_current_value(value, NULL);
629 gdouble max_val = atspi_value_get_maximum_value(value, NULL);
630 gdouble min_val = atspi_value_get_minimum_value(value, NULL);
632 int proc = (current_val / fabs(max_val - min_val)) * 100;
634 char buf[256] = "\0";
635 snprintf(buf, sizeof(buf), "%d percent", proc);
636 DEBUG("has value %s", buf);
637 tts_speak(buf, EINA_TRUE);
640 static void _value_inc(void)
642 AtspiAccessible *current_widget = NULL;
648 current_widget = current_obj;
650 AtspiValue *value_interface = atspi_accessible_get_value_iface(current_widget);
651 if (value_interface) {
652 DEBUG("Value interface supported!\n");
653 gdouble current_val = atspi_value_get_current_value(value_interface, &err);
655 DEBUG("Current value: %f\n ", (double)current_val);
656 gdouble minimum_inc = atspi_value_get_minimum_increment(value_interface, &err);
657 DEBUG("Minimum increment: %f\n ", (double)minimum_inc);
659 atspi_value_set_current_value(value_interface, current_val + minimum_inc, &err);
661 _read_value(value_interface);
662 g_object_unref(value_interface);
665 ERROR("No value interface supported!\n");
668 static void _value_dec(void)
670 AtspiAccessible *current_widget = NULL;
675 current_widget = current_obj;
677 AtspiValue *value_interface = atspi_accessible_get_value_iface(current_widget);
678 if (value_interface) {
679 DEBUG("Value interface supported!\n");
680 gdouble current_val = atspi_value_get_current_value(value_interface, &err);
682 DEBUG("Current value: %f\n ", (double)current_val);
683 gdouble minimum_inc = atspi_value_get_minimum_increment(value_interface, &err);
685 DEBUG("Minimum increment: %f\n ", (double)minimum_inc);
686 atspi_value_set_current_value(value_interface, current_val - minimum_inc, &err);
688 _read_value(value_interface);
689 g_object_unref(value_interface);
692 ERROR("No value interface supported!\n");
695 static void _activate_widget(void)
697 //activate the widget
698 //only if activate mean click
699 //special behavior for entry, caret should move from first/last last/first
701 AtspiAccessible *current_widget = NULL;
702 AtspiComponent *focus_component = NULL;
703 AtspiAccessible *parent = NULL;
704 AtspiStateSet *ss = NULL;
705 AtspiSelection *selection = NULL;
707 AtspiEditableText *edit = NULL;
710 gchar *actionName = NULL;
714 Eina_Bool activate_found = EINA_FALSE;
715 AtspiRole role = ATSPI_ROLE_INVALID;
720 if (!_widget_has_state(current_obj, ATSPI_STATE_ENABLED)) {
721 DEBUG("Widget is disabled so cannot be activated");
725 current_widget = current_obj;
727 role = atspi_accessible_get_role(current_widget, NULL);
728 if (role == ATSPI_ROLE_SLIDER) {
732 display_info_about_object(current_widget, false);
734 edit = atspi_accessible_get_editable_text_iface(current_widget);
736 DEBUG("Activated object has editable Interface");
737 focus_component = atspi_accessible_get_component_iface(current_widget);
738 if (focus_component) {
739 if (atspi_component_grab_focus(focus_component, &err) == TRUE) {
742 DEBUG("Entry activated\n");
744 char *text_to_speak = NULL;
745 text_to_speak = generate_what_to_read(current_widget);
747 DEBUG("SPEAK:%s", text_to_speak);
749 tts_speak(text_to_speak, EINA_TRUE);
750 g_free(text_to_speak);
751 g_object_unref(focus_component);
754 g_object_unref(edit);
758 action = atspi_accessible_get_action_iface(current_widget);
760 number = atspi_action_get_n_actions(action, &err);
761 DEBUG("Number of available action = %d\n", number);
763 activate_found = EINA_FALSE;
764 while (i < number && !activate_found) {
765 actionName = atspi_action_get_name(action, i, &err);
766 if (actionName && !strcmp("activate", actionName)) {
767 DEBUG("There is activate action");
768 activate_found = EINA_TRUE;
774 if (activate_found) {
775 DEBUG("PERFORMING ATSPI ACTION NO.%d", i);
776 atspi_action_do_action(action, i, &err);
777 } else if (number > 0) {
778 DEBUG("PERFORMING ATSPI DEFAULT ACTION");
779 atspi_action_do_action(action, 0, &err);
781 ERROR("There is no actions inside Action interface");
783 g_object_unref(action);
788 ss = atspi_accessible_get_state_set(current_widget);
789 if (atspi_state_set_contains(ss, ATSPI_STATE_SELECTABLE) == EINA_TRUE) {
790 DEBUG("OBJECT IS SELECTABLE");
791 parent = atspi_accessible_get_parent(current_widget, NULL);
793 index = atspi_accessible_get_index_in_parent(current_widget, NULL);
794 selection = atspi_accessible_get_selection_iface(parent);
796 if(atspi_state_set_contains(ss, ATSPI_STATE_SELECTED)) {
797 atspi_selection_deselect_child (selection, index, NULL);
800 DEBUG("SELECT CHILD NO:%d\n", index);
801 atspi_selection_select_child(selection, index, NULL);
804 g_object_unref(selection);
805 g_object_unref(parent);
809 ERROR("no selection iterface in parent");
811 g_object_unref(parent);
818 static void _quickpanel_change_state(gboolean quickpanel_switch)
822 Ecore_X_Window xwin = 0;
824 if (quickpanel_switch)
825 DEBUG("QUICKPANEL STATE ON");
827 DEBUG("QUICKPANEL STATE OFF");
829 Ecore_X_Illume_Quickpanel_State state;
831 ecore_x_window_prop_xid_get(ecore_x_window_root_first_get(), ECORE_X_ATOM_NET_ACTIVE_WINDOW, ECORE_X_ATOM_WINDOW, &xwin, 1);
833 state = quickpanel_switch ? ECORE_X_ILLUME_QUICKPANEL_STATE_ON : ECORE_X_ILLUME_QUICKPANEL_STATE_OFF;
835 ecore_x_e_illume_quickpanel_state_set(xwin, state);
837 ecore_x_e_illume_quickpanel_state_send(ecore_x_e_illume_zone_get(xwin), state);
839 ecore_main_loop_iterate();
844 * @brief Gets 'deepest' Scrollable accessible containing (x,y) point
847 static AtspiScrollable*
848 _find_scrollable_ancestor_at_xy(int x, int y)
850 AtspiAccessible *ret = NULL;
854 if (!top_window || !ATSPI_IS_COMPONENT(top_window))
856 DEBUG("No active window detected or no AtspiComponent interface available");
860 rect = atspi_component_get_extents(ATSPI_COMPONENT(top_window), ATSPI_COORD_TYPE_SCREEN, &err);
864 ERROR("Unable to fetch window screen coordinates");
868 // Scroll must originate within window borders
869 if ((x < rect->x) || (x > rect->x + rect->width) ||
870 (y < rect->y) || (y > rect->y + rect->height))
872 DEBUG("Scroll don't start within active window borders");
877 ret = atspi_component_get_accessible_at_point(ATSPI_COMPONENT(top_window), x, y, ATSPI_COORD_TYPE_SCREEN, &err);
881 ERROR("Unable to get accessible objct at (%d, %d) screen coordinates.", x, y);
886 // find accessible object with Scrollable interface
887 while (ret && (ret != top_window))
889 name = atspi_accessible_get_name(ret, &err);
891 role = atspi_accessible_get_role_name(ret, &err);
893 DEBUG("Testing for scrollability: %s %s",
895 if (atspi_accessible_get_scrollable(ret))
897 DEBUG("Scrollable widget found at (%d, %d), name: %s, role: %s", x, y,
901 return ATSPI_SCROLLABLE(ret);
905 ret = atspi_accessible_get_parent(ret, &err);
908 ERROR("Unable to fetch AT-SPI parent");
917 static void _widget_scroll_begin(Gesture_Info *gi)
923 ERROR("Scrolling context active when initializing new scrolling context! This should never happen.");
924 ERROR("Force reset of current scrolling context...");
925 atspi_scrollable_scroll_after_pointer(scrolled_obj, ATSPI_SCROLL_POINTER_END, gi->x_begin, gi->y_begin, &err);
928 ERROR("Failed to reset scroll context.");
934 scrolled_obj = _find_scrollable_ancestor_at_xy(gi->x_begin, gi->y_begin);
938 DEBUG("No scrollable widget found at (%d, %d) coordinates", gi->x_begin, gi->y_begin);
942 atspi_scrollable_scroll_after_pointer(scrolled_obj, ATSPI_SCROLL_POINTER_START, gi->x_begin, gi->y_begin, &err);
945 ERROR("Failed to initialize scroll operation");
951 static void _widget_scroll_continue(Gesture_Info *gi)
956 DEBUG("Scrolling context not initialized!");
959 atspi_scrollable_scroll_after_pointer(scrolled_obj, ATSPI_SCROLL_POINTER_CONTINUE, gi->x_begin, gi->y_begin, &err);
963 static void _widget_scroll_end(Gesture_Info *gi)
968 ERROR("Scrolling context not initialized!");
972 atspi_scrollable_scroll_after_pointer(scrolled_obj, ATSPI_SCROLL_POINTER_END, gi->x_begin, gi->y_begin, &err);
978 static void _widget_scroll(Gesture_Info * gi)
980 DEBUG("Recognized gesture state: %d", gi->state);
982 if (gi->state == 0) {
983 DEBUG("save coordinates %d %d", gesture_start_p.x, gesture_start_p.y);
984 gesture_start_p.x = gi->x_beg;
985 gesture_start_p.y = gi->y_beg;
988 if (gi->state != 2) {
989 DEBUG("Scroll not finished yet");
993 AtspiAccessible *obj = NULL;
994 obj = flat_navi_context_current_get(context);
1000 AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
1006 if (!atspi_state_set_contains(ss, ATSPI_STATE_SHOWING)) {
1007 DEBUG("current context do not have visible state, swith to next/prev");
1008 if (gesture_start_p.y > gi->y_end || gesture_start_p.x > gi->x_end) {
1010 _focus_next_visible();
1011 } else if (gesture_start_p.y < gi->y_end || gesture_start_p.x < gi->x_end) {
1013 _focus_prev_visible();
1018 g_object_unref(obj);
1021 static void _read_quickpanel(void)
1026 device_battery_get();
1027 device_bluetooth_get();
1028 device_signal_strenght_get();
1031 device_missed_events_get();
1035 static void _set_pause(void)
1039 Eina_Bool res = EINA_FALSE;
1040 bool pause = tts_pause_get();
1041 res = tts_pause_set(!pause);
1043 ERROR("Failed to set pause state");
1049 void auto_review_highlight_set(void)
1051 AtspiAccessible *obj = flat_navi_context_next(context);
1056 DEBUG("obj == NULL");
1057 s_auto_review.auto_review_on = false;
1059 } else if (obj == flat_navi_context_last_get(context)) {
1060 DEBUG("obj == flat_navi_context_last_get()");
1061 s_auto_review.auto_review_on = false;
1064 _current_highlight_object_set(obj);
1069 void auto_review_highlight_top(void)
1072 char *text_to_speak = NULL;
1073 AtspiAccessible *obj = flat_navi_context_current_get(context);
1074 AtspiAccessible *first = flat_navi_context_first(context);
1077 _current_highlight_object_set(first);
1079 text_to_speak = generate_what_to_read(obj);
1080 DEBUG("Text to speak: %s", text_to_speak);
1081 tts_speak(text_to_speak, EINA_TRUE);
1082 free(text_to_speak);
1088 static void _on_auto_review_stop(void)
1091 s_auto_review.auto_review_on = false;
1095 static void _on_utterance(void)
1098 DEBUG("s_auto_review.auto_review_on == %d", s_auto_review.auto_review_on);
1100 if (s_auto_review.auto_review_on) {
1101 auto_review_highlight_set();
1106 static void _review_from_current(void)
1110 s_auto_review.focused_object = flat_navi_context_current_get(context);
1111 s_auto_review.auto_review_on = true;
1112 auto_review_highlight_set();
1117 static void _review_from_top()
1121 s_auto_review.focused_object = flat_navi_context_current_get(context);
1122 s_auto_review.auto_review_on = true;
1123 auto_review_highlight_top();
1128 static void _direct_scroll_back(void)
1130 DEBUG("ONE_FINGER_FLICK_LEFT_RETURN");
1132 ERROR("No navigation context created");
1136 AtspiAccessible *obj = NULL;
1137 AtspiAccessible *current = NULL;
1138 AtspiAccessible *parent = NULL;
1141 current = flat_navi_context_current_get(context);
1142 parent = atspi_accessible_get_parent(current, NULL);
1143 role = atspi_accessible_get_role(parent, NULL);
1145 if (role != ATSPI_ROLE_LIST) {
1146 DEBUG("That operation can be done only on list, it is:%s", atspi_accessible_get_role_name(parent, NULL));
1147 g_object_unref(parent);
1148 g_object_unref(current);
1152 int index = atspi_accessible_get_index_in_parent(current, NULL);
1153 int children_count = atspi_accessible_get_child_count(parent, NULL);
1155 if (children_count <= 0) {
1156 ERROR("NO visible element on list");
1157 g_object_unref(parent);
1158 g_object_unref(current);
1162 DEBUG("start from element with index:%d/%d", index, children_count);
1165 DEBUG("first element");
1166 obj = atspi_accessible_get_child_at_index(parent, 0, NULL);
1167 smart_notification(FOCUS_CHAIN_END_NOTIFICATION_EVENT, 0, 0);
1171 DEBUG("go back to %d element", index);
1172 obj = atspi_accessible_get_child_at_index(parent, index, NULL);
1176 DEBUG("Will set highlight and context");
1177 if (flat_navi_context_current_set(context, obj)) {
1178 DEBUG("current obj set");
1180 _current_highlight_object_set(obj);
1182 g_object_unref(parent);
1183 g_object_unref(current);
1186 static void _direct_scroll_forward(void)
1188 DEBUG("ONE_FINGER_FLICK_RIGHT_RETURN");
1191 ERROR("No navigation context created");
1195 AtspiAccessible *obj = NULL;
1196 AtspiAccessible *current = NULL;
1197 AtspiAccessible *parent = NULL;
1200 current = flat_navi_context_current_get(context);
1201 parent = atspi_accessible_get_parent(current, NULL);
1202 role = atspi_accessible_get_role(parent, NULL);
1204 if (role != ATSPI_ROLE_LIST) {
1205 DEBUG("That operation can be done only on list, it is:%s", atspi_accessible_get_role_name(parent, NULL));
1206 g_object_unref(parent);
1207 g_object_unref(current);
1211 int index = atspi_accessible_get_index_in_parent(current, NULL);
1212 int children_count = atspi_accessible_get_child_count(parent, NULL);
1214 if (children_count <= 0) {
1215 ERROR("NO visible element on list");
1216 g_object_unref(parent);
1217 g_object_unref(current);
1221 DEBUG("start from element with index:%d/%d", index, children_count);
1223 if (index >= children_count) {
1224 DEBUG("last element");
1225 obj = atspi_accessible_get_child_at_index(parent, children_count - 1, NULL);
1226 smart_notification(FOCUS_CHAIN_END_NOTIFICATION_EVENT, 0, 0);
1230 DEBUG("go back to %d element", index);
1231 obj = atspi_accessible_get_child_at_index(parent, index, NULL);
1235 DEBUG("Will set highlight and context");
1236 if (flat_navi_context_current_set(context, obj)) {
1237 DEBUG("current obj set");
1239 _current_highlight_object_set(obj);
1241 g_object_unref(parent);
1242 g_object_unref(current);
1245 static void _direct_scroll_to_first(void)
1247 DEBUG("ONE_FINGER_FLICK_UP_RETURN");
1249 ERROR("No navigation context created");
1252 AtspiAccessible *obj = flat_navi_context_first(context);
1254 _current_highlight_object_set(obj);
1256 DEBUG("First widget not found. Abort");
1260 static void _direct_scroll_to_last(void)
1262 DEBUG("ONE_FINGER_FLICK_DOWN_RETURN");
1264 ERROR("No navigation context created");
1267 AtspiAccessible *obj = flat_navi_context_last(context);
1269 _current_highlight_object_set(obj);
1271 DEBUG("Last widget not found. Abort");
1275 static Eina_Bool _has_value(void)
1278 AtspiAccessible *obj = NULL;
1288 AtspiValue *value = atspi_accessible_get_value_iface(obj);
1291 g_object_unref(value);
1298 static Eina_Bool _is_enabled(void)
1304 return _widget_has_state(current_obj, ATSPI_STATE_ENABLED);
1307 static Eina_Bool _is_active_entry(void)
1312 ERROR("No navigation context created");
1315 AtspiAccessible *obj = NULL;
1317 obj = flat_navi_context_current_get(context);
1322 role = atspi_accessible_get_role(obj, NULL);
1323 if (role == ATSPI_ROLE_ENTRY) {
1324 AtspiStateSet *state_set = atspi_accessible_get_state_set(obj);
1325 if (atspi_state_set_contains(state_set, ATSPI_STATE_FOCUSED)) {
1326 g_object_unref(state_set);
1329 g_object_unref(state_set);
1337 static Eina_Bool _is_slider(AtspiAccessible * obj)
1346 role = atspi_accessible_get_role(obj, NULL);
1347 if (role == ATSPI_ROLE_SLIDER) {
1353 static void _move_slider(Gesture_Info * gi)
1355 DEBUG("ONE FINGER DOUBLE TAP AND HOLD");
1358 ERROR("No navigation context created");
1362 AtspiAccessible *obj = NULL;
1363 AtspiValue *value = NULL;
1364 AtspiComponent *comp = NULL;
1365 AtspiRect *rect = NULL;
1366 int click_point_x = 0;
1367 int click_point_y = 0;
1377 if (!_is_slider(obj)) {
1378 DEBUG("Object is not a slider");
1383 if (!_widget_has_state(obj, ATSPI_STATE_ENABLED)) {
1384 DEBUG("Slider is disabled");
1389 if (gi->state == 0) {
1390 comp = atspi_accessible_get_component_iface(obj);
1392 ERROR("that slider do not have component interface");
1397 rect = atspi_component_get_extents(comp, ATSPI_COORD_TYPE_SCREEN, NULL);
1399 DEBUG("Current object is in:%d %d", rect->x, rect->y);
1400 DEBUG("Current object has size:%d %d", rect->width, rect->height);
1402 click_point_x = rect->x + rect->width / 2;
1403 click_point_y = rect->y + rect->height / 2;
1404 DEBUG("Click on point %d %d", click_point_x, click_point_y);
1405 start_scroll(click_point_x, click_point_y);
1408 if (gi->state == 1) {
1410 DEBUG("SCROLLING but not meet counter:%d", counter);
1411 if (counter >= GESTURE_LIMIT) {
1413 DEBUG("Scroll on point %d %d", gi->x_end, gi->y_end);
1414 continue_scroll(gi->x_end, gi->y_end);
1418 if (gi->state == 2) {
1419 DEBUG("state == 2");
1420 end_scroll(gi->x_end, gi->y_end);
1422 value = atspi_accessible_get_value_iface(obj);
1425 g_object_unref(value);
1427 ERROR("There is not value interface in slider");
1433 AtspiAction *_get_main_window(void)
1435 AtspiAccessible *win = flat_navi_context_root_get(context);
1437 ERROR("win == NULL");
1441 AtspiAction *action = atspi_accessible_get_action_iface(win);
1443 ERROR("action == NULL");
1450 static int _find_action_index(AtspiAction * action, char *action_name_to_find)
1452 int action_num = atspi_action_get_n_actions(action, NULL);
1453 char *action_name = NULL;
1456 for (i = 0; i < action_num; ++i) {
1457 action_name = atspi_action_get_action_name(action, i, NULL);
1459 if (!strcmp(action_name_to_find, action_name)) {
1467 static void _start_stop_signal_send(void)
1469 int action_index = -1;
1470 char *action_name = "pause_play";
1471 AtspiAction *action = _get_main_window();
1473 ERROR("Could not get the action inteface");
1477 ERROR("action == NULL");
1481 action_index = _find_action_index(action, action_name);
1482 if (action_index < 0) {
1483 ERROR("Pause_play action not found");
1487 DEBUG("ACTION: %s has index: %d", action_name, action_index);
1488 atspi_action_do_action(action, action_index, NULL);
1491 static void on_gesture_detected(void *data, Gesture_Info * info)
1494 Ecore_X_Window keyboard_win;
1496 _on_auto_review_stop();
1498 if (info->type == ONE_FINGER_SINGLE_TAP && info->state == 3) {
1499 DEBUG("One finger single tap aborted");
1503 switch (info->type) {
1504 case ONE_FINGER_HOVER:
1506 DEBUG("Prepare to move slider");
1509 if (_last_hover_event_time < 0)
1510 _last_hover_event_time = info->event_time;
1511 //info->event_time and _last_hover_event_time contain timestamp in ms.
1512 //RETURN so we do not handle all incoming event
1513 if ((info->event_time - _last_hover_event_time) < ONGOING_HOVER_GESTURE_INTERPRETATION_INTERVAL && info->state == 1)
1515 _last_hover_event_time = info->state != 1 ? -1 : info->event_time;
1516 #if defined(ELM_ACCESS_KEYBOARD) && defined(X11_ENABLED)
1517 keyboard_win = top_window_get(info->x_end, info->y_end);
1518 if (keyboard_win && ecore_x_e_virtual_keyboard_get(keyboard_win)) {
1519 elm_access_adaptor_emit_read(keyboard_win, info->x_end, info->y_end);
1523 _focus_widget(info);
1526 case TWO_FINGERS_HOVER:
1527 _widget_scroll(info);
1529 case ONE_FINGER_FLICK_LEFT:
1532 case ONE_FINGER_FLICK_RIGHT:
1535 case ONE_FINGER_FLICK_UP:
1536 if (_is_active_entry())
1537 _caret_move_backward();
1538 else if (_has_value() && _is_enabled())
1543 case ONE_FINGER_FLICK_DOWN:
1544 if (_is_active_entry())
1545 _caret_move_forward();
1546 else if (_has_value() && _is_enabled())
1551 case ONE_FINGER_SINGLE_TAP:
1552 #if defined(ELM_ACCESS_KEYBOARD) && defined(X11_ENABLED)
1553 keyboard_win = top_window_get(info->x_end, info->y_end);
1554 if (keyboard_win && ecore_x_e_virtual_keyboard_get(keyboard_win)) {
1555 elm_access_adaptor_emit_read(keyboard_win, info->x_end, info->y_end);
1560 _focus_widget(info);
1562 case ONE_FINGER_DOUBLE_TAP:
1563 #if defined(ELM_ACCESS_KEYBOARD) && defined(X11_ENABLED)
1564 keyboard_win = top_window_get(info->x_end, info->y_end);
1565 if (keyboard_win && ecore_x_e_virtual_keyboard_get(keyboard_win)) {
1566 elm_access_adaptor_emit_activate(keyboard_win, info->x_end, info->y_end);
1572 case TWO_FINGERS_SINGLE_TAP:
1575 case TWO_FINGERS_DOUBLE_TAP:
1576 _start_stop_signal_send();
1578 case TWO_FINGERS_TRIPLE_TAP:
1579 #ifndef SCREEN_READER_TV
1583 case THREE_FINGERS_SINGLE_TAP:
1586 case THREE_FINGERS_DOUBLE_TAP:
1587 _review_from_current();
1589 case THREE_FINGERS_FLICK_DOWN:
1590 _quickpanel_change_state(QUICKPANEL_DOWN);
1592 case THREE_FINGERS_FLICK_UP:
1593 _quickpanel_change_state(QUICKPANEL_UP);
1595 case ONE_FINGER_FLICK_LEFT_RETURN:
1596 _direct_scroll_back();
1598 case ONE_FINGER_FLICK_RIGHT_RETURN:
1599 _direct_scroll_forward();
1601 case ONE_FINGER_FLICK_UP_RETURN:
1602 if (_is_active_entry())
1605 _direct_scroll_to_first();
1607 case ONE_FINGER_FLICK_DOWN_RETURN:
1608 if (_is_active_entry())
1611 _direct_scroll_to_last();
1614 DEBUG("Gesture type %d not handled in switch", info->type);
1617 dbus_gesture_adapter_emit(info);
1620 static void _view_content_changed(AtspiAccessible * root, void *user_data)
1622 if (flat_navi_is_valid(context, root))
1624 if (!_widget_has_state(root, ATSPI_STATE_SHOWING))
1626 flat_navi_context_free(context);
1627 context = flat_navi_context_create(root);
1628 _current_highlight_object_set(flat_navi_context_current_get(context));
1631 static void _new_highlighted_obj_changed(AtspiAccessible * new_highlighted_obj, void *user_data)
1633 DEBUG("context: %p, current: %p, new_highlighted_obj: %p", context, flat_navi_context_current_get(context), new_highlighted_obj);
1634 if (context && flat_navi_context_current_get(context) != new_highlighted_obj) {
1635 flat_navi_context_current_set(context, g_object_ref(new_highlighted_obj));
1639 void clear(gpointer d)
1641 AtspiAccessible **data = d;
1642 AtspiAccessible *obj = *data;
1643 g_object_unref(obj);
1646 static AtspiAccessible *_get_modal_descendant(AtspiAccessible * root)
1649 AtspiStateSet *states = atspi_state_set_new(NULL);
1650 atspi_state_set_add(states, ATSPI_STATE_MODAL);
1651 atspi_state_set_add(states, ATSPI_STATE_SHOWING);
1652 atspi_state_set_add(states, ATSPI_STATE_VISIBLE);
1653 DEBUG("GET MODAL: STATE SET PREPARED");
1654 AtspiMatchRule *rule = atspi_match_rule_new(states,
1655 ATSPI_Collection_MATCH_ALL,
1657 ATSPI_Collection_MATCH_INVALID,
1659 ATSPI_Collection_MATCH_INVALID,
1661 ATSPI_Collection_MATCH_INVALID,
1663 DEBUG("GET MODAL: MATCHING RULE PREPARED");
1664 AtspiAccessible *ret = NULL;
1665 AtspiCollection *col_iface = atspi_accessible_get_collection_iface(root);
1666 GArray *result = atspi_collection_get_matches(col_iface,
1668 ATSPI_Collection_SORT_ORDER_CANONICAL,
1673 DEBUG("GET MODAL: QUERY PERFORMED");
1674 g_object_unref(states);
1675 g_object_unref(rule);
1676 g_object_unref(col_iface);
1677 if (result && result->len > 0) {
1678 DEBUG("GET MODAL: MODAL FOUND");
1679 g_array_set_clear_func(result, clear);
1680 ret = g_object_ref(g_array_index(result, AtspiAccessible *, 0));
1681 g_array_free(result, TRUE);
1686 static void on_window_activate(void *data, AtspiAccessible * window)
1691 app_tracker_callback_unregister(top_window, _view_content_changed, NULL);
1694 DEBUG("Window name: %s", atspi_accessible_get_name(window, NULL));
1695 // TODO: modal descendant of window should be used (if exists) otherwise window
1696 AtspiAccessible *modal_descendant = _get_modal_descendant(window);
1697 app_tracker_callback_register(modal_descendant ? modal_descendant : window, _view_content_changed, NULL);
1698 _view_content_changed(modal_descendant ? modal_descendant : window, NULL);
1699 g_object_unref(modal_descendant);
1701 flat_navi_context_free(context);
1702 ERROR("No top window found!");
1704 top_window = window;
1708 void kb_tracker(void *data, Key k)
1718 DEBUG("Key %d not supported \n", k);
1722 void navigator_init(void)
1726 set_utterance_cb(_on_utterance);
1728 screen_reader_gestures_tracker_register(on_gesture_detected, NULL);
1729 //FIXME add some config to get script path
1730 if (lua_engine_init(SCRIPTDIR "/mobile.lua"))
1731 ERROR("Failed to init lua engine.");
1732 dbus_gesture_adapter_init();
1734 app_tracker_new_obj_highlighted_callback_register(_new_highlighted_obj_changed);
1735 window_tracker_init();
1736 window_tracker_register(on_window_activate, NULL);
1737 window_tracker_active_window_request();
1738 smart_notification_init();
1739 #ifndef SCREEN_READER_TV
1740 system_notifications_init();
1742 keyboard_tracker_init();
1743 keyboard_tracker_register(kb_tracker, NULL);
1746 void navigator_shutdown(void)
1750 AtspiComponent *comp = atspi_accessible_get_component_iface(current_obj);
1752 atspi_component_clear_highlight(comp, &err);
1757 flat_navi_context_free(context);
1760 lua_engine_shutdown();
1761 dbus_gesture_adapter_shutdown();
1762 app_tracker_shutdown();
1763 window_tracker_shutdown();
1764 smart_notification_shutdown();
1765 #ifndef SCREEN_READER_TV
1766 system_notifications_shutdown();
1768 keyboard_tracker_shutdown();