tizen 2.4 release accepted/tizen_2.4_mobile accepted/tizen/2.4/mobile/20151029.035109 submit/tizen_2.4/20151028.063441 tizen_2.4_mobile_release
authorjk7744.park <jk7744.park@samsung.com>
Mon, 26 Oct 2015 06:41:38 +0000 (15:41 +0900)
committerjk7744.park <jk7744.park@samsung.com>
Mon, 26 Oct 2015 06:41:38 +0000 (15:41 +0900)
53 files changed:
CMakeLists.sub [new file with mode: 0755]
CMakeLists.txt [new file with mode: 0755]
LICENSE [new file with mode: 0644]
README [new file with mode: 0644]
include/app_tracker.h [new file with mode: 0644]
include/common_helpers.h [new file with mode: 0644]
include/dbus_gesture_adapter.h [new file with mode: 0644]
include/elm_access_adapter.h [new file with mode: 0644]
include/etest.h [new file with mode: 0644]
include/flat_navi.h [new file with mode: 0644]
include/keyboard_tracker.h [new file with mode: 0644]
include/logger.h [new file with mode: 0644]
include/navigator.h [new file with mode: 0644]
include/pivot_chooser.h [new file with mode: 0644]
include/screen_reader.h [new file with mode: 0644]
include/screen_reader_gestures.h [new file with mode: 0644]
include/screen_reader_haptic.h [new file with mode: 0644]
include/screen_reader_spi.h [new file with mode: 0644]
include/screen_reader_switch.h [new file with mode: 0644]
include/screen_reader_system.h [new file with mode: 0644]
include/screen_reader_tts.h [new file with mode: 0644]
include/screen_reader_vconf.h [new file with mode: 0644]
include/smart_notification.h [new file with mode: 0644]
include/window_tracker.h [new file with mode: 0644]
org.tizen.screen-reader.manifest [new file with mode: 0644]
org.tizen.screen-reader.xml [new file with mode: 0755]
packaging/org.tizen.screen-reader.spec [new file with mode: 0755]
res/icons/screen-reader.png [new file with mode: 0755]
res/po/CMakeLists.txt [new file with mode: 0644]
res/po/en_US.po [new file with mode: 0644]
src/app_tracker.c [new file with mode: 0644]
src/dbus_gesture_adapter.c [new file with mode: 0644]
src/elm_access_adapter.c [new file with mode: 0644]
src/flat_navi.c [new file with mode: 0644]
src/keyboard_tracker.c [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/navigator.c [new file with mode: 0644]
src/pivot_chooser.c [new file with mode: 0644]
src/screen_reader.c [new file with mode: 0644]
src/screen_reader_gestures.c [new file with mode: 0644]
src/screen_reader_haptic.c [new file with mode: 0644]
src/screen_reader_spi.c [new file with mode: 0644]
src/screen_reader_switch.c [new file with mode: 0644]
src/screen_reader_system.c [new file with mode: 0644]
src/screen_reader_tts.c [new file with mode: 0644]
src/screen_reader_vconf.c [new file with mode: 0644]
src/smart_notification.c [new file with mode: 0644]
src/window_tracker.c [new file with mode: 0644]
tests/CMakeLists.sub [new file with mode: 0755]
tests/CMakeLists.txt [new file with mode: 0644]
tests/atspi/atspi.c [new file with mode: 0644]
tests/atspi/atspi.h [new file with mode: 0644]
tests/smart_navi_suite.c [new file with mode: 0644]

diff --git a/CMakeLists.sub b/CMakeLists.sub
new file mode 100755 (executable)
index 0000000..eab1e8b
--- /dev/null
@@ -0,0 +1,26 @@
+## PROJECT NAME
+PROJECT(screen-reader C)
+
+## INCLUDES
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
+
+## DEFINITIONS
+ADD_DEFINITIONS("")
+
+## LIBRARY PATH
+SET(SLP_LD_PATH_FLAGS "")
+
+## LIBRARY
+SET(SLP_LD_FLAGS "")
+
+## DEBUG
+SET(SLP_DEBUG_FLAGS "-g")
+
+## OPTIMIZATION
+SET(SLP_OPT_FLAGS "-O0")
+
+## COMPILER FLAGS
+SET(SLP_COMPILER_FLAGS "-Wall -Wunused -Wno-format -Wno-format-security")
+
+## LINKER FLAGS
+SET(SLP_LINKER_FLAGS "")
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..20a7467
--- /dev/null
@@ -0,0 +1,64 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+ENABLE_TESTING()
+
+# FIND ALL SOURCE IN A SOURCE DIRECTORY
+AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src SRCS)
+
+#INCLUDE FILE
+INCLUDE(CMakeLists.sub)
+SET(RESOURCE_DIR "${CMAKE_SOURCE_DIR}/res")
+SET(LOCALEDIR "/usr/apps/org.tizen.screen-reader/res/locale")
+
+IF("${SEC_FEATURE_TAPI_ENABLE}" STREQUAL "1")
+    MESSAGE("SEC_FEATURE_TAPI_ENABLE: ${SEC_FEATURE_TAPI_ENABLE}")
+    SET(TAPI_REQUIRED_PKG "tapi")
+ELSE()
+    MESSAGE("SEC_FEATURE_TAPI_ENABLE: ${SEC_FEATURE_TAPI_ENABLE}")
+    ADD_DEFINITIONS("-DSCREEN_READER_TV")
+ENDIF()
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs REQUIRED
+       bundle
+       appcore-efl
+       eldbus
+       elementary
+       ecore
+       atspi-2
+       gobject-2.0
+       ecore-x
+       dlog
+       vconf
+       tts
+       capi-media-tone-player
+       capi-system-device
+       capi-network-bluetooth
+       notification
+       capi-network-wifi
+       capi-appfw-service-application
+       ${TAPI_REQUIRED_PKG}
+)
+
+FOREACH(flag ${pkgs_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS_DUMP "${CMAKE_C_FLAGS}")
+SET(CMAKE_CXX_FLAGS_DUMP "${CMAKE_CXX_FLAGS}")
+SET(CMAKE_C_FLAGS "${SLP_DEBUG_FLAGS} ${SLP_OPT_FLAGS} ${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} ${SLP_COMPILER_FLAGS} -fPIE")
+SET(CMAKE_CXX_FLAGS "${SLP_DEBUG_FLAGS} ${SLP_OPT_FLAGS} ${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} ${SLP_COMPILER_FLAGS}")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS})
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} ${SLP_LD_PATH_FLAGS} ${SLP_LD_FLAGS} ${SLP_LINKER_FLAGS} "-pie")
+
+# Install 
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
+INSTALL(DIRECTORY ${RESOURCE_DIR}/icons DESTINATION res) 
+# Install Manifest File
+INSTALL(FILES org.tizen.screen-reader.xml DESTINATION /opt/share/packages)
+
+ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/res/po)
+ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/tests)
+ADD_TEST(NAME smart_navi_tests COMMAND ${CMAKE_SOURCE_DIR}/tests/smart_navi_test_suite)
+# END OF A FILE
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..ca008cc
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,208 @@
+Copyright (C) 2015 Samsung Electronics Co., Ltd All rights reserved.
+
+Flora License
+
+Version 1.1, April, 2013
+
+http://floralicense.org/license/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction,
+and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by
+the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and
+all other entities that control, are controlled by, or are
+under common control with that entity. For the purposes of
+this definition, "control" means (i) the power, direct or indirect,
+to cause the direction or management of such entity,
+whether by contract or otherwise, or (ii) ownership of fifty percent (50%)
+or more of the outstanding shares, or (iii) beneficial ownership of
+such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity
+exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications,
+including but not limited to software source code, documentation source,
+and configuration files.
+
+"Object" form shall mean any form resulting from mechanical
+transformation or translation of a Source form, including but
+not limited to compiled object code, generated documentation,
+and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form,
+made available under the License, as indicated by a copyright notice
+that is included in or attached to the work (an example is provided
+in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form,
+that is based on (or derived from) the Work and for which the editorial
+revisions, annotations, elaborations, or other modifications represent,
+as a whole, an original work of authorship. For the purposes of this License,
+Derivative Works shall not include works that remain separable from,
+or merely link (or bind by name) to the interfaces of, the Work and
+Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original
+version of the Work and any modifications or additions to that Work or
+Derivative Works thereof, that is intentionally submitted to Licensor
+for inclusion in the Work by the copyright owner or by an individual or
+Legal Entity authorized to submit on behalf of the copyright owner.
+For the purposes of this definition, "submitted" means any form of
+electronic, verbal, or written communication sent to the Licensor or
+its representatives, including but not limited to communication on
+electronic mailing lists, source code control systems, and issue
+tracking systems that are managed by, or on behalf of, the Licensor
+for the purpose of discussing and improving the Work, but excluding
+communication that is conspicuously marked or otherwise designated
+in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity
+on behalf of whom a Contribution has been received by Licensor and
+subsequently incorporated within the Work.
+
+"Tizen Certified Platform" shall mean a software platform that complies
+with the standards set forth in the Tizen Compliance Specification
+and passes the Tizen Compliance Tests as defined from time to time
+by the Tizen Technical Steering Group and certified by the Tizen
+Association or its designated agent.
+
+2. Grant of Copyright License.  Subject to the terms and conditions of
+this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the
+Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License.  Subject to the terms and conditions of
+this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+(except as stated in this section) patent license to make, have made,
+use, offer to sell, sell, import, and otherwise transfer the Work
+solely as incorporated into a Tizen Certified Platform, where such
+license applies only to those patent claims licensable by such
+Contributor that are necessarily infringed by their Contribution(s)
+alone or by combination of their Contribution(s) with the Work solely
+as incorporated into a Tizen Certified Platform to which such
+Contribution(s) was submitted. If You institute patent litigation
+against any entity (including a cross-claim or counterclaim
+in a lawsuit) alleging that the Work or a Contribution incorporated
+within the Work constitutes direct or contributory patent infringement,
+then any patent licenses granted to You under this License for that
+Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.  You may reproduce and distribute copies of the
+Work or Derivative Works thereof pursuant to the copyright license
+above, in any medium, with or without modifications, and in Source or
+Object form, provided that You meet the following conditions:
+
+  1. You must give any other recipients of the Work or Derivative Works
+     a copy of this License; and
+  2. You must cause any modified files to carry prominent notices stating
+     that You changed the files; and
+  3. You must retain, in the Source form of any Derivative Works that
+     You distribute, all copyright, patent, trademark, and attribution
+     notices from the Source form of the Work, excluding those notices
+     that do not pertain to any part of the Derivative Works; and
+  4. If the Work includes a "NOTICE" text file as part of its distribution,
+     then any Derivative Works that You distribute must include a readable
+     copy of the attribution notices contained within such NOTICE file,
+     excluding those notices that do not pertain to any part of
+     the Derivative Works, in at least one of the following places:
+     within a NOTICE text file distributed as part of the Derivative Works;
+     within the Source form or documentation, if provided along with the
+     Derivative Works; or, within a display generated by the Derivative Works,
+     if and wherever such third-party notices normally appear.
+     The contents of the NOTICE file are for informational purposes only
+     and do not modify the License. You may add Your own attribution notices
+     within Derivative Works that You distribute, alongside or as an addendum
+     to the NOTICE text from the Work, provided that such additional attribution
+     notices cannot be construed as modifying the License. You may add Your own
+     copyright statement to Your modifications and may provide additional or
+     different license terms and conditions for use, reproduction, or
+     distribution of Your modifications, or for any such Derivative Works
+     as a whole, provided Your use, reproduction, and distribution of
+     the Work otherwise complies with the conditions stated in this License
+     and your own copyright statement or terms and conditions do not conflict
+     the conditions stated in the License including section 3.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+any Contribution intentionally submitted for inclusion in the Work
+by You to the Licensor shall be under the terms and conditions of
+this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify
+the terms of any separate license agreement you may have executed
+with Licensor regarding such Contributions.
+
+6. Trademarks.  This License does not grant permission to use the trade
+names, trademarks, service marks, or product names of the Licensor,
+except as required for reasonable and customary use in describing the
+origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+agreed to in writing, Licensor provides the Work (and each
+Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied, including, without limitation, any warranties or conditions
+of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+PARTICULAR PURPOSE. You are solely responsible for determining the
+appropriateness of using or redistributing the Work and assume any
+risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+whether in tort (including negligence), contract, or otherwise,
+unless required by applicable law (such as deliberate and grossly
+negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special,
+incidental, or consequential damages of any character arising as a
+result of this License or out of the use or inability to use the
+Work (including but not limited to damages for loss of goodwill,
+work stoppage, computer failure or malfunction, or any and all
+other commercial damages or losses), even if such Contributor
+has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+the Work or Derivative Works thereof, You may choose to offer,
+and charge a fee for, acceptance of support, warranty, indemnity,
+or other liability obligations and/or rights consistent with this
+License. However, in accepting such obligations, You may act only
+on Your own behalf and on Your sole responsibility, not on behalf
+of any other Contributor, and only if You agree to indemnify,
+defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason
+of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Flora License to your work
+
+To apply the Flora License to your work, attach the following
+boilerplate notice, with the fields enclosed by brackets "[]"
+replaced with your own identifying information. (Don't include
+the brackets!) The text should be enclosed in the appropriate
+comment syntax for the file format. We also recommend that a
+file or class name and description of purpose be included on the
+same "printed page" as the copyright notice for easier
+identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Flora License, Version 1.1 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://floralicense.org/license/
+
+   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.
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..fc09a6f
--- /dev/null
+++ b/README
@@ -0,0 +1,9 @@
+Before running smart navigator extend /usr/lib/systemd/system/pulseaudio.service
+[Service]
+...
+ExecStartPre=/bin/chmod 777 /tmp/pulseaudio
+...
+To give other processes like smart-navigator access to pulseaudio tmp.
+
+Extending service with User=pulse or changing owner ship on /tmp/pulseaudio
+results with malfunction of service.
diff --git a/include/app_tracker.h b/include/app_tracker.h
new file mode 100644 (file)
index 0000000..8e2ad7c
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef APP_TRACKER_H_
+#define APP_TRACKER_H_
+
+#include <string.h>
+#include <atspi/atspi.h>
+
+/**
+ * @brief Callback
+ */
+typedef void (*AppTrackerEventCB)(AtspiAccessible *root, void *user_data);
+
+/**
+ * @brief Register listener on given event type.
+ *
+ * @param obj AtspiAccessible application object type
+ * @param cb callback type
+ * @param user_data pointer passed to cb function.
+ */
+void app_tracker_callback_register(AtspiAccessible *app, AppTrackerEventCB cb, void *user_data);
+void app_tracker_new_obj_highlighted_callback_register(AppTrackerEventCB cb);
+
+/**
+ * @brief Unregister listener on given event type.
+ *
+ * @param obj AtspiAccessible application object type
+ * @param cb callback type
+ * @param user_data pointer passed to cb function.
+ */
+void app_tracker_callback_unregister(AtspiAccessible *app, AppTrackerEventCB cb, void *user_data);
+void app_tracker_new_obj_highlighted_callback_unregister(AppTrackerEventCB cb);
+
+/**
+ * @brief Initialize app tracker module.
+ *
+ * @return initi count.
+ *
+ * @nore function atspi_init will be called.
+ */
+int app_tracker_init(void);
+
+/**
+ * @brief Shutdown app tracker module
+ */
+void app_tracker_shutdown(void);
+
+#endif /* end of include guard: APP_TRACKER_H_ */
diff --git a/include/common_helpers.h b/include/common_helpers.h
new file mode 100644 (file)
index 0000000..28fb9d8
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef COMMON_HELPERS_H_
+#define COMMON_HELPERS_H_
+
+#include <dlog.h>
+
+#ifdef DEBUG
+#define PLOG(fmt, ...) \
+               fprintf(stderr, "<D> %s(%d) --> ", __FUNCTION__, __LINE__); \
+               fprintf(stderr, fmt, ##__VA_ARGS__);\
+               fprintf(stderr, "\n");\
+               LOGD(fmt, ##__VA_ARGS__);
+
+#define PLOGD(fmt, ...) \
+               PLOG(fmt, ##__VA_ARGS__)
+
+#define PLOGW(fmt, ...) \
+               fprintf(stderr, "<W> %s(%d) --> ", __FUNCTION__, __LINE__); \
+               fprintf(stderr, fmt, ##__VA_ARGS__);\
+               fprintf(stderr, "\n");\
+               LOGD(fmt, ##__VA_ARGS__);
+
+#define PLOGE(fmt, ...) \
+               fprintf(stderr, "<E> %s(%d) --> ", __FUNCTION__, __LINE__); \
+               fprintf(stderr, fmt, ##__VA_ARGS__);\
+               fprintf(stderr, "\n");\
+               LOGD(fmt, ##__VA_ARGS__);
+
+#define PLOGI(fmt, ...) \
+               fprintf(stderr, "<I> %s(%d) --> ", __FUNCTION__, __LINE__); \
+               fprintf(stderr, fmt, ##__VA_ARGS__);\
+               fprintf(stderr, "\n");\
+               LOGD(fmt, ##__VA_ARGS__);
+
+#else
+
+#define PLOG(fmt, ...) \
+               LOGD(fmt, ##__VA_ARGS__);
+
+#define PLOGD(fmt, ...) \
+               PLOG(fmt, ##__VA_ARGS__);
+
+#define PLOGW(fmt, ...) \
+               LOGW(fmt, ##__VA_ARGS__);
+
+#define PLOGE(fmt, ...) \
+               LOGE(fmt, ##__VA_ARGS__);
+
+#define PLOGI(fmt, ...) \
+               LOGI(fmt, ##__VA_ARGS__);\
+#endif
+
+#endif /* COMMON_HELPERS_H_ */
diff --git a/include/dbus_gesture_adapter.h b/include/dbus_gesture_adapter.h
new file mode 100644 (file)
index 0000000..ec42f29
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef DBUS_GESTURE_ADAPTER_H_
+#define DBUS_GESTURE_ADAPTER_H_
+
+#include "screen_reader_gestures.h"
+
+void dbus_gesture_adapter_init(void);
+
+void dbus_gesture_adapter_shutdown(void);
+
+void dbus_gesture_adapter_emit(const Gesture_Info *info);
+
+#endif
diff --git a/include/elm_access_adapter.h b/include/elm_access_adapter.h
new file mode 100644 (file)
index 0000000..027108b
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef ELM_ACCESS_KEYBOARD_ADAPTER_H_
+#define ELM_ACCESS_KEYBOARD_ADAPTER_H_
+
+#include <Ecore.h>
+#include <Ecore_X.h>
+
+/**
+ * @brief Send ecore x message with elm access read action
+ *
+ * @param win keyboard window
+ * @param x x coordinate of gesture relative to X root window
+ * @param y y coordinate of gesture relative to X root window
+ *
+ */
+void elm_access_adaptor_emit_read(Ecore_X_Window win, int x, int y);
+
+/**
+ * @brief Send ecore x message with elm access activate action
+ *
+ * @param win keyboard window
+ * @param x x coordinate of gesture relative to X root window
+ * @param y y coordinate of gesture relative to X root window
+ *
+ */
+void elm_access_adaptor_emit_activate(Ecore_X_Window win, int x, int y);
+
+#endif
diff --git a/include/etest.h b/include/etest.h
new file mode 100644 (file)
index 0000000..69b14fc
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __ETEST_H__
+#define __ETEST_H__
+
+#include <Elementary.h>
+#include <Ecore_X.h>
+#include <Elementary.h>
+#include <Evas.h>
+
+struct appdata
+{
+   Evas_Object *win;
+};
+
+#endif
\ No newline at end of file
diff --git a/include/flat_navi.h b/include/flat_navi.h
new file mode 100644 (file)
index 0000000..0b1e49d
--- /dev/null
@@ -0,0 +1,141 @@
+#ifndef FLAT_NAVI_H_
+#define FLAT_NAVI_H_
+
+#include <atspi/atspi.h>
+#include <Eina.h>
+
+typedef struct _FlatNaviContext FlatNaviContext;
+
+/**
+ * @brief Creates new FlatNaviContext.
+ *
+ * FlatNaviContext organizes UI elements in a way that is similar to
+ * reading orded (from left to right) and allow it traversing.
+ *
+ * @param root AtspiAccessible root object on which descendants FlatNaviContext
+ * will be builded.
+ *
+ * @return New FlatNaviContext. Should be free with flat_navi_context_free.
+ *
+ * @note Context will use positions obtain from object_cache. It is up
+ * to developer to guarantee that cache is up-to-date.
+ * @note Context will use position_sort to sort elements into lines.
+ * @note Context will use heuristics to those choose elements of UI
+ * that will be segmented into lines. Plese refer to source code for further details.
+ * @note default current element is first object in first line.
+ *
+ */
+FlatNaviContext *flat_navi_context_create(AtspiAccessible *root);
+
+/**
+ * @brief Frees FlatNaviContext
+ * @param ctx FlatNaviContext
+ */
+void flat_navi_context_free(FlatNaviContext *ctx);
+
+/**
+ * Returns the  root of given context
+ * @param ctx Flat navi context
+ * @return
+ */
+AtspiAccessible *flat_navi_context_root_get(FlatNaviContext *ctx);
+
+/**
+ * Advances to next element in natural reading order and returns
+ * new current element.
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @return AtspiAccessible* pointer to current object
+ *
+ * @note If current element is last in line function returns NULL
+ */
+AtspiAccessible *flat_navi_context_next(FlatNaviContext *ctx);
+
+/**
+ * Advances to previous element in natural reading order and returns
+ * new current element.
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @return AtspiAccessible* pointer to current object
+ *
+ * @note If current element is first in line function returns NULL
+ */
+AtspiAccessible *flat_navi_context_prev(FlatNaviContext *ctx);
+
+/**
+ * Advances to last element in current line.
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @return AtspiAccessible* pointer to current object
+ */
+AtspiAccessible *flat_navi_context_last(FlatNaviContext *ctx);
+
+/**
+ * Advances to first element in current line.
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @return AtspiAccessible* pointer to current object
+ */
+AtspiAccessible *flat_navi_context_first(FlatNaviContext *ctx);
+
+/**
+ * Get current element
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @return AtspiAccessible* pointer to current object
+ */
+AtspiAccessible *flat_navi_context_current_get(FlatNaviContext *ctx);
+
+/**
+ * Set current element.
+ *
+ * @note There might be situation when target object is not in FlatNaviContext.
+ * (eg. was filtered by internal heuristic) in this situations function returns
+ * EINA_FALSE.
+ *
+ * @param ctx FlatNaviContext
+ * @param target new current FlatNaviContext object
+ *
+ * @return EINA_TRUE is operation successed, EINA_FALSE otherwise
+ */
+Eina_Bool flat_navi_context_current_set(FlatNaviContext *ctx, AtspiAccessible *target);
+
+/**
+ * Set current context at given position.
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @param x_cord gint X coordinate
+ *
+ * @param y_cord gint Y coordinate
+ *
+ * @param obj AtspiAccessible Reference to object on point
+ *
+ * @return Eina_Bool true on success
+ *
+ * @note current element will be first of line.
+ */
+Eina_Bool flat_navi_context_current_at_x_y_set( FlatNaviContext *ctx, gint x_cord, gint y_cord , AtspiAccessible **obj);
+
+/**
+ * Returns the first item in context;
+ * @param ctx FlatNaviContext
+ * @return Pointer to the first item in context
+ */
+const AtspiAccessible *flat_navi_context_first_get(FlatNaviContext *ctx);
+
+/**
+ * Returns the last item in context;
+ * @param ctx FlatNaviContext
+ * @return Pointer to the last item in context
+ */
+const AtspiAccessible *flat_navi_context_last_get(FlatNaviContext *ctx);
+
+Eina_Bool flat_navi_is_valid(FlatNaviContext *context, AtspiAccessible *new_root);
+
+#endif /* end of include guard: FLAT_NAVI_H_ */
diff --git a/include/keyboard_tracker.h b/include/keyboard_tracker.h
new file mode 100644 (file)
index 0000000..f94f881
--- /dev/null
@@ -0,0 +1,18 @@
+/**
+ * @brief Supported Keys
+ */
+enum _Key
+{
+   KEY_LEFT,
+   KEY_RIGHT,
+   KEY_UP,
+   KEY_DOWN,
+   KEY_COUNT
+};
+
+typedef enum _Key Key;
+
+typedef void (*Keyboard_Tracker_Cb) (void *data, Key k);
+void keyboard_tracker_init(void);
+void keyboard_tracker_register(Keyboard_Tracker_Cb cb, void *data);
+void keyboard_tracker_shutdown(void);
diff --git a/include/logger.h b/include/logger.h
new file mode 100644 (file)
index 0000000..f9d6a0d
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#define TIZEN_ENGINEER_MODE
+#include <libintl.h>
+#include <dlog.h>
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "SCREEN-READER"
+
+#define INFO(format, arg...) LOGI(format, ##arg)
+#define DEBUG(format, arg...) LOGD(format, ##arg)
+#define ERROR(format, arg...) LOGE(format, ##arg)
+#define WARNING(format, arg...) LOGW(format, ##arg)
+
+#define MEMORY_ERROR "Memory allocation error"
+
+#ifdef _
+#undef _
+#endif
+
+#define _(str) (gettext(str))
+
+#endif /* end of include guard: LOGGER_H_ */
diff --git a/include/navigator.h b/include/navigator.h
new file mode 100644 (file)
index 0000000..a61932a
--- /dev/null
@@ -0,0 +1,4 @@
+#include <atspi/atspi.h>
+
+void navigator_init(void);
+void navigator_shutdown(void);
diff --git a/include/pivot_chooser.h b/include/pivot_chooser.h
new file mode 100644 (file)
index 0000000..164e84c
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef SMART_NAVI_PIVOT_CHOOSER_H_
+#define SMART_NAVI_PIVOT_CHOOSER_H_
+
+#include <atspi/atspi.h>
+
+/**
+ * @brief Some heuristic choosing candidate to reacieve highlight.
+ *
+ * @param win Accessibility search tree object root.
+ *
+ * @return Highlight candidate
+ */
+AtspiAccessible *pivot_chooser_pivot_get(AtspiAccessible *win);
+
+#endif /* end of include guard: PIVOT_CHOOSER_H_ */
diff --git a/include/screen_reader.h b/include/screen_reader.h
new file mode 100644 (file)
index 0000000..7608a6f
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef __screen_reader_H__
+#define __screen_reader_H__
+
+#include <atspi/atspi.h>
+#include <Eldbus.h>
+#include <tts.h>
+
+#define LANGUAGE_NAME_SIZE 6
+
+#define FOCUS_CHANGED_SIG "object:state-changed:focused"
+#define HIGHLIGHT_CHANGED_SIG "object:state-change:highlighted"
+#define VALUE_CHANGED_SIG "object:property-change:accessible-value"
+#define CARET_MOVED_SIG "object:text-caret-moved"
+
+typedef struct
+{
+   char *language;
+   int voice_type;
+} Voice_Info;
+
+
+typedef enum
+{
+   read_as_xml,
+   read_as_plain,
+   dont_read
+} Wrong_Validation_Reacction;
+
+typedef struct _Service_Data
+{
+   //Set by vconf
+   bool run_service;
+   char display_language[LANGUAGE_NAME_SIZE];
+   char *tracking_signal_name;
+
+   //Set by tts
+   tts_h tts;
+   Eina_List *available_languages;
+
+   char *text_to_say_info;
+   char *current_value;
+   char *current_char;
+
+   //Actions to do when tts state is 'ready'
+   int _dbus_txt_readed;
+   bool say_text;
+   bool update_language_list;
+
+   //Set by spi
+   AtspiEventListener *state_changed_listener;
+   AtspiEventListener *value_changed_listener;
+   AtspiEventListener *caret_moved_listener;
+   AtspiEventListener *spi_listener;
+
+   AtspiAccessible  *currently_focused;
+   AtspiAccessible  *mouse_down_widget;
+   AtspiAccessible  *clicked_widget;
+
+   //Set by dbus
+   Eldbus_Proxy *proxy;
+   char **last_tokens;
+   char *available_requests;
+   char **available_apps;
+
+   const char *text_from_dbus;
+} Service_Data;
+
+Service_Data *get_pointer_to_service_data_struct();
+
+int screen_reader_create_service(void *data);
+
+int screen_reader_terminate_service(void *data);
+
+#endif
diff --git a/include/screen_reader_gestures.h b/include/screen_reader_gestures.h
new file mode 100644 (file)
index 0000000..0e5b1d4
--- /dev/null
@@ -0,0 +1,107 @@
+#ifndef SCREEN_READER_GESTURES_H_
+#define SCREEN_READER_GESTURES_H_
+
+#include <Ecore.h>
+#include <Ecore_X.h>
+
+/**
+ * @brief Accessibility gestures
+ */
+enum _Gesture
+{
+   ONE_FINGER_HOVER,
+   TWO_FINGERS_HOVER,
+   THREE_FINGERS_HOVER,
+   ONE_FINGER_FLICK_LEFT,
+   ONE_FINGER_FLICK_RIGHT,
+   ONE_FINGER_FLICK_UP,
+   ONE_FINGER_FLICK_DOWN,
+   TWO_FINGERS_FLICK_LEFT,
+   TWO_FINGERS_FLICK_RIGHT,
+   TWO_FINGERS_FLICK_UP,
+   TWO_FINGERS_FLICK_DOWN,
+   THREE_FINGERS_FLICK_LEFT,
+   THREE_FINGERS_FLICK_RIGHT,
+   THREE_FINGERS_FLICK_UP,
+   THREE_FINGERS_FLICK_DOWN,
+   ONE_FINGER_SINGLE_TAP,
+   ONE_FINGER_DOUBLE_TAP,
+   ONE_FINGER_TRIPLE_TAP,
+   TWO_FINGERS_SINGLE_TAP,
+   TWO_FINGERS_DOUBLE_TAP,
+   TWO_FINGERS_TRIPLE_TAP,
+   THREE_FINGERS_SINGLE_TAP,
+   THREE_FINGERS_DOUBLE_TAP,
+   THREE_FINGERS_TRIPLE_TAP,
+   ONE_FINGER_FLICK_LEFT_RETURN,
+   ONE_FINGER_FLICK_RIGHT_RETURN,
+   ONE_FINGER_FLICK_UP_RETURN,
+   ONE_FINGER_FLICK_DOWN_RETURN,
+   TWO_FINGERS_FLICK_LEFT_RETURN,
+   TWO_FINGERS_FLICK_RIGHT_RETURN,
+   TWO_FINGERS_FLICK_UP_RETURN,
+   TWO_FINGERS_FLICK_DOWN_RETURN,
+   THREE_FINGERS_FLICK_LEFT_RETURN,
+   THREE_FINGERS_FLICK_RIGHT_RETURN,
+   THREE_FINGERS_FLICK_UP_RETURN,
+   THREE_FINGERS_FLICK_DOWN_RETURN,
+   GESTURES_COUNT,
+};
+typedef enum _Gesture Gesture;
+
+typedef struct
+{
+   Gesture type;         // Type of recognized gesture
+   int x_beg, x_end;     // (x,y) coordinates when gesture begin (screen coords)
+   int y_beg, y_end;     // (x,y) coordinates when gesture ends (screen coords)
+   pid_t pid;            // pid of process on which gesture took place.
+   int state;            // 0 - begin, 1 - ongoing, 2 - ended, 3 - aborted
+   int event_time;
+} Gesture_Info;
+
+/**
+ * @brief Initialize gesture navigation profile.
+ *
+ * @return EINA_TRUE is initialization was successfull, EINA_FALSE otherwise
+ */
+Eina_Bool screen_reader_gestures_init(void);
+
+/**
+ * @brief Shutdown gesture navigation profile.
+ */
+void screen_reader_gestures_shutdown(void);
+
+Eina_Bool screen_reader_gesture_x_grab_touch_devices(Ecore_X_Window win);
+
+typedef void (*GestureCB)(void *data, Gesture_Info *info);
+
+/**
+ * @brief Registers callback on gestures
+ */
+void screen_reader_gestures_tracker_register(GestureCB cb, void *data);
+
+/**
+ * @brief Start event emission
+ */
+void start_scroll(int x, int y);
+
+/**
+ * @brief Continue event emission
+ */
+void continue_scroll(int x, int y);
+
+/**
+ * @brief End event emit
+ */
+void end_scroll(int x, int y);
+
+/**
+ * @brief Get top window object on which gesture occure
+ *
+ * @param x Gesture X coordinate
+ * @param y Gesture Y coordinate
+ *
+ * @return Ecore_X_Window Object which represents top window on which gesture occure
+ */
+Ecore_X_Window top_window_get (int x, int y);
+#endif
diff --git a/include/screen_reader_haptic.h b/include/screen_reader_haptic.h
new file mode 100644 (file)
index 0000000..d3f9beb
--- /dev/null
@@ -0,0 +1,4 @@
+void haptic_module_init(void);
+void haptic_module_disconnect(void);
+void haptic_vibrate_start(void);
+void haptic_vibrate_stop(void);
diff --git a/include/screen_reader_spi.h b/include/screen_reader_spi.h
new file mode 100644 (file)
index 0000000..c616369
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef SCREEN_READER_SPI_H_
+#define SCREEN_READER_SPI_H_
+
+#include <atspi/atspi.h>
+#include "screen_reader.h"
+
+void spi_init(Service_Data *sd);
+void spi_event_listener_cb(AtspiEvent *event, void *user_data);
+char *spi_event_get_text_to_read(AtspiEvent *event, void *user_data);
+
+#endif /* SCREEN_READER_SPI_H_ */
diff --git a/include/screen_reader_switch.h b/include/screen_reader_switch.h
new file mode 100644 (file)
index 0000000..67cadb3
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _SCREEN_READER_SWITCH
+#define _SCREEN_READER_SWITCH
+
+/**
+ * @brief Set "ScreenReaderEnabled" status of AT-SPI framework
+ *
+ * Function used to iniform applications that screen reader has been enabled in
+ * the system.
+ *
+ * ScreenReaderEnabled property refers to
+ * org.a11y.Bus bus at /org/a11y/bus object path in org.a11y.Status interface
+ *
+ * @param value EINA_TRUE if screen reader should be enabled, EINA_FALSE otherwise.
+ * @return EINA_TRUE if setting 'ScreenReaderEnabled' to value has successed,
+ * EINA_FALSE otherwise
+ */
+Eina_Bool screen_reader_switch_enabled_set(Eina_Bool value);
+
+
+/**
+ * @brief Get "ScreenReaderEnabled" status of AT-SPI framework
+ *
+ * ScreenReaderEnabled property refers to
+ * org.a11y.Bus bus at /org/a11y/bus object path in org.a11y.Status interface
+ *
+ * @param value EINA_TRUE if screen reader is enabled, EINA_FALSE otherwise.
+ * @return EINA_TRUE if getting of value has successed,
+ * EINA_FALSE otherwise
+ */
+Eina_Bool screen_reader_switch_enabled_get(Eina_Bool *value);
+
+#endif
diff --git a/include/screen_reader_system.h b/include/screen_reader_system.h
new file mode 100644 (file)
index 0000000..c2031a6
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef SCREEN_READER_TV
+void system_notifications_init(void);
+void system_notifications_shutdown(void);
+
+void device_time_get(void);
+void device_battery_get(void);
+void device_signal_strenght_get(void);
+void device_missed_events_get(void);
+void device_date_get(void);
+void device_bluetooth_get(void);
+#endif
diff --git a/include/screen_reader_tts.h b/include/screen_reader_tts.h
new file mode 100644 (file)
index 0000000..9c039f1
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef SCREEN_READER_TTS_H_
+#define SCREEN_READER_TTS_H_
+
+#include <tts.h>
+#include "screen_reader.h"
+
+extern tts_h h_tts;
+extern char* language;
+extern int voice;
+extern int speed;
+
+bool tts_init(void *data);
+void state_changed_cb(tts_h tts, tts_state_e previous, tts_state_e current, void* user_data);
+void tts_speak(char *text_to_speak, Eina_Bool flush_switch);
+void spi_stop(void *data);
+
+void tts_stop_set(void);
+Eina_Bool tts_pause_get(void);
+Eina_Bool tts_pause_set(Eina_Bool pause_switch);
+
+void set_utterance_cb( void(*utter_cb)(void));
+
+#endif /* SCREEN_READER_TTS_H_ */
diff --git a/include/screen_reader_vconf.h b/include/screen_reader_vconf.h
new file mode 100644 (file)
index 0000000..0affa51
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef SCREEN_READER_VCONF_H_
+#define SCREEN_READER_VCONF_H_
+
+#include "screen_reader.h"
+#include "logger.h"
+
+bool vconf_init(Service_Data *service_data);
+void screen_reader_vconf_update_tts_language(Service_Data *service_data);
+
+#endif /* SCREEN_READER_VCONF_H_ */
diff --git a/include/smart_notification.h b/include/smart_notification.h
new file mode 100644 (file)
index 0000000..7255419
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef SMART_NOTIFICATION_H_
+#define SMART_NOTIFICATION_H_
+
+/**
+ * @brief Type of notification events.
+ *
+ * @FOCUS_CHAIN_END_NOTIFICATION_EVENT emitted when
+ * currnetly focued or highlighted widget is the last one
+ * in focus chain for application current view.
+ *
+ * @REALIZED_ITEMS_NOTIFICATION_EVENT
+ */
+enum _Notification_Type
+{
+   FOCUS_CHAIN_END_NOTIFICATION_EVENT,
+   REALIZED_ITEMS_NOTIFICATION_EVENT,
+};
+
+typedef enum _Notification_Type Notification_Type;
+
+/**
+ * @brief Initializes notification submodule.
+ *
+ * @description
+ * Notification submodule is resposnisle for providing
+ * voice output to end user when on of following events
+ * occur:
+ *
+ * 1. User starts scrolling active application view.
+ * 2. User finished scrolling (and all scrolling related
+ *    animations has stopped) active application view.
+ * 3. When user has navigated to the last widget in focus
+ *    chain.
+ * 4. After scrolling lists, some of item becomes visible
+ *
+ * @nore
+ * 1 and 2 are handled internally by smart navigation submodule.
+ * about events 3 and 4 submodule has to be informed by the developer
+ * by using smart_notification API.
+ */
+void smart_notification_init(void);
+
+/**
+ * @brief Notifies smart_notification about UI event.
+ *
+ * @param nt Type of the occured event.
+ * @param start_index Item of the first item that becomes visible
+ * after scrolling list widget.
+ * @param end_index index of the last item that becomes visible
+ * after scrolling list widget.
+ *
+ * @note start_index and end_index are interpreted only
+ * when nt parameter is REALIZED_ITEMS_NOTIFICATION_EVENT
+ * @note initializes haptic module.
+ */
+void smart_notification(Notification_Type nt, int start_index, int end_index);
+
+/**
+ * @brief Shutdowns notification subsystem.
+ */
+void smart_notification_shutdown(void);
+
+#endif /* end of include guard: SMART_NOTIFICATION_H_ */
diff --git a/include/window_tracker.h b/include/window_tracker.h
new file mode 100644 (file)
index 0000000..a36fd28
--- /dev/null
@@ -0,0 +1,8 @@
+#include <atspi/atspi.h>
+
+void window_tracker_init(void);
+void window_tracker_shutdown(void);
+
+typedef void (*Window_Tracker_Cb) (void *data, AtspiAccessible *window);
+void window_tracker_register(Window_Tracker_Cb cb, void *data);
+void window_tracker_active_window_request(void);
diff --git a/org.tizen.screen-reader.manifest b/org.tizen.screen-reader.manifest
new file mode 100644 (file)
index 0000000..cbe437f
--- /dev/null
@@ -0,0 +1,47 @@
+<manifest>
+ <define>
+    <domain name="org.tizen.screen-reader" />
+    <permit>
+       <smack permit="dbus" type="rwx" />
+       <smack permit="atspi" type="rwx" />
+       <smack permit="e17" type="rwx" />
+       <smack permit="tts-server" type="rwx" />
+       <smack permit="deviced" type="rwx" />
+       <smack permit="sensord" type="rwx" />
+       <smack permit="media-server" type="rwx" />
+       <smack permit="telephony_framework" type="rw"/>
+    </permit>
+    <request>
+       <smack request="sdbd" type="rwx" />
+       <smack request="atspi" type="rwx" />
+       <smack request="dbus" type="rwx" />
+       <smack request="xorg" type="rwx" />
+       <smack request="pulseaudio" type="rwxat" />
+       <smack request="pkgmgr::db" type="rwx" />
+       <smack request="device::app_logging" type="rwx" />
+       <smack request="isf" type="rwx" />
+       <smack request="system::homedir" type="rwx" />
+       <smack request="system::tmpdir" type="rwx" />
+       <smack request="sys-assert::core" type="rwxat" />
+       <smack request="device::sys_logging" type="rwx" />
+       <smack request="svi-data" type="rwx" />
+       <smack request="e17" type="rwx" />
+       <smack request="tts-server" type="rwx" />
+       <smack request="sound_server" type="rwx" />
+       <smack request="deviced" type="rwx" />
+       <smack request="sensord" type="rwx" />
+       <smack request="media-server" type="rwx" />
+       <smack request="notification::db" type="rw" />
+       <smack request="data-provider-master::notification" type="rw" />
+       <smack request="data-provider-master::notification.client" type="rw" />
+       <smack request="tizen::vconf::public::r" type="rwx" />
+       <smack request="tizen::vconf::public::r::platform::rw" type="rwx" />
+       <smack request="tizen::vconf::setting::admin" type="rwx" />
+       <smack request="tizen::vconf::display" type="rwx" />
+    </request>
+ </define>
+
+<request>
+<domain name="org.tizen.screen-reader"/>
+</request>
+</manifest>
diff --git a/org.tizen.screen-reader.xml b/org.tizen.screen-reader.xml
new file mode 100755 (executable)
index 0000000..10932c6
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="2.4" package="org.tizen.screen-reader" version="0.1.0" install-location="internal-only">
+        <label>screen-reader</label>
+        <description>SMART NAVIGATOR</description>
+        <service-application appid="org.tizen.screen-reader" exec="/usr/apps/org.tizen.screen-reader/bin/screen-reader" nodisplay="true" multiple="false" type="capp" taskmanage="false">
+                <label>screen-reader</label>
+                <icon>/usr/apps/org.tizen.screen-reader/res/icons/screen-reader.png</icon>
+                <label xml:lang="en-us">screen-reader</label>
+                <app-control>
+                    <operation name="http://tizen.org/appcontrol/operation/read_screen"/>
+                </app-control>
+                <background-category value="media"/>
+        </service-application>
+</manifest>
+
diff --git a/packaging/org.tizen.screen-reader.spec b/packaging/org.tizen.screen-reader.spec
new file mode 100755 (executable)
index 0000000..77e376c
--- /dev/null
@@ -0,0 +1,78 @@
+%define AppInstallPath /usr/apps/%{name}
+%define Exec screen-reader
+
+
+Name:       org.tizen.screen-reader
+Summary:    Screen Reader Assistive Technology
+Version:    0.0.7
+Release:    1
+License:    Flora-1.1
+Source0:    %{name}-%{version}.tar.gz
+
+BuildRequires:  at-spi2-core
+BuildRequires:  at-spi2-core-devel
+BuildRequires:  cmake
+BuildRequires:  pkgconfig(appcore-efl)
+BuildRequires:  pkgconfig(ecore)
+BuildRequires:  pkgconfig(ecore-x)
+BuildRequires:  pkgconfig(eina)
+BuildRequires:  pkgconfig(eldbus)
+BuildRequires:  pkgconfig(elementary)
+BuildRequires:  pkgconfig(capi-media-tone-player)
+BuildRequires:  pkgconfig(capi-system-device)
+BuildRequires:  tts
+BuildRequires:  tts-devel
+BuildRequires:  vconf
+BuildRequires:  gettext-tools
+BuildRequires:  pkgconfig(check)
+BuildRequires:  pkgconfig(capi-network-bluetooth)
+BuildRequires:  pkgconfig(notification)
+BuildRequires:  pkgconfig(capi-network-wifi)
+BuildRequires:  pkgconfig(capi-appfw-service-application)
+%if "%{?tizen_profile_name}" != "tv"
+BuildRequires:  pkgconfig(tapi)
+%endif
+
+%description
+An utility library for developers of the menu screen.
+
+%prep
+%setup -q
+
+%build
+rm -rf CMakeFiles CMakeCache.txt
+
+%if "%{?tizen_profile_name}" != "tv"
+        export SEC_FEATURE_TAPI_ENABLE="1"
+%else
+        export SEC_FEATURE_TAPI_ENABLE="0"
+%endif
+export CFLAGS+=" -DELM_ACCESS_KEYBOARD"
+
+cmake . -DCMAKE_INSTALL_PREFIX="%{AppInstallPath}" \
+        -DCMAKE_TARGET="%{Exec}" \
+        -DCMAKE_PACKAGE="%{name}" \
+        -DSEC_FEATURE_TAPI_ENABLE=${SEC_FEATURE_TAPI_ENABLE}
+
+make %{?jobs:-j%jobs} \
+2>&1 | sed \
+-e 's%^.*: error: .*$%\x1b[37;41m&\x1b[m%' \
+-e 's%^.*: warning: .*$%\x1b[30;43m&\x1b[m%'
+export LD_LIBRARY_PATH=/emul/ia32-linux/lib:/emul/ia32-linux/usr/lib:$LD_LIBRARY_PATH
+make test
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+%post 
+/sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%manifest org.tizen.screen-reader.manifest
+%{AppInstallPath}/bin/screen-reader
+%{AppInstallPath}/res/icons/screen-reader.png
+%{AppInstallPath}/res/locale/*/LC_MESSAGES/*
+/opt/share/packages/%{name}.xml
diff --git a/res/icons/screen-reader.png b/res/icons/screen-reader.png
new file mode 100755 (executable)
index 0000000..778d06b
Binary files /dev/null and b/res/icons/screen-reader.png differ
diff --git a/res/po/CMakeLists.txt b/res/po/CMakeLists.txt
new file mode 100644 (file)
index 0000000..cb79e62
--- /dev/null
@@ -0,0 +1,21 @@
+SET(POFILES en_US.po)
+SET(MSGFMT "/usr/bin/msgfmt")
+
+MESSAGE(STATUS "po files ---")
+FOREACH(pofile ${POFILES})
+       SET(pofile ${CMAKE_CURRENT_SOURCE_DIR}/${pofile})
+       MESSAGE(PO: "  ${pofile}")
+       GET_FILENAME_COMPONENT(absPofile ${pofile} ABSOLUTE)
+       GET_FILENAME_COMPONENT(lang ${absPofile} NAME_WE)
+       SET(moFile ${CMAKE_CURRENT_BINARY_DIR}/${lang}.mo)
+       ADD_CUSTOM_COMMAND(
+                       OUTPUT ${moFile}
+                       COMMAND ${MSGFMT} -o ${moFile} ${absPofile}
+                       DEPENDS ${absPofile}
+       )
+       INSTALL(FILES ${moFile}
+               DESTINATION ${LOCALEDIR}/${lang}/LC_MESSAGES RENAME ${PROJECT_NAME}.mo)
+       SET(moFiles ${moFiles} ${moFile})
+ENDFOREACH(pofile)
+
+ADD_CUSTOM_TARGET(po ALL DEPENDS ${moFiles})
diff --git a/res/po/en_US.po b/res/po/en_US.po
new file mode 100644 (file)
index 0000000..04325e4
--- /dev/null
@@ -0,0 +1,226 @@
+
+msgid "IDS_TRAIT_SLIDER_SWIPE_COMMUNICATE"
+msgstr "flick up and down to adjust the position. "
+
+msgid "IDS_TRAIT_SLIDER_VALUE"
+msgstr " from %.*f to %.*f, current value %.*f, "
+
+msgid "IDS_SCROLLING_FINISHED"
+msgstr "Scrolling finished"
+
+msgid "IDS_SCROLLING_STARTED"
+msgstr "Scrolling started"
+
+msgid "IDS_VISUAL_KEYBOARD_DISABLED"
+msgstr "Visual keyboard disabled"
+
+msgid "IDS_VISUAL_KEYBOARD_ENABLED"
+msgstr "Visual keyboard enabled"
+
+msgid "IDS_REACHED_MAX_VAL"
+msgstr ", maximum value reached"
+
+msgid "IDS_REACHED_MIN_VAL"
+msgstr ", minimum value reached"
+
+msgid "IDS_REACHED_MAX_POS"
+msgstr ", end of text reached"
+
+msgid "IDS_REACHED_MIN_POS"
+msgstr ", begin of text reached"
+
+msgid "IDS_NO_VALUE_INTERFACE"
+msgstr "No value interface present"
+
+msgid "IDS_NO_TEXT_INTERFACE"
+msgstr "No text interface present"
+
+msgid "IDS_REACHED_ITEMS_NOTIFICATION"
+msgstr "Visible items from %1$d to %2$d"
+
+msgid "IDS_TRAIT_PD_HOVERSEL"
+msgstr "Dropdown list. Showing %d items. Double tap to open the menu."
+
+msgid "IDS_TRAIT_GROUP_INDEX"
+msgstr "group index"
+
+msgid "IDS_TRAIT_GROUP_INDEX_EXPANDED"
+msgstr "Expandable list, Double tap to collapse"
+
+msgid "IDS_TRAIT_GROUP_INDEX_COLLAPSED"
+msgstr "Expandable list, Double tap to expand"
+
+msgid "IDS_TRAIT_MENU_ITEM_TAB_INDEX"
+msgstr "Tab %1$d of %2$d"
+
+msgid "IDS_TRAIT_TEXT_EDIT"
+msgstr "Double tap to edit"
+
+msgid "IDS_TRAIT_CTX_POPUP"
+msgstr "Contextual popup"
+
+msgid "IDS_TRAIT_POPUP"
+msgstr "popup"
+
+msgid "IDS_TRAIT_SHOWING"
+msgstr "Showing"
+
+msgid "IDS_TRAIT_ITEMS"
+msgstr "Items"
+
+msgid "IDS_TRAIT_POPUP_CLOSE"
+msgstr "double tap to close it"
+
+msgid "IDS_TRAIT_TEXT_EDIT_FOCUSED"
+msgstr "Editing, flick up and down to adjust position."
+
+msgid "IDS_TRAIT_PD_PROGRESSBAR"
+msgstr "Progressing"
+
+msgid "IDS_TRAIT_PD_PROGRESSBAR_PERCENT"
+msgstr "%.0f percent, Progressing"
+
+msgid "IDS_TRAIT_PUSH_BUTTON"
+msgstr "button"
+
+msgid "IDS_TRAIT_CHECK_BOX_SELECTED"
+msgstr "selected"
+
+msgid "IDS_TRAIT_CHECK_BOX_NOT_SELECTED"
+msgstr "not selected"
+
+msgid "IDS_TEXT_BEGIN"
+msgstr "Cursor is at the begining of text"
+
+msgid "IDS_TEXT_END"
+msgstr "Cursor is at the end of text"
+
+msgid "IDS_SYSTEM_CHARGING"
+msgstr "Battery charger connected"
+
+msgid "IDS_SYSTEM_NOT_CHARGING"
+msgstr "Battery charger disconnected"
+
+msgid "IDS_SYSTEM_SCREEN_ON"
+msgstr "Screen is on"
+
+msgid "IDS_SYSTEM_SCREEN_OFF"
+msgstr "Screen is off"
+
+msgid "IDS_SYSTEM_BATTERY_EMPTY"
+msgstr "Battery level is empty"
+
+msgid "IDS_SYSTEM_BATTERY_LOW"
+msgstr "Battery level is low"
+
+msgid "IDS_SYSTEM_BATTERY_HIGH"
+msgstr "Battery level is high"
+
+msgid "IDS_SYSTEM_BATTERY_FULL"
+msgstr "Battery level is full"
+
+msgid "IDS_SYSTEM_BATTERY_CRITICAL"
+msgstr "Battery level is critical"
+
+msgid "IDS_SYSTEM_BATTERY_INFO_CHARGING"
+msgstr "Charging."
+
+msgid "IDS_SYSTEM_BATTERY_INFO_BATTERY_STR"
+msgstr "Battery"
+
+msgid "IDS_SYSTEM_BATTERY_FULLY_CHARGED_STR"
+msgstr "Fully charged"
+
+msgid "IDS_SYSTEM_SIGNAL_STRENGTH"
+msgstr "Signal strength equals:"
+
+msgid "IDS_SYSTEM_SIGNAL_SIMCARD"
+msgstr "Simcard"
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_LTE"
+msgstr "LTE"
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_HSDPA"
+msgstr "HSDPA"
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_3G"
+msgstr "3G"
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_EDGE"
+msgstr "EDGE"
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_25G"
+msgstr "2.5G"
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_2G"
+msgstr "2G"
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_SEARCHING"
+msgstr "Searching..."
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_EMERGENCY"
+msgstr "Emergency"
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_NO_SERVICE"
+msgstr "No service"
+
+msgid "IDS_SYSTEM_NETWORK_SERVICE_UNKNOWN"
+msgstr "Unknown"
+
+msgid "IDS_SYSTEM_NETWORK_TYPE_WIFI"
+msgstr "Wi fi"
+
+msgid "IDS_SYSTEM_WIFI_SIGNAL_GOOD"
+msgstr "Good"
+
+msgid "IDS_SYSTEM_WIFI_SIGNAL_MEDIUM"
+msgstr "Medium"
+
+msgid "IDS_SYSTEM_WIFI_SIGNAL_WEAK"
+msgstr "Weak"
+
+msgid "IDS_SYSTEM_WIFI_SIGNAL_POOR"
+msgstr "Poor"
+
+msgid "IDS_SYSTEM_WIFI_SIGNAL_NO_SIGNAL"
+msgstr "No signal"
+
+msgid "IDS_SYSTEM_NOTIFICATIONS_UNREAD_0"
+msgstr "No event"
+
+msgid "IDS_SYSTEM_NOTIFICATIONS_UNREAD_1"
+msgstr "1 notification unread"
+
+msgid "IDS_SYSTEM_NOTIFICATIONS_UNREAD_MANY"
+msgstr "Notifications unread"
+
+msgid "IDS_SYSTEM_BT_BLUETOOTH_OFF"
+msgstr "Bluetooth off"
+
+msgid "IDS_SYSTEM_BT_NO_DEVICES_CONNECTED"
+msgstr "Nothing connected"
+
+msgid "IDS_SYSTEM_BT_DEVICES_CONNECTED_COUNT"
+msgstr "Devices connected"
+
+msgid "IDS_TRAIT_TOGGLE_BUTTON_ON"
+msgstr "On"
+
+msgid "IDS_TRAIT_TOGGLE_BUTTON_OFF"
+msgstr "Off"
+
+msgid "IDS_TRAIT_TOGGLE_BUTTON"
+msgstr "button"
+
+msgid "IDS_TRAIT_GROUP_INDEX_CHECK_BOX"
+msgstr "The Tick box for the entire group"
+
+msgid "IDS_TRAIT_LIST_ITEM_ICON"
+msgstr "icon"
+
+msgid "IDS_TRAIT_ITEM_SELECTED"
+msgstr "selected"
+
+msgid "IDS_TRAIT_ITEM_SELECTED_COUNT"
+msgstr "%d selected in total"
+
diff --git a/src/app_tracker.c b/src/app_tracker.c
new file mode 100644 (file)
index 0000000..b88389f
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 "app_tracker.h"
+#include "screen_reader_tts.h"
+#include "logger.h"
+
+typedef struct {
+       AtspiAccessible *base_root;
+       AtspiAccessible *root;
+       GList *callbacks;
+       guint timer;
+} SubTreeRootData;
+
+typedef struct {
+       AppTrackerEventCB func;
+       AtspiAccessible *root;
+       void *user_data;
+} EventCallbackData;
+
+#define APP_TRACKER_INVACTIVITY_TIMEOUT 200
+
+static int _init_count;
+static GList *_roots;
+static AtspiEventListener *_listener;
+static AppTrackerEventCB _new_obj_highlighted_callback;
+
+static int _is_descendant(AtspiAccessible * ancestor, AtspiAccessible * descendant)
+{
+       return 1;
+
+#if 0
+       do {
+               if (ancestor == descendant)
+                       return 1;
+       }
+       while ((descendant = atspi_accessible_get_parent(descendant, NULL)) != NULL);
+
+       return 0;
+#endif
+}
+
+static Eina_Bool _object_has_modal_state(AtspiAccessible * obj)
+{
+       if (!obj)
+               return EINA_FALSE;
+
+       Eina_Bool ret = EINA_FALSE;
+
+       AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
+
+       if (atspi_state_set_contains(ss, ATSPI_STATE_MODAL))
+               ret = EINA_TRUE;
+       g_object_unref(ss);
+       return ret;
+}
+
+static Eina_Bool _object_has_showing_state(AtspiAccessible * obj)
+{
+       if (!obj)
+               return EINA_FALSE;
+
+       Eina_Bool ret = EINA_FALSE;
+
+       AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
+
+       if (atspi_state_set_contains(ss, ATSPI_STATE_SHOWING))
+               ret = EINA_TRUE;
+       g_object_unref(ss);
+       return ret;
+}
+
+static Eina_Bool _object_has_highlighted_state(AtspiAccessible * obj)
+{
+       if (!obj)
+               return EINA_FALSE;
+
+       Eina_Bool ret = EINA_FALSE;
+
+       AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
+
+       if (atspi_state_set_contains(ss, ATSPI_STATE_HIGHLIGHTED))
+               ret = EINA_TRUE;
+       g_object_unref(ss);
+       return ret;
+}
+
+static void _subtree_callbacks_call(SubTreeRootData * std)
+{
+       DEBUG("START");
+       GList *l;
+       EventCallbackData *ecd;
+
+       for (l = std->callbacks; l != NULL; l = l->next) {
+               ecd = l->data;
+               ecd->func(std->root, ecd->user_data);
+       }
+       DEBUG("END");
+}
+
+static gboolean _on_timeout_cb(gpointer user_data)
+{
+       DEBUG("START");
+       SubTreeRootData *std = user_data;
+
+       _subtree_callbacks_call(std);
+
+       std->timer = 0;
+       DEBUG("END");
+       return FALSE;
+}
+
+static void _print_event_object_info(const AtspiEvent * event)
+{
+       gchar *name = atspi_accessible_get_name(event->source, NULL), *role = atspi_accessible_get_role_name(event->source, NULL);
+
+       DEBUG("signal:%s, name: %s, role: %s, detail1:%i, detail2: %i", event->type, role, name, event->detail1, event->detail2);
+       g_free(name);
+       g_free(role);
+}
+
+static void _on_atspi_event_cb(const AtspiEvent * event)
+{
+       GList *l;
+       SubTreeRootData *std;
+
+       if (!event)
+               return;
+       if (!event->source) {
+               ERROR("empty event source");
+               return;
+       }
+
+       if ((atspi_accessible_get_role(event->source, NULL) == ATSPI_ROLE_DESKTOP_FRAME)) {
+               return;
+       }
+
+       _print_event_object_info(event);
+
+       if (!strcmp(event->type, "object:property-change:accessible-name") && _object_has_highlighted_state(event->source)) {
+               gchar *name = atspi_accessible_get_name(event->source, NULL);
+               DEBUG("New name for object, read:%s", name);
+               tts_speak (name, EINA_TRUE);
+               g_free(name);
+               return;
+       }
+       AtspiAccessible *new_highlighted_obj = NULL;
+
+       if (!strcmp(event->type, "object:state-changed:highlighted"))
+               new_highlighted_obj = event->source;
+       else if (!strcmp(event->type, "object:active-descendant-changed"))
+               new_highlighted_obj = atspi_accessible_get_child_at_index(event->source, event->detail1, NULL);
+
+       if (new_highlighted_obj && _new_obj_highlighted_callback && _object_has_highlighted_state(new_highlighted_obj)) {
+               DEBUG("HIGHLIGHTED OBJECT IS ABOUT TO CHANGE");
+               _new_obj_highlighted_callback(new_highlighted_obj, NULL);
+               g_object_unref(new_highlighted_obj);
+               new_highlighted_obj = NULL;
+       }
+
+       if (!strcmp("object:state-changed:showing", event->type) ||
+               !strcmp("object:state-changed:visible", event->type) ||
+               !strcmp("object:state-changed:defunct", event->type)) {
+               for (l = _roots; l != NULL; l = l->next) {
+                       std = l->data;
+
+                       if (!_object_has_showing_state(std->root) && std->base_root) {
+                               std->root = std->base_root;
+                               std->base_root = NULL;
+                       }
+
+                       if (_is_descendant(std->root, event->source)) {
+                               if (std->timer)
+                                       g_source_remove(std->timer);
+                                       DEBUG("Before Checking if modal is showing");
+                                       if (_object_has_modal_state(event->source)) {
+                                               DEBUG("Object is modal");
+                                               std->base_root = std->root;
+                                               std->root = event->source;
+                                       }
+                               std->timer = g_timeout_add(APP_TRACKER_INVACTIVITY_TIMEOUT, _on_timeout_cb, std);
+                       }
+               }
+       }
+}
+
+static int _app_tracker_init_internal(void)
+{
+       DEBUG("START");
+       _new_obj_highlighted_callback = NULL;
+       _listener = atspi_event_listener_new_simple(_on_atspi_event_cb, NULL);
+
+       atspi_event_listener_register(_listener, "object:state-changed:showing", NULL);
+       atspi_event_listener_register(_listener, "object:state-changed:visible", NULL);
+       atspi_event_listener_register(_listener, "object:state-changed:defunct", NULL);
+       atspi_event_listener_register(_listener, "object:state-changed:highlighted", NULL);
+       atspi_event_listener_register(_listener, "object:bounds-changed", NULL);
+       atspi_event_listener_register(_listener, "object:visible-data-changed", NULL);
+       atspi_event_listener_register(_listener, "object:active-descendant-changed", NULL);
+       atspi_event_listener_register(_listener, "object:property-change", NULL);
+
+       return 0;
+}
+
+static void _free_callbacks(gpointer data)
+{
+       g_free(data);
+}
+
+static void _free_rootdata(gpointer data)
+{
+       SubTreeRootData *std = data;
+       g_list_free_full(std->callbacks, _free_callbacks);
+       if (std->timer)
+               g_source_remove(std->timer);
+       g_free(std);
+}
+
+static void _app_tracker_shutdown_internal(void)
+{
+       atspi_event_listener_deregister(_listener, "object:state-changed:showing", NULL);
+       atspi_event_listener_deregister(_listener, "object:state-changed:visible", NULL);
+       atspi_event_listener_deregister(_listener, "object:state-changed:highlighted", NULL);
+       atspi_event_listener_deregister(_listener, "object:bounds-changed", NULL);
+       atspi_event_listener_deregister(_listener, "object:state-changed:defunct", NULL);
+       atspi_event_listener_deregister(_listener, "object:visible-data-changed", NULL);
+       atspi_event_listener_deregister(_listener, "object:active-descendant-changed", NULL);
+       atspi_event_listener_deregister(_listener, "object:property-change", NULL);
+
+       g_object_unref(_listener);
+       _listener = NULL;
+       _new_obj_highlighted_callback = NULL;
+       g_list_free_full(_roots, _free_rootdata);
+       _roots = NULL;
+}
+
+int app_tracker_init(void)
+{
+       DEBUG("START");
+       if (!_init_count)
+               if (_app_tracker_init_internal())
+                       return -1;
+       return ++_init_count;
+}
+
+void app_tracker_shutdown(void)
+{
+       if (_init_count == 1)
+               _app_tracker_shutdown_internal();
+       if (--_init_count < 0)
+               _init_count = 0;
+}
+
+void app_tracker_callback_register(AtspiAccessible * app, AppTrackerEventCB cb, void *user_data)
+{
+       DEBUG("START");
+       SubTreeRootData *rd = NULL;
+       EventCallbackData *cd;
+       GList *l;
+
+       if (!_init_count || !cb)
+               return;
+
+       for (l = _roots; l != NULL; l = l->next) {
+               rd = l->data;
+               if (((SubTreeRootData *) l->data)->root == app) {
+                       rd = l->data;
+                       break;
+               }
+       }
+
+       if (!rd) {
+               rd = g_new(SubTreeRootData, 1);
+               rd->root = app;
+               rd->base_root = NULL;
+               rd->callbacks = NULL;
+               rd->timer = 0;
+               _roots = g_list_append(_roots, rd);
+       }
+
+       cd = g_new(EventCallbackData, 1);
+       cd->func = cb;
+       cd->user_data = user_data;
+
+       rd->callbacks = g_list_append(rd->callbacks, cd);
+       DEBUG("END");
+}
+
+void app_tracker_new_obj_highlighted_callback_register(AppTrackerEventCB cb)
+{
+       _new_obj_highlighted_callback = cb;
+}
+
+void app_tracker_callback_unregister(AtspiAccessible * app, AppTrackerEventCB cb, void *user_data)
+{
+       DEBUG("START");
+       GList *l;
+       EventCallbackData *ecd;
+       SubTreeRootData *std = NULL;
+
+       for (l = _roots; l != NULL; l = l->next) {
+               if (((SubTreeRootData *) l->data)->root == app || ((SubTreeRootData *) l->data)->base_root == app) {
+                       std = l->data;
+                       break;
+               }
+       }
+
+       if (!std)
+               return;
+
+       for (l = std->callbacks; l != NULL; l = l->next) {
+               ecd = l->data;
+               if ((ecd->func == cb) && (ecd->user_data == user_data)) {
+                       std->callbacks = g_list_delete_link(std->callbacks, l);
+                       break;
+               }
+       }
+
+       if (!std->callbacks) {
+               if (std->timer)
+                       g_source_remove(std->timer);
+               _roots = g_list_remove(_roots, std);
+               g_free(std);
+       }
+}
+
+void app_tracker_new_obj_highlighted_callback_unregister(AppTrackerEventCB cb)
+{
+       _new_obj_highlighted_callback = NULL;
+}
diff --git a/src/dbus_gesture_adapter.c b/src/dbus_gesture_adapter.c
new file mode 100644 (file)
index 0000000..e4427ad
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 "dbus_gesture_adapter.h"
+#include "logger.h"
+
+#include <Eldbus.h>
+
+#define E_A11Y_SERVICE_BUS_NAME "com.samsung.EModule"
+#define E_A11Y_SERVICE_NAVI_IFC_NAME "com.samsung.GestureNavigation"
+#define E_A11Y_SERVICE_NAVI_OBJ_PATH "/com/samsung/GestureNavigation"
+
+static Eldbus_Connection *conn;
+static Eldbus_Service_Interface *service;
+
+enum _Signals {
+       GESTURE_DETECTED = 0,
+};
+
+static const Eldbus_Signal _signals[] = {
+       [GESTURE_DETECTED] = {"GestureDetected", ELDBUS_ARGS({"siiiiu", NULL}), 0},
+       {NULL, ELDBUS_ARGS({NULL, NULL}), 0}
+};
+
+static const Eldbus_Service_Interface_Desc desc = {
+       E_A11Y_SERVICE_NAVI_IFC_NAME, NULL, _signals, NULL, NULL, NULL
+};
+
+void _on_get_a11y_address(void *data, const Eldbus_Message * msg, Eldbus_Pending * pending)
+{
+       const char *sock_addr;
+       const char *errname, *errmsg;
+       Eldbus_Connection *session = data;
+
+       if (eldbus_message_error_get(msg, &errname, &errmsg)) {
+               ERROR("GetAddress failed: %s %s", errname, errmsg);
+               return;
+       }
+
+       if (!eldbus_message_arguments_get(msg, "s", &sock_addr) || !sock_addr) {
+               ERROR("Could not get A11Y Bus socket address.");
+               goto end;
+       }
+
+       if (!(conn = eldbus_address_connection_get(sock_addr))) {
+               ERROR("Failed to connect to %s", sock_addr);
+               goto end;
+       }
+
+       if (!(service = eldbus_service_interface_register(conn, E_A11Y_SERVICE_NAVI_OBJ_PATH, &desc))) {
+               ERROR("Failed to register %s interface", E_A11Y_SERVICE_NAVI_IFC_NAME);
+               eldbus_connection_unref(conn);
+               conn = NULL;
+               goto end;
+       }
+
+       eldbus_name_request(conn, E_A11Y_SERVICE_BUS_NAME, ELDBUS_NAME_REQUEST_FLAG_DO_NOT_QUEUE, NULL, NULL);
+
+ end:
+       eldbus_connection_unref(session);
+}
+
+void dbus_gesture_adapter_init(void)
+{
+       Eldbus_Connection *session;
+       Eldbus_Message *msg;
+
+       eldbus_init();
+
+       if (!(session = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION))) {
+               ERROR("Unable to get session bus");
+               return;
+       }
+
+       if (!(msg = eldbus_message_method_call_new("org.a11y.Bus", "/org/a11y/bus", "org.a11y.Bus", "GetAddress"))) {
+               ERROR("DBus message allocation failed");
+               goto fail_msg;
+       }
+
+       if (!eldbus_connection_send(session, msg, _on_get_a11y_address, session, -1)) {
+               ERROR("Message send failed");
+               goto fail_send;
+       }
+
+       return;
+
+ fail_send:
+       eldbus_message_unref(msg);
+ fail_msg:
+       eldbus_connection_unref(session);
+}
+
+void dbus_gesture_adapter_shutdown(void)
+{
+       if (service)
+               eldbus_service_object_unregister(service);
+       if (conn)
+               eldbus_connection_unref(conn);
+
+       conn = NULL;
+       service = NULL;
+
+       eldbus_shutdown();
+}
+
+static const char *_gesture_enum_to_string(Gesture g)
+{
+       switch (g) {
+       case ONE_FINGER_HOVER:
+               return "OneFingerHover";
+       case TWO_FINGERS_HOVER:
+               return "TwoFingersHover";
+       case THREE_FINGERS_HOVER:
+               return "ThreeFingersHover";
+       case ONE_FINGER_FLICK_LEFT:
+               return "OneFingerFlickLeft";
+       case ONE_FINGER_FLICK_RIGHT:
+               return "OneFingerFlickRight";
+       case ONE_FINGER_FLICK_UP:
+               return "OneFingerFlickUp";
+       case ONE_FINGER_FLICK_DOWN:
+               return "OneFingerFlickDown";
+       case TWO_FINGERS_FLICK_UP:
+               return "TwoFingersFlickUp";
+       case TWO_FINGERS_FLICK_DOWN:
+               return "TwoFingersFlickDown";
+       case TWO_FINGERS_FLICK_LEFT:
+               return "TwoFingersFlickLeft";
+       case TWO_FINGERS_FLICK_RIGHT:
+               return "TwoFingersFlickRight";
+       case THREE_FINGERS_FLICK_LEFT:
+               return "ThreeFingersFlickLeft";
+       case THREE_FINGERS_FLICK_RIGHT:
+               return "ThreeFingersFlickRight";
+       case THREE_FINGERS_FLICK_UP:
+               return "ThreeFingersFlickUp";
+       case THREE_FINGERS_FLICK_DOWN:
+               return "ThreeFingersFlickDown";
+       case ONE_FINGER_SINGLE_TAP:
+               return "OneFingerSingleTap";
+       case ONE_FINGER_DOUBLE_TAP:
+               return "OneFingerDoubleTap";
+       case ONE_FINGER_TRIPLE_TAP:
+               return "OneFingerTripleTap";
+       case TWO_FINGERS_SINGLE_TAP:
+               return "TwoFingersSingleTap";
+       case TWO_FINGERS_DOUBLE_TAP:
+               return "TwoFingersDoubleTap";
+       case TWO_FINGERS_TRIPLE_TAP:
+               return "TwoFingersTripleTap";
+       case THREE_FINGERS_SINGLE_TAP:
+               return "ThreeFingersSingleTap";
+       case THREE_FINGERS_DOUBLE_TAP:
+               return "ThreeFingersDoubleTap";
+       case THREE_FINGERS_TRIPLE_TAP:
+               return "ThreeFingersTripleTap";
+       case ONE_FINGER_FLICK_LEFT_RETURN:
+               return "OneFingerFlickLeftReturn";
+       case ONE_FINGER_FLICK_RIGHT_RETURN:
+               return "OneFingerFlickRightReturn";
+       case ONE_FINGER_FLICK_UP_RETURN:
+               return "OneFingerFlickUpReturn";
+       case ONE_FINGER_FLICK_DOWN_RETURN:
+               return "OneFingerFlickDownReturn";
+       case TWO_FINGERS_FLICK_LEFT_RETURN:
+               return "TwoFingersFlickLeftReturn";
+       case TWO_FINGERS_FLICK_RIGHT_RETURN:
+               return "TwoFingersFlickRightReturn";
+       case TWO_FINGERS_FLICK_UP_RETURN:
+               return "TwoFingersFlickUpReturn";
+       case TWO_FINGERS_FLICK_DOWN_RETURN:
+               return "TwoFingersFlickDownReturn";
+       case THREE_FINGERS_FLICK_LEFT_RETURN:
+               return "ThreeFingersFlickLeftReturn";
+       case THREE_FINGERS_FLICK_RIGHT_RETURN:
+               return "ThreeFingersFlickRightReturn";
+       case THREE_FINGERS_FLICK_UP_RETURN:
+               return "ThreeFingersFlickUpReturn";
+       case THREE_FINGERS_FLICK_DOWN_RETURN:
+               return "ThreeFingersFlickDownReturn";
+       default:
+               ERROR("unhandled gesture enum");
+               return NULL;
+       }
+}
+
+void dbus_gesture_adapter_emit(const Gesture_Info * info)
+{
+       const char *name;
+
+       if (!service)
+               return;
+
+       name = _gesture_enum_to_string(info->type);
+       if (!name)
+               return;
+
+       if (!eldbus_service_signal_emit(service, GESTURE_DETECTED, name, info->x_beg, info->y_beg, info->x_end, info->y_end, info->state)) {
+               ERROR("Unable to send GestureDetected signal");
+       } else
+               DEBUG("Successfullt send GestureDetected singal");
+}
diff --git a/src/elm_access_adapter.c b/src/elm_access_adapter.c
new file mode 100644 (file)
index 0000000..fc46227
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 "elm_access_adapter.h"
+#include "logger.h"
+
+static void _get_root_coords(Ecore_X_Window win, int *x, int *y)
+{
+       Ecore_X_Window root = ecore_x_window_root_first_get();
+       Ecore_X_Window parent = ecore_x_window_parent_get(win);
+       int wx, wy;
+
+       if (x)
+               *x = 0;
+       if (y)
+               *y = 0;
+
+       while (parent && (root != parent)) {
+               ecore_x_window_geometry_get(parent, &wx, &wy, NULL, NULL);
+               if (x)
+                       *x += wx;
+               if (y)
+                       *y += wy;
+               parent = ecore_x_window_parent_get(parent);
+       }
+}
+
+static void _send_ecore_x_client_msg(Ecore_X_Window win, int x, int y, Eina_Bool activate)
+{
+       int x_win, y_win;
+       long type;
+       _get_root_coords(win, &x_win, &y_win);
+       DEBUG("Window screen size:%d %d", x_win, y_win);
+       DEBUG("activate keyboard: %d %d", x, y);
+
+       if (activate)
+               type = ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_ACTIVATE;
+       else
+               type = ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_READ;
+
+       ecore_x_client_message32_send(win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL, ECORE_X_EVENT_MASK_WINDOW_CONFIGURE, win, type, x - x_win, y - y_win, 0);
+}
+
+void elm_access_adaptor_emit_activate(Ecore_X_Window win, int x, int y)
+{
+       _send_ecore_x_client_msg(win, x, y, EINA_TRUE);
+}
+
+void elm_access_adaptor_emit_read(Ecore_X_Window win, int x, int y)
+{
+       _send_ecore_x_client_msg(win, x, y, EINA_FALSE);
+}
diff --git a/src/flat_navi.c b/src/flat_navi.c
new file mode 100644 (file)
index 0000000..72aac40
--- /dev/null
@@ -0,0 +1,693 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 "flat_navi.h"
+#include "logger.h"
+
+struct _FlatNaviContext {
+       AtspiAccessible *root;
+       AtspiAccessible *current;
+       AtspiAccessible *first;
+       AtspiAccessible *last;
+};
+
+static const AtspiStateType required_states[] = {
+       ATSPI_STATE_SHOWING,
+       ATSPI_STATE_VISIBLE,
+       ATSPI_STATE_FOCUSABLE,
+       ATSPI_STATE_LAST_DEFINED
+};
+
+static const AtspiRole interesting_roles[] = {
+       ATSPI_ROLE_CALENDAR,
+       ATSPI_ROLE_CHECK_BOX,
+       ATSPI_ROLE_COLOR_CHOOSER,
+       ATSPI_ROLE_COMBO_BOX,
+       ATSPI_ROLE_DATE_EDITOR,
+       ATSPI_ROLE_DIALOG,
+       ATSPI_ROLE_FILE_CHOOSER,
+       ATSPI_ROLE_FILLER,
+       ATSPI_ROLE_FONT_CHOOSER,
+       ATSPI_ROLE_GLASS_PANE,
+       ATSPI_ROLE_HEADER,
+       ATSPI_ROLE_HEADING,
+       ATSPI_ROLE_ICON,
+       ATSPI_ROLE_ENTRY,
+       ATSPI_ROLE_LABEL,
+       ATSPI_ROLE_LINK,
+       ATSPI_ROLE_LIST_ITEM,
+       ATSPI_ROLE_MENU_ITEM,
+       ATSPI_ROLE_PANEL,
+       ATSPI_ROLE_PARAGRAPH,
+       ATSPI_ROLE_PASSWORD_TEXT,
+       ATSPI_ROLE_POPUP_MENU,
+       ATSPI_ROLE_PUSH_BUTTON,
+       ATSPI_ROLE_PROGRESS_BAR,
+       ATSPI_ROLE_RADIO_BUTTON,
+       ATSPI_ROLE_RADIO_MENU_ITEM,
+       ATSPI_ROLE_SLIDER,
+       ATSPI_ROLE_SPIN_BUTTON,
+       ATSPI_ROLE_TABLE_CELL,
+       ATSPI_ROLE_TEXT,
+       ATSPI_ROLE_TOGGLE_BUTTON,
+       ATSPI_ROLE_TOOL_TIP,
+       ATSPI_ROLE_TREE_ITEM,
+       ATSPI_ROLE_LAST_DEFINED
+};
+
+static Eina_Bool _has_escape_action(AtspiAccessible * obj)
+{
+       Eina_Bool ret = EINA_FALSE;
+
+       AtspiAction *action = NULL;
+
+       action = atspi_accessible_get_action_iface(obj);
+       if (action) {
+               int i = 0;
+               for (; i < atspi_action_get_n_actions(action, NULL); i++) {
+                       gchar *action_name = atspi_action_get_action_name(action, i, NULL);
+                       Eina_Bool equal = !strcmp(action_name, "escape");
+                       g_free(action_name);
+                       if (equal) {
+                               ret = EINA_TRUE;
+                               break;
+                       }
+               }
+               g_object_unref(action);
+       }
+       DEBUG("Obj %s %s escape action", atspi_accessible_get_role_name(obj, NULL), ret ? "has" : "doesn't have");
+       return ret;
+}
+
+static Eina_Bool _is_collapsed(AtspiStateSet * ss)
+{
+       if (!ss)
+               return EINA_FALSE;
+
+       Eina_Bool ret = EINA_FALSE;
+       if (atspi_state_set_contains(ss, ATSPI_STATE_EXPANDABLE) && !atspi_state_set_contains(ss, ATSPI_STATE_EXPANDED))
+               ret = EINA_TRUE;
+
+       return ret;
+}
+
+static Eina_Bool _object_is_item(AtspiAccessible * obj)
+{
+       if (!obj)
+               return EINA_FALSE;
+
+       Eina_Bool ret = EINA_FALSE;
+       AtspiRole role = atspi_accessible_get_role(obj, NULL);
+       if (role == ATSPI_ROLE_LIST_ITEM || role == ATSPI_ROLE_MENU_ITEM)
+               ret = EINA_TRUE;
+       DEBUG("IS ITEM %d", ret);
+       return ret;
+}
+
+static AtspiAccessible *_get_object_in_relation(AtspiAccessible * source, AtspiRelationType search_type)
+{
+       GArray *relations;
+       AtspiAccessible *ret = NULL;
+       AtspiRelation *relation;
+       AtspiRelationType type;
+       int i;
+       if (source) {
+               DEBUG("CHECKING RELATIONS");
+               relations = atspi_accessible_get_relation_set(source, NULL);
+               if (relations) {
+                       for (i = 0; i < relations->len; i++) {
+                               DEBUG("ALL RELATIONS FOUND: %d", relations->len);
+                               relation = g_array_index(relations, AtspiRelation *, i);
+                               type = atspi_relation_get_relation_type(relation);
+                               DEBUG("RELATION:  %d", type);
+
+                               if (type == search_type) {
+                                       ret = atspi_relation_get_target(relation, 0);
+                                       DEBUG("SEARCHED RELATION FOUND");
+                                       break;
+                               }
+                       }
+                       g_array_free(relations, TRUE);
+               }
+       }
+       return ret;
+}
+
+static Eina_Bool _accept_object(AtspiAccessible * obj)
+{
+       DEBUG("START");
+       if (!obj)
+               return EINA_FALSE;
+
+       Eina_Bool ret = EINA_FALSE;
+       gchar *name = NULL;
+       gchar *desc = NULL;
+       AtspiAction *action = NULL;
+       AtspiEditableText *etext = NULL;
+       AtspiText *text = NULL;
+       AtspiValue *value = NULL;
+       AtspiStateSet *ss = NULL;
+       AtspiComponent *component;
+       AtspiRect *extent;
+
+       AtspiRole r = atspi_accessible_get_role(obj, NULL);
+
+       switch (r) {
+       case ATSPI_ROLE_APPLICATION:
+       case ATSPI_ROLE_FILLER:
+       case ATSPI_ROLE_SCROLL_PANE:
+       case ATSPI_ROLE_SPLIT_PANE:
+       case ATSPI_ROLE_WINDOW:
+       case ATSPI_ROLE_IMAGE:
+       case ATSPI_ROLE_LIST:
+       case ATSPI_ROLE_PAGE_TAB_LIST:
+       case ATSPI_ROLE_TOOL_BAR:
+       case ATSPI_ROLE_REDUNDANT_OBJECT:
+               return EINA_FALSE;
+       case ATSPI_ROLE_DIALOG:
+               if (!_has_escape_action(obj))
+                       return EINA_FALSE;
+               break;
+       default:
+               break;
+       }
+
+       // When given accessibility object is controlled by other object we consider
+       // it as not "user-presentable" on and skip it in navigation tree
+       AtspiAccessible *relation = _get_object_in_relation(obj, ATSPI_RELATION_CONTROLLED_BY);
+       if (relation)
+       {
+               g_object_unref(relation);
+               return EINA_FALSE;
+       }
+
+       ss = atspi_accessible_get_state_set(obj);
+       if (ss) {
+               if (_object_is_item(obj)) {
+                       AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
+                       if (parent) {
+                               AtspiStateSet *pss = atspi_accessible_get_state_set(parent);
+                               g_object_unref(parent);
+                               if (pss) {
+                                       ret = atspi_state_set_contains(pss, ATSPI_STATE_SHOWING) && atspi_state_set_contains(pss, ATSPI_STATE_VISIBLE) && !_is_collapsed(pss);
+                                       DEBUG("ITEM HAS SHOWING && VISIBLE && NOT COLLAPSED PARENT %d", ret);
+                                       g_object_unref(pss);
+                                       g_object_unref(ss);
+                                       return ret;
+                               }
+                       }
+               } else {
+                       /* Extent of candidate object could be 0 */
+                       component = atspi_accessible_get_component_iface(obj);
+                       extent = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL);
+                       g_object_unref(component);
+
+                       if (extent->width <= 0 || extent->height <= 0) {
+                               g_free(extent);
+                               g_object_unref(ss);
+                               return EINA_FALSE;
+                       }
+                       g_free(extent);
+
+                       ret = atspi_state_set_contains(ss, ATSPI_STATE_SHOWING) && atspi_state_set_contains(ss, ATSPI_STATE_VISIBLE);
+               }
+               g_object_unref(ss);
+       }
+       if (!ret) {
+               return EINA_FALSE;
+       }
+
+       name = atspi_accessible_get_name(obj, NULL);
+
+       ret = EINA_FALSE;
+       if (name) {
+               if (strncmp(name, "\0", 1)) {
+                       DEBUG("Has name:[%s]", name);
+                       ret = EINA_TRUE;
+               }
+               g_free(name);
+       }
+       if (!ret) {
+               desc = atspi_accessible_get_description(obj, NULL);
+               if (desc) {
+                       if (strncmp(desc, "\0", 1)) {
+                               DEBUG("Has description:[%s]", desc);
+                               ret = EINA_TRUE;
+                       }
+                       g_free(desc);
+               }
+       }
+       if (!ret) {
+               action = atspi_accessible_get_action_iface(obj);
+               if (action) {
+                       DEBUG("Has action interface");
+                       ret = EINA_TRUE;
+                       g_object_unref(action);
+               }
+       }
+       if (!ret) {
+               value = atspi_accessible_get_value_iface(obj);
+               if (value) {
+                       DEBUG("Has value interface");
+                       ret = EINA_TRUE;
+                       g_object_unref(value);
+               }
+       }
+       if (!ret) {
+               etext = atspi_accessible_get_editable_text_iface(obj);
+               if (etext) {
+                       DEBUG("Has editable text interface");
+                       ret = EINA_TRUE;
+                       g_object_unref(etext);
+               }
+       }
+       if (!ret) {
+               text = atspi_accessible_get_text_iface(obj);
+               if (text) {
+                       DEBUG("Has text interface");
+                       ret = EINA_TRUE;
+                       g_object_unref(text);
+               }
+       }
+
+       DEBUG("END:%d", ret);
+       return ret;
+}
+
+#ifdef SCREEN_READER_FLAT_NAVI_TEST_DUMMY_IMPLEMENTATION
+Eina_Bool flat_navi_context_current_at_x_y_set(FlatNaviContext * ctx, gint x_cord, gint y_cord, AtspiAccessible ** target)
+{
+       return EINA_FALSE;
+}
+#else
+
+int _object_has_modal_state(AtspiAccessible * obj)
+{
+       if (!obj)
+               return EINA_FALSE;
+
+       Eina_Bool ret = EINA_FALSE;
+
+       AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
+
+       if (atspi_state_set_contains(ss, ATSPI_STATE_MODAL))
+               ret = EINA_TRUE;
+       g_object_unref(ss);
+       return ret;
+}
+
+Eina_Bool flat_navi_context_current_at_x_y_set(FlatNaviContext * ctx, gint x_cord, gint y_cord, AtspiAccessible ** target)
+{
+       if (!ctx || !target)
+               return EINA_FALSE;
+
+       if (!ctx->root) {
+               DEBUG("NO top window");
+               return EINA_FALSE;
+       }
+
+       AtspiAccessible *current_obj = flat_navi_context_current_get(ctx);
+
+       Eina_Bool ret = EINA_FALSE;
+       GError *error = NULL;
+
+       AtspiAccessible *obj = g_object_ref(ctx->root);
+       AtspiAccessible *youngest_ancestor_in_context = (_accept_object(obj) ? g_object_ref(obj) : NULL);
+       AtspiComponent *component;
+       Eina_Bool look_for_next_descendant = EINA_TRUE;
+
+       while (look_for_next_descendant) {
+               component = atspi_accessible_get_component_iface(obj);
+
+               g_object_unref(obj);
+               obj = component ? atspi_component_get_accessible_at_point(component, x_cord, y_cord, ATSPI_COORD_TYPE_WINDOW, &error) : NULL;
+               g_clear_object(&component);
+
+               if (error) {
+                       DEBUG("Got error from atspi_component_get_accessible_at_point, domain: %i, code: %i, message: %s", error->domain, error->code, error->message);
+                       g_clear_error(&error);
+                       g_clear_object(&obj);
+                       if (youngest_ancestor_in_context)
+                               g_clear_object(&youngest_ancestor_in_context);
+                       look_for_next_descendant = EINA_FALSE;
+               } else if (obj) {
+                       DEBUG("Found object %s, role %s", atspi_accessible_get_name(obj, NULL), atspi_accessible_get_role_name(obj, NULL));
+                       if (_accept_object(obj)) {
+                               DEBUG("Object  %s with role %s fulfills highlight conditions", atspi_accessible_get_name(obj, NULL), atspi_accessible_get_role_name(obj, NULL));
+                               if (youngest_ancestor_in_context)
+                                       g_object_unref(youngest_ancestor_in_context);
+                               youngest_ancestor_in_context = g_object_ref(obj);
+                       }
+               } else {
+                       g_clear_object(&obj);
+                       look_for_next_descendant = EINA_FALSE;
+               }
+       }
+
+       if (youngest_ancestor_in_context && !_object_has_modal_state(youngest_ancestor_in_context)) {
+               if (youngest_ancestor_in_context == current_obj || flat_navi_context_current_set(ctx, youngest_ancestor_in_context)) {
+                       DEBUG("Setting highlight to object %s with role %s", atspi_accessible_get_name(youngest_ancestor_in_context, NULL), atspi_accessible_get_role_name(youngest_ancestor_in_context, NULL));
+                       *target = youngest_ancestor_in_context;
+                       ret = EINA_TRUE;
+               }
+       } else
+               DEBUG("NO widget under (%d, %d) found or the same widget under hover", x_cord, y_cord);
+       DEBUG("END");
+       return ret;
+}
+#endif
+
+AtspiAccessible *_get_child(AtspiAccessible * obj, int i)
+{
+       DEBUG("START:%d", i);
+       if (i < 0) {
+               DEBUG("END");
+               return NULL;
+       }
+       if (!obj) {
+               DEBUG("END");
+               return NULL;
+       }
+       int cc = atspi_accessible_get_child_count(obj, NULL);
+       if (cc == 0 || i >= cc) {
+               DEBUG("END");
+               return NULL;
+       }
+       return atspi_accessible_get_child_at_index(obj, i, NULL);
+}
+
+static Eina_Bool _has_next_sibling(AtspiAccessible * obj, int next_sibling_idx_modifier)
+{
+       Eina_Bool ret = EINA_FALSE;
+       if (!obj) return ret;
+
+       int idx = atspi_accessible_get_index_in_parent(obj, NULL);
+       if (idx >= 0) {
+               AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
+               int cc = atspi_accessible_get_child_count(parent, NULL);
+               g_object_unref(parent);
+               if ((next_sibling_idx_modifier > 0 && idx < cc - 1) || (next_sibling_idx_modifier < 0 && idx > 0)) {
+                       ret = EINA_TRUE;
+               }
+       }
+       return ret;
+}
+
+AtspiAccessible *_directional_depth_first_search(AtspiAccessible * root, AtspiAccessible * start, int next_sibling_idx_modifier, Eina_Bool(*stop_condition) (AtspiAccessible *))
+{
+       Eina_Bool start_is_not_defunct = EINA_FALSE;
+       AtspiStateSet *ss;
+
+       if (start) {
+               AtspiStateSet *ss = atspi_accessible_get_state_set(start);
+               start_is_not_defunct = !atspi_state_set_contains(ss, ATSPI_STATE_DEFUNCT);
+               g_object_unref(ss);
+               if (!start_is_not_defunct)
+                       DEBUG("Start is defunct!");
+       }
+
+       AtspiAccessible *node = (start && start_is_not_defunct)
+               ? g_object_ref(start)
+               : (root ? g_object_ref(root) : NULL);
+
+       if (!node)
+               return NULL;
+
+       AtspiAccessible *next_related_in_direction = (next_sibling_idx_modifier > 0)
+               ? _get_object_in_relation(node, ATSPI_RELATION_FLOWS_TO)
+               : _get_object_in_relation(node, ATSPI_RELATION_FLOWS_FROM);
+
+       Eina_Bool relation_mode = EINA_FALSE;
+       if (next_related_in_direction) {
+               relation_mode = EINA_TRUE;
+               g_object_unref(next_related_in_direction);
+       }
+
+       while (node) {
+               AtspiAccessible *prev_related_in_direction = (next_sibling_idx_modifier > 0)
+                       ? _get_object_in_relation(node, ATSPI_RELATION_FLOWS_FROM)
+                       : _get_object_in_relation(node, ATSPI_RELATION_FLOWS_TO);
+
+               if (node != start && (relation_mode || !prev_related_in_direction) && stop_condition(node)) {
+                       g_object_unref(prev_related_in_direction);
+                       return node;
+               }
+
+               AtspiAccessible *next_related_in_direction = (next_sibling_idx_modifier > 0)
+                       ? _get_object_in_relation(node, ATSPI_RELATION_FLOWS_TO)
+                       : _get_object_in_relation(node, ATSPI_RELATION_FLOWS_FROM);
+
+               DEBUG("RELATION MODE: %d", relation_mode);
+               if (!prev_related_in_direction)
+                       DEBUG("PREV IN RELATION NULL");
+               if (!next_related_in_direction)
+                       DEBUG("NEXT IN RELATION NULL");
+
+               if ((!relation_mode && !prev_related_in_direction && next_related_in_direction) || (relation_mode && next_related_in_direction)) {
+                       DEBUG("APPLICABLE FOR RELATION NAVIG");
+                       g_object_unref(prev_related_in_direction);
+                       relation_mode = EINA_TRUE;
+                       g_object_unref(node);
+                       node = next_related_in_direction;
+               } else {
+                       g_object_unref(prev_related_in_direction);
+                       g_object_unref(next_related_in_direction);
+                       relation_mode = EINA_FALSE;
+                       int cc = atspi_accessible_get_child_count(node, NULL);
+                       ss = atspi_accessible_get_state_set(node);
+
+                       if (cc > 0 && atspi_state_set_contains(ss, ATSPI_STATE_SHOWING))                        // walk down
+                       {
+                               int idx = next_sibling_idx_modifier > 0 ? 0 : cc - 1;
+                               g_object_unref(node);
+                               node = atspi_accessible_get_child_at_index(node, idx, NULL);
+                               DEBUG("DFS DOWN");
+                       } else {
+                               while (!_has_next_sibling(node, next_sibling_idx_modifier) || node == root)     // no next sibling
+                               {
+                                       DEBUG("DFS NO SIBLING");
+                                       if (!node || node == root) {
+                                               DEBUG("DFS END");
+                                               g_object_unref(node);
+                                               g_object_unref(ss);
+                                               return NULL;
+                                       }
+                                       g_object_unref(node);
+                                       node = atspi_accessible_get_parent(node, NULL); // walk up...
+                                       DEBUG("DFS UP");
+                               }
+                               int idx = atspi_accessible_get_index_in_parent(node, NULL);
+                               g_object_unref(node);
+                               node = atspi_accessible_get_child_at_index(atspi_accessible_get_parent(node, NULL), idx + next_sibling_idx_modifier, NULL);     //... and next
+                               DEBUG("DFS NEXT %d", idx + next_sibling_idx_modifier);
+                       }
+                       g_object_unref(ss);
+               }
+       }
+       DEBUG("DFS END");
+       return NULL;
+}
+
+AtspiAccessible *_first(FlatNaviContext * ctx)
+{
+       DEBUG("START");
+       return _directional_depth_first_search(ctx->root, NULL, 1, &_accept_object);
+}
+
+AtspiAccessible *_last(FlatNaviContext * ctx)
+{
+       DEBUG("START");
+       return _directional_depth_first_search(ctx->root, NULL, -1, &_accept_object);
+}
+
+AtspiAccessible *_next(FlatNaviContext * ctx)
+{
+       DEBUG("START");
+       AtspiAccessible *root = ctx->root;
+       AtspiAccessible *current = ctx->current;
+       AtspiAccessible *ret = NULL;
+
+       ret = _directional_depth_first_search(root, current, 1, &_accept_object);
+
+       if (current && !ret) {
+               DEBUG("DFS SECOND PASS");
+               ret = _directional_depth_first_search(root, NULL, 1, &_accept_object);
+       }
+       return ret;
+}
+
+AtspiAccessible *_prev(FlatNaviContext * ctx)
+{
+       DEBUG("START\n");
+       AtspiAccessible *root = ctx->root;
+       AtspiAccessible *current = ctx->current;
+       AtspiAccessible *ret = NULL;
+
+       ret = _directional_depth_first_search(root, current, -1, &_accept_object);
+       if (current && !ret) {
+               DEBUG("DFS SECOND PASS");
+               ret = _directional_depth_first_search(root, NULL, -1, &_accept_object);
+       }
+       return ret;
+}
+
+int flat_navi_context_current_children_count_visible_get(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return -1;
+       int count = 0;
+       /*
+          AtspiAccessible *obj = NULL;
+          AtspiStateSet *ss = NULL;
+
+          Eina_List *l, *l2, *line;
+          AtspiAccessible *current = flat_navi_context_current_get(ctx);
+          AtspiAccessible *parent = atspi_accessible_get_parent (current, NULL);
+
+          EINA_LIST_FOREACH(ctx->lines, l, line)
+          {
+          EINA_LIST_FOREACH(line, l2, obj)
+          {
+          ss = atspi_accessible_get_state_set(obj);
+          if (atspi_state_set_contains(ss, ATSPI_STATE_SHOWING) && parent == atspi_accessible_get_parent(obj, NULL))
+          count++;
+          g_object_unref(ss);
+          }
+          }
+        */
+       return count;
+}
+
+FlatNaviContext *flat_navi_context_create(AtspiAccessible * root)
+{
+       DEBUG("START");
+       if (!root)
+               return NULL;
+       FlatNaviContext *ret;
+       ret = calloc(1, sizeof(FlatNaviContext));
+       if (!ret)
+               return NULL;
+
+       ret->root = root;
+       ret->current = _first(ret);
+       return ret;
+}
+
+void flat_navi_context_free(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return;
+       free(ctx);
+}
+
+AtspiAccessible *flat_navi_context_root_get(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return NULL;
+
+       return ctx->root;
+}
+
+const AtspiAccessible *flat_navi_context_first_get(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return NULL;
+
+       return _first(ctx);
+}
+
+const AtspiAccessible *flat_navi_context_last_get(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return NULL;
+
+       return _last(ctx);
+}
+
+AtspiAccessible *flat_navi_context_current_get(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return NULL;
+
+       return ctx->current;
+}
+
+Eina_Bool flat_navi_context_current_set(FlatNaviContext * ctx, AtspiAccessible * target)
+{
+       if (!ctx || !target)
+               return EINA_FALSE;
+
+       ctx->current = target;
+
+       return EINA_TRUE;
+}
+
+AtspiAccessible *flat_navi_context_next(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return NULL;
+
+       AtspiAccessible *ret = _next(ctx);
+       ctx->current = ret;
+
+       return ret;
+
+}
+
+AtspiAccessible *flat_navi_context_prev(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return NULL;
+
+       AtspiAccessible *ret = _prev(ctx);
+       ctx->current = ret;
+
+       return ret;
+}
+
+AtspiAccessible *flat_navi_context_first(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return NULL;
+
+       AtspiAccessible *ret = _first(ctx);
+       ctx->current = ret;
+
+       return ret;
+}
+
+AtspiAccessible *flat_navi_context_last(FlatNaviContext * ctx)
+{
+       if (!ctx)
+               return NULL;
+
+       AtspiAccessible *ret = _last(ctx);
+       ctx->current = ret;
+
+       return ret;
+}
+
+Eina_Bool flat_navi_is_valid(FlatNaviContext * context, AtspiAccessible * new_root)
+{
+       Eina_Bool ret = EINA_FALSE;
+       if (!context || !context->current || context->root != new_root)
+               return ret;
+       AtspiStateSet *ss = atspi_accessible_get_state_set(context->current);
+
+       ret = atspi_state_set_contains(ss, ATSPI_STATE_SHOWING) && !atspi_state_set_contains(ss, ATSPI_STATE_DEFUNCT);
+       g_object_unref(ss);
+       return ret;
+}
diff --git a/src/keyboard_tracker.c b/src/keyboard_tracker.c
new file mode 100644 (file)
index 0000000..70f62d9
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 <string.h>
+#include <atspi/atspi.h>
+#include <Ecore.h>
+#include <Ecore_X.h>
+#include "keyboard_tracker.h"
+#include "logger.h"
+
+static AtspiDeviceListener *listener;
+static Keyboard_Tracker_Cb user_cb;
+static void *user_data;
+static Ecore_Event_Handler *root_xwindow_property_changed_hld = NULL;
+static Ecore_Event_Handler *active_xwindow_property_changed_hld = NULL;
+
+static void _check_keyboard_state(Ecore_X_Window keyboard_win)
+{
+       Ecore_X_Virtual_Keyboard_State keyboard_state;
+       static Ecore_X_Virtual_Keyboard_State last_keyboard_state = ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF;
+
+       if (!keyboard_win)
+       {
+               return;
+       }
+
+       keyboard_state = ecore_x_e_virtual_keyboard_state_get(keyboard_win);
+       if (keyboard_state == last_keyboard_state)
+       {
+               return;
+       }
+
+       if (keyboard_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_ON)
+       {
+               tts_speak (_("IDS_VISUAL_KEYBOARD_ENABLED"), EINA_FALSE);
+               last_keyboard_state = keyboard_state;
+       }
+       else if (keyboard_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF)
+       {
+               tts_speak (_("IDS_VISUAL_KEYBOARD_DISABLED"), EINA_FALSE);
+               last_keyboard_state = keyboard_state;
+       }
+}
+
+static Eina_Bool _active_xwindow_property_changed_cb(void *data, int type, void *event)
+{
+       Ecore_X_Event_Window_Property *wp;
+       wp = (Ecore_X_Event_Window_Property *)event;
+
+       if (!wp)
+       {
+               return EINA_FALSE;
+       }
+
+       if (wp->atom == ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_STATE)
+       {
+               DEBUG("keyboard state event");
+               _check_keyboard_state(wp->win);
+       }
+
+       return EINA_TRUE;
+}
+
+void active_xwindow_property_tracker_register()
+{
+       Ecore_X_Window active_window = 0;
+       ecore_x_window_prop_xid_get(ecore_x_window_root_first_get(), ECORE_X_ATOM_NET_ACTIVE_WINDOW, ECORE_X_ATOM_WINDOW, &active_window, 1);
+       if (active_window)
+       {
+               ecore_x_event_mask_set(active_window, ECORE_X_EVENT_MASK_WINDOW_PROPERTY);
+               active_xwindow_property_changed_hld = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_PROPERTY, _active_xwindow_property_changed_cb, NULL);
+       }
+}
+
+void active_xwindow_property_tracker_unregister()
+{
+       if (active_xwindow_property_changed_hld)
+       {
+               ecore_event_handler_del(active_xwindow_property_changed_hld);
+               active_xwindow_property_changed_hld = NULL;
+       }
+}
+
+static Eina_Bool _root_xwindow_property_changed_cb(void *data, int type, void *event)
+{
+       Ecore_X_Event_Window_Property *wp;
+       wp = (Ecore_X_Event_Window_Property *)event;
+
+       if (!wp)
+       {
+               return EINA_FALSE;
+       }
+
+       if (wp->atom == ECORE_X_ATOM_NET_ACTIVE_WINDOW)
+       {
+               DEBUG("active window change");
+               active_xwindow_property_tracker_unregister();
+               active_xwindow_property_tracker_register();
+       }
+
+       return EINA_TRUE;
+}
+
+void root_xwindow_property_tracker_register()
+{
+       Ecore_X_Window root_window;
+
+       root_window = ecore_x_window_root_first_get();
+       if (root_window)
+       {
+               ecore_x_event_mask_set(root_window, ECORE_X_EVENT_MASK_WINDOW_PROPERTY);
+               root_xwindow_property_changed_hld = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_PROPERTY, _root_xwindow_property_changed_cb, NULL);
+       }
+}
+
+void root_xwindow_property_tracker_unregister()
+{
+       if (root_xwindow_property_changed_hld)
+       {
+               ecore_event_handler_del(root_xwindow_property_changed_hld);
+               root_xwindow_property_changed_hld = NULL;
+       }
+}
+
+static gboolean device_cb(const AtspiDeviceEvent * stroke, void *data)
+{
+       Key k;
+       if (!strcmp(stroke->event_string, "KP_Up"))
+               k = KEY_UP;
+       else if (!strcmp(stroke->event_string, "KP_Down"))
+               k = KEY_DOWN;
+       else if (!strcmp(stroke->event_string, "KP_Left"))
+               k = KEY_LEFT;
+       else if (!strcmp(stroke->event_string, "KP_Right"))
+               k = KEY_RIGHT;
+       else
+               return FALSE;
+
+       if (user_cb)
+               user_cb(user_data, k);
+
+       return TRUE;
+}
+
+void keyboard_tracker_init(void)
+{
+       listener = atspi_device_listener_new(device_cb, NULL, NULL);
+       atspi_register_keystroke_listener(listener, NULL, 0, ATSPI_KEY_PRESSED, ATSPI_KEYLISTENER_SYNCHRONOUS | ATSPI_KEYLISTENER_CANCONSUME, NULL);
+       active_xwindow_property_tracker_register();
+       root_xwindow_property_tracker_register();
+       DEBUG("keyboard tracker init");
+}
+
+void keyboard_tracker_register(Keyboard_Tracker_Cb cb, void *data)
+{
+       user_cb = cb;
+       user_data = data;
+}
+
+void keyboard_tracker_shutdown(void)
+{
+       atspi_deregister_keystroke_listener(listener, NULL, 0, ATSPI_KEY_PRESSED, NULL);
+       root_xwindow_property_tracker_unregister();
+       active_xwindow_property_tracker_unregister();
+       DEBUG("keyboard tracker shutdown");
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..2b20ab7
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 <time.h>
+#include <signal.h>
+#include <err.h>
+#include <execinfo.h>
+#include <appcore-efl.h>
+#include <Elementary.h>
+#include <eldbus-1/Eldbus.h>
+#include <service_app.h>
+#include <vconf.h>
+#include "navigator.h"
+#include "window_tracker.h"
+#include "logger.h"
+#include "screen_reader.h"
+#include "screen_reader_gestures.h"
+#include "screen_reader_switch.h"
+
+#define MAX_STACK_FRAMES 64
+static void *stack_traces[MAX_STACK_FRAMES];
+
+void posix_print_stack_trace(FILE * log_file)
+{
+       int i, trace_size = 0;
+       char **messages = (char **)NULL;
+
+       trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
+       messages = backtrace_symbols(stack_traces, trace_size);
+
+       /* skip the first couple stack frames (as they are this function and
+          our handler) and also skip the last frame as it's (always?) junk. */
+       // for (i = 3; i < (trace_size - 1); ++i)
+       for (i = 0; i < trace_size; ++i)        // we'll use this for now so you can see what's going on
+       {
+               fprintf(log_file, "  BACKTRACE LINE %i: %s\n", i, messages[i]);
+       }
+       if (messages) {
+               free(messages);
+       }
+}
+
+void print_warning(int sig, siginfo_t * siginfo, FILE * log_file)
+{
+       switch (sig) {
+       case SIGSEGV:
+               fputs("Caught SIGSEGV: Segmentation Fault\n", log_file);
+               break;
+       case SIGINT:
+               fputs("Caught SIGINT: Interactive attention signal, (usually ctrl+c)\n", log_file);
+               break;
+       case SIGFPE:
+               switch (siginfo->si_code) {
+               case FPE_INTDIV:
+                       fputs("Caught SIGFPE: (integer divide by zero)\n", log_file);
+                       break;
+               case FPE_INTOVF:
+                       fputs("Caught SIGFPE: (integer overflow)\n", log_file);
+                       break;
+               case FPE_FLTDIV:
+                       fputs("Caught SIGFPE: (floating-point divide by zero)\n", log_file);
+                       break;
+               case FPE_FLTOVF:
+                       fputs("Caught SIGFPE: (floating-point overflow)\n", log_file);
+                       break;
+               case FPE_FLTUND:
+                       fputs("Caught SIGFPE: (floating-point underflow)\n", log_file);
+                       break;
+               case FPE_FLTRES:
+                       fputs("Caught SIGFPE: (floating-point inexact result)\n", log_file);
+                       break;
+               case FPE_FLTINV:
+                       fputs("Caught SIGFPE: (floating-point invalid operation)\n", log_file);
+                       break;
+               case FPE_FLTSUB:
+                       fputs("Caught SIGFPE: (subscript out of range)\n", log_file);
+                       break;
+               default:
+                       fputs("Caught SIGFPE: Arithmetic Exception\n", log_file);
+                       break;
+               }
+               break;
+       case SIGILL:
+               switch (siginfo->si_code) {
+               case ILL_ILLOPC:
+                       fputs("Caught SIGILL: (illegal opcode)\n", log_file);
+                       break;
+               case ILL_ILLOPN:
+                       fputs("Caught SIGILL: (illegal operand)\n", log_file);
+                       break;
+               case ILL_ILLADR:
+                       fputs("Caught SIGILL: (illegal addressing mode)\n", log_file);
+                       break;
+               case ILL_ILLTRP:
+                       fputs("Caught SIGILL: (illegal trap)\n", log_file);
+                       break;
+               case ILL_PRVOPC:
+                       fputs("Caught SIGILL: (privileged opcode)\n", log_file);
+                       break;
+               case ILL_PRVREG:
+                       fputs("Caught SIGILL: (privileged register)\n", log_file);
+                       break;
+               case ILL_COPROC:
+                       fputs("Caught SIGILL: (coprocessor error)\n", log_file);
+                       break;
+               case ILL_BADSTK:
+                       fputs("Caught SIGILL: (internal stack error)\n", log_file);
+                       break;
+               default:
+                       fputs("Caught SIGILL: Illegal Instruction\n", log_file);
+                       break;
+               }
+               break;
+       case SIGTERM:
+               fputs("Caught SIGTERM: a termination request was sent to the program\n", log_file);
+               break;
+       case SIGABRT:
+               fputs("Caught SIGABRT: usually caused by an abort() or assert()\n", log_file);
+               break;
+       default:
+               break;
+       }
+}
+
+void posix_signal_handler(int sig, siginfo_t * siginfo, void *context)
+{
+       char file_name[256];
+       struct tm *timeinfo;
+       time_t rawtime = time(NULL);
+       timeinfo = localtime(&rawtime);
+       if (timeinfo)
+               snprintf(file_name, sizeof(file_name), "/tmp/screen_reader_crash_stacktrace_%i%i%i_%i:%i:%i_pid_%i.log", timeinfo->tm_year, timeinfo->tm_mon, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, getpid());
+       else
+               snprintf(file_name, sizeof(file_name), "/tmp/screen_reader_crash_stacktrace_pid_%i.log", getpid());
+
+       FILE *log_file = fopen(file_name, "w");
+       if (log_file) {
+               (void)context;
+               print_warning(sig, siginfo, stderr);
+
+               /* check file if it is symbolic link */
+               struct stat lstat_info;
+               if (lstat(file_name, &lstat_info) != -1) {
+                       print_warning(sig, siginfo, log_file);
+                       posix_print_stack_trace(log_file);
+               }
+
+               fclose(log_file);
+               log_file = NULL;
+       }
+
+       _Exit(1);
+}
+
+static uint8_t alternate_stack[SIGSTKSZ];
+void set_signal_handler()
+{
+       /* setup alternate stack */
+       {
+               stack_t ss = { };
+               /* malloc is usually used here, I'm not 100% sure my static allocation
+                  is valid but it seems to work just fine. */
+               ss.ss_sp = (void *)alternate_stack;
+               ss.ss_size = SIGSTKSZ;
+               ss.ss_flags = 0;
+
+               if (sigaltstack(&ss, NULL) != 0) {
+                       err(1, "sigaltstack");
+               }
+       }
+
+       /* register our signal handlers */
+       {
+               struct sigaction sig_action = { };
+               sig_action.sa_sigaction = posix_signal_handler;
+               sigemptyset(&sig_action.sa_mask);
+
+               sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+
+               if (sigaction(SIGSEGV, &sig_action, NULL) != 0) {
+                       err(1, "sigaction");
+               }
+               if (sigaction(SIGFPE, &sig_action, NULL) != 0) {
+                       err(1, "sigaction");
+               }
+               if (sigaction(SIGINT, &sig_action, NULL) != 0) {
+                       err(1, "sigaction");
+               }
+               if (sigaction(SIGILL, &sig_action, NULL) != 0) {
+                       err(1, "sigaction");
+               }
+               if (sigaction(SIGTERM, &sig_action, NULL) != 0) {
+                       err(1, "sigaction");
+               }
+               if (sigaction(SIGABRT, &sig_action, NULL) != 0) {
+                       err(1, "sigaction");
+               }
+       }
+}
+
+static bool app_create(void *data)
+{
+       if (vconf_set_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, 1))
+               ERROR("Can't set value of %s vconf key to 1", VCONFKEY_SETAPPL_ACCESSIBILITY_TTS);
+       DEBUG("atspi_init");
+       atspi_init();
+       DEBUG("logger_init");
+       DEBUG("screen_reader_create_service");
+       screen_reader_create_service(data);
+#ifndef SCREEN_READER_TV
+       DEBUG("screen_reader_gestures_init");
+       screen_reader_gestures_init();
+       DEBUG("navigator_init");
+       navigator_init();
+#endif
+       DEBUG("screen_reader_switch_enabled_set");
+       screen_reader_switch_enabled_set(EINA_TRUE);
+       return true;
+}
+
+static void app_terminate(void *data)
+{
+       DEBUG("screen reader terminating");
+#ifndef SCREEN_READER_TV
+       DEBUG("terminate navigator");
+       navigator_shutdown();
+       DEBUG("terminate gestures");
+       screen_reader_gestures_shutdown();
+#endif
+       DEBUG("terminate service");
+       screen_reader_terminate_service(data);
+       DEBUG("clear ScreenReaderEnabled property");
+       screen_reader_switch_enabled_set(EINA_FALSE);
+       DEBUG("screen reader terminated");
+       if (vconf_set_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, 0))
+               ERROR("Can't set value of %s vconf key to 0", VCONFKEY_SETAPPL_ACCESSIBILITY_TTS);
+       DEBUG("libatspi terminated");
+       atspi_exit();
+}
+
+static void app_control(app_control_h app_control, void *data)
+{
+       return;
+}
+
+int main(int argc, char **argv)
+{
+       set_signal_handler();
+       unsetenv("ELM_ATSPI_MODE");
+
+
+       service_app_lifecycle_callback_s event_callback;
+
+       event_callback.create = app_create;
+       event_callback.terminate = app_terminate;
+       event_callback.app_control = app_control;
+
+       return service_app_main(argc, argv, &event_callback, get_pointer_to_service_data_struct());
+}
diff --git a/src/navigator.c b/src/navigator.c
new file mode 100644 (file)
index 0000000..c965cd8
--- /dev/null
@@ -0,0 +1,2292 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <Ecore_X.h>
+#include <Ecore.h>
+#include <math.h>
+#include <atspi/atspi.h>
+#include "logger.h"
+#include "navigator.h"
+#include "window_tracker.h"
+#include "keyboard_tracker.h"
+#include "pivot_chooser.h"
+#include "flat_navi.h"
+#include "app_tracker.h"
+#include "smart_notification.h"
+#include "screen_reader_system.h"
+#include "screen_reader_haptic.h"
+#include "screen_reader_tts.h"
+#include "screen_reader_gestures.h"
+#include "dbus_gesture_adapter.h"
+#include "elm_access_adapter.h"
+
+#define QUICKPANEL_DOWN TRUE
+#define QUICKPANEL_UP FALSE
+
+#define DISTANCE_NB 8
+#define MENU_ITEM_TAB_INDEX_SIZE 16
+#define HOVERSEL_TRAIT_SIZE 200
+#define TTS_MAX_TEXT_SIZE  2000
+#define GESTURE_LIMIT 10
+
+//Timeout in ms which will be used as interval for handling ongoing
+//hoved gesture updates. It is introduced to improve performance.
+//Even if user makes many mouse move events within hover gesture
+//only 5 highlight updates a second will be performed. Else we will
+//highly pollute dbus bus and and decrease highlight performance.
+#define ONGOING_HOVER_GESTURE_INTERPRETATION_INTERVAL 200
+
+#define DEBUG_MODE
+
+#define GERROR_CHECK(error)\
+  if (error)\
+   {\
+     ERROR("Error_log:%s",error->message);\
+     g_error_free(error);\
+     error = NULL;\
+   }
+
+static void on_window_activate(void *data, AtspiAccessible * window);
+
+typedef struct {
+       int x, y;
+} last_focus_t;
+
+static last_focus_t gesture_start_p = { -1, -1 };
+static last_focus_t last_focus = { -1, -1 };
+
+static AtspiAccessible *current_obj;
+static AtspiComponent *current_comp = NULL;
+static AtspiAccessible *top_window;
+static FlatNaviContext *context;
+static bool prepared = false;
+static int counter = 0;
+int _last_hover_event_time = -1;
+
+static struct {
+       AtspiAccessible *focused_object;
+       bool auto_review_on;
+} s_auto_review = {
+.focused_object = NULL,.auto_review_on = false};
+
+char *state_to_char(AtspiStateType state)
+{
+       switch (state) {
+       case ATSPI_STATE_INVALID:
+               return strdup("ATSPI_STATE_INVALID");
+       case ATSPI_STATE_ACTIVE:
+               return strdup("ATSPI_STATE_ACTIVE");
+       case ATSPI_STATE_ARMED:
+               return strdup("ATSPI_STATE_ARMED");
+       case ATSPI_STATE_BUSY:
+               return strdup("ATSPI_STATE_BUSY");
+       case ATSPI_STATE_CHECKED:
+               return strdup("ATSPI_STATE_CHECKED");
+       case ATSPI_STATE_COLLAPSED:
+               return strdup("ATSPI_STATE_COLLAPSED");
+       case ATSPI_STATE_DEFUNCT:
+               return strdup("ATSPI_STATE_DEFUNCT");
+       case ATSPI_STATE_EDITABLE:
+               return strdup("ATSPI_STATE_EDITABLE");
+       case ATSPI_STATE_ENABLED:
+               return strdup("ATSPI_STATE_ENABLED");
+       case ATSPI_STATE_EXPANDABLE:
+               return strdup("ATSPI_STATE_EXPANDABLE");
+       case ATSPI_STATE_EXPANDED:
+               return strdup("ATSPI_STATE_EXPANDED");
+       case ATSPI_STATE_FOCUSABLE:
+               return strdup("ATSPI_STATE_FOCUSABLE");
+       case ATSPI_STATE_FOCUSED:
+               return strdup("ATSPI_STATE_FOCUSED");
+       case ATSPI_STATE_HAS_TOOLTIP:
+               return strdup("ATSPI_STATE_HAS_TOOLTIP");
+       case ATSPI_STATE_HORIZONTAL:
+               return strdup("ATSPI_STATE_HORIZONTAL");
+       case ATSPI_STATE_ICONIFIED:
+               return strdup("ATSPI_STATE_ICONIFIED");
+       case ATSPI_STATE_MULTI_LINE:
+               return strdup("ATSPI_STATE_MULTI_LINE");
+       case ATSPI_STATE_MULTISELECTABLE:
+               return strdup("ATSPI_STATE_MULTISELECTABLE");
+       case ATSPI_STATE_OPAQUE:
+               return strdup("ATSPI_STATE_OPAQUE");
+       case ATSPI_STATE_PRESSED:
+               return strdup("ATSPI_STATE_PRESSED");
+       case ATSPI_STATE_RESIZABLE:
+               return strdup("ATSPI_STATE_RESIZABLE");
+       case ATSPI_STATE_SELECTABLE:
+               return strdup("ATSPI_STATE_SELECTABLE");
+       case ATSPI_STATE_SELECTED:
+               return strdup("ATSPI_STATE_SELECTED");
+       case ATSPI_STATE_SENSITIVE:
+               return strdup("ATSPI_STATE_SENSITIVE");
+       case ATSPI_STATE_SHOWING:
+               return strdup("ATSPI_STATE_SHOWING");
+       case ATSPI_STATE_SINGLE_LINE:
+               return strdup("ATSPI_STATE_SINGLE_LINE");
+       case ATSPI_STATE_STALE:
+               return strdup("ATSPI_STATE_STALE");
+       case ATSPI_STATE_TRANSIENT:
+               return strdup("ATSPI_STATE_TRANSIENT");
+       case ATSPI_STATE_VERTICAL:
+               return strdup("ATSPI_STATE_VERTICAL");
+       case ATSPI_STATE_VISIBLE:
+               return strdup("ATSPI_STATE_VISIBLE");
+       case ATSPI_STATE_MANAGES_DESCENDANTS:
+               return strdup("ATSPI_STATE_MANAGES_DESCENDANTS");
+       case ATSPI_STATE_INDETERMINATE:
+               return strdup("ATSPI_STATE_INDETERMINATE");
+       case ATSPI_STATE_REQUIRED:
+               return strdup("ATSPI_STATE_REQUIRED");
+       case ATSPI_STATE_TRUNCATED:
+               return strdup("ATSPI_STATE_TRUNCATED");
+       case ATSPI_STATE_ANIMATED:
+               return strdup("ATSPI_STATE_ANIMATED");
+       case ATSPI_STATE_INVALID_ENTRY:
+               return strdup("ATSPI_STATE_INVALID_ENTRY");
+       case ATSPI_STATE_SUPPORTS_AUTOCOMPLETION:
+               return strdup("ATSPI_STATE_SUPPORTS_AUTOCOMPLETION");
+       case ATSPI_STATE_SELECTABLE_TEXT:
+               return strdup("ATSPI_STATE_SELECTABLE_TEXT");
+       case ATSPI_STATE_IS_DEFAULT:
+               return strdup("ATSPI_STATE_IS_DEFAULT");
+       case ATSPI_STATE_VISITED:
+               return strdup("ATSPI_STATE_VISITED");
+       case ATSPI_STATE_CHECKABLE:
+               return strdup("ATSPI_STATE_CHECKABLE");
+       case ATSPI_STATE_HAS_POPUP:
+               return strdup("ATSPI_STATE_HAS_POPUP");
+       case ATSPI_STATE_READ_ONLY:
+               return strdup("ATSPI_STATE_READ_ONLY");
+       case ATSPI_STATE_LAST_DEFINED:
+               return strdup("ATSPI_STATE_LAST_DEFINED");
+       case ATSPI_STATE_MODAL:
+               return strdup("ATSPI_STATE_MODAL");
+       case ATSPI_STATE_HIGHLIGHTED:
+               return strdup("ATSPI_STATE_HIGHLIGHTED");
+       case ATSPI_STATE_HIGHLIGHTABLE:
+               return strdup("ATSPI_STATE_HIGHLIGHTABLE");
+       default:
+               return strdup("\0");
+       }
+
+}
+
+static void display_info_about_object(AtspiAccessible * obj, bool display_parent_info)
+{
+       if(!obj)
+       {
+               return;
+       }
+
+       DEBUG("START");
+       DEBUG("------------------------");
+       gchar *name = atspi_accessible_get_name(obj, NULL);
+       gchar *role = atspi_accessible_get_localized_role_name(obj, NULL);
+       gchar *description = atspi_accessible_get_description(obj, NULL);
+       char *state_name = NULL;
+       AtspiStateSet *st = atspi_accessible_get_state_set(obj);
+       GArray *states = atspi_state_set_get_states(st);
+       AtspiComponent *comp = atspi_accessible_get_component_iface(obj);
+       AtspiValue *value = atspi_accessible_get_value_iface(obj);
+       AtspiRect *rect_screen = atspi_component_get_extents(comp, ATSPI_COORD_TYPE_SCREEN, NULL);
+       AtspiRect *rect_win = atspi_component_get_extents(comp, ATSPI_COORD_TYPE_WINDOW, NULL);
+
+       if(display_parent_info) {
+               AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
+               display_info_about_object(parent, false);
+               g_object_unref(parent);
+       }
+
+       DEBUG("NAME:%s", name);
+       DEBUG("ROLE:%s", role);
+       DEBUG("DESCRIPTION:%s", description);
+       DEBUG("CHILDS:%d", atspi_accessible_get_child_count(obj, NULL));
+       DEBUG("HIGHLIGHT_INDEX:%d", atspi_component_get_highlight_index(comp, NULL));
+       DEBUG("INDEX IN PARENT:%d", atspi_accessible_get_index_in_parent(obj, NULL));
+       if (value) {
+               DEBUG("VALUE:%f", atspi_value_get_current_value(value, NULL));
+               DEBUG("VALUE MAX:%f", atspi_value_get_maximum_value(value, NULL));
+               DEBUG("VALUE MIN:%f", atspi_value_get_minimum_value(value, NULL));
+               g_object_unref(value);
+       }
+       DEBUG("STATES:");
+       int a;
+       AtspiStateType stat;
+       for (a = 0; states && (a < states->len); ++a) {
+               stat = g_array_index(states, AtspiStateType, a);
+               state_name = state_to_char(stat);
+               DEBUG("   %s", state_name);
+               free(state_name);
+       }
+       g_array_free(states, 0);
+       DEBUG("LOCALE:%s", atspi_accessible_get_object_locale(obj, NULL));
+       DEBUG("SIZE ON SCREEN, width:%d, height:%d", rect_screen->width, rect_screen->height);
+       DEBUG("POSITION ON SCREEN: x:%d y:%d", rect_screen->x, rect_screen->y);
+       DEBUG("SIZE ON WIN, width:%d, height:%d", rect_win->width, rect_win->height);
+       DEBUG("POSITION ON WIN: x:%d y:%d", rect_win->x, rect_win->y);
+       DEBUG("INTERFACES:");
+       GArray *ifaces = atspi_accessible_get_interfaces(obj);
+       for (a = 0; ifaces && (a < ifaces->len); ++a) {
+               gchar * interface_name = g_array_index(ifaces, gchar *, a);
+               DEBUG("   %s", interface_name);
+               g_free(interface_name);
+       }
+       if (ifaces)
+               g_array_free(ifaces, FALSE);
+
+       DEBUG("------------------------");
+       DEBUG("END");
+       g_free(name);
+       g_free(role);
+       g_free(description);
+}
+
+char *generate_description_for_subtrees(AtspiAccessible * obj)
+{
+       DEBUG("START");
+
+       if (!obj)
+               return strdup("");
+       return strdup("");
+       /*
+          AtspiRole role;
+          int child_count;
+          int i;
+          char *name = NULL;
+          char *below = NULL;
+          char ret[TTS_MAX_TEXT_SIZE] = "\0";
+          AtspiAccessible *child = NULL;
+
+          int child_count = atspi_accessible_get_child_count(obj, NULL);
+
+          role = atspi_accessible_get_role(obj, NULL);
+
+          // Do not generate that for popups
+          if (role == ATSPI_ROLE_POPUP_MENU || role == ATSPI_ROLE_DIALOG)
+          return strdup("");
+
+          child_count = atspi_accessible_get_child_count(obj, NULL);
+
+          DEBUG("There is %d children inside this object", child_count);
+          if (!child_count)
+          return strdup("");
+
+          for (i=0; i < child_count; i++)
+          {
+          child = atspi_accessible_get_child_at_index(obj, i, NULL);
+          name = atspi_accessible_get_name(child, NULL);
+          DEBUG("%d child name:%s", i, name);
+          if (name && strncmp(name, "\0", 1))
+          {
+          strncat(ret, name, sizeof(ret) - strlen(ret) - 1);
+          }
+          strncat(ret, " ", sizeof(ret) - strlen(ret) - 1);
+          below = generate_description_for_subtrees(child);
+          DEBUG("%s from below", below);
+          if (strncmp(below, "\0", 1))
+          {
+          strncat(ret, below, sizeof(ret) - strlen(ret) - 1);
+          }
+
+          g_object_unref(child);
+          free(below);
+          free(name);
+          }
+          return strdup(ret);
+        */
+}
+
+static int _check_list_children_count(AtspiAccessible * obj)
+{
+       int list_count = 0;
+       int i;
+       AtspiAccessible *child = NULL;
+
+       if (!obj)
+               return 0;
+
+       if (atspi_accessible_get_role(obj, NULL) == ATSPI_ROLE_LIST) {
+               int children_count = atspi_accessible_get_child_count(obj, NULL);
+
+               for (i = 0; i < children_count; i++) {
+                       child = atspi_accessible_get_child_at_index(obj, i, NULL);
+                       if (atspi_accessible_get_role(child, NULL) == ATSPI_ROLE_LIST_ITEM)
+                               list_count++;
+                       g_object_unref(child);
+               }
+       }
+
+       return list_count;
+}
+
+static int _find_popup_list_children_count(AtspiAccessible * obj)
+{
+       int list_items_count = 0;
+       int children_count = atspi_accessible_get_child_count(obj, NULL);
+       int i;
+       AtspiAccessible *child = NULL;
+
+       list_items_count = _check_list_children_count(obj);
+       if (list_items_count > 0)
+               return list_items_count;
+
+       for (i = 0; i < children_count; i++) {
+               child = atspi_accessible_get_child_at_index(obj, i, NULL);
+               list_items_count = _find_popup_list_children_count(child);
+               if (list_items_count > 0)
+                       return list_items_count;
+               g_object_unref(child);
+       }
+
+       return 0;
+}
+
+static bool _widget_has_state(AtspiAccessible * obj, AtspiStateType type)
+{
+       Eina_Bool ret = EINA_FALSE;
+       AtspiStateSet *st = atspi_accessible_get_state_set(obj);
+       if (atspi_state_set_contains(st, type))
+               ret = EINA_TRUE;
+       g_object_unref(st);
+       return ret;
+}
+
+int get_accuracy(double val, int max_accuracy)
+{
+       char val_str[HOVERSEL_TRAIT_SIZE] = "";
+       int position;
+       int accuracy;
+
+       snprintf(val_str, HOVERSEL_TRAIT_SIZE, "%.*f", max_accuracy, val);
+       accuracy = max_accuracy;
+       position = strlen(val_str) - 1;
+       while ( position > 0 && val_str[position] == '0' ) {
+               --position;
+               --accuracy;
+       }
+       return accuracy;
+}
+
+void add_slider_description(char *dest, uint dest_size, AtspiAccessible *obj)
+{
+       gchar *role_name;
+       AtspiValue *value_iface;
+       double val;
+       double min_val;
+       double max_val;
+       char trait[HOVERSEL_TRAIT_SIZE] = "";
+       int accuracy;
+
+       role_name = atspi_accessible_get_localized_role_name(obj, NULL);
+       if (role_name) {
+               strncat(dest, role_name, dest_size - strlen(dest) - 1);
+               g_free(role_name);
+       }
+
+       value_iface = atspi_accessible_get_value_iface(obj);
+       if (!value_iface) {
+               return;
+       }
+
+       accuracy = get_accuracy( atspi_value_get_minimum_increment(value_iface, NULL), 3 );
+       val = atspi_value_get_current_value(value_iface, NULL);
+       max_val = atspi_value_get_maximum_value(value_iface, NULL);
+       min_val = atspi_value_get_minimum_value(value_iface, NULL);
+       snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_SLIDER_VALUE"), accuracy, min_val, accuracy, max_val, accuracy, val);
+       strncat(dest, trait, dest_size - strlen(dest) - 1);
+
+       if (_widget_has_state(obj, ATSPI_STATE_ENABLED)) {
+               strncat(dest, _("IDS_TRAIT_SLIDER_SWIPE_COMMUNICATE"), dest_size - strlen(dest) - 1);
+       }
+       g_object_unref(value_iface);
+}
+
+char *generate_trait(AtspiAccessible * obj)
+{
+       if (!obj)
+               return strdup("");
+
+       AtspiRole role = atspi_accessible_get_role(obj, NULL);
+       AtspiStateSet *state_set = atspi_accessible_get_state_set(obj);
+       char ret[TTS_MAX_TEXT_SIZE] = "\0";
+       switch (role) {
+       case ATSPI_ROLE_ENTRY: {
+               gchar *role_name = atspi_accessible_get_localized_role_name(obj, NULL);
+               if (role_name) {
+                       strncat(ret, role_name, sizeof(ret) - strlen(ret) - 1);
+                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+                       if (atspi_state_set_contains(state_set, ATSPI_STATE_FOCUSED))
+                               strncat(ret, _("IDS_TRAIT_TEXT_EDIT_FOCUSED"), sizeof(ret) - strlen(ret) - 1);
+                       else
+                               strncat(ret, _("IDS_TRAIT_TEXT_EDIT"), sizeof(ret) - strlen(ret) - 1);
+                       g_free(role_name);
+               }
+               break;
+       }
+       case ATSPI_ROLE_MENU_ITEM: {
+               AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
+               int children_count = atspi_accessible_get_child_count(parent, NULL);
+               int index = atspi_accessible_get_index_in_parent(obj, NULL);
+               char tab_index[MENU_ITEM_TAB_INDEX_SIZE];
+               snprintf(tab_index, MENU_ITEM_TAB_INDEX_SIZE, _("IDS_TRAIT_MENU_ITEM_TAB_INDEX"), index + 1, children_count);
+               strncat(ret, tab_index, sizeof(ret) - strlen(ret) - 1);
+               g_object_unref(parent);
+               break;
+       }
+       case ATSPI_ROLE_POPUP_MENU: {
+               int children_count = atspi_accessible_get_child_count(obj, NULL);
+               char trait[HOVERSEL_TRAIT_SIZE];
+
+               snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_CTX_POPUP"));
+               strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+
+               snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_SHOWING"));
+               strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, " ", sizeof(ret) - strlen(ret) - 1);
+
+               snprintf(trait, HOVERSEL_TRAIT_SIZE, "%d", children_count);
+               strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, " ", sizeof(ret) - strlen(ret) - 1);
+
+               snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_ITEMS"));
+               strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+
+               snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_POPUP_CLOSE"));
+               strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+               break;
+       }
+       case ATSPI_ROLE_DIALOG: {
+               int children_count = _find_popup_list_children_count(obj);
+               char trait[HOVERSEL_TRAIT_SIZE];
+
+               snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_POPUP"));
+               strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+
+               if (children_count > 0) {
+                       snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_SHOWING"));
+                       strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+                       strncat(ret, " ", sizeof(ret) - strlen(ret) - 1);
+
+                       snprintf(trait, HOVERSEL_TRAIT_SIZE, "%d", children_count);
+                       strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+                       strncat(ret, " ", sizeof(ret) - strlen(ret) - 1);
+
+                       snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_ITEMS"));
+                       strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+               }
+
+               snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_POPUP_CLOSE"));
+               strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+               break;
+       }
+       case ATSPI_ROLE_GLASS_PANE: {
+               AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
+               int children_count = atspi_accessible_get_child_count(parent, NULL);
+               char trait[HOVERSEL_TRAIT_SIZE];
+               snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_PD_HOVERSEL"), children_count);
+               strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+               g_object_unref(parent);
+               break;
+       }
+       case ATSPI_ROLE_LIST_ITEM: {
+               AtspiAccessible *parent = atspi_accessible_get_parent(obj, NULL);
+               AtspiRole parent_role = atspi_accessible_get_role(parent, NULL);
+
+               if(parent_role == ATSPI_ROLE_TREE_TABLE) {
+
+                       AtspiStateSet *state_set = atspi_accessible_get_state_set(obj);
+                       gboolean is_selected = atspi_state_set_contains(state_set, ATSPI_STATE_SELECTED);
+                       g_object_unref(state_set);
+
+                       if(is_selected) {
+                               strncat(ret, _("IDS_TRAIT_ITEM_SELECTED"), sizeof(ret) - strlen(ret) - 1);
+                       }
+
+                       AtspiStateSet *parent_state_set = atspi_accessible_get_state_set(parent);
+                       bool is_parent_multiselectable = atspi_state_set_contains(parent_state_set, ATSPI_STATE_MULTISELECTABLE);
+
+                       g_object_unref(parent_state_set);
+                       g_object_unref(parent);
+
+                       if(is_parent_multiselectable) {
+
+                               char buf[200];
+
+                               AtspiSelection *parent_selection = atspi_accessible_get_selection(parent);
+                               int selected_children_count = atspi_selection_get_n_selected_children(parent_selection, NULL);
+
+                               if(is_selected) {
+                                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+                               }
+
+                               snprintf(buf, 200, _("IDS_TRAIT_ITEM_SELECTED_COUNT"), selected_children_count);
+                               strncat(ret, buf, sizeof(ret) - strlen(ret) - 1);
+
+                               g_object_unref(parent_selection);
+                       }
+
+               } else if (atspi_state_set_contains(state_set, ATSPI_STATE_EXPANDABLE)) {
+                       if (atspi_state_set_contains(state_set, ATSPI_STATE_EXPANDED)) {
+                               strncat(ret, _("IDS_TRAIT_GROUP_INDEX_EXPANDED"), sizeof(ret) - strlen(ret) - 1);
+                       } else {
+                               strncat(ret, _("IDS_TRAIT_GROUP_INDEX_COLLAPSED"), sizeof(ret) - strlen(ret) - 1);
+                       }
+               }
+               g_object_unref(parent);
+               break;
+       }
+       case ATSPI_ROLE_CHECK_BOX:
+       case ATSPI_ROLE_RADIO_BUTTON: {
+               if (atspi_state_set_contains(state_set, ATSPI_STATE_CHECKED)) {
+                       strncat(ret, _("IDS_TRAIT_CHECK_BOX_SELECTED"), sizeof(ret) - strlen(ret) - 1);
+               } else {
+                       strncat(ret, _("IDS_TRAIT_CHECK_BOX_NOT_SELECTED"), sizeof(ret) - strlen(ret) - 1);
+               }
+
+               if (role == ATSPI_ROLE_RADIO_BUTTON) {
+                       /* Say role name ("radio button"), but only if it's not a color chooser */
+                       AtspiAccessible *parent;
+                       AtspiRole parent_role;
+                       parent = atspi_accessible_get_parent(obj, NULL);
+                       parent_role = atspi_accessible_get_role(parent, NULL);
+                       if (parent_role != ATSPI_ROLE_COLOR_CHOOSER) {
+                               gchar *role_name;
+                               role_name = atspi_accessible_get_localized_role_name(obj, NULL);
+                               if (role_name) {
+                                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+                                       strncat(ret, role_name, sizeof(ret) - strlen(ret) - 1);
+                                       g_free(role_name);
+                               }
+                       }
+                       g_object_unref(parent);
+               }
+               break;
+       }
+       case ATSPI_ROLE_PUSH_BUTTON: {
+               strncat(ret, _("IDS_TRAIT_PUSH_BUTTON"), sizeof(ret) - strlen(ret) - 1);
+               break;
+       }
+       case ATSPI_ROLE_PROGRESS_BAR: {
+               AtspiValue *value = atspi_accessible_get_value_iface(obj);
+               if (value) {
+                       double val = atspi_value_get_current_value(value, NULL);
+                       char trait[HOVERSEL_TRAIT_SIZE];
+                       if (val > 0) {
+                               snprintf(trait, HOVERSEL_TRAIT_SIZE, _("IDS_TRAIT_PD_PROGRESSBAR_PERCENT"), val * 100);
+                               strncat(ret, trait, sizeof(ret) - strlen(ret) - 1);
+                       } else {
+                               strncat(ret, _("IDS_TRAIT_PD_PROGRESSBAR"), sizeof(ret) - strlen(ret) - 1);
+                       }
+                       g_object_unref(value);
+               }
+               break;
+       }
+       case ATSPI_ROLE_TOGGLE_BUTTON: {
+               strncat(ret, _("IDS_TRAIT_TOGGLE_BUTTON"), sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+               if (atspi_state_set_contains(state_set, ATSPI_STATE_CHECKED)) {
+                       strncat(ret, _("IDS_TRAIT_TOGGLE_BUTTON_ON"), sizeof(ret) - strlen(ret) - 1);
+               } else {
+                       strncat(ret, _("IDS_TRAIT_TOGGLE_BUTTON_OFF"), sizeof(ret) - strlen(ret) - 1);
+               }
+               break;
+       }
+       case ATSPI_ROLE_SLIDER: {
+               add_slider_description(ret, sizeof(ret), obj);
+               break;
+       }
+       case ATSPI_ROLE_HEADING:
+       case ATSPI_ROLE_GROUPING: {
+               break;
+       }
+       default: {
+               gchar *role_name = atspi_accessible_get_localized_role_name(obj, NULL);
+               if (role_name) {
+                       strncat(ret, role_name, sizeof(ret) - strlen(ret) - 1);
+                       g_free(role_name);
+               }
+       }
+       }
+
+       if (state_set)
+               g_object_unref(state_set);
+
+       return strdup(ret);
+}
+
+char *generate_text_for_relation_objects(AtspiAccessible * obj, AtspiRelationType search, char *(*text_generate_cb)(AtspiAccessible *obj))
+{
+       GError *err = NULL;
+       GArray *relations;
+       AtspiRelation *relation;
+       AtspiRelationType type;
+       Eina_Strbuf *buf;
+       int i, j;
+       char *ret = NULL;
+
+       if (!obj || !text_generate_cb) return NULL;
+
+       relations = atspi_accessible_get_relation_set(obj, &err);
+       if (err || !relations)
+       {
+               if (err) g_error_free(err);
+               return NULL;
+       }
+
+       buf = eina_strbuf_new();
+
+       for (i = 0; i < relations->len; i++)
+       {
+               relation = g_array_index(relations, AtspiRelation *, i);
+               type = atspi_relation_get_relation_type(relation);
+               if (type == search)
+               {
+                       for (j = 0; j < atspi_relation_get_n_targets(relation); j++)
+                       {
+                               AtspiAccessible *target = atspi_relation_get_target(relation, j);
+                               char *text = text_generate_cb(target);
+                               if (j == 0)
+                                       eina_strbuf_append_printf(buf, "%s", text);
+                               else
+                                       eina_strbuf_append_printf(buf, ", %s", text);
+                               g_object_unref(target);
+                               free(text);
+                       }
+               }
+               g_object_unref(relation);
+       }
+       g_array_free(relations, TRUE);
+       ret = eina_strbuf_string_steal(buf);
+       eina_strbuf_free(buf);
+
+       return ret;
+}
+
+static char *generate_description_from_relation_object(AtspiAccessible *obj)
+{
+       GError *err = NULL;
+       char *ret = generate_trait(obj);
+       char *desc = atspi_accessible_get_description(obj, &err);
+
+       if (err)
+       {
+               g_error_free(err);
+               g_free(desc);
+               return ret;
+       }
+
+       if (desc) {
+               if (desc[0] != '\0') {
+                       char *tmp = ret;
+                       if (asprintf(&ret, "%s, %s", desc, ret) < 0)
+                               ERROR("asprintf failed.");
+                       free(tmp);
+               }
+               g_free(desc);
+       }
+
+
+       return ret;
+}
+
+static char *generate_name_from_relation_object(AtspiAccessible *obj)
+{
+       GError *err = NULL;
+       char *name = atspi_accessible_get_name(obj, &err);
+
+       if(err)
+       {
+               g_error_free(err);
+               g_free(name);
+               return NULL;
+       }
+
+       return name;
+}
+
+static char *generate_what_to_read(AtspiAccessible * obj)
+{
+       char *name;
+       char *names = NULL;
+       char *description;
+       char *role_name;
+       char *other;
+       char *text = NULL;
+       char ret[TTS_MAX_TEXT_SIZE] = "\0";
+       char *description_from_relation;
+       char *name_from_relation;
+
+       description = atspi_accessible_get_description(obj, NULL);
+       name = atspi_accessible_get_name(obj, NULL);
+       role_name = generate_trait(obj);
+       other = generate_description_for_subtrees(obj);
+       description_from_relation = generate_text_for_relation_objects(obj, ATSPI_RELATION_DESCRIBED_BY, generate_description_from_relation_object);
+       name_from_relation = generate_text_for_relation_objects(obj, ATSPI_RELATION_LABELLED_BY, generate_name_from_relation_object);
+       AtspiText *iface_text = atspi_accessible_get_text_iface(obj);
+       if (iface_text) {
+               text = atspi_text_get_text(iface_text, 0, atspi_text_get_character_count(iface_text, NULL), NULL);
+               g_object_unref(iface_text);
+       }
+
+       DEBUG("->->->->->-> WIDGET GAINED HIGHLIGHT: %s <-<-<-<-<-<-<-", name);
+       DEBUG("->->->->->-> FROM SUBTREE HAS NAME:  %s <-<-<-<-<-<-<-", other);
+
+       display_info_about_object(obj, false);
+
+       if (name && strncmp(name, "\0", 1))
+               names = strdup(name);
+       else if (other && strncmp(other, "\0", 1))
+               names = strdup(other);
+
+       if (text) {
+               strncat(ret, text, sizeof(ret) - strlen(ret) - 1);
+       }
+
+       DEBUG("Text:%s", text);
+
+       if (names && strlen(names) > 0) {
+               if (strlen(ret) > 0)
+                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, names, sizeof(ret) - strlen(ret) - 1);
+       }
+
+       if (name_from_relation && strlen(name_from_relation) > 0) {
+               if(strlen(ret) > 0)
+                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, name_from_relation, sizeof(ret) - strlen(ret) - 1);
+       }
+
+       if (role_name && strlen(role_name) > 0) {
+               if (strlen(ret) > 0)
+                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, role_name, sizeof(ret) - strlen(ret) - 1);
+       }
+
+       if (description && strlen(description) > 0) {
+               if (strlen(ret) > 0)
+                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, description, sizeof(ret) - strlen(ret) - 1);
+       }
+
+       if (description_from_relation && (description_from_relation[0] != '\n')) {
+               if (strlen(ret) > 0)
+                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, description_from_relation, sizeof(ret) - strlen(ret) - 1);
+       }
+
+       free(text);
+       free(name);
+       free(names);
+       free(name_from_relation);
+       free(description);
+       free(role_name);
+       free(other);
+       free(description_from_relation);
+
+       return strdup(ret);
+}
+
+static void _current_highlight_object_set(AtspiAccessible * obj)
+{
+       DEBUG("START");
+       GError *err = NULL;
+       gchar *role = NULL;
+
+       if (!obj) {
+               DEBUG("Clearing highlight object");
+               current_obj = NULL;
+               if (current_comp) {
+                       atspi_component_clear_highlight(current_comp, &err);
+                       g_object_ref(current_comp);
+                       current_comp = NULL;
+               }
+
+               return;
+       }
+       if (current_obj == obj) {
+               DEBUG("Object already highlighted");
+               DEBUG("Object name:%s", atspi_accessible_get_name(obj, NULL));
+               return;
+       }
+       if (obj && ATSPI_IS_COMPONENT(obj)) {
+               DEBUG("OBJ WITH COMPONENT");
+               AtspiComponent *comp = atspi_accessible_get_component_iface(obj);
+               if (!comp) {
+                       GError *err = NULL;
+                       role = atspi_accessible_get_role_name(obj, &err);
+                       ERROR("AtspiComponent *comp NULL, [%s]", role);
+                       GERROR_CHECK(err);
+                       g_free(role);
+                       return;
+               }
+               if (current_comp) {
+                       atspi_component_clear_highlight(current_comp, &err);
+               }
+               atspi_component_grab_highlight(comp, &err);
+               current_comp = comp;
+               GERROR_CHECK(err)
+
+               Eina_Bool is_paused = tts_pause_get();
+               if (is_paused) {
+                       tts_stop_set();
+                       tts_pause_set(EINA_FALSE);
+               }
+               current_obj = obj;
+               char *text_to_speak = NULL;
+               text_to_speak = generate_what_to_read(obj);
+               DEBUG("SPEAK:%s", text_to_speak);
+
+               tts_speak(text_to_speak, EINA_TRUE);
+               g_free(text_to_speak);
+       } else
+               DEBUG("Unable to highlight on object");
+       DEBUG("END");
+}
+
+void test_debug(AtspiAccessible * current_widget)
+{
+       int jdx;
+       int count_child;
+       gchar *role;
+       GError *err = NULL;
+       AtspiAccessible *child_iter = NULL;
+       AtspiAccessible *parent = atspi_accessible_get_parent(current_widget, &err);
+       GERROR_CHECK(err)
+
+               if (!parent)
+               return;
+       count_child = atspi_accessible_get_child_count(parent, &err);
+       GERROR_CHECK(err)
+               DEBUG("Total childs in parent: %d\n", count_child);
+       if (!count_child)
+       {
+               g_object_unref(parent);
+               return;
+       }
+
+       for (jdx = 0; jdx < count_child; jdx++) {
+               child_iter = atspi_accessible_get_child_at_index(parent, jdx, &err);
+               GERROR_CHECK(err)
+
+                       if (current_widget == child_iter) {
+                       role = atspi_accessible_get_role_name(parent, &err);
+                       DEBUG("Childen found in parent: %s at index: %d\n", role, jdx);
+               } else {
+                       role = atspi_accessible_get_role_name(parent, &err);
+                       DEBUG("Childen not found in parent: %s at index: %d\n", role, jdx);
+               }
+               g_free(role);
+               GERROR_CHECK(err)
+       }
+       g_object_unref(parent);
+}
+
+static void _focus_widget(Gesture_Info * info)
+{
+       DEBUG("START");
+
+       if ((last_focus.x == info->x_beg) && (last_focus.y == info->y_beg))
+               return;
+
+       AtspiAccessible *obj = NULL;
+       if (flat_navi_context_current_at_x_y_set(context, info->x_beg, info->y_beg, &obj)) {
+               last_focus.x = info->x_beg;
+               last_focus.y = info->y_beg;
+               _current_highlight_object_set(obj);
+       }
+
+       DEBUG("END");
+}
+
+static void _focus_next(void)
+{
+       DEBUG("START");
+       AtspiAccessible *obj;
+       if (!context) {
+               ERROR("No navigation context created");
+               return;
+       }
+
+       obj = flat_navi_context_next(context);
+       if (obj)
+               _current_highlight_object_set(obj);
+       else
+               DEBUG("Next widget not found. Abort");
+       DEBUG("END");
+}
+
+static void _focus_next_visible(void)
+{
+       DEBUG("START");
+       AtspiAccessible *obj;
+       AtspiStateSet *ss = NULL;
+       Eina_Bool visible = EINA_FALSE;
+       if (!context) {
+               ERROR("No navigation context created");
+               return;
+       }
+
+       do {
+               obj = flat_navi_context_next(context);
+               // try 'cycle' objects in context
+               if (obj) {
+                       ss = atspi_accessible_get_state_set(obj);
+                       visible = atspi_state_set_contains(ss, ATSPI_STATE_SHOWING);
+                       g_object_unref(ss);
+               }
+       }
+       while (obj && !visible);
+
+       if (obj)
+               _current_highlight_object_set(obj);
+       else
+               DEBUG("Next widget not found. Abort");
+       DEBUG("END");
+}
+
+static void _focus_prev_visible(void)
+{
+       AtspiAccessible *obj;
+       AtspiStateSet *ss = NULL;
+       Eina_Bool visible = EINA_FALSE;
+       if (!context) {
+               ERROR("No navigation context created");
+               return;
+       }
+       do {
+               obj = flat_navi_context_prev(context);
+               // try 'cycle' objects in context
+               if (obj) {
+                       ss = atspi_accessible_get_state_set(obj);
+                       visible = atspi_state_set_contains(ss, ATSPI_STATE_SHOWING);
+                       g_object_unref(ss);
+               }
+       }
+       while (obj && !visible);
+
+       if (obj)
+               _current_highlight_object_set(obj);
+       else
+               DEBUG("Previous widget not found. Abort");
+}
+
+static void _focus_prev(void)
+{
+       AtspiAccessible *obj;
+       if (!context) {
+               ERROR("No navigation context created");
+               return;
+       }
+
+       obj = flat_navi_context_prev(context);
+       if (obj)
+               _current_highlight_object_set(obj);
+       else
+               DEBUG("Previous widget not found. Abort");
+}
+
+static void _caret_move_beg(void)
+{
+       AtspiAccessible *current_widget = NULL;
+       AtspiText *text_interface;
+       gboolean ret;
+       GError *err = NULL;
+
+       if (!current_obj)
+               return;
+
+       current_widget = current_obj;
+
+       text_interface = atspi_accessible_get_text_iface(current_widget);
+       if (text_interface) {
+               ret = atspi_text_set_caret_offset(text_interface, 0, &err);
+               GERROR_CHECK(err)
+                       if (ret) {
+                       DEBUG("Caret position increment done");
+                       gchar *text = atspi_text_get_text(text_interface, 0, 1, NULL);
+                       DEBUG("SPEAK:%s", text);
+                       tts_speak(text, EINA_TRUE);
+                       tts_speak(_("IDS_TEXT_BEGIN"), EINA_FALSE);
+                       g_free(text);
+               } else {
+                       ERROR("Caret position increment error");
+               }
+               g_object_unref(text_interface);
+       } else
+               ERROR("No text interface supported!");
+}
+
+static void _caret_move_end(void)
+{
+       AtspiAccessible *current_widget = NULL;
+       AtspiText *text_interface;
+       gboolean ret;
+       GError *err = NULL;
+
+       if (!current_obj)
+               return;
+
+       current_widget = current_obj;
+
+       text_interface = atspi_accessible_get_text_iface(current_widget);
+       if (text_interface) {
+               int len = atspi_text_get_character_count(text_interface, NULL);
+               ret = atspi_text_set_caret_offset(text_interface, len, &err);
+               if (ret) {
+                       DEBUG("Caret position increment done");
+                       DEBUG("SPEAK:%s", _("IDS_TEXT_END"));
+                       tts_speak(_("IDS_TEXT_END"), EINA_TRUE);
+               } else
+                       ERROR("Caret position to end error");
+               g_object_unref(text_interface);
+       }
+}
+
+static void _caret_move_forward(void)
+{
+       AtspiAccessible *current_widget = NULL;
+       AtspiText *text_interface;
+       gint current_offset;
+       gboolean ret;
+       int offset_pos;
+       gchar *text;
+       GError *err = NULL;
+       if (!current_obj)
+               return;
+
+       current_widget = current_obj;
+
+       text_interface = atspi_accessible_get_text_iface(current_widget);
+       if (text_interface) {
+               current_offset = atspi_text_get_caret_offset(text_interface, &err);
+               GERROR_CHECK(err)
+                       ret = atspi_text_set_caret_offset(text_interface, current_offset + 1, &err);
+               GERROR_CHECK(err)
+                       if (ret) {
+                       offset_pos = atspi_text_get_caret_offset(text_interface, NULL);
+                       text = atspi_text_get_text(text_interface, offset_pos, offset_pos + 1, NULL);
+                       DEBUG("Caret position increment done");
+                       DEBUG("Current caret position:%d", offset_pos);
+                       DEBUG("Current caret offset:%d", current_offset);
+                       if (offset_pos == atspi_text_get_character_count(text_interface, NULL)) {
+                               DEBUG("SPEAK:%s", _("IDS_TEXT_END"));
+                               tts_speak(_("IDS_TEXT_END"), EINA_FALSE);
+                       } else {
+                               DEBUG("SPEAK:%s", text);
+                               tts_speak(text, EINA_TRUE);
+                       }
+                       g_free(text);
+               } else {
+                       ERROR("Caret position increment error");
+               }
+               g_object_unref(text_interface);
+       } else
+               ERROR("No text interface supported!");
+       return;
+
+}
+
+static void _caret_move_backward(void)
+{
+       AtspiAccessible *current_widget = NULL;
+       AtspiText *text_interface;
+       gint current_offset;
+       int offset_pos;
+       gchar *text;
+       GError *err = NULL;
+       gboolean ret;
+
+       if (!current_obj)
+               return;
+
+       current_widget = current_obj;
+
+       GERROR_CHECK(err)
+
+               text_interface = atspi_accessible_get_text_iface(current_widget);
+       if (text_interface) {
+               current_offset = atspi_text_get_caret_offset(text_interface, &err);
+               GERROR_CHECK(err)
+                       ret = atspi_text_set_caret_offset(text_interface, current_offset - 1, &err);
+               GERROR_CHECK(err)
+                       if (ret) {
+                       offset_pos = atspi_text_get_caret_offset(text_interface, NULL);
+                       text = atspi_text_get_text(text_interface, offset_pos, offset_pos + 1, NULL);
+                       DEBUG("Caret position decrement done");
+                       DEBUG("Current caret position:%d", offset_pos);
+                       DEBUG("SPEAK:%s", text);
+                       tts_speak(text, EINA_TRUE);
+                       g_free(text);
+                       if (offset_pos == 0) {
+                               DEBUG("SPEAK:%s", _("IDS_TEXT_BEGIN"));
+                               tts_speak(_("IDS_TEXT_BEGIN"), EINA_FALSE);
+                       }
+               } else {
+                       ERROR("Caret position decrement error");
+               }
+               g_object_unref(text_interface);
+       } else
+               ERROR("No text interface supported!");
+       return;
+}
+
+static void _read_value(AtspiValue * value)
+{
+       if (!value)
+               return;
+
+       gdouble current_val = atspi_value_get_current_value(value, NULL);
+       gdouble max_val = atspi_value_get_maximum_value(value, NULL);
+       gdouble min_val = atspi_value_get_minimum_value(value, NULL);
+
+       int proc = (current_val / fabs(max_val - min_val)) * 100;
+
+       char buf[256] = "\0";
+       snprintf(buf, sizeof(buf), "%d percent", proc);
+       DEBUG("has value %s", buf);
+       tts_speak(buf, EINA_TRUE);
+}
+
+static void _value_inc(void)
+{
+       AtspiAccessible *current_widget = NULL;
+       GError *err = NULL;
+
+       if (!current_obj)
+               return;
+
+       current_widget = current_obj;
+
+       AtspiValue *value_interface = atspi_accessible_get_value_iface(current_widget);
+       if (value_interface) {
+               DEBUG("Value interface supported!\n");
+               gdouble current_val = atspi_value_get_current_value(value_interface, &err);
+               GERROR_CHECK(err)
+                       DEBUG("Current value: %f\n ", (double)current_val);
+               gdouble minimum_inc = atspi_value_get_minimum_increment(value_interface, &err);
+               DEBUG("Minimum increment: %f\n ", (double)minimum_inc);
+               GERROR_CHECK(err)
+                       atspi_value_set_current_value(value_interface, current_val + minimum_inc, &err);
+               GERROR_CHECK(err)
+                       _read_value(value_interface);
+               g_object_unref(value_interface);
+               return;
+       }
+       ERROR("No value interface supported!\n");
+}
+
+static void _value_dec(void)
+{
+       AtspiAccessible *current_widget = NULL;
+       GError *err = NULL;
+
+       if (!current_obj)
+               return;
+       current_widget = current_obj;
+
+       AtspiValue *value_interface = atspi_accessible_get_value_iface(current_widget);
+       if (value_interface) {
+               DEBUG("Value interface supported!\n");
+               gdouble current_val = atspi_value_get_current_value(value_interface, &err);
+               GERROR_CHECK(err)
+                       DEBUG("Current value: %f\n ", (double)current_val);
+               gdouble minimum_inc = atspi_value_get_minimum_increment(value_interface, &err);
+               GERROR_CHECK(err)
+                       DEBUG("Minimum increment: %f\n ", (double)minimum_inc);
+               atspi_value_set_current_value(value_interface, current_val - minimum_inc, &err);
+               GERROR_CHECK(err)
+                       _read_value(value_interface);
+               g_object_unref(value_interface);
+               return;
+       }
+       ERROR("No value interface supported!\n");
+}
+
+static void _activate_widget(void)
+{
+       //activate the widget
+       //only if activate mean click
+       //special behavior for entry, caret should move from first/last last/first
+       DEBUG("START");
+       AtspiAccessible *current_widget = NULL;
+       AtspiComponent *focus_component = NULL;
+       AtspiAccessible *parent = NULL;
+       AtspiStateSet *ss = NULL;
+       AtspiSelection *selection = NULL;
+       AtspiAction *action;
+       AtspiEditableText *edit = NULL;
+
+       GError *err = NULL;
+       gchar *actionName = NULL;
+       gint number = 0;
+       gint i = 0;
+       gint index = 0;
+       Eina_Bool activate_found = EINA_FALSE;
+       AtspiRole role = ATSPI_ROLE_INVALID;
+
+       if (!current_obj)
+               return;
+
+       if (!_widget_has_state(current_obj, ATSPI_STATE_ENABLED)) {
+               DEBUG("Widget is disabled so cannot be activated");
+               return;
+       }
+
+       current_widget = current_obj;
+
+       role = atspi_accessible_get_role(current_widget, NULL);
+       if (role == ATSPI_ROLE_SLIDER) {
+               return;
+       }
+
+       display_info_about_object(current_widget, false);
+
+       edit = atspi_accessible_get_editable_text_iface(current_widget);
+       if (edit) {
+               DEBUG("Activated object has editable Interface");
+               focus_component = atspi_accessible_get_component_iface(current_widget);
+               if (focus_component) {
+                       if (atspi_component_grab_focus(focus_component, &err) == TRUE) {
+                               GERROR_CHECK(err)
+
+                                       DEBUG("Entry activated\n");
+
+                               char *text_to_speak = NULL;
+                               text_to_speak = generate_what_to_read(current_widget);
+
+                               DEBUG("SPEAK:%s", text_to_speak);
+
+                               tts_speak(text_to_speak, EINA_TRUE);
+                               g_free(text_to_speak);
+                               g_object_unref(focus_component);
+                       }
+               }
+               g_object_unref(edit);
+               return;
+       }
+
+       action = atspi_accessible_get_action_iface(current_widget);
+       if (action) {
+               number = atspi_action_get_n_actions(action, &err);
+               DEBUG("Number of available action = %d\n", number);
+               GERROR_CHECK(err)
+                       activate_found = EINA_FALSE;
+               while (i < number && !activate_found) {
+                       actionName = atspi_action_get_name(action, i, &err);
+                       if (actionName && !strcmp("activate", actionName)) {
+                               DEBUG("There is activate action");
+                               activate_found = EINA_TRUE;
+                       } else {
+                               i++;
+                       }
+                       g_free(actionName);
+               }
+               if (activate_found) {
+                       DEBUG("PERFORMING ATSPI ACTION NO.%d", i);
+                       atspi_action_do_action(action, i, &err);
+               } else if (number > 0) {
+                       DEBUG("PERFORMING ATSPI DEFAULT ACTION");
+                       atspi_action_do_action(action, 0, &err);
+               } else
+                       ERROR("There is no actions inside Action interface");
+               if (action)
+                       g_object_unref(action);
+               GERROR_CHECK(err)
+                       return;
+       }
+
+       ss = atspi_accessible_get_state_set(current_widget);
+       if (atspi_state_set_contains(ss, ATSPI_STATE_SELECTABLE) == EINA_TRUE) {
+               DEBUG("OBJECT IS SELECTABLE");
+               parent = atspi_accessible_get_parent(current_widget, NULL);
+               if (parent) {
+                       index = atspi_accessible_get_index_in_parent(current_widget, NULL);
+                       selection = atspi_accessible_get_selection_iface(parent);
+                       if (selection) {
+                               if(atspi_state_set_contains(ss, ATSPI_STATE_SELECTED)) {
+                                       atspi_selection_deselect_child (selection, index, NULL);
+
+                               } else {
+                                       DEBUG("SELECT CHILD NO:%d\n", index);
+                                       atspi_selection_select_child(selection, index, NULL);
+                               }
+
+                               g_object_unref(selection);
+                               g_object_unref(parent);
+                               g_object_unref(ss);
+                               return;
+                       } else
+                               ERROR("no selection iterface in parent");
+
+                       g_object_unref(parent);
+               }
+       }
+       g_object_unref(ss);
+
+}
+
+static void _quickpanel_change_state(gboolean quickpanel_switch)
+{
+       DEBUG("START");
+       Ecore_X_Window xwin = 0;
+
+       if (quickpanel_switch)
+               DEBUG("QUICKPANEL STATE ON");
+       else
+               DEBUG("QUICKPANEL STATE OFF");
+
+       Ecore_X_Illume_Quickpanel_State state;
+
+       ecore_x_window_prop_xid_get(ecore_x_window_root_first_get(), ECORE_X_ATOM_NET_ACTIVE_WINDOW, ECORE_X_ATOM_WINDOW, &xwin, 1);
+
+       state = quickpanel_switch ? ECORE_X_ILLUME_QUICKPANEL_STATE_ON : ECORE_X_ILLUME_QUICKPANEL_STATE_OFF;
+
+       ecore_x_e_illume_quickpanel_state_set(xwin, state);
+
+       ecore_x_e_illume_quickpanel_state_send(ecore_x_e_illume_zone_get(xwin), state);
+
+       ecore_main_loop_iterate();
+}
+
+/**
+ * @brief Gets 'deepest' Scrollable accessible containing (x,y) point
+ */
+/*
+static AtspiScrollable*
+_find_scrollable_ancestor_at_xy(int x, int y)
+{
+   AtspiAccessible *ret = NULL;
+   AtspiRect *rect;
+   GError *err = NULL;
+
+   if (!top_window || !ATSPI_IS_COMPONENT(top_window))
+     {
+        DEBUG("No active window detected or no AtspiComponent interface available");
+        return NULL;
+     }
+
+   rect = atspi_component_get_extents(ATSPI_COMPONENT(top_window), ATSPI_COORD_TYPE_SCREEN, &err);
+   GERROR_CHECK(err)
+   if (!rect)
+     {
+        ERROR("Unable to fetch window screen coordinates");
+        return NULL;
+     }
+
+   // Scroll must originate within window borders
+   if ((x < rect->x) || (x > rect->x + rect->width) ||
+       (y < rect->y) || (y > rect->y + rect->height))
+     {
+        DEBUG("Scroll don't start within active window borders");
+        g_free(rect);
+        return NULL;
+     }
+
+   ret = atspi_component_get_accessible_at_point(ATSPI_COMPONENT(top_window), x, y, ATSPI_COORD_TYPE_SCREEN, &err);
+   GERROR_CHECK(err)
+   if (!ret)
+     {
+        ERROR("Unable to get accessible objct at (%d, %d) screen coordinates.", x, y);
+        return NULL;
+     }
+gchar *name;
+gchar *role;
+   // find accessible object with Scrollable interface
+   while (ret && (ret != top_window))
+     {
+       name = atspi_accessible_get_name(ret, &err);
+       GERROR_CHECK(err)
+       role = atspi_accessible_get_role_name(ret, &err);
+       GERROR_CHECK(err)
+       DEBUG("Testing for scrollability: %s %s",
+                   name, role);
+        if (atspi_accessible_get_scrollable(ret))
+          {
+             DEBUG("Scrollable widget found at (%d, %d), name: %s, role: %s", x, y,
+                   name ,role);
+             g_free(name);
+             g_free(role);
+             return ATSPI_SCROLLABLE(ret);
+          }
+        g_free(name);
+        g_free(role);
+        ret = atspi_accessible_get_parent(ret, &err);
+        if (err)
+          {
+             ERROR("Unable to fetch AT-SPI parent");
+             GERROR_CHECK(err)
+             return NULL;
+          }
+     }
+
+   return NULL;
+}
+
+static void _widget_scroll_begin(Gesture_Info *gi)
+{
+   GError *err = NULL;
+
+   if (scrolled_obj)
+     {
+        ERROR("Scrolling context active when initializing new scrolling context! This should never happen.");
+        ERROR("Force reset of current scrolling context...");
+        atspi_scrollable_scroll_after_pointer(scrolled_obj, ATSPI_SCROLL_POINTER_END, gi->x_begin, gi->y_begin, &err);
+        if (err)
+          {
+             ERROR("Failed to reset scroll context.");
+             GERROR_CHECK(err)
+             scrolled_obj = NULL;
+          }
+     }
+
+   scrolled_obj = _find_scrollable_ancestor_at_xy(gi->x_begin, gi->y_begin);
+
+   if (!scrolled_obj)
+     {
+        DEBUG("No scrollable widget found at (%d, %d) coordinates", gi->x_begin, gi->y_begin);
+        return;
+     }
+
+   atspi_scrollable_scroll_after_pointer(scrolled_obj, ATSPI_SCROLL_POINTER_START, gi->x_begin, gi->y_begin, &err);
+   if (err)
+     {
+        ERROR("Failed to initialize scroll operation");
+        GERROR_CHECK(err)
+        scrolled_obj = NULL;
+     }
+}
+
+static void _widget_scroll_continue(Gesture_Info *gi)
+{
+  GError *err = NULL;
+   if (!scrolled_obj)
+     {
+        DEBUG("Scrolling context not initialized!");
+        return;
+     }
+   atspi_scrollable_scroll_after_pointer(scrolled_obj, ATSPI_SCROLL_POINTER_CONTINUE, gi->x_begin, gi->y_begin, &err);
+   GERROR_CHECK(err)
+}
+
+static void _widget_scroll_end(Gesture_Info *gi)
+{
+  GError *err = NULL;
+   if (!scrolled_obj)
+     {
+        ERROR("Scrolling context not initialized!");
+        return;
+     }
+
+   atspi_scrollable_scroll_after_pointer(scrolled_obj, ATSPI_SCROLL_POINTER_END, gi->x_begin, gi->y_begin, &err);
+   scrolled_obj = NULL;
+   GERROR_CHECK(err)
+}
+*/
+
+static void _widget_scroll(Gesture_Info * gi)
+{
+       DEBUG("Recognized gesture state: %d", gi->state);
+
+       if (gi->state == 0) {
+               DEBUG("save coordinates %d %d", gesture_start_p.x, gesture_start_p.y);
+               gesture_start_p.x = gi->x_beg;
+               gesture_start_p.y = gi->y_beg;
+       }
+
+       if (gi->state != 2) {
+               DEBUG("Scroll not finished yet");
+               return;
+       }
+
+       AtspiAccessible *obj = NULL;
+       obj = flat_navi_context_current_get(context);
+       if (!obj) {
+               ERROR("No context");
+               return;
+       }
+
+       AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
+       if (!ss) {
+               ERROR("no stetes");
+               return;
+       }
+
+       if (!atspi_state_set_contains(ss, ATSPI_STATE_SHOWING)) {
+               DEBUG("current context do not have visible state, swith to next/prev");
+               if (gesture_start_p.y > gi->y_end || gesture_start_p.x > gi->x_end) {
+                       DEBUG("NEXT");
+                       _focus_next_visible();
+               } else if (gesture_start_p.y < gi->y_end || gesture_start_p.x < gi->x_end) {
+                       DEBUG("PREVIOUS");
+                       _focus_prev_visible();
+               }
+       }
+       DEBUG("end");
+       g_object_unref(ss);
+       g_object_unref(obj);
+}
+
+static void _read_quickpanel(void)
+{
+       DEBUG("START");
+
+       device_time_get();
+       device_battery_get();
+       device_bluetooth_get();
+       device_signal_strenght_get();
+
+       device_date_get();
+       device_missed_events_get();
+       DEBUG("END");
+}
+
+static void _set_pause(void)
+{
+       DEBUG("START");
+
+       Eina_Bool res = EINA_FALSE;
+       bool pause = tts_pause_get();
+       res = tts_pause_set(!pause);
+       if (!res) {
+               ERROR("Failed to set pause state");
+       }
+
+       DEBUG("END");
+}
+
+void auto_review_highlight_set(void)
+{
+       AtspiAccessible *obj = flat_navi_context_next(context);
+
+       DEBUG("START");
+
+       if (!obj) {
+               DEBUG("obj == NULL");
+               s_auto_review.auto_review_on = false;
+               return;
+       } else if (obj == flat_navi_context_last_get(context)) {
+               DEBUG("obj == flat_navi_context_last_get()");
+               s_auto_review.auto_review_on = false;
+       }
+
+       _current_highlight_object_set(obj);
+
+       DEBUG("END");
+}
+
+void auto_review_highlight_top(void)
+{
+       DEBUG("START");
+       char *text_to_speak = NULL;
+       AtspiAccessible *obj = flat_navi_context_current_get(context);
+       AtspiAccessible *first = flat_navi_context_first(context);
+
+       if (first != obj) {
+               _current_highlight_object_set(first);
+       } else {
+               text_to_speak = generate_what_to_read(obj);
+               DEBUG("Text to speak: %s", text_to_speak);
+               tts_speak(text_to_speak, EINA_TRUE);
+               free(text_to_speak);
+       }
+
+       DEBUG("END");
+}
+
+static void _on_auto_review_stop(void)
+{
+       DEBUG("START");
+       s_auto_review.auto_review_on = false;
+       DEBUG("END");
+}
+
+static void _on_utterance(void)
+{
+       DEBUG("START");
+       DEBUG("s_auto_review.auto_review_on == %d", s_auto_review.auto_review_on);
+
+       if (s_auto_review.auto_review_on) {
+               auto_review_highlight_set();
+       }
+       DEBUG("END");
+}
+
+static void _review_from_current(void)
+{
+       DEBUG("START");
+
+       s_auto_review.focused_object = flat_navi_context_current_get(context);
+       s_auto_review.auto_review_on = true;
+       auto_review_highlight_set();
+
+       DEBUG("END");
+}
+
+static void _review_from_top()
+{
+       DEBUG("START");
+
+       s_auto_review.focused_object = flat_navi_context_current_get(context);
+       s_auto_review.auto_review_on = true;
+       auto_review_highlight_top();
+
+       DEBUG("END");
+}
+
+static void _direct_scroll_back(void)
+{
+       DEBUG("ONE_FINGER_FLICK_LEFT_RETURN");
+       if (!context) {
+               ERROR("No navigation context created");
+               return;
+       }
+
+       AtspiAccessible *obj = NULL;
+       AtspiAccessible *current = NULL;
+       AtspiAccessible *parent = NULL;
+       AtspiRole role;
+
+       current = flat_navi_context_current_get(context);
+       parent = atspi_accessible_get_parent(current, NULL);
+       role = atspi_accessible_get_role(parent, NULL);
+
+       if (role != ATSPI_ROLE_LIST) {
+               DEBUG("That operation can be done only on list, it is:%s", atspi_accessible_get_role_name(parent, NULL));
+               g_object_unref(parent);
+               g_object_unref(current);
+               return;
+       }
+
+       int index = atspi_accessible_get_index_in_parent(current, NULL);
+       int children_count = atspi_accessible_get_child_count(parent, NULL);
+
+       if (children_count <= 0) {
+               ERROR("NO visible element on list");
+               g_object_unref(parent);
+               g_object_unref(current);
+               return;
+       }
+
+       DEBUG("start from element with index:%d/%d", index, children_count);
+
+       if (index <= 0) {
+               DEBUG("first element");
+               obj = atspi_accessible_get_child_at_index(parent, 0, NULL);
+               smart_notification(FOCUS_CHAIN_END_NOTIFICATION_EVENT, 0, 0);
+       }
+
+       else {
+               DEBUG("go back to %d element", index);
+               obj = atspi_accessible_get_child_at_index(parent, index, NULL);
+       }
+
+       if (obj) {
+               DEBUG("Will set highlight and context");
+               if (flat_navi_context_current_set(context, obj)) {
+                       DEBUG("current obj set");
+               }
+               _current_highlight_object_set(obj);
+       }
+       g_object_unref(parent);
+       g_object_unref(current);
+}
+
+static void _direct_scroll_forward(void)
+{
+       DEBUG("ONE_FINGER_FLICK_RIGHT_RETURN");
+
+       if (!context) {
+               ERROR("No navigation context created");
+               return;
+       }
+
+       AtspiAccessible *obj = NULL;
+       AtspiAccessible *current = NULL;
+       AtspiAccessible *parent = NULL;
+       AtspiRole role;
+
+       current = flat_navi_context_current_get(context);
+       parent = atspi_accessible_get_parent(current, NULL);
+       role = atspi_accessible_get_role(parent, NULL);
+
+       if (role != ATSPI_ROLE_LIST) {
+               DEBUG("That operation can be done only on list, it is:%s", atspi_accessible_get_role_name(parent, NULL));
+               g_object_unref(parent);
+               g_object_unref(current);
+               return;
+       }
+
+       int index = atspi_accessible_get_index_in_parent(current, NULL);
+       int children_count = atspi_accessible_get_child_count(parent, NULL);
+
+       if (children_count <= 0) {
+               ERROR("NO visible element on list");
+               g_object_unref(parent);
+               g_object_unref(current);
+               return;
+       }
+
+       DEBUG("start from element with index:%d/%d", index, children_count);
+
+       if (index >= children_count) {
+               DEBUG("last element");
+               obj = atspi_accessible_get_child_at_index(parent, children_count - 1, NULL);
+               smart_notification(FOCUS_CHAIN_END_NOTIFICATION_EVENT, 0, 0);
+       }
+
+       else {
+               DEBUG("go back to %d element", index);
+               obj = atspi_accessible_get_child_at_index(parent, index, NULL);
+       }
+
+       if (obj) {
+               DEBUG("Will set highlight and context");
+               if (flat_navi_context_current_set(context, obj)) {
+                       DEBUG("current obj set");
+               }
+               _current_highlight_object_set(obj);
+       }
+       g_object_unref(parent);
+       g_object_unref(current);
+}
+
+static void _direct_scroll_to_first(void)
+{
+       DEBUG("ONE_FINGER_FLICK_UP_RETURN");
+       if (!context) {
+               ERROR("No navigation context created");
+               return;
+       }
+       AtspiAccessible *obj = flat_navi_context_first(context);
+       if (obj)
+               _current_highlight_object_set(obj);
+       else
+               DEBUG("First widget not found. Abort");
+       DEBUG("END");
+}
+
+static void _direct_scroll_to_last(void)
+{
+       DEBUG("ONE_FINGER_FLICK_DOWN_RETURN");
+       if (!context) {
+               ERROR("No navigation context created");
+               return;
+       }
+       AtspiAccessible *obj = flat_navi_context_last(context);
+       if (obj)
+               _current_highlight_object_set(obj);
+       else
+               DEBUG("Last widget not found. Abort");
+       DEBUG("END");
+}
+
+static Eina_Bool _has_value(void)
+{
+       DEBUG("START");
+       AtspiAccessible *obj = NULL;
+
+       if (!current_obj)
+               return EINA_FALSE;
+
+       obj = current_obj;
+
+       if (!obj)
+               return EINA_FALSE;
+
+       AtspiValue *value = atspi_accessible_get_value_iface(obj);
+
+       if (value) {
+               g_object_unref(value);
+               return EINA_TRUE;
+       }
+
+       return EINA_FALSE;
+}
+
+static Eina_Bool _is_enabled(void)
+{
+       if (!current_obj) {
+               return EINA_FALSE;
+       }
+
+       return _widget_has_state(current_obj, ATSPI_STATE_ENABLED);
+}
+
+static Eina_Bool _is_active_entry(void)
+{
+       DEBUG("START");
+
+       if (!context) {
+               ERROR("No navigation context created");
+               return EINA_FALSE;
+       }
+       AtspiAccessible *obj = NULL;
+       AtspiRole role;
+       obj = flat_navi_context_current_get(context);
+
+       if (!obj)
+               return EINA_FALSE;
+
+       role = atspi_accessible_get_role(obj, NULL);
+       if (role == ATSPI_ROLE_ENTRY) {
+               AtspiStateSet *state_set = atspi_accessible_get_state_set(obj);
+               if (atspi_state_set_contains(state_set, ATSPI_STATE_FOCUSED)) {
+                       g_object_unref(state_set);
+                       return EINA_TRUE;
+               }
+               g_object_unref(state_set);
+               return EINA_FALSE;
+       }
+
+       DEBUG("END");
+       return EINA_FALSE;
+}
+
+static Eina_Bool _is_slider(AtspiAccessible * obj)
+{
+       DEBUG("START");
+
+       if (!obj)
+               return EINA_FALSE;
+
+       AtspiRole role;
+
+       role = atspi_accessible_get_role(obj, NULL);
+       if (role == ATSPI_ROLE_SLIDER) {
+               return EINA_TRUE;
+       }
+       return EINA_FALSE;
+}
+
+static void _move_slider(Gesture_Info * gi)
+{
+       DEBUG("ONE FINGER DOUBLE TAP AND HOLD");
+
+       if (!context) {
+               ERROR("No navigation context created");
+               return;
+       }
+
+       AtspiAccessible *obj = NULL;
+       AtspiValue *value = NULL;
+       AtspiComponent *comp = NULL;
+       AtspiRect *rect = NULL;
+       int click_point_x = 0;
+       int click_point_y = 0;
+
+       obj = current_obj;
+
+       if (!obj) {
+               DEBUG("no object");
+               prepared = false;
+               return;
+       }
+
+       if (!_is_slider(obj)) {
+               DEBUG("Object is not a slider");
+               prepared = false;
+               return;
+       }
+
+       if (!_widget_has_state(obj, ATSPI_STATE_ENABLED)) {
+               DEBUG("Slider is disabled");
+               prepared = false;
+               return;
+       }
+
+       if (gi->state == 0) {
+               comp = atspi_accessible_get_component_iface(obj);
+               if (!comp) {
+                       ERROR("that slider do not have component interface");
+                       prepared = false;
+                       return;
+               }
+
+               rect = atspi_component_get_extents(comp, ATSPI_COORD_TYPE_SCREEN, NULL);
+
+               DEBUG("Current object is in:%d %d", rect->x, rect->y);
+               DEBUG("Current object has size:%d %d", rect->width, rect->height);
+
+               click_point_x = rect->x + rect->width / 2;
+               click_point_y = rect->y + rect->height / 2;
+               DEBUG("Click on point %d %d", click_point_x, click_point_y);
+               start_scroll(click_point_x, click_point_y);
+       }
+
+       if (gi->state == 1) {
+               counter++;
+               DEBUG("SCROLLING but not meet counter:%d", counter);
+                       if (counter >= GESTURE_LIMIT) {
+                       counter = 0;
+                       DEBUG("Scroll on point %d %d", gi->x_end, gi->y_end);
+                       continue_scroll(gi->x_end, gi->y_end);
+               }
+       }
+
+       if (gi->state == 2) {
+               DEBUG("state == 2");
+               end_scroll(gi->x_end, gi->y_end);
+               prepared = false;
+               value = atspi_accessible_get_value_iface(obj);
+               if (value) {
+                       _read_value(value);
+                       g_object_unref(value);
+               } else {
+                       ERROR("There is not value interface in slider");
+               }
+       }
+       DEBUG("END");
+}
+
+AtspiAction *_get_main_window(void)
+{
+       AtspiAccessible *win = flat_navi_context_root_get(context);
+       if (!win) {
+               ERROR("win == NULL");
+               return NULL;
+       }
+
+       AtspiAction *action = atspi_accessible_get_action_iface(win);
+       if (!action) {
+               ERROR("action == NULL");
+               return NULL;
+       }
+
+       return action;
+}
+
+static int _find_action_index(AtspiAction * action, char *action_name_to_find)
+{
+       int action_num = atspi_action_get_n_actions(action, NULL);
+       char *action_name = NULL;
+
+       int i = 0;
+       for (i = 0; i < action_num; ++i) {
+               action_name = atspi_action_get_action_name(action, i, NULL);
+
+               if (!strcmp(action_name_to_find, action_name)) {
+                       return i;
+               }
+       }
+
+       return -i;
+}
+
+static void _start_stop_signal_send(void)
+{
+       int action_index = -1;
+       char *action_name = "pause_play";
+       AtspiAction *action = _get_main_window();
+       if (!action) {
+               ERROR("Could not get the action inteface");
+       }
+
+       if (!action) {
+               ERROR("action == NULL");
+               return;
+       }
+
+       action_index = _find_action_index(action, action_name);
+       if (action_index < 0) {
+               ERROR("Pause_play action not found");
+               return;
+       }
+
+       DEBUG("ACTION: %s has index: %d", action_name, action_index);
+       atspi_action_do_action(action, action_index, NULL);
+}
+
+static void on_gesture_detected(void *data, Gesture_Info * info)
+{
+       Ecore_X_Window keyboard_win;
+       _on_auto_review_stop();
+
+       if (info->type == ONE_FINGER_SINGLE_TAP && info->state == 3) {
+               DEBUG("One finger single tap aborted");
+               prepared = true;
+       }
+
+       switch (info->type) {
+       case ONE_FINGER_HOVER:
+               if (prepared) {
+                       DEBUG("Prepare to move slider");
+                       _move_slider(info);
+               } else {
+                       if (_last_hover_event_time < 0)
+                               _last_hover_event_time = info->event_time;
+                       //info->event_time and _last_hover_event_time contain timestamp in ms.
+                       //RETURN so we do not handle all incoming event
+                       if ((info->event_time - _last_hover_event_time) < ONGOING_HOVER_GESTURE_INTERPRETATION_INTERVAL && info->state == 1)
+                               return;
+                       _last_hover_event_time = info->state != 1 ? -1 : info->event_time;
+#ifdef ELM_ACCESS_KEYBOARD
+                       keyboard_win = top_window_get(info->x_end, info->y_end);
+                       if (keyboard_win && ecore_x_e_virtual_keyboard_get(keyboard_win)) {
+                               elm_access_adaptor_emit_read(keyboard_win, info->x_end, info->y_end);
+                               break;
+                       }
+#endif
+                       _focus_widget(info);
+               }
+               break;
+       case TWO_FINGERS_HOVER:
+               _widget_scroll(info);
+               break;
+       case ONE_FINGER_FLICK_LEFT:
+               _focus_prev();
+               break;
+       case ONE_FINGER_FLICK_RIGHT:
+               _focus_next();
+               break;
+       case ONE_FINGER_FLICK_UP:
+               if (_is_active_entry())
+                       _caret_move_backward();
+               else if (_has_value() && _is_enabled())
+                       _value_inc();
+               else
+                       _focus_prev();
+               break;
+       case ONE_FINGER_FLICK_DOWN:
+               if (_is_active_entry())
+                       _caret_move_forward();
+               else if (_has_value() && _is_enabled())
+                       _value_dec();
+               else
+                       _focus_next();
+               break;
+       case ONE_FINGER_SINGLE_TAP:
+#ifdef ELM_ACCESS_KEYBOARD
+               keyboard_win = top_window_get(info->x_end, info->y_end);
+               if (keyboard_win && ecore_x_e_virtual_keyboard_get(keyboard_win)) {
+                       elm_access_adaptor_emit_read(keyboard_win, info->x_end, info->y_end);
+                       break;
+               }
+#endif
+               if (!prepared)
+                       _focus_widget(info);
+               break;
+       case ONE_FINGER_DOUBLE_TAP:
+#ifdef ELM_ACCESS_KEYBOARD
+               keyboard_win = top_window_get(info->x_end, info->y_end);
+               if (keyboard_win && ecore_x_e_virtual_keyboard_get(keyboard_win)) {
+                       elm_access_adaptor_emit_activate(keyboard_win, info->x_end, info->y_end);
+                       break;
+               }
+#endif
+               _activate_widget();
+               break;
+       case TWO_FINGERS_SINGLE_TAP:
+               _set_pause();
+               break;
+       case TWO_FINGERS_DOUBLE_TAP:
+               _start_stop_signal_send();
+               break;
+       case TWO_FINGERS_TRIPLE_TAP:
+#ifndef SCREEN_READER_TV
+               _read_quickpanel();
+#endif
+               break;
+       case THREE_FINGERS_SINGLE_TAP:
+               _review_from_top();
+               break;
+       case THREE_FINGERS_DOUBLE_TAP:
+               _review_from_current();
+               break;
+       case THREE_FINGERS_FLICK_DOWN:
+               _quickpanel_change_state(QUICKPANEL_DOWN);
+               break;
+       case THREE_FINGERS_FLICK_UP:
+               _quickpanel_change_state(QUICKPANEL_UP);
+               break;
+       case ONE_FINGER_FLICK_LEFT_RETURN:
+               _direct_scroll_back();
+               break;
+       case ONE_FINGER_FLICK_RIGHT_RETURN:
+               _direct_scroll_forward();
+               break;
+       case ONE_FINGER_FLICK_UP_RETURN:
+               if (_is_active_entry())
+                       _caret_move_beg();
+               else
+                       _direct_scroll_to_first();
+               break;
+       case ONE_FINGER_FLICK_DOWN_RETURN:
+               if (_is_active_entry())
+                       _caret_move_end();
+               else
+                       _direct_scroll_to_last();
+               break;
+       default:
+               DEBUG("Gesture type %d not handled in switch", info->type);
+       }
+
+       dbus_gesture_adapter_emit(info);
+}
+
+static void _view_content_changed(AtspiAccessible * root, void *user_data)
+{
+       if (flat_navi_is_valid(context, root))
+               return;
+       if (!_widget_has_state(root, ATSPI_STATE_SHOWING))
+               return;
+       flat_navi_context_free(context);
+       context = flat_navi_context_create(root);
+       _current_highlight_object_set(flat_navi_context_current_get(context));
+}
+
+static void _new_highlighted_obj_changed(AtspiAccessible * new_highlighted_obj, void *user_data)
+{
+       DEBUG("context: %p, current: %p, new_highlighted_obj: %p", context, flat_navi_context_current_get(context), new_highlighted_obj);
+       if (context && flat_navi_context_current_get(context) != new_highlighted_obj) {
+               flat_navi_context_current_set(context, g_object_ref(new_highlighted_obj));
+       }
+}
+
+void clear(gpointer d)
+{
+       AtspiAccessible **data = d;
+       AtspiAccessible *obj = *data;
+       g_object_unref(obj);
+}
+
+static AtspiAccessible *_get_modal_descendant(AtspiAccessible * root)
+{
+       GError *err = NULL;
+       AtspiStateSet *states = atspi_state_set_new(NULL);
+       atspi_state_set_add(states, ATSPI_STATE_MODAL);
+       atspi_state_set_add(states, ATSPI_STATE_SHOWING);
+       atspi_state_set_add(states, ATSPI_STATE_VISIBLE);
+       DEBUG("GET MODAL: STATE SET PREPARED");
+       AtspiMatchRule *rule = atspi_match_rule_new(states,
+                                                                                               ATSPI_Collection_MATCH_ALL,
+                                                                                               NULL,
+                                                                                               ATSPI_Collection_MATCH_INVALID,
+                                                                                               NULL,
+                                                                                               ATSPI_Collection_MATCH_INVALID,
+                                                                                               NULL,
+                                                                                               ATSPI_Collection_MATCH_INVALID,
+                                                                                               FALSE);
+       DEBUG("GET MODAL: MATCHING RULE PREPARED");
+       AtspiAccessible *ret = NULL;
+       AtspiCollection *col_iface = atspi_accessible_get_collection_iface(root);
+       GArray *result = atspi_collection_get_matches(col_iface,
+                                                                                                 rule,
+                                                                                                 ATSPI_Collection_SORT_ORDER_CANONICAL,
+                                                                                                 1,
+                                                                                                 TRUE,
+                                                                                                 &err);
+       GERROR_CHECK(err);
+       DEBUG("GET MODAL: QUERY PERFORMED");
+       g_object_unref(states);
+       g_object_unref(rule);
+       g_object_unref(col_iface);
+       if (result && result->len > 0) {
+               DEBUG("GET MODAL: MODAL FOUND");
+               g_array_set_clear_func(result, clear);
+               ret = g_object_ref(g_array_index(result, AtspiAccessible *, 0));
+               g_array_free(result, TRUE);
+       }
+       return ret;
+}
+
+static void on_window_activate(void *data, AtspiAccessible * window)
+{
+       DEBUG("START");
+
+       if (top_window)
+               app_tracker_callback_unregister(top_window, _view_content_changed, NULL);
+
+       if (window) {
+               DEBUG("Window name: %s", atspi_accessible_get_name(window, NULL));
+               // TODO: modal descendant of window should be used (if exists) otherwise window
+               AtspiAccessible *modal_descendant = _get_modal_descendant(window);
+               app_tracker_callback_register(modal_descendant ? modal_descendant : window, _view_content_changed, NULL);
+               _view_content_changed(modal_descendant ? modal_descendant : window, NULL);
+               g_object_unref(modal_descendant);
+       } else {
+               flat_navi_context_free(context);
+               ERROR("No top window found!");
+       }
+       top_window = window;
+       DEBUG("END");
+}
+
+void kb_tracker(void *data, Key k)
+{
+       switch (k) {
+       case KEY_LEFT:
+               _focus_prev();
+               break;
+       case KEY_RIGHT:
+               _focus_next();
+               break;
+       default:
+               DEBUG("Key %d not supported \n", k);
+       }
+}
+
+void navigator_init(void)
+{
+       DEBUG("START");
+
+       set_utterance_cb(_on_utterance);
+
+       screen_reader_gestures_tracker_register(on_gesture_detected, NULL);
+       // register on active_window
+       dbus_gesture_adapter_init();
+       app_tracker_init();
+       app_tracker_new_obj_highlighted_callback_register(_new_highlighted_obj_changed);
+       window_tracker_init();
+       window_tracker_register(on_window_activate, NULL);
+       window_tracker_active_window_request();
+       smart_notification_init();
+#ifndef SCREEN_READER_TV
+       system_notifications_init();
+#endif
+       keyboard_tracker_init();
+       keyboard_tracker_register(kb_tracker, NULL);
+}
+
+void navigator_shutdown(void)
+{
+       GError *err = NULL;
+       if (current_obj) {
+               AtspiComponent *comp = atspi_accessible_get_component_iface(current_obj);
+               if (comp) {
+                       atspi_component_clear_highlight(comp, &err);
+                       GERROR_CHECK(err);
+               }
+       }
+       if (context) {
+               flat_navi_context_free(context);
+               context = NULL;
+       }
+       dbus_gesture_adapter_shutdown();
+       app_tracker_shutdown();
+       window_tracker_shutdown();
+       smart_notification_shutdown();
+#ifndef SCREEN_READER_TV
+       system_notifications_shutdown();
+#endif
+       keyboard_tracker_shutdown();
+}
diff --git a/src/pivot_chooser.c b/src/pivot_chooser.c
new file mode 100644 (file)
index 0000000..481663b
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 <atspi/atspi.h>
+#include "logger.h"
+#include <Eina.h>
+
+/**
+ * @brief Finds first leaf in object hierarchy with given states,
+ * starting from object given as parent.
+ *
+ * This heuristic assumes that focused element have focused
+ * parent widgets.
+ */
+static AtspiAccessible *_pivot_with_state_top_down_find(AtspiAccessible * parent, AtspiStateType type)
+{
+       AtspiAccessible *ret = NULL;
+       AtspiStateSet *states;
+       int i;
+
+       states = atspi_accessible_get_state_set(parent);
+       if (!states || atspi_state_set_contains(states, type)) {
+               int n = atspi_accessible_get_child_count(parent, NULL);
+               if (n == 0)
+                       ret = parent;
+               for (i = 0; i < n; i++) {
+                       AtspiAccessible *child = atspi_accessible_get_child_at_index(parent, i, NULL);
+                       if (!child)
+                               continue;
+
+                       ret = _pivot_with_state_top_down_find(child, type);
+
+                       g_object_unref(child);
+
+                       if (ret)
+                               break;
+               }
+       }
+
+       g_object_unref(states);
+
+       return ret;
+}
+
+/**
+ * @brief Finds first leaf descendant of given object with state @p type
+ */
+static AtspiAccessible *_pivot_with_state_flat_find(AtspiAccessible * parent, AtspiStateType type)
+{
+       Eina_List *candidates = NULL, *queue = NULL;
+
+       // ref object to keep same ref count
+       g_object_ref(parent);
+       queue = eina_list_append(queue, parent);
+
+       while (queue) {
+               AtspiAccessible *obj = eina_list_data_get(queue);
+               queue = eina_list_remove_list(queue, queue);
+
+               int n = atspi_accessible_get_child_count(obj, NULL);
+               if (n == 0)
+                       candidates = eina_list_append(candidates, obj);
+               else {
+                       int i;
+                       for (i = 0; i < n; i++) {
+                               AtspiAccessible *child = atspi_accessible_get_child_at_index(obj, i, NULL);
+                               if (child)
+                                       queue = eina_list_append(queue, child);
+                       }
+                       g_object_unref(obj);
+               }
+       }
+
+       // FIXME sort by (x,y) first ??
+       while (candidates) {
+               AtspiAccessible *obj = eina_list_data_get(candidates);
+               candidates = eina_list_remove_list(candidates, candidates);
+
+               AtspiStateSet *states = atspi_accessible_get_state_set(obj);
+               if (states && atspi_state_set_contains(states, type)) {
+                       g_object_unref(states);
+                       g_object_unref(obj);
+                       eina_list_free(candidates);
+
+                       return obj;
+               }
+
+               g_object_unref(states);
+               g_object_unref(obj);
+       }
+
+       return NULL;
+}
+
+/**
+ * @brief Purpose of this methods is to find first visible object in
+ * hierarchy
+ */
+AtspiAccessible *pivot_chooser_pivot_get(AtspiAccessible * win)
+{
+       AtspiAccessible *ret;
+
+       if (atspi_accessible_get_role(win, NULL) != ATSPI_ROLE_WINDOW) {
+               ERROR("Pivot search entry point must be a Window!");
+               return NULL;
+       }
+
+       DEBUG("Finding SHOWING widget using top-down method.");
+       ret = _pivot_with_state_top_down_find(win, ATSPI_STATE_SHOWING);
+       if (ret)
+               return ret;
+
+       DEBUG("Finding SHOWING widget using top-down method.");
+       ret = _pivot_with_state_flat_find(win, ATSPI_STATE_SHOWING);
+       if (ret)
+               return ret;
+
+       DEBUG("Finding FOCUSED widget using top-down method.");
+       ret = _pivot_with_state_top_down_find(win, ATSPI_STATE_FOCUSED);
+       if (ret)
+               return ret;
+
+       DEBUG("Finding FOCUSED widget using flat search method.");
+       ret = _pivot_with_state_flat_find(win, ATSPI_STATE_FOCUSED);
+       if (ret)
+               return ret;
+
+       return NULL;
+}
diff --git a/src/screen_reader.c b/src/screen_reader.c
new file mode 100644 (file)
index 0000000..e465e1b
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 "screen_reader.h"
+#include "screen_reader_tts.h"
+#include "screen_reader_vconf.h"
+#include "screen_reader_spi.h"
+#include <vconf.h>
+#include "logger.h"
+
+#ifdef RUN_IPC_TEST_SUIT
+#include "test_suite/test_suite.h"
+#endif
+
+#define BUF_SIZE 1024
+
+Service_Data service_data = {
+       //Set by vconf
+       .run_service = 1,
+#ifdef SCREEN_READER_TV
+       .tracking_signal_name = FOCUS_CHANGED_SIG,
+#else
+       .tracking_signal_name = HIGHLIGHT_CHANGED_SIG,
+#endif
+
+       //Set by tts
+       .tts = NULL,
+       .available_languages = NULL,
+
+       //Actions to do when tts state is 'ready'
+       .update_language_list = false,
+
+       .text_to_say_info = NULL
+};
+
+Service_Data *get_pointer_to_service_data_struct()
+{
+       return &service_data;
+}
+
+int screen_reader_create_service(void *data)
+{
+       Service_Data *service_data = data;
+
+       vconf_init(service_data);
+       tts_init(service_data);
+
+#ifdef SCREEN_READER_TV
+       spi_init(service_data);
+#endif
+
+       /* XML TEST */
+#ifdef RUN_IPC_TEST_SUIT
+       run_xml_tests();
+       test_suite_init();
+#endif
+
+       return 0;
+}
+
+int screen_reader_terminate_service(void *data)
+{
+       DEBUG("Service Terminate Callback \n");
+
+       Service_Data *service_data = data;
+
+       tts_stop(service_data->tts);
+       tts_unprepare(service_data->tts);
+       tts_destroy(service_data->tts);
+       service_data->text_from_dbus = NULL;
+       service_data->current_value = NULL;
+
+       return 0;
+}
diff --git a/src/screen_reader_gestures.c b/src/screen_reader_gestures.c
new file mode 100644 (file)
index 0000000..c55d606
--- /dev/null
@@ -0,0 +1,1126 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 "screen_reader_gestures.h"
+#include "logger.h"
+
+#include <Ecore.h>
+#include <Ecore_Input.h>
+#include <Ecore_X.h>
+
+static GestureCB _global_cb;
+static void *_global_data;
+static Ecore_Window win;
+static Ecore_Event_Handler *property_changed_hld;
+
+struct _Gestures_Config {
+       // minimal required length of flick gesture (in pixels)
+       int one_finger_flick_min_length;
+       // maximal time of gesture
+       int one_finger_flick_max_time;
+       // timeout period to activate hover gesture (first longpress timeout)
+       double one_finger_hover_longpress_timeout;
+       // to activate flick gesture by 2 fingers (it is hotfix - gestures need serious refactoring)
+       int two_finger_flick_to_scroll_timeout;
+       // after mowing this pixels flick two finger flick to scroll gesture is started
+       int two_finger_flick_to_scroll_min_length;
+       // tap timeout - maximal ammount of time allowed between seqiential taps
+       double one_finger_tap_timeout;
+       // tap radius(in pixels)
+       int one_finger_tap_radius;
+};
+typedef struct _Gestures_Config Gestures_Config;
+
+typedef enum {
+       FLICK_DIRECTION_UNDEFINED,
+       FLICK_DIRECTION_DOWN,
+       FLICK_DIRECTION_UP,
+       FLICK_DIRECTION_LEFT,
+       FLICK_DIRECTION_RIGHT,
+       FLICK_DIRECTION_DOWN_RETURN,
+       FLICK_DIRECTION_UP_RETURN,
+       FLICK_DIRECTION_LEFT_RETURN,
+       FLICK_DIRECTION_RIGHT_RETURN,
+} flick_direction_e;
+
+typedef enum {
+       GESTURE_NOT_STARTED = 0,        // Gesture is ready to start
+       GESTURE_ONGOING,                        // Gesture in progress.
+       GESTURE_FINISHED,                       // Gesture finished - should be emited
+       GESTURE_ABORTED                         // Gesture aborted
+} gesture_state_e;
+
+typedef enum {
+       ONE_FINGER_GESTURE = 1,
+       TWO_FINGERS_GESTURE,
+       THREE_FINGERS_GESTURE
+} gesture_type_e;
+
+struct _Cover {
+       Ecore_X_Window win;      /**< Input window covering given zone */
+       unsigned int n_taps;   /**< Number of fingers touching screen */
+       unsigned int event_time;
+
+       struct {
+               gesture_state_e state;  // current state of gesture
+               unsigned int timestamp[3];      // time of gesture;
+               int finger[3];                  // finger number which initiates gesture
+               int x_org[3], y_org[3]; // coorinates of finger down event
+               int x_end[3], y_end[3]; // coorinates of finger up event
+               flick_direction_e dir;  // direction of flick
+               int n_fingers;                  // number of fingers in gesture
+               int n_fingers_left;             // number of fingers in gesture
+               //         still touching screen
+               Eina_Bool finger_out[3];        // finger is out of the finger boundary
+               Eina_Bool return_flick[3];
+               Eina_Bool flick_to_scroll;
+               int flick_to_scroll_last_x;
+               int flick_to_scroll_last_y;
+       } flick_gesture;
+
+       struct {
+               gesture_state_e state;  // currest gesture state
+               int x[2], y[2];
+               int n_fingers;
+               int finger[2];
+               unsigned int timestamp; // time of gesture;
+               unsigned int last_emission_time;        // last time of gesture emission
+               Ecore_Timer *timer;
+               Eina_Bool longpressed;
+       } hover_gesture;
+
+       struct {
+               Eina_Bool started;              // indicates if taps recognition process has started
+               Eina_Bool pressed;              // indicates if finger is down
+               int n_taps;                             // number of taps captures in sequence
+               int finger[3];                  // device id of finget
+               Ecore_Timer *timer;             // sequence expiration timer
+               int x_org[3], y_org[3]; // coordinates of first tap
+               gesture_type_e tap_type;
+       } tap_gesture_data;
+};
+typedef struct _Cover Cover;
+
+Gestures_Config *_e_mod_config;
+static Ecore_X_Window scrolled_win;
+static int rx, ry;
+static Eina_List *handlers;
+static Cover *cov;
+static int win_angle;
+
+static void _hover_event_emit(Cover * cov, int state);
+static unsigned int _win_angle_get(void);
+
+void __transform_coordinates(int *ax, int *ay)
+{
+       Ecore_X_Window root;
+       int w;
+       int h;
+       int tmp;
+
+       win_angle = _win_angle_get();
+
+       switch (win_angle) {
+       case 90:
+               root = ecore_x_window_root_first_get();
+               ecore_x_window_geometry_get(root, NULL, NULL, &w, &h);
+               tmp = *ax;
+               *ax = h - *ay;
+               *ay = tmp;
+               break;
+       case 270:
+               root = ecore_x_window_root_first_get();
+               ecore_x_window_geometry_get(root, NULL, NULL, &w, &h);
+               tmp = *ax;
+               *ax = *ay;
+               *ay = w - tmp;
+               break;
+       }
+}
+
+static void _event_emit(Gesture g, int x, int y, int x_e, int y_e, int state, int event_time)
+{
+       Gesture_Info *info = calloc(sizeof(Gesture_Info), 1);
+       EINA_SAFETY_ON_NULL_RETURN(info);
+
+       __transform_coordinates(&x, &y);
+       __transform_coordinates(&x_e, &y_e);
+
+       info->type = g;
+       info->x_beg = x;
+       info->x_end = x_e;
+       info->y_beg = y;
+       info->y_end = y_e;
+       info->state = state;
+       info->event_time = event_time;
+
+       if (_global_cb)
+               _global_cb(_global_data, info);
+       free(info);
+}
+
+static void _flick_gesture_mouse_down(Ecore_Event_Mouse_Button * ev, Cover * cov)
+{
+       if (cov->flick_gesture.state == GESTURE_NOT_STARTED) {
+               cov->flick_gesture.state = GESTURE_ONGOING;
+               cov->flick_gesture.finger[0] = ev->multi.device;
+               cov->flick_gesture.x_org[0] = ev->root.x;
+               cov->flick_gesture.y_org[0] = ev->root.y;
+               cov->flick_gesture.timestamp[0] = ev->timestamp;
+               cov->flick_gesture.flick_to_scroll = EINA_FALSE;
+               cov->flick_gesture.n_fingers = 1;
+               cov->flick_gesture.n_fingers_left = 1;
+               cov->flick_gesture.dir = FLICK_DIRECTION_UNDEFINED;
+               cov->flick_gesture.finger_out[0] = EINA_FALSE;
+               cov->flick_gesture.return_flick[0] = EINA_FALSE;
+       } else if (cov->flick_gesture.state == GESTURE_ONGOING) {
+               // abort gesture if too many fingers touched screen
+               if ((cov->n_taps > 3) || (cov->flick_gesture.n_fingers > 2)) {
+                       cov->flick_gesture.state = GESTURE_ABORTED;
+                       return;
+               }
+
+               cov->flick_gesture.x_org[cov->flick_gesture.n_fingers] = ev->root.x;
+               cov->flick_gesture.y_org[cov->flick_gesture.n_fingers] = ev->root.y;
+               cov->flick_gesture.timestamp[cov->flick_gesture.n_fingers] = ev->timestamp;
+               cov->flick_gesture.finger[cov->flick_gesture.n_fingers] = ev->multi.device;
+               cov->flick_gesture.n_fingers++;
+               cov->flick_gesture.n_fingers_left++;
+               if (cov->flick_gesture.n_fingers < 3) { /* n_fingers == 3 makes out of bounds write */
+                       cov->flick_gesture.finger_out[cov->flick_gesture.n_fingers] = EINA_FALSE;
+                       cov->flick_gesture.return_flick[cov->flick_gesture.n_fingers] = EINA_FALSE;
+               }
+       }
+}
+
+static Eina_Bool _flick_gesture_time_check(unsigned int event_time, unsigned int gesture_time)
+{
+       DEBUG("Flick time: %d", event_time - gesture_time);
+       if ((event_time - gesture_time) < _e_mod_config->one_finger_flick_max_time * 2) //Double time because of the possible of return flick
+               return EINA_TRUE;
+       else
+               return EINA_FALSE;
+}
+
+static Eina_Bool _flick_gesture_length_check(int x, int y, int x_org, int y_org)
+{
+       int dx = x - x_org;
+       int dy = y - y_org;
+
+       if ((dx * dx + dy * dy) > (_e_mod_config->one_finger_flick_min_length * _e_mod_config->one_finger_flick_min_length))
+               return EINA_TRUE;
+       else
+               return EINA_FALSE;
+}
+
+static flick_direction_e _flick_gesture_direction_get(int x, int y, int x_org, int y_org)
+{
+       int dx = x - x_org;
+       int dy = y - y_org;
+       int tmp;
+
+       switch (win_angle) {
+       case 90:
+               tmp = dx;
+               dx = -dy;
+               dy = tmp;
+               break;
+       case 270:
+               tmp = dx;
+               dx = dy;
+               dy = -tmp;
+               break;
+       }
+
+       if ((dy < 0) && (abs(dx) < -dy))
+               return FLICK_DIRECTION_UP;
+       if ((dy > 0) && (abs(dx) < dy))
+               return FLICK_DIRECTION_DOWN;
+       if ((dx > 0) && (dx > abs(dy)))
+               return FLICK_DIRECTION_RIGHT;
+       if ((dx < 0) && (-dx > abs(dy)))
+               return FLICK_DIRECTION_LEFT;
+
+       return FLICK_DIRECTION_UNDEFINED;
+}
+
+static void _flick_event_emit(Cover * cov)
+{
+       int ax, ay, axe, aye, i, type = -1;
+       ax = ay = axe = aye = 0;
+
+       for (i = 0; i < cov->flick_gesture.n_fingers; i++) {
+               ax += cov->flick_gesture.x_org[i];
+               ay += cov->flick_gesture.y_org[i];
+               axe += cov->flick_gesture.x_end[i];
+               aye += cov->flick_gesture.y_end[i];
+       }
+
+       ax /= cov->flick_gesture.n_fingers;
+       ay /= cov->flick_gesture.n_fingers;
+       axe /= cov->flick_gesture.n_fingers;
+       aye /= cov->flick_gesture.n_fingers;
+
+       if (cov->flick_gesture.dir == FLICK_DIRECTION_LEFT) {
+               if (cov->flick_gesture.n_fingers == 1)
+                       type = ONE_FINGER_FLICK_LEFT;
+               if (cov->flick_gesture.n_fingers == 2)
+                       type = TWO_FINGERS_FLICK_LEFT;
+               if (cov->flick_gesture.n_fingers == 3)
+                       type = THREE_FINGERS_FLICK_LEFT;
+       } else if (cov->flick_gesture.dir == FLICK_DIRECTION_RIGHT) {
+               if (cov->flick_gesture.n_fingers == 1)
+                       type = ONE_FINGER_FLICK_RIGHT;
+               if (cov->flick_gesture.n_fingers == 2)
+                       type = TWO_FINGERS_FLICK_RIGHT;
+               if (cov->flick_gesture.n_fingers == 3)
+                       type = THREE_FINGERS_FLICK_RIGHT;
+       } else if (cov->flick_gesture.dir == FLICK_DIRECTION_UP) {
+               if (cov->flick_gesture.n_fingers == 1)
+                       type = ONE_FINGER_FLICK_UP;
+               if (cov->flick_gesture.n_fingers == 2)
+                       type = TWO_FINGERS_FLICK_UP;
+               if (cov->flick_gesture.n_fingers == 3)
+                       type = THREE_FINGERS_FLICK_UP;
+       } else if (cov->flick_gesture.dir == FLICK_DIRECTION_DOWN) {
+               if (cov->flick_gesture.n_fingers == 1)
+                       type = ONE_FINGER_FLICK_DOWN;
+               if (cov->flick_gesture.n_fingers == 2)
+                       type = TWO_FINGERS_FLICK_DOWN;
+               if (cov->flick_gesture.n_fingers == 3)
+                       type = THREE_FINGERS_FLICK_DOWN;
+       } else if (cov->flick_gesture.dir == FLICK_DIRECTION_DOWN_RETURN) {
+               if (cov->flick_gesture.n_fingers == 1)
+                       type = ONE_FINGER_FLICK_DOWN_RETURN;
+               if (cov->flick_gesture.n_fingers == 2)
+                       type = TWO_FINGERS_FLICK_DOWN_RETURN;
+               if (cov->flick_gesture.n_fingers == 3)
+                       type = THREE_FINGERS_FLICK_DOWN_RETURN;
+       } else if (cov->flick_gesture.dir == FLICK_DIRECTION_UP_RETURN) {
+               if (cov->flick_gesture.n_fingers == 1)
+                       type = ONE_FINGER_FLICK_UP_RETURN;
+               if (cov->flick_gesture.n_fingers == 2)
+                       type = TWO_FINGERS_FLICK_UP_RETURN;
+               if (cov->flick_gesture.n_fingers == 3)
+                       type = THREE_FINGERS_FLICK_UP_RETURN;
+       } else if (cov->flick_gesture.dir == FLICK_DIRECTION_LEFT_RETURN) {
+               if (cov->flick_gesture.n_fingers == 1)
+                       type = ONE_FINGER_FLICK_LEFT_RETURN;
+               if (cov->flick_gesture.n_fingers == 2)
+                       type = TWO_FINGERS_FLICK_LEFT_RETURN;
+               if (cov->flick_gesture.n_fingers == 3)
+                       type = THREE_FINGERS_FLICK_LEFT_RETURN;
+       } else if (cov->flick_gesture.dir == FLICK_DIRECTION_RIGHT_RETURN) {
+               if (cov->flick_gesture.n_fingers == 1)
+                       type = ONE_FINGER_FLICK_RIGHT_RETURN;
+               if (cov->flick_gesture.n_fingers == 2)
+                       type = TWO_FINGERS_FLICK_RIGHT_RETURN;
+               if (cov->flick_gesture.n_fingers == 3)
+                       type = THREE_FINGERS_FLICK_RIGHT_RETURN;
+       }
+       DEBUG("FLICK GESTURE: N: %d F: %d", cov->flick_gesture.n_fingers, cov->flick_gesture.dir);
+       _event_emit(type, ax, ay, axe, aye, 2, cov->event_time);
+}
+
+static void _flick_gesture_mouse_up(Ecore_Event_Mouse_Button * ev, Cover * cov)
+{
+       if (cov->flick_gesture.state == GESTURE_ONGOING) {
+               int i;
+               // check if fingers match
+               for (i = 0; i < cov->flick_gesture.n_fingers; i++) {
+                       if (cov->flick_gesture.finger[i] == ev->multi.device)
+                               break;
+               }
+               if (i == cov->flick_gesture.n_fingers) {
+                       DEBUG("Finger id not recognized. Gesture aborted.");
+                       cov->flick_gesture.state = GESTURE_ABORTED;
+                       goto end;
+               }
+               if (cov->flick_gesture.flick_to_scroll)
+               {
+                       if (ev->multi.device == 1) {
+                               //if it is second finger then update x and y,
+                               //We use last x and y coordinates in end_scroll.
+                               //So if the first finger is up before
+                               //the second one we will use latest x and y of second finger
+                               //because second was the finger that scroll follows.
+                               //Else we can encounter that delta between last continue_scroll
+                               //coordinates and end_scroll coordinates will be high.
+                               cov->flick_gesture.flick_to_scroll_last_x = ev->x;
+                               cov->flick_gesture.flick_to_scroll_last_y = ev->y;
+                       }
+
+                       DEBUG("Flick gesture was interpreted as scroll so we aborting it.");
+                       cov->flick_gesture.state = GESTURE_ABORTED;
+                       goto end;
+               }
+               // check if flick for given finger is valid
+               if (!_flick_gesture_time_check(ev->timestamp, cov->flick_gesture.timestamp[i])) {
+                       DEBUG("finger flick gesture timeout expired. Gesture aborted.");
+                       cov->flick_gesture.state = GESTURE_ABORTED;
+                       goto end;
+               }
+               // check minimal flick length
+               if (!_flick_gesture_length_check(ev->root.x, ev->root.y, cov->flick_gesture.x_org[i], cov->flick_gesture.y_org[i])) {
+                       if (!cov->flick_gesture.finger_out[i]) {
+                               DEBUG("Minimal gesture length not reached and no return flick. Gesture aborted.");
+                               cov->flick_gesture.state = GESTURE_ABORTED;
+                               goto end;
+                       }
+                       cov->flick_gesture.return_flick[i] = EINA_TRUE;
+               }
+
+               flick_direction_e s = cov->flick_gesture.return_flick[i] ? cov->flick_gesture.dir : _flick_gesture_direction_get(ev->root.x, ev->root.y,
+                                                                                                                                                                                                                                                cov->flick_gesture.x_org[i],
+                                                                                                                                                                                                                                                cov->flick_gesture.y_org[i]);
+
+               cov->flick_gesture.n_fingers_left--;
+
+               if ((cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED || cov->flick_gesture.dir > FLICK_DIRECTION_RIGHT)
+                       && cov->flick_gesture.return_flick[i] == EINA_FALSE) {
+                       DEBUG("Flick gesture");
+                       cov->flick_gesture.dir = s;
+               }
+               // gesture is valid only if all flicks are in same direction
+               if (cov->flick_gesture.dir != s) {
+                       DEBUG("Flick in different direction. Gesture aborted.");
+                       cov->flick_gesture.state = GESTURE_ABORTED;
+                       goto end;
+               }
+
+               cov->flick_gesture.x_end[i] = ev->root.x;
+               cov->flick_gesture.y_end[i] = ev->root.y;
+
+               if (!cov->flick_gesture.n_fingers_left) {
+                       _flick_event_emit(cov);
+                       cov->flick_gesture.state = GESTURE_NOT_STARTED;
+               }
+       }
+
+ end:
+       // if no finger is touching a screen, gesture will be reseted.
+       if (cov->flick_gesture.state == GESTURE_ABORTED) {
+               if (cov->flick_gesture.flick_to_scroll) {
+                       end_scroll(cov->flick_gesture.flick_to_scroll_last_x, cov->flick_gesture.flick_to_scroll_last_y);
+                       cov->flick_gesture.flick_to_scroll = EINA_FALSE;
+               }
+               if (cov->n_taps == 0)
+                       cov->flick_gesture.state = GESTURE_NOT_STARTED;
+       }
+}
+
+static Eina_Bool _flick_to_scroll_gesture_conditions_met(Ecore_Event_Mouse_Move * ev, int gesture_timestamp, int dx, int dy)
+{
+       if (ev->timestamp - gesture_timestamp > _e_mod_config->two_finger_flick_to_scroll_timeout)
+               if (abs(dx) > _e_mod_config->two_finger_flick_to_scroll_min_length || abs(dy) > _e_mod_config->two_finger_flick_to_scroll_min_length)
+                       return EINA_TRUE;
+
+       return EINA_FALSE;
+}
+
+static void _flick_gesture_mouse_move(Ecore_Event_Mouse_Move * ev, Cover * cov)
+{
+       if (cov->flick_gesture.state == GESTURE_ONGOING) {
+               int i;
+               for (i = 0; i < cov->flick_gesture.n_fingers; ++i) {
+                       if (cov->flick_gesture.finger[i] == ev->multi.device)
+                               break;
+               }
+               if (i == cov->flick_gesture.n_fingers) {
+                       if (cov->flick_gesture.n_fingers >= 3)  //that is because of the EFL bug. Mouse move event before mouse down(!)
+                       {
+                               ERROR("Finger id not recognized. Gesture aborted.");
+                               cov->flick_gesture.state = GESTURE_ABORTED;
+                               return;
+                       }
+               }
+
+               int dx = ev->root.x - cov->flick_gesture.x_org[i];
+               int dy = ev->root.y - cov->flick_gesture.y_org[i];
+               int tmp;
+
+               switch (win_angle) {
+               case 90:
+                       tmp = dx;
+                       dx = -dy;
+                       dy = tmp;
+                       break;
+               case 270:
+                       tmp = dx;
+                       dx = dy;
+                       dy = -tmp;
+                       break;
+               }
+               if (i == 1) {
+                       if (cov->flick_gesture.flick_to_scroll || _flick_to_scroll_gesture_conditions_met(ev, cov->flick_gesture.timestamp[i], dx, dy)) {
+                               if (!cov->flick_gesture.flick_to_scroll) {
+                                       start_scroll(ev->x, ev->y);
+                                       cov->flick_gesture.flick_to_scroll = EINA_TRUE;
+                               } else {
+                                       continue_scroll(ev->x, ev->y);
+                               }
+                               cov->flick_gesture.flick_to_scroll_last_x = ev->x;
+                               cov->flick_gesture.flick_to_scroll_last_y = ev->y;
+                               return;
+                       }
+               }
+
+               if (!cov->flick_gesture.finger_out[i]) {
+                       if (abs(dx) > _e_mod_config->one_finger_flick_min_length) {
+                               cov->flick_gesture.finger_out[i] = EINA_TRUE;
+                               if (dx > 0) {
+                                       if (cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED || cov->flick_gesture.dir == FLICK_DIRECTION_RIGHT_RETURN) {
+                                               cov->flick_gesture.dir = FLICK_DIRECTION_RIGHT_RETURN;
+                                       } else {
+                                               ERROR("Invalid direction, abort");
+                                               cov->flick_gesture.state = GESTURE_ABORTED;
+                                       }
+                               } else {
+                                       if (cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED || cov->flick_gesture.dir == FLICK_DIRECTION_LEFT_RETURN) {
+                                               cov->flick_gesture.dir = FLICK_DIRECTION_LEFT_RETURN;
+                                       } else {
+                                               ERROR("Invalid direction, abort");
+                                               cov->flick_gesture.state = GESTURE_ABORTED;
+                                       }
+                               }
+                               return;
+                       }
+
+                       else if (abs(dy) > _e_mod_config->one_finger_flick_min_length) {
+                               cov->flick_gesture.finger_out[i] = EINA_TRUE;
+                               if (dy > 0) {
+                                       if (cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED || cov->flick_gesture.dir == FLICK_DIRECTION_DOWN_RETURN) {
+                                               cov->flick_gesture.dir = FLICK_DIRECTION_DOWN_RETURN;
+                                       } else {
+                                               ERROR("Invalid direction, abort");
+                                               cov->flick_gesture.state = GESTURE_ABORTED;
+                                       }
+                               } else {
+                                       if (cov->flick_gesture.dir == FLICK_DIRECTION_UNDEFINED || cov->flick_gesture.dir == FLICK_DIRECTION_UP_RETURN) {
+                                               cov->flick_gesture.dir = FLICK_DIRECTION_UP_RETURN;
+                                       } else {
+                                               ERROR("Invalid direction, abort");
+                                               cov->flick_gesture.state = GESTURE_ABORTED;
+                                       }
+                               }
+                               return;
+                       }
+               }
+       }
+       return;
+}
+
+static Eina_Bool _on_hover_timeout(void *data)
+{
+       Cover *cov = data;
+       DEBUG("Hover timer expierd");
+
+       cov->hover_gesture.longpressed = EINA_TRUE;
+       cov->hover_gesture.timer = NULL;
+
+       if (cov->hover_gesture.last_emission_time == -1) {
+               _hover_event_emit(cov, 0);
+               cov->hover_gesture.last_emission_time = cov->event_time;
+       }
+       return EINA_FALSE;
+}
+
+static void _hover_gesture_timer_reset(Cover * cov, double time)
+{
+       DEBUG("Hover timer reset");
+       cov->hover_gesture.longpressed = EINA_FALSE;
+       if (cov->hover_gesture.timer) {
+               ecore_timer_reset(cov->hover_gesture.timer);
+               return;
+       }
+       cov->hover_gesture.timer = ecore_timer_add(time, _on_hover_timeout, cov);
+}
+
+static void _hover_gesture_mouse_down(Ecore_Event_Mouse_Button * ev, Cover * cov)
+{
+       if (cov->hover_gesture.state == GESTURE_NOT_STARTED && cov->n_taps == 1) {
+               cov->hover_gesture.state = GESTURE_ONGOING;
+               cov->hover_gesture.timestamp = ev->timestamp;
+               cov->hover_gesture.last_emission_time = -1;
+               cov->hover_gesture.x[0] = ev->root.x;
+               cov->hover_gesture.y[0] = ev->root.y;
+               cov->hover_gesture.finger[0] = ev->multi.device;
+               cov->hover_gesture.n_fingers = 1;
+               _hover_gesture_timer_reset(cov, _e_mod_config->one_finger_hover_longpress_timeout);
+       }
+       if (cov->hover_gesture.state == GESTURE_ONGOING && cov->n_taps == 2) {
+               if (cov->hover_gesture.longpressed) {
+                       _hover_event_emit(cov, 2);
+                       goto abort;
+               }
+               cov->hover_gesture.timestamp = -1;
+               cov->hover_gesture.last_emission_time = -1;
+               cov->hover_gesture.x[1] = ev->root.x;
+               cov->hover_gesture.y[1] = ev->root.y;
+               cov->hover_gesture.finger[1] = ev->multi.device;
+               cov->hover_gesture.n_fingers = 2;
+               _hover_gesture_timer_reset(cov, _e_mod_config->one_finger_hover_longpress_timeout);
+       }
+       // abort gesture if more then 2 fingers touched screen
+       if ((cov->hover_gesture.state == GESTURE_ONGOING) && cov->n_taps > 2) {
+               DEBUG("More then 2 finged. Abort hover gesture");
+               _hover_event_emit(cov, 2);
+               goto abort;
+       }
+       return;
+
+ abort:
+       cov->hover_gesture.state = GESTURE_ABORTED;
+       if (cov->hover_gesture.timer)
+               ecore_timer_del(cov->hover_gesture.timer);
+       cov->hover_gesture.timer = NULL;
+}
+
+static void _hover_gesture_mouse_up(Ecore_Event_Mouse_Button * ev, Cover * cov)
+{
+       int i;
+       if (cov->hover_gesture.state == GESTURE_ONGOING) {
+
+               for (i = 0; i < cov->hover_gesture.n_fingers; i++) {
+                       if (cov->hover_gesture.finger[i] == ev->multi.device)
+                               break;
+               }
+               if (i == cov->hover_gesture.n_fingers) {
+                       DEBUG("Invalid finger id: %d", ev->multi.device);
+                       return;
+               } else {
+                       cov->hover_gesture.state = GESTURE_ABORTED;
+                       if (cov->hover_gesture.timer)
+                               ecore_timer_del(cov->hover_gesture.timer);
+                       cov->hover_gesture.timer = NULL;
+                       // aditionally emit event to complete sequence
+                       if (cov->hover_gesture.longpressed)
+                               _hover_event_emit(cov, 2);
+               }
+       }
+       // reset gesture only if user released all his fingers
+       if (cov->n_taps == 0)
+               cov->hover_gesture.state = GESTURE_NOT_STARTED;
+}
+
+static void _get_root_coords(Ecore_X_Window win, int *x, int *y)
+{
+       Ecore_X_Window root = ecore_x_window_root_first_get();
+       Ecore_X_Window parent = ecore_x_window_parent_get(win);
+       int wx, wy;
+
+       if (x)
+               *x = 0;
+       if (y)
+               *y = 0;
+
+       while (parent && (root != parent)) {
+               ecore_x_window_geometry_get(parent, &wx, &wy, NULL, NULL);
+               if (x)
+                       *x += wx;
+               if (y)
+                       *y += wy;
+               parent = ecore_x_window_parent_get(parent);
+       }
+}
+
+Ecore_X_Window top_window_get(int x, int y)
+{
+       Ecore_X_Window wins[1] = { win };
+       Ecore_X_Window under = ecore_x_window_at_xy_with_skip_get(x, y, wins, sizeof(wins) / sizeof(wins[0]));
+       if (under) {
+               _get_root_coords(under, &rx, &ry);
+               DEBUG("Recieved window with coords:%d %d", rx, ry);
+               return under;
+       }
+       return 0;
+}
+
+void start_scroll(int x, int y)
+{
+       Ecore_X_Window wins[1] = { win };
+       Ecore_X_Window under = ecore_x_window_at_xy_with_skip_get(x, y, wins, sizeof(wins) / sizeof(wins[0]));
+       _get_root_coords(under, &rx, &ry);
+       ecore_x_mouse_in_send(under, x - rx, y - ry);
+       ecore_x_window_focus(under);
+       ecore_x_mouse_down_send(under, x - rx, y - ry, 1);
+       scrolled_win = under;
+}
+
+void continue_scroll(int x, int y)
+{
+       ecore_x_mouse_move_send(scrolled_win, x - rx, y - ry);
+}
+
+void end_scroll(int x, int y)
+{
+       ecore_x_mouse_up_send(scrolled_win, x - rx, y - ry, 1);
+       ecore_x_mouse_out_send(scrolled_win, x - rx, y - ry);
+}
+
+static unsigned int _win_angle_get(void)
+{
+       Ecore_X_Window root, first_root;
+       int ret;
+       int count;
+       int angle = 0;
+       unsigned char *prop_data = NULL;
+
+       first_root = ecore_x_window_root_first_get();
+       root = ecore_x_window_root_get(first_root);
+       ret = ecore_x_window_prop_property_get(root, ECORE_X_ATOM_E_ILLUME_ROTATE_ROOT_ANGLE, ECORE_X_ATOM_CARDINAL, 32, &prop_data, &count);
+
+       if (ret && prop_data)
+               memcpy(&angle, prop_data, sizeof(int));
+
+       if (prop_data)
+               free(prop_data);
+
+       return angle;
+}
+
+static void _hover_event_emit(Cover * cov, int state)
+{
+       int ax = 0, ay = 0, j;
+
+       for (j = 0; j < cov->hover_gesture.n_fingers; j++) {
+               ax += cov->hover_gesture.x[j];
+               ay += cov->hover_gesture.y[j];
+       }
+
+       ax /= cov->hover_gesture.n_fingers;
+       ay /= cov->hover_gesture.n_fingers;
+
+       switch (cov->hover_gesture.n_fingers) {
+       case 1:
+               INFO("ONE FINGER HOVER");
+               _event_emit(ONE_FINGER_HOVER, ax, ay, ax, ay, state, cov->event_time);
+               break;
+       default:
+               break;
+       }
+}
+
+static void _hover_gesture_mouse_move(Ecore_Event_Mouse_Move * ev, Cover * cov)
+{
+       if (cov->hover_gesture.state == GESTURE_ONGOING) {
+               // check fingers
+               int i;
+               if (!cov->hover_gesture.longpressed)
+                       return;
+
+               for (i = 0; i < cov->hover_gesture.n_fingers; i++) {
+                       if (cov->hover_gesture.finger[i] == ev->multi.device)
+                               break;
+               }
+               if (i == cov->hover_gesture.n_fingers) {
+                       DEBUG("Invalid finger id: %d", ev->multi.device);
+                       return;
+               }
+               cov->hover_gesture.x[i] = ev->root.x;
+               cov->hover_gesture.y[i] = ev->root.y;
+               _hover_event_emit(cov, 1);
+       }
+}
+
+static void _tap_event_emit(Cover * cov, int state)
+{
+       switch (cov->tap_gesture_data.n_taps) {
+       case 1:
+               if (cov->tap_gesture_data.tap_type == ONE_FINGER_GESTURE) {
+                       DEBUG("ONE_FINGER_SINGLE_TAP");
+                       _event_emit(ONE_FINGER_SINGLE_TAP, cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], state, cov->event_time);
+               } else if (cov->tap_gesture_data.tap_type == TWO_FINGERS_GESTURE) {
+                       DEBUG("TWO_FINGERS_SINGLE_TAP");
+                       _event_emit(TWO_FINGERS_SINGLE_TAP, cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], cov->tap_gesture_data.x_org[1], cov->tap_gesture_data.y_org[1], state, cov->event_time);
+               } else if (cov->tap_gesture_data.tap_type == THREE_FINGERS_GESTURE) {
+                       DEBUG("THREE_FINGERS_SINGLE_TAP");
+                       _event_emit(THREE_FINGERS_SINGLE_TAP, cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], cov->tap_gesture_data.x_org[2], cov->tap_gesture_data.y_org[2], state, cov->event_time);
+               } else {
+                       ERROR("Unknown tap");
+               }
+               break;
+       case 2:
+               if (cov->tap_gesture_data.tap_type == ONE_FINGER_GESTURE) {
+                       DEBUG("ONE_FINGER_DOUBLE_TAP");
+                       _event_emit(ONE_FINGER_DOUBLE_TAP, cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], state, cov->event_time);
+               } else if (cov->tap_gesture_data.tap_type == TWO_FINGERS_GESTURE) {
+                       DEBUG("TWO_FINGERS_DOUBLE_TAP");
+                       _event_emit(TWO_FINGERS_DOUBLE_TAP, cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], cov->tap_gesture_data.x_org[1], cov->tap_gesture_data.y_org[1], state, cov->event_time);
+               } else if (cov->tap_gesture_data.tap_type == THREE_FINGERS_GESTURE) {
+                       DEBUG("THREE_FINGERS_DOUBLE_TAP");
+                       _event_emit(THREE_FINGERS_DOUBLE_TAP, cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], cov->tap_gesture_data.x_org[2], cov->tap_gesture_data.y_org[2], state, cov->event_time);
+               } else {
+                       ERROR("Unknown tap");
+               }
+               break;
+       case 3:
+               if (cov->tap_gesture_data.tap_type == ONE_FINGER_GESTURE) {
+                       DEBUG("ONE_FINGER_TRIPLE_TAP");
+                       _event_emit(ONE_FINGER_TRIPLE_TAP, cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], state, cov->event_time);
+               } else if (cov->tap_gesture_data.tap_type == TWO_FINGERS_GESTURE) {
+                       DEBUG("TWO_FINGERS_TRIPLE_TAP");
+                       _event_emit(TWO_FINGERS_TRIPLE_TAP, cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], cov->tap_gesture_data.x_org[1], cov->tap_gesture_data.y_org[1], state, cov->event_time);
+               } else if (cov->tap_gesture_data.tap_type == THREE_FINGERS_GESTURE) {
+                       DEBUG("THREE_FINGERS_TRIPLE_TAP");
+                       _event_emit(THREE_FINGERS_TRIPLE_TAP, cov->tap_gesture_data.x_org[0], cov->tap_gesture_data.y_org[0], cov->tap_gesture_data.x_org[2], cov->tap_gesture_data.y_org[2], state, cov->event_time);
+               } else {
+                       ERROR("Unknown tap");
+               }
+               break;
+       default:
+               ERROR("Unknown tap");
+               break;
+       }
+}
+
+static Eina_Bool _on_tap_timer_expire(void *data)
+{
+       Cover *cov = data;
+       DEBUG("Timer expired");
+
+       if (cov->tap_gesture_data.started && !cov->tap_gesture_data.pressed)
+               _tap_event_emit(cov, 2);
+       else
+               _tap_event_emit(cov, 3);
+
+       // finish gesture
+       cov->tap_gesture_data.started = EINA_FALSE;
+       cov->tap_gesture_data.timer = NULL;
+       cov->tap_gesture_data.tap_type = ONE_FINGER_GESTURE;
+       cov->tap_gesture_data.finger[0] = -1;
+       cov->tap_gesture_data.finger[1] = -1;
+       cov->tap_gesture_data.finger[2] = -1;
+
+       return EINA_FALSE;
+}
+
+static int _tap_gesture_finger_check(Cover * cov, int x, int y)
+{
+       int dx = x - cov->tap_gesture_data.x_org[0];
+       int dy = y - cov->tap_gesture_data.y_org[0];
+
+       if (cov->tap_gesture_data.finger[0] != -1 && (dx * dx + dy * dy < _e_mod_config->one_finger_tap_radius * _e_mod_config->one_finger_tap_radius)) {
+               return 0;
+       }
+
+       dx = x - cov->tap_gesture_data.x_org[1];
+       dy = y - cov->tap_gesture_data.y_org[1];
+       if (cov->tap_gesture_data.finger[1] != -1 && (dx * dx + dy * dy < _e_mod_config->one_finger_tap_radius * _e_mod_config->one_finger_tap_radius)) {
+               return 1;
+       }
+
+       dx = x - cov->tap_gesture_data.x_org[2];
+       dy = y - cov->tap_gesture_data.y_org[2];
+       if (cov->tap_gesture_data.finger[2] != -1 && (dx * dx + dy * dy < _e_mod_config->one_finger_tap_radius * _e_mod_config->one_finger_tap_radius)) {
+               return 2;
+       }
+
+       return -1;
+}
+
+static void _tap_gestures_mouse_down(Ecore_Event_Mouse_Button * ev, Cover * cov)
+{
+       if (cov->n_taps > 4) {
+               ERROR("Too many fingers");
+               return;
+       }
+
+       cov->tap_gesture_data.pressed = EINA_TRUE;
+
+       if (cov->tap_gesture_data.started == EINA_FALSE) {
+               DEBUG("First finger down");
+               cov->tap_gesture_data.started = EINA_TRUE;
+               cov->tap_gesture_data.finger[0] = ev->multi.device;
+               cov->tap_gesture_data.x_org[0] = ev->root.x;
+               cov->tap_gesture_data.y_org[0] = ev->root.y;
+               cov->tap_gesture_data.finger[1] = -1;
+               cov->tap_gesture_data.finger[2] = -1;
+               cov->tap_gesture_data.n_taps = 0;
+               cov->tap_gesture_data.timer = ecore_timer_add(_e_mod_config->one_finger_tap_timeout, _on_tap_timer_expire, cov);
+               cov->tap_gesture_data.tap_type = ONE_FINGER_GESTURE;
+       }
+
+       else {
+               if (ev->multi.device == cov->tap_gesture_data.finger[0]) {
+                       DEBUG("First finger down");
+
+                       if (_tap_gesture_finger_check(cov, ev->root.x, ev->root.y) == -1) {
+                               ERROR("Abort gesture");
+                               cov->tap_gesture_data.started = EINA_FALSE;
+                               ecore_timer_del(cov->tap_gesture_data.timer);
+                               cov->tap_gesture_data.timer = NULL;
+                               cov->tap_gesture_data.tap_type = ONE_FINGER_GESTURE;
+                               cov->tap_gesture_data.finger[0] = -1;
+                               cov->tap_gesture_data.finger[1] = -1;
+                               cov->tap_gesture_data.finger[2] = -1;
+                               _tap_gestures_mouse_down(ev, cov);
+                               return;
+                       }
+
+                       cov->tap_gesture_data.x_org[0] = ev->root.x;
+                       cov->tap_gesture_data.y_org[0] = ev->root.y;
+               } else if (cov->tap_gesture_data.finger[1] == -1 || cov->tap_gesture_data.finger[1] == ev->multi.device) {
+                       DEBUG("Second finger down");
+                       cov->tap_gesture_data.finger[1] = ev->multi.device;
+
+                       cov->tap_gesture_data.x_org[1] = ev->root.x;
+                       cov->tap_gesture_data.y_org[1] = ev->root.y;
+                       if (cov->tap_gesture_data.tap_type < TWO_FINGERS_GESTURE)
+                               cov->tap_gesture_data.tap_type = TWO_FINGERS_GESTURE;
+               } else if (cov->tap_gesture_data.finger[2] == -1 || cov->tap_gesture_data.finger[2] == ev->multi.device) {
+                       DEBUG("Third finger down");
+                       cov->tap_gesture_data.finger[2] = ev->multi.device;
+
+                       cov->tap_gesture_data.x_org[2] = ev->root.x;
+                       cov->tap_gesture_data.y_org[2] = ev->root.y;
+                       if (cov->tap_gesture_data.tap_type < THREE_FINGERS_GESTURE)
+                               cov->tap_gesture_data.tap_type = THREE_FINGERS_GESTURE;
+               } else {
+                       ERROR("Unknown finger down");
+               }
+               ecore_timer_reset(cov->tap_gesture_data.timer);
+       }
+}
+
+static void _tap_gestures_mouse_up(Ecore_Event_Mouse_Button * ev, Cover * cov)
+{
+       if (cov->tap_gesture_data.timer) {
+               cov->tap_gesture_data.pressed = EINA_FALSE;
+
+               if (ev->multi.device == cov->tap_gesture_data.finger[0]) {
+                       DEBUG("First finger up");
+
+                       int dx = ev->root.x - cov->tap_gesture_data.x_org[0];
+                       int dy = ev->root.y - cov->tap_gesture_data.y_org[0];
+
+                       if ((dx * dx + dy * dy) < _e_mod_config->one_finger_tap_radius * _e_mod_config->one_finger_tap_radius) {
+                               if (cov->n_taps == 0) {
+                                       cov->tap_gesture_data.n_taps++;
+                               }
+                       } else {
+                               ERROR("Abort gesture");
+                               cov->tap_gesture_data.started = EINA_FALSE;
+                       }
+               } else if (ev->multi.device == cov->tap_gesture_data.finger[1]) {
+                       DEBUG("Second finger up");
+
+                       int dx = ev->root.x - cov->tap_gesture_data.x_org[1];
+                       int dy = ev->root.y - cov->tap_gesture_data.y_org[1];
+
+                       if ((dx * dx + dy * dy) < _e_mod_config->one_finger_tap_radius * _e_mod_config->one_finger_tap_radius) {
+                               if (cov->n_taps == 0) {
+                                       cov->tap_gesture_data.n_taps++;
+                               }
+                       } else {
+                               ERROR("Abort gesture");
+                               cov->tap_gesture_data.started = EINA_FALSE;
+                       }
+               } else if (ev->multi.device == cov->tap_gesture_data.finger[2]) {
+                       DEBUG("Third finger up");
+
+                       int dx = ev->root.x - cov->tap_gesture_data.x_org[2];
+                       int dy = ev->root.y - cov->tap_gesture_data.y_org[2];
+
+                       if ((dx * dx + dy * dy) < _e_mod_config->one_finger_tap_radius * _e_mod_config->one_finger_tap_radius) {
+                               if (cov->n_taps == 0) {
+                                       cov->tap_gesture_data.n_taps++;
+                               }
+                       } else {
+                               ERROR("Abort gesture");
+                               cov->tap_gesture_data.started = EINA_FALSE;
+                       }
+               } else {
+                       ERROR("Unknown finger up, abort gesture");
+                       cov->tap_gesture_data.started = EINA_FALSE;
+               }
+       }
+}
+
+static void _tap_gestures_move(Ecore_Event_Mouse_Move * ev, Cover * cov)
+{
+       int i;
+       for (i = 0; i < sizeof(cov->tap_gesture_data.finger) / sizeof(cov->tap_gesture_data.finger[0]); i++) {
+               if (ev->multi.device == cov->tap_gesture_data.finger[i]) {
+                       int dx = ev->root.x - cov->tap_gesture_data.x_org[i];
+                       int dy = ev->root.y - cov->tap_gesture_data.y_org[i];
+
+                       if ((dx * dx + dy * dy) > _e_mod_config->one_finger_tap_radius * _e_mod_config->one_finger_tap_radius) {
+                               DEBUG("abort tap gesutre");
+                               cov->tap_gesture_data.started = EINA_FALSE;
+                               ecore_timer_del(cov->tap_gesture_data.timer);
+                               cov->tap_gesture_data.timer = NULL;
+                               cov->tap_gesture_data.tap_type = ONE_FINGER_GESTURE;
+                               cov->tap_gesture_data.finger[0] = -1;
+                               cov->tap_gesture_data.finger[1] = -1;
+                               cov->tap_gesture_data.finger[2] = -1;
+                       }
+                       break;
+               }
+       }
+}
+
+static Eina_Bool _cb_mouse_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+       Ecore_Event_Mouse_Button *ev = event;
+
+       cov->n_taps++;
+       cov->event_time = ev->timestamp;
+
+       DEBUG("mouse down: multi.device: %d, taps: %d", ev->multi.device, cov->n_taps);
+
+       win_angle = _win_angle_get();
+
+       _flick_gesture_mouse_down(ev, cov);
+       _hover_gesture_mouse_down(ev, cov);
+       _tap_gestures_mouse_down(ev, cov);
+
+       return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool _cb_mouse_up(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+       Ecore_Event_Mouse_Button *ev = event;
+
+       cov->n_taps--;
+       cov->event_time = ev->timestamp;
+
+       DEBUG("mouse up, multi.device: %d, taps: %d", ev->multi.device, cov->n_taps);
+
+       _flick_gesture_mouse_up(ev, cov);
+       _hover_gesture_mouse_up(ev, cov);
+       _tap_gestures_mouse_up(ev, cov);
+
+       return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool _cb_mouse_move(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+       Ecore_Event_Mouse_Move *ev = event;
+
+       cov->event_time = ev->timestamp;
+
+       _flick_gesture_mouse_move(ev, cov);
+       _hover_gesture_mouse_move(ev, cov);
+       _tap_gestures_move(ev, cov);
+
+       return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool _gesture_input_win_create(void)
+{
+       int w, h;
+
+       if (!win) {
+               Ecore_Window root = ecore_x_window_root_first_get();
+               if (!root)
+                       return EINA_FALSE;
+               ecore_x_window_geometry_get(root, NULL, NULL, &w, &h);
+               win = ecore_x_window_input_new(root, 0, 0, w, h);
+       }
+       if (!win)
+               return EINA_FALSE;
+
+       ecore_x_input_multi_select(win);
+       ecore_x_window_show(win);
+       ecore_x_window_raise(win);
+
+       // restet gestures
+       memset(cov, 0x0, sizeof(Cover));
+
+       return EINA_TRUE;
+}
+
+static Eina_Bool _win_property_changed(void *data, int type, void *event)
+{
+       Ecore_X_Event_Window_Property *wp = event;
+
+       if (wp->atom != ECORE_X_ATOM_NET_CLIENT_LIST_STACKING)
+               return EINA_TRUE;
+
+       _gesture_input_win_create();
+
+       return EINA_TRUE;
+}
+
+static Eina_Bool _gestures_input_window_init(void)
+{
+       Ecore_Window root = ecore_x_window_root_first_get();
+       if (!root) {
+               ERROR("No root window found. Is Window manager running?");
+               return EINA_FALSE;
+       }
+       ecore_x_event_mask_set(root, ECORE_X_EVENT_MASK_WINDOW_PROPERTY);
+       property_changed_hld = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_PROPERTY, _win_property_changed, NULL);
+
+       return _gesture_input_win_create();
+}
+
+static void _gestures_input_widnow_shutdown(void)
+{
+       ecore_event_handler_del(property_changed_hld);
+       if (win)
+               ecore_x_window_free(win);
+       win = 0;
+}
+
+Eina_Bool screen_reader_gestures_init(void)
+{
+       ecore_init();
+       ecore_x_init(NULL);
+
+       cov = calloc(sizeof(Cover), 1);
+
+       if (!_gestures_input_window_init()) {
+               free(cov);
+               return EINA_FALSE;
+       }
+
+       _e_mod_config = calloc(sizeof(Gestures_Config), 1);
+       _e_mod_config->one_finger_flick_min_length = 100;
+       _e_mod_config->one_finger_flick_max_time = 400;
+       _e_mod_config->two_finger_flick_to_scroll_timeout = 100;
+       _e_mod_config->two_finger_flick_to_scroll_min_length = 50;
+       _e_mod_config->one_finger_hover_longpress_timeout = 0.81;
+       _e_mod_config->one_finger_tap_timeout = 0.4;
+       _e_mod_config->one_finger_tap_radius = 100;
+
+       handlers = eina_list_append(NULL, ecore_event_handler_add(ECORE_EVENT_MOUSE_MOVE, _cb_mouse_move, NULL));
+       handlers = eina_list_append(handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP, _cb_mouse_up, NULL));
+       handlers = eina_list_append(handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, _cb_mouse_down, NULL));
+
+       return EINA_TRUE;
+}
+
+void screen_reader_gestures_shutdown(void)
+{
+       Ecore_Event_Handler *hdlr;
+       EINA_LIST_FREE(handlers, hdlr) {
+               ecore_event_handler_del(hdlr);
+       }
+       _gestures_input_widnow_shutdown();
+
+       ecore_x_shutdown();
+       ecore_shutdown();
+       free(_e_mod_config);
+       free(cov);
+}
+
+void screen_reader_gestures_tracker_register(GestureCB cb, void *data)
+{
+       _global_cb = cb;
+       _global_data = data;
+}
diff --git a/src/screen_reader_haptic.c b/src/screen_reader_haptic.c
new file mode 100644 (file)
index 0000000..f099cb4
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 <device/haptic.h>
+#include "logger.h"
+#include "smart_notification.h"
+
+static haptic_device_h handle;
+static haptic_effect_h effect_handle;
+
+#define RED  "\x1B[31m"
+#define RESET "\033[0m"
+
+/**
+ * @brief Initializer for haptic module
+ *
+ */
+void haptic_module_init(void)
+{
+       int num;
+
+       if (!device_haptic_get_count(&num)) {
+               DEBUG(RED "Haptic device received!" RESET);
+       } else {
+               ERROR("Cannot receive haptic device count");
+               return;
+       }
+
+       if (!device_haptic_open(0, &handle)) {
+               DEBUG(RED "Device connected!" RESET);
+       } else {
+               ERROR("Cannot open haptic device");
+       }
+}
+
+/**
+ * @brief Disconnect haptic handle
+ *
+ */
+void haptic_module_disconnect(void)
+{
+       if (!handle) {
+               ERROR("Haptic handle lost");
+               return;
+       }
+       if (!device_haptic_close(handle)) {
+               DEBUG("Haptic disconnected");
+       } else {
+               ERROR("Haptic close error");
+       }
+}
+
+/**
+ * @brief Start vibrations
+ *
+ */
+void haptic_vibrate_start(void)
+{
+       if (!handle) {
+               ERROR("Haptic handle lost");
+               return;
+       }
+       if (!device_haptic_vibrate(handle, 1000, 100, &effect_handle)) {
+               DEBUG(RED "Vibrations started!" RESET);
+       } else {
+               ERROR("Cannot start vibration");
+       }
+}
+
+/**
+ * @brief Stop vibrations
+ *
+ */
+void haptic_vibrate_stop(void)
+{
+       if (!handle) {
+               ERROR("Haptic handle lost");
+               return;
+       }
+       if (!device_haptic_stop(handle, &effect_handle)) {
+               ERROR("Vibrations stopped!");
+       } else {
+               DEBUG(RED "Cannot stop vibration" RESET);
+       }
+}
diff --git a/src/screen_reader_spi.c b/src/screen_reader_spi.c
new file mode 100644 (file)
index 0000000..10645de
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include "screen_reader_spi.h"
+#include "screen_reader_tts.h"
+#include "logger.h"
+#ifdef RUN_IPC_TEST_SUIT
+#include "test_suite/test_suite.h"
+#endif
+
+#define EPS 0.000000001
+
+/** @brief Service_Data used as screen reader internal data struct*/
+static Service_Data *service_data;
+
+typedef struct {
+       char *key;
+       char *val;
+} Attr;
+
+/**
+ * @brief Debug function. Print current toolkit version/event
+ * type/event source/event detail1/event detail2
+ *
+ * @param AtspiEvent instance
+ *
+ */
+static void display_info(const AtspiEvent * event)
+{
+       AtspiAccessible *source = event->source;
+       gchar *name = atspi_accessible_get_name(source, NULL);
+       gchar *role = atspi_accessible_get_localized_role_name(source, NULL);
+       gchar *toolkit = atspi_accessible_get_toolkit_name(source, NULL);
+
+       DEBUG("--------------------------------------------------------");
+       DEBUG("Toolkit: %s; Event_type: %s; (%d, %d)", toolkit, event->type, event->detail1, event->detail2);
+       DEBUG("Name: %s; Role: %s", name, role);
+       DEBUG("--------------------------------------------------------");
+}
+
+Eina_Bool double_click_timer_cb(void *data)
+{
+       Service_Data *sd = data;
+       sd->clicked_widget = NULL;
+
+       return EINA_FALSE;
+}
+
+bool allow_recursive_name(AtspiAccessible * obj)
+{
+       AtspiRole r = atspi_accessible_get_role(obj, NULL);
+       if (r == ATSPI_ROLE_FILLER)
+               return true;
+       return false;
+}
+
+char *generate_description_for_subtree(AtspiAccessible * obj)
+{
+       DEBUG("START");
+       if (!allow_recursive_name(obj))
+               return strdup("");
+
+       if (!obj)
+               return strdup("");
+       int child_count = atspi_accessible_get_child_count(obj, NULL);
+
+       DEBUG("There is %d children inside this filler", child_count);
+       if (!child_count)
+               return strdup("");
+
+       int i;
+       char *name = NULL;
+       char *below = NULL;
+       char ret[256] = "\0";
+       AtspiAccessible *child = NULL;
+       for (i = 0; i < child_count; i++) {
+               child = atspi_accessible_get_child_at_index(obj, i, NULL);
+               name = atspi_accessible_get_name(child, NULL);
+               DEBUG("%d child name:%s", i, name);
+               if (name && strncmp(name, "\0", 1)) {
+                       strncat(ret, name, sizeof(ret) - strlen(ret) - 1);
+               }
+               strncat(ret, " ", sizeof(ret) - strlen(ret) - 1);
+               below = generate_description_for_subtree(child);
+               if (strncmp(below, "\0", 1)) {
+                       strncat(ret, below, sizeof(ret) - strlen(ret) - 1);
+               }
+               g_object_unref(child);
+               free(below);
+               free(name);
+       }
+       return strdup(ret);
+}
+
+static char *spi_on_state_changed_get_text(AtspiEvent * event, void *user_data)
+{
+       Service_Data *sd = (Service_Data *) user_data;
+       char *name;
+       char *names = NULL;
+       char *description;
+       char *role_name;
+       char *other;
+       char ret[256] = "\0";
+       sd->currently_focused = event->source;
+
+       description = atspi_accessible_get_description(sd->currently_focused, NULL);
+       name = atspi_accessible_get_name(sd->currently_focused, NULL);
+       role_name = atspi_accessible_get_localized_role_name(sd->currently_focused, NULL);
+       other = generate_description_for_subtree(sd->currently_focused);
+
+       DEBUG("->->->->->-> WIDGET GAINED HIGHLIGHT: %s <-<-<-<-<-<-<-", name);
+       DEBUG("->->->->->-> FROM SUBTREE HAS NAME:  %s <-<-<-<-<-<-<-", other);
+
+       if (name && strncmp(name, "\0", 1))
+               names = strdup(name);
+       else if (other && strncmp(other, "\0", 1))
+               names = strdup(other);
+
+       if (names) {
+               strncat(ret, names, sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+       }
+
+       if (role_name)
+               strncat(ret, role_name, sizeof(ret) - strlen(ret) - 1);
+
+       if (description) {
+               if (strncmp(description, "\0", 1))
+                       strncat(ret, ", ", sizeof(ret) - strlen(ret) - 1);
+               strncat(ret, description, sizeof(ret) - strlen(ret) - 1);
+       }
+
+       free(name);
+       free(names);
+       free(description);
+       free(role_name);
+       free(other);
+
+       return strdup(ret);
+}
+
+static char *spi_on_caret_move_get_text(AtspiEvent * event, void *user_data)
+{
+       Service_Data *sd = (Service_Data *) user_data;
+       sd->currently_focused = event->source;
+       char *return_text;
+
+       AtspiText *text_interface = atspi_accessible_get_text_iface(sd->currently_focused);
+       if (text_interface) {
+               DEBUG("->->->->->-> WIDGET CARET MOVED: %s <-<-<-<-<-<-<-", atspi_accessible_get_name(sd->currently_focused, NULL));
+
+               int char_count = (int)atspi_text_get_character_count(text_interface, NULL);
+               int caret_pos = atspi_text_get_caret_offset(text_interface, NULL);
+               if (!caret_pos) {
+                       DEBUG("MIN POSITION REACHED");
+                       if (asprintf(&return_text, "%s %s", (char *)atspi_text_get_text(text_interface, caret_pos, caret_pos + 1, NULL), _("IDS_REACHED_MIN_POS")) < 0) {
+                               ERROR(MEMORY_ERROR);
+                               return NULL;
+                       }
+               } else if (char_count == caret_pos) {
+                       DEBUG("MAX POSITION REACHED");
+                       if (asprintf(&return_text, "%s %s", (char *)atspi_text_get_text(text_interface, caret_pos, caret_pos + 1, NULL), _("IDS_REACHED_MAX_POS")) < 0) {
+                               ERROR(MEMORY_ERROR);
+                               return NULL;
+                       }
+               } else {
+                       if (asprintf(&return_text, "%s", (char *)atspi_text_get_text(text_interface, caret_pos, caret_pos + 1, NULL)) < 0) {
+                               ERROR(MEMORY_ERROR);
+                               return NULL;
+                       }
+               }
+       } else {
+               ERROR(MEMORY_ERROR);
+               return NULL;
+       }
+       return return_text;
+}
+
+static char *spi_on_value_changed_get_text(AtspiEvent * event, void *user_data)
+{
+       Service_Data *sd = (Service_Data *) user_data;
+       char *text_to_read = NULL;
+
+       sd->currently_focused = event->source;
+
+       AtspiValue *value_interface = atspi_accessible_get_value_iface(sd->currently_focused);
+       if (value_interface) {
+               DEBUG("->->->->->-> WIDGET VALUE CHANGED: %s <-<-<-<-<-<-<-", atspi_accessible_get_name(sd->currently_focused, NULL));
+
+               double current_temp_value = (double)atspi_value_get_current_value(value_interface, NULL);
+               if (abs(current_temp_value - atspi_value_get_maximum_value(value_interface, NULL)) < EPS) {
+                       DEBUG("MAX VALUE REACHED");
+                       if (asprintf(&text_to_read, "%.2f %s", current_temp_value, _("IDS_REACHED_MAX_VAL")) < 0) {
+                               ERROR(MEMORY_ERROR);
+                               return NULL;
+                       }
+               } else if (abs(current_temp_value - atspi_value_get_minimum_value(value_interface, NULL)) < EPS) {
+                       DEBUG("MIN VALUE REACHED");
+                       if (asprintf(&text_to_read, "%.2f %s", current_temp_value, _("IDS_REACHED_MIN_VAL")) < 0) {
+                               ERROR(MEMORY_ERROR);
+                               return NULL;
+                       }
+               } else {
+                       if (asprintf(&text_to_read, "%.2f", current_temp_value) < 0) {
+                               ERROR(MEMORY_ERROR);
+                               return NULL;
+                       }
+               }
+       }
+
+       return text_to_read;
+}
+
+char *spi_event_get_text_to_read(AtspiEvent * event, void *user_data)
+{
+       DEBUG("START");
+       Service_Data *sd = (Service_Data *) user_data;
+       char *text_to_read;
+
+       DEBUG("TRACK SIGNAL:%s", sd->tracking_signal_name);
+       DEBUG("WENT EVENT:%s", event->type);
+
+       if (!sd->tracking_signal_name) {
+               ERROR("Invalid tracking signal name");
+               return NULL;
+       }
+
+       if (!strncmp(event->type, sd->tracking_signal_name, strlen(event->type)) && event->detail1 == 1) {
+               text_to_read = spi_on_state_changed_get_text(event, user_data);
+       } else if (!strncmp(event->type, CARET_MOVED_SIG, strlen(event->type))) {
+               text_to_read = spi_on_caret_move_get_text(event, user_data);
+       } else if (!strncmp(event->type, VALUE_CHANGED_SIG, strlen(event->type))) {
+               text_to_read = spi_on_value_changed_get_text(event, user_data);
+       } else {
+               ERROR("Unknown event type");
+               return NULL;
+       }
+
+       return text_to_read;
+}
+
+void spi_event_listener_cb(AtspiEvent * event, void *user_data)
+{
+       DEBUG("START");
+       display_info(event);
+
+       if (!user_data) {
+               ERROR("Invalid parameter");
+               return;
+       }
+
+       char *text_to_read = spi_event_get_text_to_read(event, user_data);
+       if (!text_to_read) {
+               ERROR("Can not prepare text to read");
+               return;
+       }
+       DEBUG("SPEAK: %s", text_to_read);
+       tts_speak(text_to_read, EINA_TRUE);
+
+       free(text_to_read);
+       DEBUG("END");
+}
+
+/**
+  * @brief Initializer for screen-reader atspi listeners
+  *
+  * @param user_data screen-reader internal data
+  *
+**/
+void spi_init(Service_Data * sd)
+{
+       if (!sd) {
+               ERROR("Invalid parameter");
+               return;
+       }
+       DEBUG("--------------------- SPI_init START ---------------------");
+       service_data = sd;
+
+       DEBUG(">>> Creating listeners <<<");
+
+       sd->spi_listener = atspi_event_listener_new(spi_event_listener_cb, service_data, NULL);
+       if (sd->spi_listener == NULL) {
+               DEBUG("FAILED TO CREATE spi state changed listener");
+       }
+       // ---------------------------------------------------------------------------------------------------
+
+       DEBUG("TRACKING SIGNAL:%s", sd->tracking_signal_name);
+
+       gboolean ret1 = atspi_event_listener_register(sd->spi_listener, sd->tracking_signal_name, NULL);
+       if (ret1 == false) {
+               DEBUG("FAILED TO REGISTER spi focus/highlight listener");
+       }
+       GError *error = NULL;
+       gboolean ret2 = atspi_event_listener_register(sd->spi_listener, CARET_MOVED_SIG, &error);
+       if (ret2 == false) {
+               DEBUG("FAILED TO REGISTER spi caret moved listener: %s", error ? error->message : "no error message");
+               if (error)
+                       g_clear_error(&error);
+       }
+
+       gboolean ret3 = atspi_event_listener_register(sd->spi_listener, VALUE_CHANGED_SIG, &error);
+       if (ret3 == false) {
+               DEBUG("FAILED TO REGISTER spi value changed listener: %s", error ? error->message : "no error message");
+               if (error)
+                       g_clear_error(&error);
+       }
+
+       if (ret1 == true && ret2 == true && ret3 == true) {
+               DEBUG("spi listener REGISTERED");
+       }
+
+       DEBUG("---------------------- SPI_init END ----------------------\n\n");
+}
diff --git a/src/screen_reader_switch.c b/src/screen_reader_switch.c
new file mode 100644 (file)
index 0000000..651d49f
--- /dev/null
@@ -0,0 +1,126 @@
+#include "logger.h"
+#include <Eldbus.h>
+
+Eina_Bool screen_reader_switch_enabled_get(Eina_Bool * value)
+{
+       Eldbus_Connection *conn;
+       Eldbus_Object *dobj;
+       Eldbus_Proxy *proxy;
+       Eldbus_Message *req, *reply;
+       const char *errname = NULL, *errmsg = NULL;
+       Eina_Bool ret = EINA_FALSE;
+       Eldbus_Message_Iter *iter;
+
+       eldbus_init();
+
+       if (!(conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION))) {
+               ERROR("Connection to session bus failed");
+               return EINA_FALSE;
+       }
+       if (!(dobj = eldbus_object_get(conn, "org.a11y.Bus", "/org/a11y/bus"))) {
+               ERROR("Failed to create eldbus object for /org/a11y/bus");
+               goto fail_obj;
+       }
+       if (!(proxy = eldbus_proxy_get(dobj, "org.freedesktop.DBus.Properties"))) {
+               ERROR("Failed to create proxy object for 'org.freedesktop.DBus.Properties'");
+               goto fail_proxy;
+       }
+       if (!(req = eldbus_proxy_method_call_new(proxy, "Get"))) {
+               ERROR("Failed to create method call on org.freedesktop.DBus.Properties.Get");
+               goto fail_proxy;
+       }
+       eldbus_message_ref(req);
+
+       if (!eldbus_message_arguments_append(req, "ss", "org.a11y.Status", "ScreenReaderEnabled")) {
+               ERROR("Failed to append message args");
+               goto fail_msg;
+       }
+
+       reply = eldbus_proxy_send_and_block(proxy, req, 100);
+       if (!reply || eldbus_message_error_get(reply, &errname, &errmsg)) {
+               ERROR("Unable to call method org.freedesktop.DBus.Properties.Get: %s %s", errname, errmsg);
+               goto fail_msg;
+       }
+
+       if (!eldbus_message_arguments_get(reply, "v", &iter)) {
+               ERROR("Invalid answer signature");
+               goto fail_msg;
+       } else {
+               if (!eldbus_message_iter_arguments_get(iter, "b", value)) {
+                       ERROR("Invalid variant signature");
+               } else
+                       ret = EINA_TRUE;
+       }
+
+ fail_msg:
+       eldbus_message_unref(req);
+ fail_proxy:
+       eldbus_object_unref(dobj);
+ fail_obj:
+       eldbus_connection_unref(conn);
+
+       eldbus_shutdown();
+
+       return ret;
+}
+
+Eina_Bool screen_reader_switch_enabled_set(Eina_Bool value)
+{
+       Eldbus_Connection *conn;
+       Eldbus_Object *dobj;
+       Eldbus_Proxy *proxy;
+       Eldbus_Message *req, *reply;
+       const char *errname = NULL, *errmsg = NULL;
+       Eina_Bool ret = EINA_FALSE;
+       Eldbus_Message_Iter *iter;
+
+       eldbus_init();
+
+       if (!(conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION))) {
+               ERROR("Connection to session bus failed");
+               return EINA_FALSE;
+       }
+       if (!(dobj = eldbus_object_get(conn, "org.a11y.Bus", "/org/a11y/bus"))) {
+               ERROR("Failed to create eldbus object");
+               goto fail_obj;
+       }
+       if (!(proxy = eldbus_proxy_get(dobj, "org.freedesktop.DBus.Properties"))) {
+               ERROR("Failed to create proxy object for 'org.freedesktop.DBus.Properties'");
+               goto fail_proxy;
+       }
+       if (!(req = eldbus_proxy_method_call_new(proxy, "Set"))) {
+               ERROR("Failed to create method call on org.freedesktop.DBus.Properties.Set");
+               goto fail_proxy;
+       }
+       eldbus_message_ref(req);
+
+       if (!eldbus_message_arguments_append(req, "ss", "org.a11y.Status", "ScreenReaderEnabled")) {
+               ERROR("Failed to append message args");
+               goto fail_msg;
+       }
+       if (!(iter = eldbus_message_iter_container_new(eldbus_message_iter_get(req), 'v', "b"))) {
+               ERROR("Unable to create variant iterator");
+               goto fail_msg;
+       }
+       if (!eldbus_message_iter_arguments_append(iter, "b", value)) {
+               ERROR("Unable to append to variant iterator");
+               goto fail_msg;
+       }
+       if (!eldbus_message_iter_container_close(eldbus_message_iter_get(req), iter)) {
+               ERROR("Failed to close variant iterator");
+               goto fail_msg;
+       }
+       eldbus_proxy_send(proxy, req, NULL, NULL, -1.0);
+       ret = EINA_TRUE;
+
+ fail_msg:
+       eldbus_message_unref(req);
+ fail_proxy:
+       eldbus_object_unref(dobj);
+ fail_obj:
+       eldbus_connection_unref(conn);
+
+       eldbus_shutdown();
+
+       return ret;
+}
diff --git a/src/screen_reader_system.c b/src/screen_reader_system.c
new file mode 100644 (file)
index 0000000..b3a457b
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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.
+ */
+
+#ifndef SCREEN_READER_TV
+
+#define _GNU_SOURCE
+
+#include <device/battery.h>
+#include <device/display.h>
+#include <device/callback.h>
+#include <bluetooth.h>
+#include <tapi_common.h>
+#include <TelNetwork.h>
+#include <vconf.h>
+#include <wifi.h>
+#include <notification.h>
+#include <notification_list.h>
+
+#include "screen_reader.h"
+#include "screen_reader_tts.h"
+#include "smart_notification.h"
+#include "logger.h"
+
+#define MAX_SIM_COUNT 2
+#define DATE_TIME_BUFFER_SIZE 26
+
+TapiHandle *tapi_handle[MAX_SIM_COUNT + 1] = { 0, };
+
+static void device_system_cb(device_callback_e type, void *value, void *user_data);
+
+static void tapi_init(void)
+{
+       int i = 0;
+       char **cp_list = tel_get_cp_name_list();
+
+       if (!cp_list) {
+               ERROR("cp name list is null");
+               return;
+       }
+
+       DEBUG("TAPI INIT");
+       for (i = 0; cp_list[i]; ++i) {
+               tapi_handle[i] = tel_init(cp_list[i]);
+               DEBUG("CP_LIST %d = %s", i, cp_list[i]);
+       }
+
+}
+
+/**
+ * @brief Initializer for smart notifications
+ *
+ */
+void system_notifications_init(void)
+{
+       DEBUG("******************** START ********************");
+       int ret = -1;
+
+       // BATTERY LOW/FULL
+       device_add_callback(DEVICE_CALLBACK_BATTERY_LEVEL, device_system_cb, NULL);
+       // BATTERY CHARGING/NOT-CHARGING
+       device_add_callback(DEVICE_CALLBACK_BATTERY_CHARGING, device_system_cb, NULL);
+       // SCREEN OFF/ON
+       device_add_callback(DEVICE_CALLBACK_DISPLAY_STATE, device_system_cb, NULL);
+
+       ret = bt_initialize();
+       if (ret != BT_ERROR_NONE) {
+               ERROR("ret == %d", ret);
+       }
+
+       ret = wifi_initialize();
+       if (ret != WIFI_ERROR_NONE) {
+               ERROR("ret == %d", ret);
+       }
+
+       tapi_init();
+
+       DEBUG(" ********************* END ********************* ");
+}
+
+/**
+ * @brief Initializer for smart notifications
+ *
+ */
+void system_notifications_shutdown(void)
+{
+       int ret = -1;
+
+       // BATTERY LOW/FULL
+       device_remove_callback(DEVICE_CALLBACK_BATTERY_LEVEL, device_system_cb);
+       // BATTERY CHARGING/NOT-CHARGING
+       device_remove_callback(DEVICE_CALLBACK_BATTERY_CHARGING, device_system_cb);
+       // SCREEN OFF/ON
+       device_remove_callback(DEVICE_CALLBACK_DISPLAY_STATE, device_system_cb);
+
+       ret = bt_deinitialize();
+       if (ret != BT_ERROR_NONE) {
+               ERROR("ret == %d", ret);
+       }
+
+       ret = wifi_deinitialize();
+       if (ret != WIFI_ERROR_NONE) {
+               ERROR("ret == %d", ret);
+               return;
+       }
+}
+
+/**
+ * @brief Device system callback handler
+ *
+ * @param type Device callback type
+ * @param value UNUSED
+ * @param user_data UNUSED
+ */
+static void device_system_cb(device_callback_e type, void *value, void *user_data)
+{
+       if (type == DEVICE_CALLBACK_BATTERY_LEVEL) {
+               device_battery_level_e status;
+               if (device_battery_get_level_status(&status)) {
+                       ERROR("Cannot get battery level status");
+                       return;
+               }
+
+               if (status == DEVICE_BATTERY_LEVEL_LOW) {
+                       tts_speak(_("IDS_SYSTEM_BATTERY_LOW"), EINA_TRUE);
+               } else if (status == DEVICE_BATTERY_LEVEL_CRITICAL) {
+                       tts_speak(_("IDS_SYSTEM_BATTERY_CRITICAL"), EINA_TRUE);
+               } else if (status == DEVICE_BATTERY_LEVEL_FULL) {
+                       tts_speak(_("IDS_SYSTEM_BATTERY_FULL"), EINA_TRUE);
+               }
+       } else if (type == DEVICE_CALLBACK_BATTERY_CHARGING) {
+               bool charging;
+               if (device_battery_is_charging(&charging)) {
+                       ERROR("Cannot check if battery is charging");
+                       return;
+               }
+
+               if (!charging)
+                       tts_speak(_("IDS_SYSTEM_NOT_CHARGING"), EINA_FALSE);
+       } else if (type == DEVICE_CALLBACK_DISPLAY_STATE) {
+               display_state_e state;
+               if (device_display_get_state(&state)) {
+                       ERROR("Cannot check if battery is charging");
+                       return;
+               }
+
+               if (state == DISPLAY_STATE_NORMAL) {
+                       tts_speak(_("IDS_SYSTEM_SCREEN_ON"), EINA_FALSE);
+               } else if (state == DISPLAY_STATE_SCREEN_OFF) {
+                       tts_speak(_("IDS_SYSTEM_SCREEN_OFF"), EINA_FALSE);
+               }
+       }
+}
+
+// ******************************** Indicator info ********************************** //
+
+static int _read_text_get(char *key)
+{
+       int read_text = 0;
+       int ret = -1;
+
+       ret = vconf_get_bool(key, &read_text);
+       if (ret != 0) {
+               ERROR("ret == %d", ret);
+               return true;
+       }
+
+       return read_text;
+}
+
+void device_time_get(void)
+{
+       char buffer[DATE_TIME_BUFFER_SIZE];
+       int disp_12_24 = VCONFKEY_TIME_FORMAT_12;
+       int ret = -1;
+       time_t rawtime = 0;
+       struct tm *timeinfo = NULL;
+
+       if (!_read_text_get(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS_INDICATOR_INFORMATION_TIME)) {
+               return;
+       }
+
+       time(&rawtime);
+       timeinfo = localtime(&rawtime);
+       if (!timeinfo) {
+               ERROR("localtime returns NULL");
+               return;
+       }
+
+       ret = vconf_get_int(VCONFKEY_REGIONFORMAT_TIME1224, &disp_12_24);
+       if (ret != 0) {
+               ERROR("ret == %d", ret);
+       }
+
+       if (disp_12_24 == VCONFKEY_TIME_FORMAT_24) {
+               strftime(buffer, DATE_TIME_BUFFER_SIZE, "Current time: %H %M", timeinfo);
+       } else {
+               strftime(buffer, DATE_TIME_BUFFER_SIZE, "Current time: %I %M %p", timeinfo);
+       }
+
+       DEBUG("Text to say: %s", buffer);
+       tts_speak(buffer, EINA_FALSE);
+}
+
+char *device_error_to_string(int e)
+{
+       switch (e) {
+       case DEVICE_ERROR_NONE:
+               return "DEVICE_ERROR_NONE";
+               break;
+
+       case DEVICE_ERROR_OPERATION_FAILED:
+               return "DEVICE_ERROR_OPERATION_FAILED";
+               break;
+
+       case DEVICE_ERROR_PERMISSION_DENIED:
+               return "DEVICE_ERROR_PERMISSION_DENIED";
+               break;
+
+       case DEVICE_ERROR_INVALID_PARAMETER:
+               return "DEVICE_ERROR_INVALID_PARAMETER";
+               break;
+
+       case DEVICE_ERROR_ALREADY_IN_PROGRESS:
+               return "DEVICE_ERROR_ALREADY_IN_PROGRESS";
+               break;
+
+       case DEVICE_ERROR_NOT_SUPPORTED:
+               return "DEVICE_ERROR_NOT_SUPPORTED";
+               break;
+
+       case DEVICE_ERROR_RESOURCE_BUSY:
+               return "DEVICE_ERROR_RESOURCE_BUSY";
+               break;
+
+       case DEVICE_ERROR_NOT_INITIALIZED:
+               return "DEVICE_ERROR_NOT_INITIALIZED";
+               break;
+
+       default:
+               return _("IDS_SYSTEM_NETWORK_SERVICE_UNKNOWN");
+               break;
+       }
+}
+
+void device_battery_get(void)
+{
+       char *buffer = NULL;
+       char *charging_text = NULL;
+       int percent;
+       bool is_charging = false;
+       int ret = -1;
+
+       if (!_read_text_get(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS_INDICATOR_INFORMATION_BATTERY)) {
+               return;
+       }
+
+       ret = device_battery_is_charging(&is_charging);
+       if (ret != DEVICE_ERROR_NONE) {
+               ERROR("ret == %s", device_error_to_string(ret));
+       }
+
+       if (is_charging) {
+               charging_text = _("IDS_SYSTEM_BATTERY_INFO_CHARGING");
+       } else {
+               charging_text = "";
+       }
+
+       ret = device_battery_get_percent(&percent);
+       if (ret != DEVICE_ERROR_NONE) {
+               ERROR("ret == %s", device_error_to_string(ret));
+               return;
+       }
+
+       if (percent == 100) {
+               ret = asprintf(&buffer, "%s %s", charging_text, _("IDS_SYSTEM_BATTERY_FULLY_CHARGED_STR"));
+               if (ret == 0) {
+                       free(buffer);
+                       ERROR("Buffer length == 0");
+                       return;
+               } else if (ret < 0) {
+                       ERROR("Buffer == NULL");
+                       return;
+               }
+       } else {
+               ret = asprintf(&buffer, "%s %d %% %s", charging_text, percent, _("IDS_SYSTEM_BATTERY_INFO_BATTERY_STR"));
+               if (ret == 0) {
+                       free(buffer);
+                       ERROR("Buffer length == 0");
+                       return;
+               } else if(ret < 0) {
+                       ERROR("Buffer == NULL");
+                       return;
+               }
+       }
+
+       if (!buffer) {
+               ERROR("buf == NULL");
+               return;
+       }
+
+       DEBUG("Text to say: %s", buffer);
+       tts_speak(buffer, EINA_FALSE);
+       free(buffer);
+}
+
+static void _signal_strength_sim_get(void)
+{
+       int i = 0;
+       int val = 0;
+       int ret = -1;
+       int sim_card_count = 0;
+       Eina_Strbuf *str_buf = NULL;
+       char *buffer = NULL;
+       int service_type = TAPI_NETWORK_SERVICE_TYPE_UNKNOWN;
+       char *service_type_text = NULL;
+
+       str_buf = eina_strbuf_new();
+
+       for (i = 0; tapi_handle[i]; ++i) {
+               ++sim_card_count;
+       }
+
+       for (i = 0; tapi_handle[i]; ++i) {
+               ret = tel_get_property_int(tapi_handle[i], TAPI_PROP_NETWORK_SIGNALSTRENGTH_LEVEL, &val);
+               if (ret != TAPI_API_SUCCESS) {
+                       ERROR("Can not get %s", TAPI_PROP_NETWORK_SIGNALSTRENGTH_LEVEL);
+                       val = 0;
+               }
+
+               if (sim_card_count > 1)
+                       eina_strbuf_append_printf(str_buf, "%s %d %s %d; ", _("IDS_SYSTEM_SIGNAL_SIMCARD"), i + 1, _("IDS_SYSTEM_SIGNAL_STRENGTH"), val);
+               else
+                       eina_strbuf_append_printf(str_buf, "%s %d; ", _("IDS_SYSTEM_SIGNAL_STRENGTH"), val);
+               DEBUG("sim: %d TAPI_PROP_NETWORK_SIGNALSTRENGTH_LEVEL %d", i, val);
+
+               ret = tel_get_property_int(tapi_handle[i], TAPI_PROP_NETWORK_SERVICE_TYPE, &service_type);
+               if (ret != TAPI_API_SUCCESS) {
+                       ERROR("Can not get %s", TAPI_PROP_NETWORK_SERVICE_TYPE);
+               }
+
+               switch (service_type) {
+               case TAPI_NETWORK_SERVICE_TYPE_UNKNOWN:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_UNKNOWN");
+                       break;
+
+               case TAPI_NETWORK_SERVICE_TYPE_NO_SERVICE:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_NO_SERVICE");
+                       break;
+
+               case TAPI_NETWORK_SERVICE_TYPE_EMERGENCY:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_EMERGENCY");
+                       break;
+
+               case TAPI_NETWORK_SERVICE_TYPE_SEARCH:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_SEARCHING");
+                       break;
+
+               case TAPI_NETWORK_SERVICE_TYPE_2G:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_2G");
+                       break;
+
+               case TAPI_NETWORK_SERVICE_TYPE_2_5G:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_25G");
+                       break;
+
+               case TAPI_NETWORK_SERVICE_TYPE_2_5G_EDGE:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_EDGE");
+                       break;
+
+               case TAPI_NETWORK_SERVICE_TYPE_3G:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_3G");
+                       break;
+
+               case TAPI_NETWORK_SERVICE_TYPE_HSDPA:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_HSDPA");
+                       break;
+
+               case TAPI_NETWORK_SERVICE_TYPE_LTE:
+                       service_type_text = _("IDS_SYSTEM_NETWORK_SERVICE_LTE");
+                       break;
+               }
+
+               eina_strbuf_append_printf(str_buf, " Service type: %s.", service_type_text);
+       }
+
+       buffer = eina_strbuf_string_steal(str_buf);
+
+       DEBUG("Text to say: %s", buffer);
+       tts_speak(buffer, EINA_FALSE);
+
+       eina_strbuf_string_free(str_buf);
+       free(buffer);
+}
+
+static void _signal_strength_wifi_get(void)
+{
+       int val = 0;
+       int ret = -1;
+       char *buffer = NULL;
+       char *wifi_text = NULL;
+       bool wifi_activated = false;
+       wifi_ap_h ap = NULL;
+
+       ret = wifi_is_activated(&wifi_activated);
+       if (ret != WIFI_ERROR_NONE) {
+               ERROR("ret == %d", ret);
+               return;
+       }
+
+       if (wifi_activated) {
+               ret = wifi_get_connected_ap(&ap);
+               if (ret != WIFI_ERROR_NONE) {
+                       ERROR("ret == %d", ret);
+                       return;
+               }
+
+               if (!ap) {
+                       DEBUG("Text to say: %s %s", _("IDS_SYSTEM_NETWORK_TYPE_WIFI"), "Not connected");
+
+                       ret = asprintf(&buffer, " %s, %s", _("IDS_SYSTEM_NETWORK_TYPE_WIFI"), "Not connected");
+                       if (ret == 0) {
+                               free(buffer);
+                               ERROR("Buffer length == 0");
+                               return;
+                       } else if (ret < 0) {
+                               ERROR("Buffer == NULL");
+                               return;
+                       }
+
+                       tts_speak(buffer, EINA_FALSE);
+                       free(buffer);
+                       return;
+               }
+
+               ret = wifi_ap_get_rssi(ap, &val);
+               if (ret != WIFI_ERROR_NONE) {
+                       ERROR("ret == %d", ret);
+                       wifi_ap_destroy(ap);
+                       return;
+               }
+
+               switch (val) {
+               case 0:
+                       wifi_text = _("IDS_SYSTEM_WIFI_SIGNAL_NO_SIGNAL");
+                       break;
+
+               case 1:
+                       wifi_text = _("IDS_SYSTEM_WIFI_SIGNAL_POOR");
+                       break;
+
+               case 2:
+                       wifi_text = _("IDS_SYSTEM_WIFI_SIGNAL_WEAK");
+                       break;
+
+               case 3:
+                       wifi_text = _("IDS_SYSTEM_WIFI_SIGNAL_MEDIUM");
+                       break;
+
+               case 4:
+                       wifi_text = _("IDS_SYSTEM_WIFI_SIGNAL_GOOD");
+                       break;
+               }
+
+               if (!asprintf(&buffer, " %s, %s", _("IDS_SYSTEM_NETWORK_TYPE_WIFI"), wifi_text)) {
+                       ERROR("buffer length == 0");
+                       wifi_ap_destroy(ap);
+                       return;
+               }
+
+               DEBUG("Text to say: %s", buffer);
+               tts_speak(buffer, EINA_FALSE);
+               free(buffer);
+               wifi_ap_destroy(ap);
+       }
+}
+
+void device_signal_strenght_get(void)
+{
+       if (!_read_text_get(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS_INDICATOR_INFORMATION_SIGNAL_STRENGHT)) {
+               return;
+       }
+       _signal_strength_sim_get();
+       _signal_strength_wifi_get();
+}
+
+void device_missed_events_get(void)
+{
+       notification_list_h list = NULL;
+       notification_list_h elem = NULL;
+       notification_h noti = NULL;
+       int ret = -1;
+       char *noti_count_text = NULL;
+       int noti_count = 0;
+       int current_noti_count = 0;
+       char *buffer = NULL;
+
+       if (!_read_text_get(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS_INDICATOR_INFORMATION_MISSED_EVENTS)) {
+               return;
+       }
+
+       ret = notification_get_list(NOTIFICATION_TYPE_NONE, -1, &list);
+       if (ret != NOTIFICATION_ERROR_NONE) {
+               ERROR("ret == %d", ret);
+               return;
+       }
+
+       elem = notification_list_get_head(list);
+
+       while (elem) {
+               noti = notification_list_get_data(elem);
+               notification_get_text(noti, NOTIFICATION_TEXT_TYPE_EVENT_COUNT, &noti_count_text);
+
+               if (noti_count_text) {
+                       current_noti_count = atoi(noti_count_text);
+                       if (current_noti_count > 0) {
+                               noti_count += current_noti_count;
+                       } else {
+                               noti_count++;
+                       }
+               } else {
+                       noti_count++;
+               }
+
+               elem = notification_list_get_next(elem);
+       }
+
+       if (noti_count == 0) {
+               tts_speak(_("IDS_SYSTEM_NOTIFICATIONS_UNREAD_0"), EINA_FALSE);
+       } else if (noti_count == 1) {
+               tts_speak(_("IDS_SYSTEM_NOTIFICATIONS_UNREAD_1"), EINA_FALSE);
+       } else {
+               DEBUG("%d %s", noti_count, _("IDS_SYSTEM_NOTIFICATIONS_UNREAD_MANY"));
+
+               if (asprintf(&buffer, "%d %s", noti_count, _("IDS_SYSTEM_NOTIFICATIONS_UNREAD_MANY"))) {
+                       ERROR("buffer length equals 0");
+               }
+
+               tts_speak(buffer, EINA_FALSE);
+               free(buffer);
+       }
+
+       ret = notification_free_list(list);
+       if (ret != NOTIFICATION_ERROR_NONE) {
+               ERROR("ret == %d", ret);
+       }
+}
+
+void device_date_get(void)
+{
+       char buffer[DATE_TIME_BUFFER_SIZE];
+       int date_format = SETTING_DATE_FORMAT_DD_MM_YYYY;
+       int ret = -1;
+       time_t rawtime = 0;
+       struct tm *timeinfo = NULL;
+
+       if (!_read_text_get(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS_INDICATOR_INFORMATION_DATE)) {
+               return;
+       }
+
+       time(&rawtime);
+       timeinfo = localtime(&rawtime);
+       if (!timeinfo) {
+               ERROR("localtime returns NULL");
+               return;
+       }
+
+       strftime(buffer, DATE_TIME_BUFFER_SIZE, "%Y:%m:%d %H:%M:%S", timeinfo);
+
+       ret = vconf_get_int(VCONFKEY_SETAPPL_DATE_FORMAT_INT, &date_format);
+       if (ret != 0) {
+               ERROR("ret == %d", ret);
+       }
+
+       switch (date_format) {
+       case SETTING_DATE_FORMAT_DD_MM_YYYY:
+               strftime(buffer, DATE_TIME_BUFFER_SIZE, "%d %B %Y", timeinfo);
+               break;
+
+       case SETTING_DATE_FORMAT_MM_DD_YYYY:
+               strftime(buffer, DATE_TIME_BUFFER_SIZE, "%B %d %Y", timeinfo);
+               break;
+
+       case SETTING_DATE_FORMAT_YYYY_MM_DD:
+               strftime(buffer, DATE_TIME_BUFFER_SIZE, "%Y %B %d", timeinfo);
+               break;
+
+       case SETTING_DATE_FORMAT_YYYY_DD_MM:
+               strftime(buffer, DATE_TIME_BUFFER_SIZE, "%Y %d %B", timeinfo);
+               break;
+       }
+
+       DEBUG("Text to say: %s", buffer);
+       tts_speak(buffer, EINA_FALSE);
+}
+
+static bool bonded_device_count_cb(bt_device_info_s * device_info, void *user_data)
+{
+       int *device_count = (int *)user_data;
+
+       (*device_count)++;
+
+       return true;
+}
+
+static bool bonded_device_get_cb(bt_device_info_s * device_info, void *user_data)
+{
+       char **device_name = (char **)user_data;
+
+       if (asprintf(device_name, "%s connected", device_info->remote_name)) {
+               ERROR("buffer length == 0");
+       }
+
+       return false;
+}
+
+void device_bluetooth_get(void)
+{
+       char *buffer = NULL;
+       int device_count = 0;
+
+       if (!_read_text_get(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS_INDICATOR_INFORMATION_BLUETOOTH)) {
+               return;
+       }
+
+       bt_adapter_state_e adapter_state = BT_ADAPTER_DISABLED;
+       int ret = bt_adapter_get_state(&adapter_state);
+       if (ret != BT_ERROR_NONE) {
+               ERROR("ret == %d", ret);
+               return;
+       }
+
+       if (adapter_state == BT_ADAPTER_DISABLED) {
+               DEBUG("Text to say: %s", _("IDS_SYSTEM_BT_BLUETOOTH_OFF"));
+               tts_speak(_("IDS_SYSTEM_BT_BLUETOOTH_OFF"), EINA_FALSE);
+               return;
+       } else {
+               bt_adapter_foreach_bonded_device(bonded_device_count_cb, (void *)&device_count);
+
+               if (device_count == 0) {
+                       if (!asprintf(&buffer, _("IDS_SYSTEM_BT_NO_DEVICES_CONNECTED"))) {
+                               ERROR("buffer length == 0");
+                       }
+               } else if (device_count == 1) {
+                       bt_adapter_foreach_bonded_device(bonded_device_get_cb, &buffer);
+               } else {
+                       if (asprintf(&buffer, "%d %s", device_count, _("IDS_SYSTEM_BT_DEVICES_CONNECTED_COUNT"))) {
+                               ERROR("buffer length == 0");
+                       }
+               }
+
+               DEBUG("Text to say: %s", buffer);
+               tts_speak(buffer, EINA_FALSE);
+               free(buffer);
+       }
+}
+
+#endif
diff --git a/src/screen_reader_tts.c b/src/screen_reader_tts.c
new file mode 100644 (file)
index 0000000..68c82ce
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <Ecore.h>
+#include "screen_reader_tts.h"
+#include "screen_reader_vconf.h"
+#include "logger.h"
+
+// ---------------------------- DEBUG HELPERS ------------------------------
+
+#define FLUSH_LIMIT 1
+
+static int last_utt_id;
+static Eina_Bool pause_state = EINA_FALSE;
+static Eina_Bool flush_flag = EINA_FALSE;
+static Eina_Strbuf *txt_keep_buff = NULL;
+
+static void (*on_utterance_end) (void);
+
+static void _text_keep(const char *txt)
+{
+       if (!txt_keep_buff)
+               return;
+       if (eina_strbuf_length_get(txt_keep_buff) > 0)
+               eina_strbuf_append(txt_keep_buff, ", ");
+       eina_strbuf_append(txt_keep_buff, txt);
+}
+
+static char *get_tts_error(int r)
+{
+       switch (r) {
+       case TTS_ERROR_NONE:
+               {
+                       return "no error";
+               }
+       case TTS_ERROR_INVALID_PARAMETER:
+               {
+                       return "inv param";
+               }
+       case TTS_ERROR_OUT_OF_MEMORY:
+               {
+                       return "out of memory";
+               }
+       case TTS_ERROR_OPERATION_FAILED:
+               {
+                       return "oper failed";
+               }
+       case TTS_ERROR_INVALID_STATE:
+               {
+                       return "inv state";
+               }
+       default:
+               {
+                       return "uknown error";
+               }
+       }
+}
+
+static char *get_tts_state(tts_state_e r)
+{
+       switch (r) {
+       case TTS_STATE_CREATED:
+               {
+                       return "created";
+               }
+       case TTS_STATE_READY:
+               {
+                       return "ready";
+               }
+       case TTS_STATE_PLAYING:
+               {
+                       return "playing";
+               }
+       case TTS_STATE_PAUSED:
+               {
+                       return "pause";
+               }
+       default:
+               {
+                       return "uknown state";
+               }
+       }
+}
+
+//-------------------------------------------------------------------------------------------------
+
+void set_utterance_cb(void (*uter_cb) (void))
+{
+       on_utterance_end = uter_cb;
+}
+
+bool get_supported_voices_cb(tts_h tts, const char *language, int voice_type, void *user_data)
+{
+       DEBUG("LANG: %s; TYPE: %d", language, voice_type);
+
+       Service_Data *sd = user_data;
+       Voice_Info *vi = calloc(1, sizeof(Voice_Info));
+       if (!vi) {
+               ERROR(MEMORY_ERROR);
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       if (asprintf(&vi->language, "%s", language) < 0) {
+               free(vi);
+               ERROR(MEMORY_ERROR);
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       vi->voice_type = voice_type;
+
+       sd->available_languages = eina_list_append(sd->available_languages, vi);
+
+       return ECORE_CALLBACK_RENEW;
+}
+
+static void __tts_test_utt_started_cb(tts_h tts, int utt_id, void *user_data)
+{
+       DEBUG("Utterance started : utt id(%d) \n", utt_id);
+       return;
+}
+
+static void __tts_test_utt_completed_cb(tts_h tts, int utt_id, void *user_data)
+{
+       DEBUG("Utterance completed : utt id(%d) \n", utt_id);
+       if (last_utt_id - utt_id > FLUSH_LIMIT)
+               flush_flag = EINA_TRUE;
+       else {
+               if (flush_flag)
+                       flush_flag = EINA_FALSE;
+       }
+
+#ifndef SCREEN_READER_TV
+       if (last_utt_id == utt_id) {
+               DEBUG("LAST UTTERANCE");
+               pause_state = EINA_FALSE;
+               on_utterance_end();
+       }
+#endif
+
+       return;
+}
+
+bool tts_init(void *data)
+{
+       DEBUG("--------------------- TTS_init START ---------------------");
+       Service_Data *sd = data;
+
+       int r = tts_create(&sd->tts);
+       DEBUG("Create tts %d (%s)", r, get_tts_error(r));
+
+       r = tts_set_mode(sd->tts, TTS_MODE_SCREEN_READER);
+       DEBUG("Set tts mode SR %d (%s)", r, get_tts_error(r));
+
+       r = tts_prepare(sd->tts);
+       DEBUG("Prepare tts %d (%s)", r, get_tts_error(r));
+
+       tts_set_state_changed_cb(sd->tts, state_changed_cb, sd);
+
+       tts_set_utterance_started_cb(sd->tts, __tts_test_utt_started_cb, sd);
+       tts_set_utterance_completed_cb(sd->tts, __tts_test_utt_completed_cb, sd);
+
+       DEBUG("---------------------- TTS_init END ----------------------\n\n");
+       txt_keep_buff = eina_strbuf_new();
+       return true;
+}
+
+Eina_Bool tts_pause_get(void)
+{
+       DEBUG("PAUSE STATE: %d", pause_state);
+       return pause_state;
+}
+
+void tts_stop_set(void)
+{
+       Service_Data *sd = get_pointer_to_service_data_struct();
+       tts_stop(sd->tts);
+}
+
+Eina_Bool tts_pause_set(Eina_Bool pause_switch)
+{
+       Service_Data *sd = get_pointer_to_service_data_struct();
+       if (!sd)
+               return EINA_FALSE;
+
+       if (pause_switch) {
+               pause_state = EINA_TRUE;
+
+               if (tts_pause(sd->tts)) {
+                       pause_state = EINA_FALSE;
+                       return EINA_FALSE;
+               }
+       } else if (!pause_switch) {
+               pause_state = EINA_FALSE;
+
+               if (tts_play(sd->tts)) {
+                       pause_state = EINA_TRUE;
+                       return EINA_FALSE;
+               }
+       }
+       return EINA_TRUE;
+}
+
+void tts_speak(char *text_to_speak, Eina_Bool flush_switch)
+{
+       int ret = 0;
+       Service_Data *sd = get_pointer_to_service_data_struct();
+       int speak_id;
+
+       if (!sd)
+               return;
+       tts_state_e state;
+       tts_get_state(sd->tts, &state);
+
+       if (state != TTS_STATE_PLAYING && state != TTS_STATE_PAUSED && state != TTS_STATE_READY) {
+               if (text_to_speak)
+                       _text_keep(text_to_speak);
+               return;
+       }
+
+       if (flush_flag || flush_switch) {
+               if (state == TTS_STATE_PLAYING || state == TTS_STATE_PAUSED) {
+                       ret = tts_stop(sd->tts);
+                       if (TTS_ERROR_NONE != ret) {
+                               DEBUG("Fail to stop TTS: resultl(%d)", ret);
+                       }
+               }
+       }
+
+       DEBUG("tts_speak\n");
+       DEBUG("text to say:%s\n", text_to_speak);
+       if (!text_to_speak)
+               return;
+       if (!text_to_speak[0])
+               return;
+
+       if ((ret = tts_add_text(sd->tts, text_to_speak, NULL, TTS_VOICE_TYPE_AUTO, TTS_SPEED_AUTO, &speak_id))) {
+               switch (ret) {
+               case TTS_ERROR_INVALID_PARAMETER:
+                       DEBUG("FAILED tts_add_text: error: TTS_ERROR_INVALID_PARAMETER");
+                       break;
+               case TTS_ERROR_INVALID_STATE:
+                       DEBUG("FAILED tts_add_text: error: TTS_ERROR_INVALID_STATE, tts_state: %d", state);
+                       break;
+               case TTS_ERROR_INVALID_VOICE:
+                       DEBUG("FAILED tts_add_text: error: TTS_ERROR_INVALID_VOICE");
+                       break;
+               case TTS_ERROR_OPERATION_FAILED:
+                       DEBUG("FAILED tts_add_text: error: TTS_ERROR_OPERATION_FAILED");
+                       break;
+               case TTS_ERROR_NOT_SUPPORTED:
+                       DEBUG("FAILED tts_add_text: error: TTS_ERROR_NOT_SUPPORTED");
+                       break;
+               default:
+                       DEBUG("FAILED tts_add_text: error: not recognized");
+               }
+               return;
+       }
+
+       DEBUG("added id to:%d\n", speak_id);
+       last_utt_id = speak_id;
+       tts_play(sd->tts);
+}
+
+Eina_Bool update_supported_voices(void *data)
+{
+       DEBUG("START");
+       tts_state_e state;
+
+       Service_Data *sd = data;
+
+       int res = tts_get_state(sd->tts, &state);
+
+       if (res != TTS_ERROR_NONE) {
+               DEBUG("CANNOT RETRIVE STATE");
+               return EINA_FALSE;
+       }
+
+       if (state == TTS_STATE_READY) {
+               tts_foreach_supported_voices(sd->tts, get_supported_voices_cb, sd);
+       } else {
+               sd->update_language_list = EINA_TRUE;
+       }
+
+       DEBUG("END");
+       return EINA_TRUE;
+}
+
+void state_changed_cb(tts_h tts, tts_state_e previous, tts_state_e current, void *user_data)
+{
+       if (pause_state) {
+               DEBUG("TTS is currently paused. Resume to start reading");
+               return;
+       }
+
+       DEBUG("++++++++++++++++state_changed_cb\n++++++++++++++++++");
+       DEBUG("current state:%s and previous state:%s\n", get_tts_state(current), get_tts_state(previous));
+       Service_Data *sd = user_data;
+
+       if (TTS_STATE_CREATED == previous && TTS_STATE_READY == current) {
+
+               update_supported_voices(sd);
+
+               char *txt;
+
+               if (!txt_keep_buff)
+                       return;
+               if (!eina_strbuf_length_get(txt_keep_buff))
+                       return;
+
+               txt = eina_strbuf_string_steal(txt_keep_buff);
+               eina_strbuf_free(txt_keep_buff);
+               txt_keep_buff = NULL;
+               tts_speak(txt, EINA_FALSE);
+               free(txt);
+       }
+}
+
+void spi_stop(void *data)
+{
+       if (!data) {
+               ERROR("Invalid parameter");
+               return;
+       }
+
+       Service_Data *sd = data;
+       sd->update_language_list = false;
+       free((char *)sd->text_from_dbus);
+       free(sd->current_value);
+       sd->text_from_dbus = NULL;
+       sd->current_value = NULL;
+       tts_stop(sd->tts);
+}
diff --git a/src/screen_reader_vconf.c b/src/screen_reader_vconf.c
new file mode 100644 (file)
index 0000000..b2daef2
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 <Elementary.h>
+#include <vconf.h>
+#include <service_app.h>
+#include "screen_reader_vconf.h"
+#include "screen_reader_spi.h"
+#include "logger.h"
+
+#ifdef RUN_IPC_TEST_SUIT
+#include "test_suite/test_suite.h"
+#endif
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "SCREEN READER VCONF"
+
+keylist_t *keys = NULL;
+
+// ------------------------------ vconf callbacks----------------------
+
+void app_termination_cb(keynode_t * node, void *user_data)
+{
+       DEBUG("START");
+       DEBUG("Application terminate %d", !node->value.i);
+
+       Service_Data *service_data = user_data;
+       service_data->run_service = node->value.i;
+
+       if (service_data->run_service == 0) {
+               service_app_exit();
+       }
+
+       DEBUG("END");
+}
+
+void display_language_cb(keynode_t * node, void *user_data)
+{
+       DEBUG("START");
+       DEBUG("Trying to set LC_MESSAGES to: %s", node->value.s);
+
+       Service_Data *sd = user_data;
+       snprintf(sd->display_language, LANGUAGE_NAME_SIZE, "%s", node->value.s);
+       //to make gettext work
+       setenv("LC_MESSAGES", sd->display_language, 1);
+
+       DEBUG("END");
+}
+
+// --------------------------------------------------------------------
+
+int get_key_values(Service_Data * sd)
+{
+       DEBUG("START");
+       int to_ret = 0;
+
+       char *display_language = vconf_get_str("db/menu_widget/language");
+       if (display_language) {
+               snprintf(sd->display_language, LANGUAGE_NAME_SIZE, "%s", display_language);
+               //to make gettext work
+               setenv("LC_MESSAGES", sd->display_language, 1);
+               free(display_language);
+       } else
+               WARNING("Can't get db/menu_widget/language value");
+
+       DEBUG("SCREEN READER DATA SET TO: Display_Language: %s, Tracking signal: %s;", sd->display_language, sd->tracking_signal_name);
+
+       DEBUG("END");
+       return to_ret;
+}
+
+int _set_vconf_callback_and_print_message_on_error_and_return_error_code(const char *in_key, vconf_callback_fn cb, void *user_data)
+{
+       int ret = vconf_notify_key_changed(in_key, cb, user_data);
+       if (ret != 0)
+               DEBUG("Could not add notify callback to %s key", in_key);
+
+       return ret;
+}
+
+bool vconf_init(Service_Data * service_data)
+{
+       DEBUG("--------------------- VCONF_init START ---------------------");
+       int ret = 0;
+
+       if (vconf_set(keys)) {
+               DEBUG("nothing is written\n");
+       } else {
+               DEBUG("everything is written\n");
+       }
+
+       vconf_keylist_free(keys);
+       // ----------------------------------------------------------------------------------
+
+       ret = get_key_values(service_data);
+       if (ret != 0) {
+               DEBUG("Could not set data from vconf: %d", ret);
+       }
+
+       _set_vconf_callback_and_print_message_on_error_and_return_error_code("db/menu_widget/language", display_language_cb, service_data);
+
+       DEBUG("---------------------- VCONF_init END ----------------------\n\n");
+       return true;
+}
diff --git a/src/smart_notification.c b/src/smart_notification.c
new file mode 100644 (file)
index 0000000..e519797
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 <Ecore.h>
+#include <Ecore_Evas.h>
+#include <Evas.h>
+#include <atspi/atspi.h>
+#include <tone_player.h>
+#include "logger.h"
+#include "screen_reader_tts.h"
+#include "screen_reader_haptic.h"
+#include "smart_notification.h"
+
+#define RED  "\x1B[31m"
+#define RESET "\033[0m"
+
+static Eina_Bool status = EINA_FALSE;
+
+static void _smart_notification_focus_chain_end(void);
+static void _smart_notification_realized_items(int start_idx, int end_idx);
+
+/**
+ * @brief Smart Notifications event handler
+ *
+ * @param nt Notification event type
+ * @param start_index int first visible items index smart_notification_realized_items
+ * @param end_index int last visible items index used for smart_notification_realized_items
+ */
+void smart_notification(Notification_Type nt, int start_index, int end_index)
+{
+       DEBUG("START");
+       if (!status)
+               return;
+
+       switch (nt) {
+       case FOCUS_CHAIN_END_NOTIFICATION_EVENT:
+               _smart_notification_focus_chain_end();
+               break;
+       case REALIZED_ITEMS_NOTIFICATION_EVENT:
+               _smart_notification_realized_items(start_index, end_index);
+               break;
+       default:
+               DEBUG("Gesture type %d not handled in switch", nt);
+       }
+}
+
+/**
+ * @brief Used for getting first and last index of visible items
+ *
+ * @param scrollable_object AtspiAccessible object on which scroll was triggered
+ * @param start_index int first visible items index smart_notification_realized_items
+ * @param end_index int last visible items index used for smart_notification_realized_items
+ */
+void get_realized_items_count(AtspiAccessible * scrollable_object, int *start_idx, int *end_idx)
+{
+       DEBUG("START");
+       int count_child, jdx;
+       AtspiAccessible *child_iter;
+       AtspiStateType state = ATSPI_STATE_SHOWING;
+
+       if (!scrollable_object) {
+               DEBUG("No scrollable object");
+               return;
+       }
+
+       count_child = atspi_accessible_get_child_count(scrollable_object, NULL);
+
+       for (jdx = 0; jdx < count_child; jdx++) {
+               child_iter = atspi_accessible_get_child_at_index(scrollable_object, jdx, NULL);
+               if (!child_iter)
+                       continue;
+
+               AtspiStateSet *state_set = atspi_accessible_get_state_set(child_iter);
+
+               gboolean is_visible = atspi_state_set_contains(state_set, state);
+               if (is_visible) {
+                       *start_idx = jdx;
+                       DEBUG("Item with index %d is visible", jdx);
+               } else
+                       DEBUG("Item with index %d is NOT visible", jdx);
+       }
+       *end_idx = *start_idx + 8;
+}
+
+/**
+ * @brief Scroll-start/Scroll-end event callback
+ *
+ * @param event AtspiEvent
+ * @param user_data UNUSED
+ */
+
+static void _scroll_event_cb(AtspiEvent * event, gpointer user_data)
+{
+       if (!status)
+               return;
+
+       int start_index, end_index;
+       start_index = 0;
+       end_index = 0;
+
+       gchar *role_name = atspi_accessible_get_role_name(event->source, NULL);
+       fprintf(stderr, "Event: %s: %d, obj: %p: role: %s\n", event->type, event->detail1, event->source, role_name);
+       g_free(role_name);
+
+       if (!strcmp(event->type, "object:scroll-start")) {
+               DEBUG("Scrolling started");
+               tts_speak(_("IDS_SCROLLING_STARTED"), EINA_TRUE);
+       } else if (!strcmp(event->type, "object:scroll-end")) {
+               DEBUG("Scrolling finished");
+               tts_speak(_("IDS_SCROLLING_FINISHED"), EINA_FALSE);
+               get_realized_items_count((AtspiAccessible *) event->source, &start_index, &end_index);
+               _smart_notification_realized_items(start_index, end_index);
+       }
+}
+
+/**
+ * @brief Initializer for smart notifications
+ *
+ *
+ */
+void smart_notification_init(void)
+{
+       DEBUG("Smart Notification init!");
+
+       AtspiEventListener *listener;
+
+       listener = atspi_event_listener_new(_scroll_event_cb, NULL, NULL);
+       atspi_event_listener_register(listener, "object:scroll-start", NULL);
+       atspi_event_listener_register(listener, "object:scroll-end", NULL);
+
+       haptic_module_init();
+
+       status = EINA_TRUE;
+}
+
+/**
+ * @brief Smart notifications shutdown
+ *
+ */
+void smart_notification_shutdown(void)
+{
+       status = EINA_FALSE;
+}
+
+/**
+ * @brief Smart notifications focus chain event handler
+ *
+ */
+static void _smart_notification_focus_chain_end(void)
+{
+       if (!status)
+               return;
+
+       DEBUG(RED "Smart notification - FOCUS CHAIN END" RESET);
+
+       tone_player_stop(0);
+       tone_player_start(TONE_TYPE_SUP_CONFIRM, SOUND_TYPE_MEDIA, 200, NULL);
+}
+
+/**
+ * @brief Smart notifications realized items event handler
+ *
+ */
+static void _smart_notification_realized_items(int start_idx, int end_idx)
+{
+       if (!status)
+               return;
+
+       if (start_idx == end_idx)
+               return;
+
+       DEBUG(RED "Smart notification - Visible items notification" RESET);
+
+       char buf[256];
+
+       snprintf(buf, sizeof(buf), _("IDS_REACHED_ITEMS_NOTIFICATION"), start_idx, end_idx);
+
+       tts_speak(buf, EINA_FALSE);
+}
diff --git a/src/window_tracker.c b/src/window_tracker.c
new file mode 100644 (file)
index 0000000..ebdb9be
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 <string.h>
+#include "window_tracker.h"
+#include "logger.h"
+#include <Ecore_X.h>
+#include <Ecore_X_Atoms.h>
+
+static Window_Tracker_Cb user_cb;
+static void *user_data;
+static AtspiEventListener *listener;
+static AtspiAccessible *last_active_win;
+
+static void _on_atspi_window_cb(const AtspiEvent * event)
+{
+       DEBUG("Event: %s: %s", event->type, atspi_accessible_get_name(event->source, NULL));
+
+       if (!strcmp(event->type, "window:activate") && last_active_win != event->source)        //if we got activate 2 times
+       {
+
+               if (user_cb)
+                       user_cb(user_data, event->source);
+               last_active_win = event->source;
+       }
+}
+
+static AtspiAccessible *_get_active_win(void)
+{
+       DEBUG("START");
+       int i, j, desktop_children_count, app_children_count;
+       last_active_win = NULL;
+       AtspiAccessible *desktop = atspi_get_desktop(0);
+       if (!desktop) {
+               ERROR("DESKTOP NOT FOUND");
+               return NULL;
+       }
+
+       Ecore_X_Window focus_window = ecore_x_window_focus_get();
+       unsigned int active_window_pid = 0;
+       if (focus_window) {
+               //invoking atspi_accessible_get_child_count for non active apps results in very long screen-reader startup
+               //not active apps have low priority and dbus calls take a lot of time (a few hundred ms per call)
+               //Hence we first try to determine accessible window using pid of currently focused window
+               if (!ecore_x_window_prop_card32_get(focus_window, ECORE_X_ATOM_NET_WM_PID, &active_window_pid, 1))
+                       active_window_pid = 0;
+               if (active_window_pid)
+                       DEBUG("First we will try filter apps by PID: %i", active_window_pid);
+       }
+       desktop_children_count = atspi_accessible_get_child_count(desktop, NULL);
+       for (i = 0; i < desktop_children_count; i++) {
+               AtspiAccessible *app = atspi_accessible_get_child_at_index(desktop, i, NULL);
+
+               if (active_window_pid == 0 || active_window_pid == atspi_accessible_get_process_id(app, NULL))
+                       app_children_count = atspi_accessible_get_child_count(app, NULL);
+               else
+                       app_children_count = 0;
+               for (j = 0; j < app_children_count; j++) {
+                       AtspiAccessible *win = atspi_accessible_get_child_at_index(app, j, NULL);
+                       AtspiStateSet *states = atspi_accessible_get_state_set(win);
+                       AtspiRole role = atspi_accessible_get_role(win, NULL);
+                       if ((atspi_state_set_contains(states, ATSPI_STATE_ACTIVE)) && (role == ATSPI_ROLE_WINDOW))
+                               last_active_win = win;
+
+                       g_object_unref(states);
+                       g_object_unref(win);
+
+                       if (last_active_win)
+                               break;
+               }
+               g_object_unref(app);
+               if (active_window_pid > 0 && (i == desktop_children_count - 1)) {
+                       // we are in last iteration and we should fall back to normal iteration over child windows
+                       // without filtering by focus windows PID
+                       i = -1;
+                       active_window_pid = 0;
+               }
+               if (last_active_win)
+                       break;
+       }
+       g_object_unref(desktop);
+       DEBUG("END last_active_win: %p", last_active_win);
+       return last_active_win;
+}
+
+void window_tracker_init(void)
+{
+       DEBUG("START");
+       listener = atspi_event_listener_new_simple(_on_atspi_window_cb, NULL);
+       atspi_event_listener_register(listener, "window:activate", NULL);
+}
+
+void window_tracker_shutdown(void)
+{
+       DEBUG("START");
+       atspi_event_listener_deregister(listener, "window:activate", NULL);
+       g_object_unref(listener);
+       listener = NULL;
+       user_cb = NULL;
+       user_data = NULL;
+       last_active_win = NULL;
+}
+
+void window_tracker_register(Window_Tracker_Cb cb, void *data)
+{
+       DEBUG("START");
+       user_cb = cb;
+       user_data = data;
+}
+
+void window_tracker_active_window_request(void)
+{
+       DEBUG("START");
+       _get_active_win();
+       if (user_cb)
+               user_cb(user_data, last_active_win);
+}
diff --git a/tests/CMakeLists.sub b/tests/CMakeLists.sub
new file mode 100755 (executable)
index 0000000..249e698
--- /dev/null
@@ -0,0 +1,26 @@
+## PROJECT NAME
+PROJECT(screen-reader-tests C)
+
+## INCLUDES
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
+
+## DEFINITIONS
+ADD_DEFINITIONS("")
+
+## LIBRARY PATH
+SET(SLP_LD_PATH_FLAGS "")
+
+## LIBRARY
+SET(SLP_LD_FLAGS "")
+
+## DEBUG
+SET(SLP_DEBUG_FLAGS "-g")
+
+## OPTIMIZATION
+SET(SLP_OPT_FLAGS "-O0")
+
+## COMPILER FLAGS
+SET(SLP_COMPILER_FLAGS "-Wall -Wunused")
+
+## LINKER FLAGS
+SET(SLP_LINKER_FLAGS "")
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..881fdb5
--- /dev/null
@@ -0,0 +1,43 @@
+
+INCLUDE_DIRECTORIES(.)
+add_library(atspi
+            atspi/atspi.h
+            atspi/atspi.c
+            )
+
+INCLUDE(CMakeLists.sub)
+set_target_properties(atspi PROPERTIES
+         LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/atspi)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(tests REQUIRED
+    gobject-2.0
+    dlog
+    dbus-1
+    ecore
+    eldbus
+    eina
+    tts
+    check
+    vconf
+    elementary
+    capi-appfw-service-application
+)
+
+FOREACH(flag ${tests_CFLAGS})
+       SET(EXTRA_CFLAGS_TESTS "${EXTRA_CFLAGS_TESTS} ${flag}")
+ENDFOREACH(flag)
+
+
+SET(CMAKE_C_FLAGS "${SLP_DEBUG_FLAGS} ${SLP_OPT_FLAGS} ${CMAKE_C_FLAGS_DUMP} ${EXTRA_CFLAGS_TESTS} ${SLP_COMPILER_FLAGS}")
+SET(CMAKE_CXX_FLAGS "${SLP_DEBUG_FLAGS} ${SLP_OPT_FLAGS} ${CMAKE_CXX_FLAGS_DUMP} ${EXTRA_CFLAGS_TESTS} ${SLP_COMPILER_FLAGS}")
+
+SET(TESTED_SRCS ${CMAKE_SOURCE_DIR}/src/screen_reader.c
+                ${CMAKE_SOURCE_DIR}/src/screen_reader_vconf.c
+                ${CMAKE_SOURCE_DIR}/src/screen_reader_spi.c
+                ${CMAKE_SOURCE_DIR}/src/screen_reader_tts.c
+                ${CMAKE_SOURCE_DIR}/src/flat_navi.c)
+ADD_DEFINITIONS(-DSCREEN_READER_FLAT_NAVI_TEST_DUMMY_IMPLEMENTATION)
+
+ADD_EXECUTABLE(smart_navi_test_suite smart_navi_suite.c ${TESTED_SRCS})
+TARGET_LINK_LIBRARIES(smart_navi_test_suite atspi ${tests_LDFLAGS} ${SLP_LD_PATH_FLAGS} ${SLP_LD_FLAGS} ${SLP_LINKER_FLAGS})
diff --git a/tests/atspi/atspi.c b/tests/atspi/atspi.c
new file mode 100644 (file)
index 0000000..096a51f
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 "atspi.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static AtspiValue *value = NULL;
+static AtspiText *text = NULL;
+static AtspiEditableText *editable_text = NULL;
+static AtspiAction *action = NULL;
+//static AtspiComponent *component = NULL;
+
+G_DEFINE_TYPE(AtspiAccessible, atspi_accessible, G_TYPE_OBJECT);
+G_DEFINE_TYPE(AtspiAction, atspi_action, G_TYPE_OBJECT);
+G_DEFINE_TYPE(AtspiComponent, atspi_component, G_TYPE_OBJECT);
+G_DEFINE_TYPE(AtspiStateSet, atspi_state_set, G_TYPE_OBJECT);
+
+void atspi_rect_free(AtspiRect * rect)
+{
+       g_free(rect);
+}
+
+AtspiRect *atspi_rect_copy(AtspiRect * src)
+{
+       AtspiRect *dst = g_new(AtspiRect, 1);
+       dst->x = src->x;
+       dst->y = src->y;
+       dst->height = src->height;
+       dst->width = src->width;
+       return dst;
+}
+
+G_DEFINE_BOXED_TYPE(AtspiRect, atspi_rect, atspi_rect_copy, atspi_rect_free)
+
+void atspi_alloc_memory()
+{
+       value = (AtspiValue *) malloc(sizeof(AtspiValue));
+       text = (AtspiText *) malloc(sizeof(AtspiText));
+       editable_text = (AtspiEditableText *) malloc(sizeof(AtspiEditableText));
+}
+
+void atspi_free_memory(void)
+{
+       free(editable_text);
+       free(value);
+       free(text);
+}
+
+gchar *atspi_accessible_get_name(AtspiAccessible * obj, GError ** error)
+{
+       if (!obj || !obj->name)
+               return strdup("\0");
+
+       return strdup(obj->name);
+}
+
+gchar *atspi_accessible_get_role_name(AtspiAccessible * obj, GError ** error)
+{
+       if (!obj)
+               return strdup("\0");
+       AtspiRole role = obj->role;
+       switch (role) {
+       case ATSPI_ROLE_APPLICATION:
+               return strdup("Application");
+       case ATSPI_ROLE_PUSH_BUTTON:
+               return strdup("Push button");
+       case ATSPI_ROLE_ICON:
+               return strdup("Icon");
+       case ATSPI_ROLE_CHECK_BOX:
+               return strdup("Check box");
+       case ATSPI_ROLE_ENTRY:
+               return strdup("Entry");
+       case ATSPI_ROLE_FILLER:
+               return strdup("filler");
+       case ATSPI_ROLE_SCROLL_PANE:
+               return strdup("scroll pane");
+       case ATSPI_ROLE_IMAGE:
+               return strdup("image");
+       case ATSPI_ROLE_SPLIT_PANE:
+               return strdup("split pane");
+       case ATSPI_ROLE_UNKNOWN:
+               return strdup("unknown");
+       case ATSPI_ROLE_RULER:
+               return strdup("ruler");
+       case ATSPI_ROLE_FOOTER:
+               return strdup("footer");
+       case ATSPI_ROLE_INFO_BAR:
+               return strdup("infobar");
+       case ATSPI_ROLE_LINK:
+               return strdup("link");
+       default:
+               return strdup("\0");
+       }
+}
+
+gchar *atspi_accessible_get_localized_role_name(AtspiAccessible * obj, GError ** error)
+{
+       if (!obj)
+               return strdup("\0");
+       AtspiRole role = obj->role;
+       switch (role) {
+       case ATSPI_ROLE_APPLICATION:
+               return strdup("Application");
+       case ATSPI_ROLE_PUSH_BUTTON:
+               return strdup("Push button");
+       case ATSPI_ROLE_ICON:
+               return strdup("Icon");
+       case ATSPI_ROLE_CHECK_BOX:
+               return strdup("Check box");
+       case ATSPI_ROLE_ENTRY:
+               return strdup("Entry");
+       case ATSPI_ROLE_FILLER:
+               return strdup("filler");
+       default:
+               return strdup("\0");
+       }
+}
+
+gchar *atspi_accessible_get_toolkit_name(AtspiAccessible * obj, GError ** error)
+{
+       return "fake atspi";
+}
+
+gchar *atspi_accessible_get_description(AtspiAccessible * obj, GError ** error)
+{
+       if (!obj || !obj->description)
+               return strdup("\0");
+       return strdup(obj->description);
+}
+
+AtspiAction *atspi_accessible_get_action_iface(AtspiAccessible * obj)
+{
+       if (!obj)
+               return NULL;
+       return action;
+}
+
+AtspiText *atspi_accessible_get_text_iface(AtspiAccessible * obj)
+{
+       if (!obj)
+               return NULL;
+       return text;
+}
+
+gint atspi_text_get_character_count(AtspiText * obj, GError ** error)
+{
+       if (!obj)
+               return -1;
+       return 6;
+}
+
+gint atspi_text_get_caret_offset(AtspiText * obj, GError ** error)
+{
+       if (!obj)
+               return -1;
+       return 5;
+}
+
+gchar *atspi_text_get_text(AtspiText * obj, gint start_offset, gint end_offset, GError ** error)
+{
+       if (!obj)
+               return NULL;
+       return "AtspiText text";
+}
+
+AtspiValue *atspi_accessible_get_value_iface(AtspiAccessible * obj)
+{
+       if (!obj)
+               return NULL;
+       return value;
+}
+
+gdouble atspi_value_get_current_value(AtspiValue * obj, GError ** error)
+{
+       return 1.0;
+}
+
+gdouble atspi_value_get_maximum_value(AtspiValue * obj, GError ** error)
+{
+       return 2.0;
+}
+
+gdouble atspi_value_get_minimum_value(AtspiValue * obj, GError ** error)
+{
+       return 0.0;
+}
+
+AtspiEventListener *atspi_event_listener_new(AtspiEventListenerCB callback, gpointer user_data, GDestroyNotify callback_destroyed)
+{
+       return NULL;
+}
+
+gboolean atspi_event_listener_register(AtspiEventListener * listener, const gchar * event_type, GError ** error)
+{
+       return FALSE;
+}
+
+gboolean atspi_event_listener_deregister(AtspiEventListener * listener, const gchar * event_type, GError ** error)
+{
+       return FALSE;
+}
+
+AtspiStateSet *atspi_accessible_get_state_set(AtspiAccessible * obj)
+{
+       if (!obj || !obj->states)
+               return NULL;
+       return obj->states;
+}
+
+gboolean atspi_state_set_contains(AtspiStateSet * set, AtspiStateType state)
+{
+       if (!set)
+               return FALSE;
+       return (set->states & ((gint64) 1 << state)) ? TRUE : FALSE;
+}
+
+void atspi_state_set_add(AtspiStateSet * set, AtspiStateType state)
+{
+       if (!set)
+               return;
+       set->states |= (((gint64) 1) << state);
+}
+
+gboolean atspi_component_grab_highlight(AtspiComponent * obj, GError ** error)
+{
+       return FALSE;
+}
+
+AtspiScrollable *atspi_accessible_get_scrollable(AtspiAccessible * accessible)
+{
+       return NULL;
+}
+
+gboolean atspi_component_clear_highlight(AtspiComponent * obj, GError ** error)
+{
+       return FALSE;
+}
+
+gboolean atspi_component_contains(AtspiComponent * obj, gint x, gint y, AtspiCoordType ctype, GError ** error)
+{
+       return TRUE;
+}
+
+GArray *atspi_state_set_get_states(AtspiStateSet * set)
+{
+       gint i = 0;
+       guint64 val = 1;
+       GArray *ret;
+
+       g_return_val_if_fail(set != NULL, NULL);
+       ret = g_array_new(TRUE, TRUE, sizeof(AtspiStateType));
+       if (!ret)
+               return NULL;
+       for (i = 0; i < 64; i++) {
+               if (set->states & val)
+                       ret = g_array_append_val(ret, i);
+               val <<= 1;
+       }
+       return ret;
+
+}
+
+AtspiRole atspi_accessible_get_role(AtspiAccessible * obj, GError ** error)
+{
+       if (!obj)
+               return ATSPI_ROLE_INVALID;
+       return obj->role;
+}
+
+gint atspi_accessible_get_child_count(AtspiAccessible * obj, GError ** error)
+{
+       if (!obj || !obj->children)
+               return 0;
+       return g_list_length(obj->children);
+}
+
+AtspiAccessible *atspi_accessible_get_child_at_index(AtspiAccessible * obj, gint child_index, GError ** error)
+{
+       if (!obj || child_index >= g_list_length(obj->children))
+               return NULL;
+       return g_object_ref(g_list_nth_data(obj->children, child_index));
+}
+
+AtspiComponent *atspi_accessible_get_component_iface(AtspiAccessible * obj)
+{
+       if (!obj)
+               return NULL;
+       AtspiComponent *component = g_object_new(ATSPI_COMPONENT_OBJECT_TYPE, 0);
+       *(component->role) = obj->role;
+       return component;
+}
+
+AtspiStateSet *atspi_state_set_new(GArray * states)
+{
+       AtspiStateSet *set = g_object_new(ATSPI_STATE_OBJECT_TYPE, NULL);
+       if (!set)
+               return NULL;
+       int i;
+       for (i = 0; i < states->len; i++) {
+               atspi_state_set_add(set, g_array_index(states, AtspiStateType, i));
+       }
+       return set;
+}
+
+AtspiRect *atspi_component_get_extents(AtspiComponent * component, AtspiCoordType ctype, GError ** error)
+{
+       if (!component)
+               return NULL;
+
+       AtspiRect rect;
+       if (*(component->role) == ATSPI_ROLE_APPLICATION) {
+               rect.x = 0;
+               rect.y = 0;
+               rect.width = 100;
+               rect.height = 100;
+       } else if (*(component->role) == ATSPI_ROLE_PUSH_BUTTON) {
+               rect.x = 1;
+               rect.y = 1;
+               rect.width = 50;
+               rect.height = 50;
+       } else if (*(component->role) == ATSPI_ROLE_ICON) {
+               rect.x = 50;
+               rect.y = 0;
+               rect.width = 50;
+               rect.height = 50;
+       } else if (*(component->role) == ATSPI_ROLE_CHECK_BOX) {
+               rect.x = 0;
+               rect.y = 50;
+               rect.width = 50;
+               rect.height = 50;
+       } else if (*(component->role) == ATSPI_ROLE_ENTRY) {
+               rect.x = 50;
+               rect.y = 50;
+               rect.width = 50;
+               rect.height = 50;
+       } else if (*(component->role) == ATSPI_ROLE_WINDOW) {
+               rect.x = 0;
+               rect.y = 0;
+               rect.width = 100;
+               rect.height = 100;
+       } else if (*(component->role) == ATSPI_ROLE_FILLER) {
+               rect.x = 50;
+               rect.y = 50;
+               rect.width = 50;
+               rect.height = 50;
+       } else {
+               rect.x = 0;
+               rect.y = 0;
+               rect.width = 0;
+               rect.height = 0;
+       }
+       return atspi_rect_copy(&rect);
+}
+
+AtspiAccessible *atspi_create_accessible()
+{
+       AtspiAccessible *obj = g_object_new(ATSPI_ACCESSIBLE_OBJECT_TYPE, 0);
+       obj->children = NULL;
+       obj->index_in_parent = 0;
+       obj->child_count = 0;
+
+       GArray *states = g_array_new(TRUE, TRUE, sizeof(AtspiStateType));
+       AtspiStateType s[] = {
+               ATSPI_STATE_VISIBLE,
+               ATSPI_STATE_SHOWING,
+               ATSPI_STATE_FOCUSABLE,
+               ATSPI_STATE_LAST_DEFINED
+       };
+       int i;
+       for (i = 0; i < (int)(sizeof(s) / sizeof(s[0])); i++)
+               g_array_append_val(states, s[i]);
+       obj->states = atspi_state_set_new(states);
+
+       return obj;
+}
+
+void atspi_delete_accessible(AtspiAccessible * obj)
+{
+       if (!obj)
+               return;
+       if (obj->children) {
+               atpis_accessible_remove_children(obj);
+       }
+       g_object_unref(obj);
+}
+
+void atspi_accessible_add_child(AtspiAccessible * obj, AtspiAccessible * child)
+{
+       child->index_in_parent = obj->child_count;
+       child->accessible_parent = obj;
+
+       obj->children = g_list_append(obj->children, child);
+       obj->child_count++;
+}
+
+void atpis_accessible_remove_children(AtspiAccessible * obj)
+{
+       GList *l = obj->children;
+       while (l != NULL) {
+               GList *next = l->next;
+               if (l->data) {
+                       atspi_delete_accessible(l->data);
+               }
+               l = next;
+       }
+       g_list_free(obj->children);
+}
+
+static void atspi_state_set_init(AtspiStateSet * set)
+{
+}
+
+static void atspi_state_set_class_init(AtspiStateSetClass * _class)
+{
+}
+
+static void atspi_action_class_init(AtspiActionClass * _class)
+{
+}
+
+static void atspi_action_init(AtspiAction * obj)
+{
+}
+
+static void atspi_accessible_class_init(AtspiAccessibleClass * _class)
+{
+}
+
+static void atspi_accessible_init(AtspiAccessible * obj)
+{
+}
+
+static void atspi_component_finalize(GObject * obj)
+{
+       AtspiComponent *component = (AtspiComponent *) obj;
+       free(component->role);
+}
+
+static void atspi_component_class_init(AtspiComponentClass * class)
+{
+       GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+
+       gobject_class->finalize = atspi_component_finalize;
+}
+
+static void atspi_component_init(AtspiComponent * obj)
+{
+       obj->role = (AtspiRole *) malloc(sizeof(AtspiRole));
+}
+
+AtspiEditableText *atspi_accessible_get_editable_text_iface(AtspiAccessible * obj)
+{
+       return editable_text;
+}
+
+GArray *atspi_accessible_get_relation_set(AtspiAccessible * obj, GError ** error)
+{
+       return NULL;
+}
+
+AtspiRelationType atspi_relation_get_relation_type(AtspiRelation * obj)
+{
+       return ATSPI_RELATION_NULL;
+}
+
+gint atspi_relation_get_n_targets(AtspiRelation * obj)
+{
+       return 0;
+}
+
+AtspiAccessible *atspi_relation_get_target(AtspiRelation * obj, gint i)
+{
+       return NULL;
+}
+
+AtspiAccessible *atspi_accessible_get_parent(AtspiAccessible * obj, GError ** error)
+{
+       return g_object_ref (obj->accessible_parent);
+}
+
+int atspi_component_get_highlight_index(AtspiComponent * obj, GError ** error)
+{
+       return 0;
+}
+
+gint atspi_action_get_n_actions(AtspiAction * obj, GError ** error)
+{
+       return 0;
+}
+
+gchar *atspi_action_get_action_name(AtspiAction * obj, gint i, GError ** error)
+{
+       return strdup("");
+}
+
+gint atspi_accessible_get_index_in_parent(AtspiAccessible * obj, GError ** error)
+{
+       return obj->index_in_parent;
+}
+
+int atspi_exit(void)
+{
+       return 1;
+}
diff --git a/tests/atspi/atspi.h b/tests/atspi/atspi.h
new file mode 100644 (file)
index 0000000..e03f548
--- /dev/null
@@ -0,0 +1,451 @@
+
+
+#ifndef __ATSPI_H__
+#define __ATSPI_H__
+
+#include <glib-2.0/glib.h>
+#include <glib-2.0/glib-object.h>
+#include <dbus/dbus.h>
+
+#define ATSPI_ACCESSIBLE_OBJECT_TYPE                (atspi_accessible_get_type ())
+#define ATSPI_ACCESSIBLE(obj)                       (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_ACCESSIBLE_OBJECT_TYPE, AtspiAccessible))
+#define ATSPI_ACCESSIBLE_IS_OBJECT(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_ACCESSIBLE_OBJECT_TYPE))
+#define ATSPI_ACCESSIBLE_CLASS(_class)              (G_TYPE_CHECK_CLASS_CAST ((_class), ATSPI_ACCESSIBLE_OBJECT_TYPE, AtspiAccessibleClass))
+#define ATSPI_ACCESSIBLE_IS_OBJECT_CLASS(_class)    (G_TYPE_CHECK_CLASS_TYPE ((_class), ATSPI_ACCESSIBLE_OBJECT_TYPE))
+#define ATSPI_ACCESSIBLE_GET_CLASS(obj)             (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_ACCESSIBLE_OBJECT_TYPE, AtspiAccessibleClass))
+
+#define ATSPI_ACTION_OBJECT_TYPE                (atspi_action_get_type ())
+#define ATSPI_ACTION(obj)                       (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_ACTION_OBJECT_TYPE, AtspiAccessible))
+#define ATSPI_ACTION_IS_OBJECT(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_ACTION_OBJECT_TYPE))
+#define ATSPI_ACTION_CLASS(_class)              (G_TYPE_CHECK_CLASS_CAST ((_class), ATSPI_ACTION_OBJECT_TYPE, AtspiAccessibleClass))
+#define ATSPI_ACTION_IS_OBJECT_CLASS(_class)    (G_TYPE_CHECK_CLASS_TYPE ((_class), ATSPI_ACTION_OBJECT_TYPE))
+#define ATSPI_ACTION_GET_CLASS(obj)             (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_ACTION_OBJECT_TYPE, AtspiAccessibleClass))
+
+#define ATSPI_COMPONENT_OBJECT_TYPE                 (atspi_component_get_type ())
+#define ATSPI_COMPONENT(obj)                        (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_COMPONENT_OBJECT_TYPE, AtspiAccessible))
+#define ATSPI_COMPONENT_IS_OBJECT(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_COMPONENT_OBJECT_TYPE))
+#define ATSPI_COMPONENT_CLASS(_class)               (G_TYPE_CHECK_CLASS_CAST ((_class), ATSPI_COMPONENT_OBJECT_TYPE, AtspiAccessibleClass))
+#define ATSPI_COMPONENT_IS_OBJECT_CLASS(_class)     (G_TYPE_CHECK_CLASS_TYPE ((_class), ATSPI_COMPONENT_OBJECT_TYPE))
+#define ATSPI_COMPONENT_GET_CLASS(obj)              (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_COMPONENT_OBJECT_TYPE, AtspiAccessibleClass))
+
+#define ATSPI_STATE_OBJECT_TYPE                     (atspi_state_set_get_type ())
+#define ATSPI_STATE(obj)                            (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_STATE_OBJECT_TYPE, AtspiStateSet))
+#define ATSPI_STATE_IS_OBJECT(obj)                  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_STATE_OBJECT_TYPE))
+#define ATSPI_STATE_CLASS(_class)                   (G_TYPE_CHECK_CLASS_CAST ((_class), ATSPI_STATE_OBJECT_TYPE, AtspiStateSetClass))
+#define ATSPI_STATE_IS_OBJECT_CLASS(_class)         (G_TYPE_CHECK_CLASS_TYPE ((_class), ATSPI_STATE_OBJECT_TYPE))
+#define ATSPI_STATE_GET_CLASS(obj)                  (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_STATE_OBJECT_TYPE, AtspiStateSetClass))
+
+
+typedef struct _AtspiApplication AtspiApplication;
+typedef struct _AtspiObject AtspiObject;
+typedef struct _AtspiAccessible AtspiAccessible;
+typedef struct _AtspiAction AtspiAction;
+typedef struct _AtspiEvent AtspiEvent;
+typedef struct _AtspiStateSet AtspiStateSet;
+typedef struct _AtspiEventListener AtspiEventListener;
+typedef struct _AtspiText AtspiText;
+typedef struct _AtspiValue AtspiValue;
+typedef struct _AtspiComponent AtspiComponent;
+typedef struct _AtspiScrollable AtspiScrollable;
+typedef struct _AtspiRect AtspiRect;
+typedef struct _AtspiEditableText AtspiEditableText;
+typedef struct _AtspiRelation AtspiRelation;
+typedef struct _AtspiAction AtspiAction;
+
+typedef struct _AtspiAccessibleClass AtspiAccessibleClass;
+typedef struct _AtspiActionClass AtspiActionClass;
+typedef struct _AtspiComponentClass AtspiComponentClass;
+typedef struct _AtspiStateSetClass AtspiStateSetClass;
+
+typedef void (*AtspiEventListenerCB) (AtspiEvent *event, void *user_data);
+
+typedef enum
+{
+   ATSPI_CACHE_NONE     = 0,
+   ATSPI_CACHE_PARENT   = 1 << 0,
+   ATSPI_CACHE_CHILDREN    = 1 << 1,
+   ATSPI_CACHE_NAME        = 1 << 2,
+   ATSPI_CACHE_DESCRIPTION = 1 << 3,
+   ATSPI_CACHE_STATES      = 1 << 4,
+   ATSPI_CACHE_ROLE        = 1 << 5,
+   ATSPI_CACHE_INTERFACES  = 1 << 6,
+   ATSPI_CACHE_ATTRIBUTES = 1 << 7,
+   ATSPI_CACHE_ALL         = 0x3fffffff,
+   ATSPI_CACHE_DEFAULT = ATSPI_CACHE_PARENT | ATSPI_CACHE_CHILDREN |
+                         ATSPI_CACHE_NAME | ATSPI_CACHE_DESCRIPTION |
+                         ATSPI_CACHE_STATES | ATSPI_CACHE_ROLE |
+                         ATSPI_CACHE_INTERFACES,
+   ATSPI_CACHE_UNDEFINED   = 0x40000000,
+} AtspiCache;
+
+typedef enum
+{
+   ATSPI_ROLE_INVALID,
+   ATSPI_ROLE_ACCELERATOR_LABEL,
+   ATSPI_ROLE_ALERT,
+   ATSPI_ROLE_ANIMATION,
+   ATSPI_ROLE_ARROW,
+   ATSPI_ROLE_CALENDAR,
+   ATSPI_ROLE_CANVAS,
+   ATSPI_ROLE_CHECK_BOX,
+   ATSPI_ROLE_CHECK_MENU_ITEM,
+   ATSPI_ROLE_COLOR_CHOOSER,
+   ATSPI_ROLE_COLUMN_HEADER,
+   ATSPI_ROLE_COMBO_BOX,
+   ATSPI_ROLE_DATE_EDITOR,
+   ATSPI_ROLE_DESKTOP_ICON,
+   ATSPI_ROLE_DESKTOP_FRAME,
+   ATSPI_ROLE_DIAL,
+   ATSPI_ROLE_DIALOG,
+   ATSPI_ROLE_DIRECTORY_PANE,
+   ATSPI_ROLE_DRAWING_AREA,
+   ATSPI_ROLE_FILE_CHOOSER,
+   ATSPI_ROLE_FILLER,
+   ATSPI_ROLE_FOCUS_TRAVERSABLE,
+   ATSPI_ROLE_FONT_CHOOSER,
+   ATSPI_ROLE_FRAME,
+   ATSPI_ROLE_GLASS_PANE,
+   ATSPI_ROLE_HTML_CONTAINER,
+   ATSPI_ROLE_ICON,
+   ATSPI_ROLE_IMAGE,
+   ATSPI_ROLE_INTERNAL_FRAME,
+   ATSPI_ROLE_LABEL,
+   ATSPI_ROLE_LAYERED_PANE,
+   ATSPI_ROLE_LIST,
+   ATSPI_ROLE_LIST_ITEM,
+   ATSPI_ROLE_MENU,
+   ATSPI_ROLE_MENU_BAR,
+   ATSPI_ROLE_MENU_ITEM,
+   ATSPI_ROLE_OPTION_PANE,
+   ATSPI_ROLE_PAGE_TAB,
+   ATSPI_ROLE_PAGE_TAB_LIST,
+   ATSPI_ROLE_PANEL,
+   ATSPI_ROLE_PASSWORD_TEXT,
+   ATSPI_ROLE_POPUP_MENU,
+   ATSPI_ROLE_PROGRESS_BAR,
+   ATSPI_ROLE_PUSH_BUTTON,
+   ATSPI_ROLE_RADIO_BUTTON,
+   ATSPI_ROLE_RADIO_MENU_ITEM,
+   ATSPI_ROLE_ROOT_PANE,
+   ATSPI_ROLE_ROW_HEADER,
+   ATSPI_ROLE_SCROLL_BAR,
+   ATSPI_ROLE_SCROLL_PANE,
+   ATSPI_ROLE_SEPARATOR,
+   ATSPI_ROLE_SLIDER,
+   ATSPI_ROLE_SPIN_BUTTON,
+   ATSPI_ROLE_SPLIT_PANE,
+   ATSPI_ROLE_STATUS_BAR,
+   ATSPI_ROLE_TABLE,
+   ATSPI_ROLE_TABLE_CELL,
+   ATSPI_ROLE_TABLE_COLUMN_HEADER,
+   ATSPI_ROLE_TABLE_ROW_HEADER,
+   ATSPI_ROLE_TEAROFF_MENU_ITEM,
+   ATSPI_ROLE_TERMINAL,
+   ATSPI_ROLE_TEXT,
+   ATSPI_ROLE_TOGGLE_BUTTON,
+   ATSPI_ROLE_TOOL_BAR,
+   ATSPI_ROLE_TOOL_TIP,
+   ATSPI_ROLE_TREE,
+   ATSPI_ROLE_TREE_TABLE,
+   ATSPI_ROLE_UNKNOWN,
+   ATSPI_ROLE_VIEWPORT,
+   ATSPI_ROLE_WINDOW,
+   ATSPI_ROLE_EXTENDED,
+   ATSPI_ROLE_HEADER,
+   ATSPI_ROLE_FOOTER,
+   ATSPI_ROLE_PARAGRAPH,
+   ATSPI_ROLE_RULER,
+   ATSPI_ROLE_APPLICATION,
+   ATSPI_ROLE_AUTOCOMPLETE,
+   ATSPI_ROLE_EDITBAR,
+   ATSPI_ROLE_EMBEDDED,
+   ATSPI_ROLE_ENTRY,
+   ATSPI_ROLE_CHART,
+   ATSPI_ROLE_CAPTION,
+   ATSPI_ROLE_DOCUMENT_FRAME,
+   ATSPI_ROLE_HEADING,
+   ATSPI_ROLE_PAGE,
+   ATSPI_ROLE_SECTION,
+   ATSPI_ROLE_REDUNDANT_OBJECT,
+   ATSPI_ROLE_FORM,
+   ATSPI_ROLE_LINK,
+   ATSPI_ROLE_INPUT_METHOD_WINDOW,
+   ATSPI_ROLE_TABLE_ROW,
+   ATSPI_ROLE_TREE_ITEM,
+   ATSPI_ROLE_DOCUMENT_SPREADSHEET,
+   ATSPI_ROLE_DOCUMENT_PRESENTATION,
+   ATSPI_ROLE_DOCUMENT_TEXT,
+   ATSPI_ROLE_DOCUMENT_WEB,
+   ATSPI_ROLE_DOCUMENT_EMAIL,
+   ATSPI_ROLE_COMMENT,
+   ATSPI_ROLE_LIST_BOX,
+   ATSPI_ROLE_GROUPING,
+   ATSPI_ROLE_IMAGE_MAP,
+   ATSPI_ROLE_NOTIFICATION,
+   ATSPI_ROLE_INFO_BAR,
+   ATSPI_ROLE_LEVEL_BAR,
+   ATSPI_ROLE_LAST_DEFINED,
+} AtspiRole;
+
+typedef enum
+{
+   ATSPI_STATE_INVALID,
+   ATSPI_STATE_ACTIVE,
+   ATSPI_STATE_ARMED,
+   ATSPI_STATE_BUSY,
+   ATSPI_STATE_CHECKED,
+   ATSPI_STATE_COLLAPSED,
+   ATSPI_STATE_DEFUNCT,
+   ATSPI_STATE_EDITABLE,
+   ATSPI_STATE_ENABLED,
+   ATSPI_STATE_EXPANDABLE,
+   ATSPI_STATE_EXPANDED,
+   ATSPI_STATE_FOCUSABLE,
+   ATSPI_STATE_FOCUSED,
+   ATSPI_STATE_HAS_TOOLTIP,
+   ATSPI_STATE_HORIZONTAL,
+   ATSPI_STATE_ICONIFIED,
+   ATSPI_STATE_MODAL,
+   ATSPI_STATE_MULTI_LINE,
+   ATSPI_STATE_MULTISELECTABLE,
+   ATSPI_STATE_OPAQUE,
+   ATSPI_STATE_PRESSED,
+   ATSPI_STATE_RESIZABLE,
+   ATSPI_STATE_SELECTABLE,
+   ATSPI_STATE_SELECTED,
+   ATSPI_STATE_SENSITIVE,
+   ATSPI_STATE_SHOWING,
+   ATSPI_STATE_SINGLE_LINE,
+   ATSPI_STATE_STALE,
+   ATSPI_STATE_TRANSIENT,
+   ATSPI_STATE_VERTICAL,
+   ATSPI_STATE_VISIBLE,
+   ATSPI_STATE_MANAGES_DESCENDANTS,
+   ATSPI_STATE_INDETERMINATE,
+   ATSPI_STATE_REQUIRED,
+   ATSPI_STATE_TRUNCATED,
+   ATSPI_STATE_ANIMATED,
+   ATSPI_STATE_INVALID_ENTRY,
+   ATSPI_STATE_SUPPORTS_AUTOCOMPLETION,
+   ATSPI_STATE_SELECTABLE_TEXT,
+   ATSPI_STATE_IS_DEFAULT,
+   ATSPI_STATE_VISITED,
+   ATSPI_STATE_HIGHLIGHTED,
+   ATSPI_STATE_LAST_DEFINED,
+} AtspiStateType;
+
+typedef enum
+{
+    ATSPI_RELATION_NULL,
+    ATSPI_RELATION_LABEL_FOR,
+    ATSPI_RELATION_LABELLED_BY,
+    ATSPI_RELATION_CONTROLLER_FOR,
+    ATSPI_RELATION_CONTROLLED_BY,
+    ATSPI_RELATION_MEMBER_OF,
+    ATSPI_RELATION_TOOLTIP_FOR,
+    ATSPI_RELATION_NODE_CHILD_OF,
+    ATSPI_RELATION_NODE_PARENT_OF,
+    ATSPI_RELATION_EXTENDED,
+    ATSPI_RELATION_FLOWS_TO,
+    ATSPI_RELATION_FLOWS_FROM,
+    ATSPI_RELATION_SUBWINDOW_OF,
+    ATSPI_RELATION_EMBEDS,
+    ATSPI_RELATION_EMBEDDED_BY,
+    ATSPI_RELATION_POPUP_FOR,
+    ATSPI_RELATION_PARENT_WINDOW_OF,
+    ATSPI_RELATION_DESCRIPTION_FOR,
+    ATSPI_RELATION_DESCRIBED_BY,
+    ATSPI_RELATION_LAST_DEFINED,
+} AtspiRelationType;
+
+struct _AtspiApplication
+{
+   GObject parent;
+   GHashTable *hash;
+   char *bus_name;
+   DBusConnection *bus;
+   AtspiAccessible *root;
+   AtspiCache cache;
+   gchar *toolkit_name;
+   gchar *toolkit_version;
+   gchar *atspi_version;
+   struct timeval time_added;
+};
+
+struct _AtspiObject
+{
+   GObject parent;
+   AtspiApplication *app;
+   char *path;
+};
+
+struct _AtspiAccessible
+{
+   //GObject parent;
+   AtspiObject parent;
+   AtspiAccessible *accessible_parent;
+   GList *children;
+   AtspiRole role;
+   gint interfaces;
+   char *name;
+   char *description;
+   AtspiStateSet *states;
+   GHashTable *attributes;
+   guint cached_properties;
+   gint index_in_parent;
+   gint child_count;
+};
+
+struct _AtspiAccessibleClass
+{
+   GObjectClass parent_class;
+};
+
+struct _AtspiActionClass
+{
+   GObjectClass parent_class;
+};
+
+struct _AtspiComponentClass
+{
+   GObjectClass parent_class;
+};
+
+struct _AtspiEvent
+{
+   gchar  *type;
+   AtspiAccessible  *source;
+   gint         detail1;
+   gint         detail2;
+   GValue any_data;
+};
+
+struct _AtspiStateSet
+{
+   GObject parent;
+   struct _AtspiAccessible *accessible;
+   gint64 states;
+};
+
+struct _AtspiStateSetClass
+{
+   GObjectClass parent_class;
+};
+
+struct _AtspiEventListener
+{
+   GObject parent;
+   AtspiEventListenerCB callback;
+   void *user_data;
+   GDestroyNotify cb_destroyed;
+};
+
+struct _AtspiAction
+{
+   GTypeInterface parent;
+};
+struct _AtspiText
+{
+   GTypeInterface parent;
+};
+
+struct _AtspiEditableText
+{
+   GTypeInterface parent;
+};
+
+struct _AtspiValue
+{
+   GTypeInterface parent;
+};
+
+struct _AtspiComponent
+{
+   GTypeInterface parent;
+   AtspiRole *role;
+};
+
+struct _AtspiScrollable
+{
+   GTypeInterface parent;
+};
+
+struct _AtspiRelation
+{
+   GTypeInterface parent;
+};
+
+struct _AtspiRect
+{
+   gint x;
+   gint y;
+   gint width;
+   gint height;
+};
+
+typedef enum
+{
+   ATSPI_COORD_TYPE_SCREEN,
+   ATSPI_COORD_TYPE_WINDOW,
+} AtspiCoordType;
+
+gchar * atspi_accessible_get_name (AtspiAccessible *obj, GError **error);
+gchar * atspi_accessible_get_role_name (AtspiAccessible *obj, GError **error);
+gchar * atspi_accessible_get_localized_role_name (AtspiAccessible *obj, GError **error);
+gchar * atspi_accessible_get_toolkit_name (AtspiAccessible *obj, GError **error);
+gchar * atspi_accessible_get_description (AtspiAccessible *obj, GError **error);
+AtspiText * atspi_accessible_get_text_iface (AtspiAccessible *obj);
+AtspiAction * atspi_accessible_get_action_iface (AtspiAccessible *obj);
+gint atspi_text_get_character_count (AtspiText *obj, GError **error);
+gint atspi_text_get_caret_offset (AtspiText *obj, GError **error);
+gchar * atspi_text_get_text (AtspiText *obj, gint start_offset, gint end_offset, GError **error);
+AtspiValue * atspi_accessible_get_value_iface (AtspiAccessible *obj);
+gdouble atspi_value_get_current_value (AtspiValue *obj, GError **error);
+gdouble atspi_value_get_maximum_value (AtspiValue *obj, GError **error);
+gdouble atspi_value_get_minimum_value (AtspiValue *obj, GError **error);
+AtspiEventListener *atspi_event_listener_new (AtspiEventListenerCB callback,
+      gpointer user_data,
+      GDestroyNotify callback_destroyed);
+gboolean atspi_event_listener_register (AtspiEventListener *listener,
+                                        const gchar *event_type,
+                                        GError **error);
+gboolean atspi_event_listener_deregister (AtspiEventListener *listener,
+      const gchar *event_type,
+      GError **error);
+AtspiStateSet * atspi_accessible_get_state_set (AtspiAccessible *obj);
+gboolean atspi_state_set_contains (AtspiStateSet *set, AtspiStateType state);
+void atspi_state_set_add (AtspiStateSet *set, AtspiStateType state);
+GArray *atspi_state_set_get_states (AtspiStateSet *set);
+AtspiStateSet * atspi_state_set_new (GArray *states);
+
+void atspi_alloc_memory(void);
+
+void atspi_free_memory(void);
+gboolean atspi_component_grab_highlight (AtspiComponent *obj, GError **error);
+AtspiScrollable *atspi_accessible_get_scrollable (AtspiAccessible *accessible);
+gboolean atspi_component_clear_highlight (AtspiComponent *obj, GError **error);
+AtspiRole atspi_accessible_get_role (AtspiAccessible *obj, GError **error);
+gint atspi_accessible_get_child_count (AtspiAccessible *obj, GError **error);
+AtspiAccessible * atspi_accessible_get_child_at_index (AtspiAccessible *obj, gint child_index, GError **error);
+AtspiComponent * atspi_accessible_get_component_iface (AtspiAccessible *obj);
+AtspiRect *atspi_component_get_extents (AtspiComponent *obj, AtspiCoordType ctype, GError **error);
+AtspiAccessible *atspi_create_accessible(void);
+void atspi_delete_accessible(AtspiAccessible *obj);
+void atspi_accessible_add_child(AtspiAccessible *obj, AtspiAccessible *child);
+void atpis_accessible_remove_children(AtspiAccessible *obj);
+AtspiEditableText * atspi_accessible_get_editable_text_iface (AtspiAccessible *obj);
+GArray * atspi_accessible_get_relation_set (AtspiAccessible *obj, GError **error);
+AtspiRelationType atspi_relation_get_relation_type (AtspiRelation *obj);
+gint atspi_relation_get_n_targets (AtspiRelation *obj);
+AtspiAccessible * atspi_relation_get_target (AtspiRelation *obj, gint i);
+AtspiAccessible * atspi_accessible_get_parent (AtspiAccessible *obj, GError **error);
+gboolean atspi_component_contains (AtspiComponent *obj, gint x, gint y, AtspiCoordType ctype, GError **error);
+int atspi_component_get_highlight_index(AtspiComponent *obj, GError **error);
+gint atspi_accessible_get_index_in_parent (AtspiAccessible *obj, GError **error);
+AtspiAction * atspi_accessible_get_action_iface (AtspiAccessible *obj);
+gint atspi_action_get_n_actions (AtspiAction *obj, GError **error);
+gchar * atspi_action_get_action_name (AtspiAction *obj, gint i, GError **error);
+
+int atspi_exit(void);
+
+#endif /*__ATSPI_H__*/
diff --git a/tests/smart_navi_suite.c b/tests/smart_navi_suite.c
new file mode 100644 (file)
index 0000000..b7b637a
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Flora License, Version 1.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://floralicense.org/license/
+ *
+ * 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 "screen_reader_spi.h"
+#include "flat_navi.h"
+#include <check.h>
+#include <stdio.h>
+#include <atspi/atspi.h>
+#include <Eina.h>
+
+static AtspiAccessible *root;
+static AtspiAccessible *app;
+static AtspiAccessible *win;
+static AtspiAccessible *ly;
+static AtspiAccessible *child1;
+static AtspiAccessible *child2;
+static AtspiAccessible *child3;
+static AtspiAccessible *child4;
+static AtspiAccessible *child5;
+static AtspiAccessible *child6;
+static AtspiAccessible *child7;
+static AtspiAccessible *child8;
+static AtspiAccessible *child9;
+static AtspiAccessible *child10;
+static AtspiAccessible *child11;
+static AtspiAccessible *child12;
+static AtspiAccessible *child13;
+static AtspiAccessible *child14;
+static AtspiAccessible *child15;
+static AtspiAccessible *child16;
+static AtspiAccessible *child17;
+static AtspiAccessible *child18;
+static AtspiAccessible *child19;
+static AtspiAccessible *child20;
+static AtspiAccessible *child21;
+static AtspiAccessible *child22;
+static AtspiAccessible *child23;
+static FlatNaviContext *ctx;
+
+void setup(void)
+{
+       Service_Data *data = get_pointer_to_service_data_struct();
+       data->run_service = 1;
+       data->tracking_signal_name = HIGHLIGHT_CHANGED_SIG;
+
+       //Set by tts
+       data->tts = NULL;
+       data->available_languages = NULL;
+
+       //Actions to do when tts state is 'ready'
+       data->say_text = false;
+       data->update_language_list = false;
+
+       data->text_to_say_info = NULL;
+}
+
+void teardown(void)
+{
+}
+
+void setup_flat_navi()
+{
+       setup();
+       root = atspi_create_accessible();
+       root->role = ATSPI_ROLE_APPLICATION;
+       child1 = atspi_create_accessible();
+       child2 = atspi_create_accessible();
+       child3 = atspi_create_accessible();
+       child4 = atspi_create_accessible();
+       child1->role = ATSPI_ROLE_PUSH_BUTTON;
+       child2->role = ATSPI_ROLE_ICON;
+       child3->role = ATSPI_ROLE_CHECK_BOX;
+       child4->role = ATSPI_ROLE_ENTRY;
+       atspi_accessible_add_child(root, child1);
+       atspi_accessible_add_child(root, child2);
+       atspi_accessible_add_child(root, child3);
+       atspi_accessible_add_child(root, child4);
+       eina_init();
+       atspi_alloc_memory();
+       ctx = flat_navi_context_create(root);
+}
+
+void setup_flat_navi2()
+{
+       setup();
+       app = atspi_create_accessible();
+       app->role = ATSPI_ROLE_APPLICATION;
+
+       root = app;
+       win = atspi_create_accessible();
+       win->role = ATSPI_ROLE_WINDOW;
+
+       child1 = atspi_create_accessible();
+       child2 = atspi_create_accessible();
+       child3 = atspi_create_accessible();
+       child4 = atspi_create_accessible();
+       child5 = atspi_create_accessible();
+       child6 = atspi_create_accessible();
+       child7 = atspi_create_accessible();
+       child8 = atspi_create_accessible();
+
+       child1->role = ATSPI_ROLE_FILLER;
+       child2->role = ATSPI_ROLE_FILLER;
+       child3->role = ATSPI_ROLE_FILLER;
+       child4->role = ATSPI_ROLE_FILLER;
+       child7->role = ATSPI_ROLE_FILLER;
+       child8->role = ATSPI_ROLE_FILLER;
+
+       child5->role = ATSPI_ROLE_PUSH_BUTTON;
+       child5->name = "btn1";
+       child6->role = ATSPI_ROLE_PUSH_BUTTON;
+       child6->name = "btn2";
+
+       atspi_accessible_add_child(app, win);
+       atspi_accessible_add_child(win, child1);
+       atspi_accessible_add_child(win, child7);
+       atspi_accessible_add_child(win, child2);
+
+       atspi_accessible_add_child(child1, child3);
+       atspi_accessible_add_child(child2, child4);
+
+       atspi_accessible_add_child(child7, child8);
+
+       atspi_accessible_add_child(child3, child5);
+       atspi_accessible_add_child(child4, child6);
+       eina_init();
+       atspi_alloc_memory();
+       ctx = flat_navi_context_create(win);
+}
+
+void setup_flat_navi3()
+{
+       setup();
+       app = atspi_create_accessible();
+       app->role = ATSPI_ROLE_APPLICATION;
+
+       root = app;
+       win = atspi_create_accessible();
+       win->role = ATSPI_ROLE_WINDOW;
+
+       ly = atspi_create_accessible();
+       ly->role = ATSPI_ROLE_FILLER;
+
+       child1 = atspi_create_accessible();
+       child2 = atspi_create_accessible();
+       child3 = atspi_create_accessible();
+       child4 = atspi_create_accessible();
+       child5 = atspi_create_accessible();
+       child6 = atspi_create_accessible();
+       child7 = atspi_create_accessible();
+       child8 = atspi_create_accessible();
+       child9 = atspi_create_accessible();
+       child10 = atspi_create_accessible();
+       child11 = atspi_create_accessible();
+       child12 = atspi_create_accessible();
+       child13 = atspi_create_accessible();
+       child14 = atspi_create_accessible();
+       child15 = atspi_create_accessible();
+       child16 = atspi_create_accessible();
+       child17 = atspi_create_accessible();
+       child18 = atspi_create_accessible();
+       child19 = atspi_create_accessible();
+       child20 = atspi_create_accessible();
+       child21 = atspi_create_accessible();
+       child22 = atspi_create_accessible();
+       child23 = atspi_create_accessible();
+
+       child1->role = ATSPI_ROLE_FILLER;
+       child3->role = ATSPI_ROLE_FILLER;
+       child4->role = ATSPI_ROLE_FILLER;
+       child5->role = ATSPI_ROLE_FILLER;
+       child6->role = ATSPI_ROLE_FILLER;
+       child7->role = ATSPI_ROLE_FILLER;
+       child8->role = ATSPI_ROLE_FILLER;
+       child9->role = ATSPI_ROLE_FILLER;
+       child10->role = ATSPI_ROLE_FILLER;
+       child11->role = ATSPI_ROLE_FILLER;
+       child12->role = ATSPI_ROLE_FILLER;
+       child14->role = ATSPI_ROLE_FILLER;
+       child15->role = ATSPI_ROLE_FILLER;
+       child17->role = ATSPI_ROLE_FILLER;
+       child18->role = ATSPI_ROLE_FILLER;
+       child20->role = ATSPI_ROLE_FILLER;
+       child21->role = ATSPI_ROLE_FILLER;
+       child23->role = ATSPI_ROLE_FILLER;
+
+       child2->role = ATSPI_ROLE_PUSH_BUTTON;
+       child2->name = "btn1";
+       child13->role = ATSPI_ROLE_PUSH_BUTTON;
+       child13->name = "btn2";
+       child16->role = ATSPI_ROLE_PUSH_BUTTON;
+       child16->name = "btn3";
+       child19->role = ATSPI_ROLE_PUSH_BUTTON;
+       child19->name = "btn4";
+       child22->role = ATSPI_ROLE_PUSH_BUTTON;
+       child22->name = "btn5";
+
+       atspi_accessible_add_child(app, win);
+       atspi_accessible_add_child(win, ly);
+
+       atspi_accessible_add_child(ly, child1);
+       atspi_accessible_add_child(ly, child3);
+       atspi_accessible_add_child(ly, child4);
+
+       atspi_accessible_add_child(child1, child2);
+
+       atspi_accessible_add_child(child4, child5);
+
+       atspi_accessible_add_child(child5, child6);
+
+       atspi_accessible_add_child(child6, child7);
+
+       atspi_accessible_add_child(child7, child8);
+       atspi_accessible_add_child(child7, child9);
+       atspi_accessible_add_child(child7, child10);
+       atspi_accessible_add_child(child7, child11);
+
+       atspi_accessible_add_child(child8, child12);
+       atspi_accessible_add_child(child8, child13);
+       atspi_accessible_add_child(child8, child14);
+
+       atspi_accessible_add_child(child9, child15);
+       atspi_accessible_add_child(child9, child16);
+       atspi_accessible_add_child(child9, child17);
+
+       atspi_accessible_add_child(child10, child18);
+       atspi_accessible_add_child(child10, child19);
+       atspi_accessible_add_child(child10, child20);
+
+       atspi_accessible_add_child(child11, child21);
+       atspi_accessible_add_child(child11, child22);
+       atspi_accessible_add_child(child11, child23);
+
+       eina_init();
+       atspi_alloc_memory();
+       ctx = flat_navi_context_create(win);
+}
+
+void teardown_flat_navi()
+{
+       flat_navi_context_free(ctx);
+       atspi_free_memory();
+       eina_shutdown();
+       atspi_delete_accessible(root);
+       atspi_delete_accessible(app);
+       teardown();
+}
+
+START_TEST(spi_init_null_parameter)
+{
+       spi_init(NULL);
+}
+
+END_TEST START_TEST(spi_init_service_data_parameter)
+{
+       Service_Data *data = get_pointer_to_service_data_struct();
+       spi_init(data);
+}
+
+END_TEST START_TEST(spi_on_state_change_name)
+{
+       Service_Data *sd = get_pointer_to_service_data_struct();
+       AtspiEvent event;
+       AtspiAccessible accessible;
+       event.type = "test_event";
+       sd->tracking_signal_name = "test_event";
+       event.detail1 = 1;
+       accessible.name = "test_name";
+       accessible.role = ATSPI_ROLE_ICON;
+       accessible.description = NULL;
+       event.source = &accessible;
+       char *return_value = spi_event_get_text_to_read(&event, sd);
+       fail_if(!return_value || strcmp(return_value, "test_name, Icon"));
+       free(return_value);
+}
+
+END_TEST START_TEST(spi_on_state_change_description)
+{
+       Service_Data *sd = get_pointer_to_service_data_struct();
+       AtspiEvent event;
+       AtspiAccessible accessible;
+       event.type = "test_event";
+       sd->tracking_signal_name = "test_event";
+       event.detail1 = 1;
+       accessible.name = "test_name";
+       accessible.description = "test description";
+       accessible.role = ATSPI_ROLE_ICON;
+       event.source = &accessible;
+       char *return_value = spi_event_get_text_to_read(&event, sd);
+       fail_if(!return_value || strcmp(return_value, "test_name, Icon, test description"));
+       free(return_value);
+}
+
+END_TEST START_TEST(spi_on_state_change_role)
+{
+       Service_Data *sd = get_pointer_to_service_data_struct();
+       AtspiEvent event;
+       AtspiAccessible accessible;
+       event.type = "test_event";
+       sd->tracking_signal_name = "test_event";
+       event.detail1 = 1;
+       accessible.role = ATSPI_ROLE_ICON;
+       accessible.name = NULL;
+       accessible.description = NULL;
+       event.source = &accessible;
+       char *return_value = spi_event_get_text_to_read(&event, sd);
+       char *role_name = atspi_accessible_get_role_name(&accessible, NULL);
+       fail_if(!return_value || (role_name && strcmp(return_value, role_name)));
+       free(return_value);
+       free(role_name);
+}
+
+END_TEST START_TEST(spi_on_caret_move)
+{
+       Service_Data *sd = get_pointer_to_service_data_struct();
+       AtspiEvent event;
+       AtspiAccessible accessible;
+       event.type = "object:text-caret-moved";
+       accessible.name = "test_name";
+       event.source = &accessible;
+       atspi_alloc_memory();
+       char *return_value = spi_event_get_text_to_read(&event, sd);
+       atspi_free_memory();
+       fail_if(!return_value || strcmp(return_value, "AtspiText text"));
+       free(return_value);
+}
+
+END_TEST START_TEST(spi_on_value_changed)
+{
+       Service_Data *sd = get_pointer_to_service_data_struct();
+       AtspiEvent event;
+       AtspiAccessible accessible;
+       event.type = VALUE_CHANGED_SIG;
+       accessible.name = "test_name";
+       event.source = &accessible;
+       atspi_alloc_memory();
+       char *return_value = spi_event_get_text_to_read(&event, sd);
+       atspi_free_memory();
+       fail_if(!return_value || strcmp(return_value, "1.00"));
+       free(return_value);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_create_null_parameter)
+{
+       FlatNaviContext *test_ctx = flat_navi_context_create(NULL);
+       fail_if(test_ctx);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_create_valid_parameter)
+{
+       FlatNaviContext *test_ctx = flat_navi_context_create(win);
+       fail_if(!test_ctx);
+       flat_navi_context_free(test_ctx);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_get_current_null_parameter)
+{
+       AtspiAccessible *current = flat_navi_context_current_get(NULL);
+       fail_if(current);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_get_current_valid_parameter)
+{
+       AtspiAccessible *current = flat_navi_context_current_get(ctx);
+       fail_if(!current || current != child5);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_next_null_parameter)
+{
+       AtspiAccessible *next = flat_navi_context_next(NULL);
+       fail_if(next);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_next_valid_parameter)
+{
+       AtspiAccessible *next = flat_navi_context_next(ctx);
+
+       fail_if(!next || next != child6);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_next_valid_parameter2)
+{
+       AtspiAccessible *next = flat_navi_context_next(ctx);
+
+       fail_if(!next || next != child13);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_next_valid_parameter3)
+{
+       AtspiAccessible *next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+
+       fail_if(!next || next != child16);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_next_valid_parameter4)
+{
+       AtspiAccessible *next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+
+       fail_if(!next || next != child19);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_next_valid_parameter5)
+{
+       AtspiAccessible *next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+
+       fail_if(!next || next != child22);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_next_valid_parameter6)
+{
+       AtspiAccessible *next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+       next = flat_navi_context_next(ctx);
+
+       fail_if(!next || next != child2);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_prev_null_parameter)
+{
+       AtspiAccessible *prev = flat_navi_context_prev(NULL);
+       fail_if(prev);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_prev_valid_parameter)
+{
+       AtspiAccessible *prev = flat_navi_context_prev(ctx);
+       fail_if(!prev || prev != child6);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_prev_valid_parameter2)
+{
+       AtspiAccessible *prev = flat_navi_context_prev(ctx);
+       fail_if(!prev || prev != child22);
+}
+
+END_TEST START_TEST(spi_flat_navi_context_prev_valid_parameter3)
+{
+       AtspiAccessible *prev = flat_navi_context_prev(ctx);
+       prev = flat_navi_context_prev(ctx);
+       fail_if(!prev || prev != child19);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_last_null_parameter)
+{
+   AtspiAccessible *last = flat_navi_context_last(NULL);
+   fail_if(last);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_last_valid_parameter)
+{
+   AtspiAccessible *last = flat_navi_context_last(ctx);
+   fail_if(!last || last != child6);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_first_null_parameter)
+{
+   AtspiAccessible *first = flat_navi_context_first(NULL);
+   fail_if(first);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_first_valid_parameter)
+{
+   AtspiAccessible *first = flat_navi_context_first(ctx);
+   fail_if(!first || first != child5);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_current_set_null_parameters)
+{
+   Eina_Bool ret = flat_navi_context_current_set(NULL, NULL);
+   fail_if(ret != EINA_FALSE);
+   ret = flat_navi_context_current_set(ctx, NULL);
+   fail_if(ret != EINA_FALSE);
+   AtspiAccessible *last = flat_navi_context_last(ctx);
+   ret = flat_navi_context_current_set(NULL, last);
+   fail_if(ret != EINA_FALSE);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_current_set_valid_parameters)
+{
+   AtspiAccessible *last = flat_navi_context_last(ctx);
+   Eina_Bool ret = flat_navi_context_current_set(ctx, last);
+   AtspiAccessible *current = flat_navi_context_current_get(ctx);
+   fail_if(ret != EINA_TRUE || current != last);
+}
+END_TEST
+
+Suite * screen_reader_suite(void)
+{
+       Suite *s;
+       TCase *tc_spi_screen_reader_init;
+       TCase *tc_spi_screen_reader_on_state_changed;
+       TCase *tc_spi_screen_reader_on_caret_move;
+       TCase *tc_spi_screen_reader_on_access_value;
+       TCase *tc_spi_screen_reader_flat_navi;
+       TCase *tc_spi_screen_reader_flat_navi2;
+
+       s = suite_create("Screen reader");
+       tc_spi_screen_reader_init = tcase_create("tc_spi_screen_reader_init");
+       tc_spi_screen_reader_on_state_changed = tcase_create("tc_spi_screen_reader_on_state_changed");
+       tc_spi_screen_reader_on_caret_move = tcase_create("tc_spi_screen_reader_on_caret_move");
+       tc_spi_screen_reader_on_access_value = tcase_create("tc_spi_screen_reader_on_access_value");
+       tc_spi_screen_reader_flat_navi = tcase_create("tc_scpi_screen_reader_flat_navi");
+       tc_spi_screen_reader_flat_navi2 = tcase_create("tc_scpi_screen_reader_flat_navi2");
+
+       tcase_add_checked_fixture(tc_spi_screen_reader_init, setup, teardown);
+       tcase_add_checked_fixture(tc_spi_screen_reader_on_state_changed, setup, teardown);
+       tcase_add_checked_fixture(tc_spi_screen_reader_on_caret_move, setup, teardown);
+       tcase_add_checked_fixture(tc_spi_screen_reader_on_access_value, setup, teardown);
+       tcase_add_checked_fixture(tc_spi_screen_reader_flat_navi, setup_flat_navi2, teardown_flat_navi);
+       tcase_add_checked_fixture(tc_spi_screen_reader_flat_navi2, setup_flat_navi3, teardown_flat_navi);
+
+       tcase_add_test(tc_spi_screen_reader_init, spi_init_null_parameter);
+       tcase_add_test(tc_spi_screen_reader_init, spi_init_service_data_parameter);
+       tcase_add_test(tc_spi_screen_reader_on_state_changed, spi_on_state_change_name);
+       tcase_add_test(tc_spi_screen_reader_on_state_changed, spi_on_state_change_description);
+       tcase_add_test(tc_spi_screen_reader_on_state_changed, spi_on_state_change_role);
+       tcase_add_test(tc_spi_screen_reader_on_caret_move, spi_on_caret_move);
+       tcase_add_test(tc_spi_screen_reader_on_access_value, spi_on_value_changed);
+
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_create_null_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_create_valid_parameter);
+
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_get_current_null_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_get_current_valid_parameter);
+
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_next_null_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_next_valid_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi2, spi_flat_navi_context_next_valid_parameter2);
+       tcase_add_test(tc_spi_screen_reader_flat_navi2, spi_flat_navi_context_next_valid_parameter3);
+       tcase_add_test(tc_spi_screen_reader_flat_navi2, spi_flat_navi_context_next_valid_parameter4);
+       tcase_add_test(tc_spi_screen_reader_flat_navi2, spi_flat_navi_context_next_valid_parameter5);
+       tcase_add_test(tc_spi_screen_reader_flat_navi2, spi_flat_navi_context_next_valid_parameter6);
+
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_prev_null_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_prev_valid_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi2, spi_flat_navi_context_prev_valid_parameter2);
+       tcase_add_test(tc_spi_screen_reader_flat_navi2, spi_flat_navi_context_prev_valid_parameter3);
+
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_last_null_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_last_valid_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_first_null_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_first_valid_parameter);
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_current_set_null_parameters);
+       tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_current_set_valid_parameters);
+
+       suite_add_tcase(s, tc_spi_screen_reader_init);
+       suite_add_tcase(s, tc_spi_screen_reader_on_state_changed);
+       suite_add_tcase(s, tc_spi_screen_reader_on_caret_move);
+       suite_add_tcase(s, tc_spi_screen_reader_on_access_value);
+       suite_add_tcase(s, tc_spi_screen_reader_flat_navi);
+       suite_add_tcase(s, tc_spi_screen_reader_flat_navi2);
+
+       return s;
+}
+
+int main()
+{
+       int number_failed;
+       Suite *s;
+       SRunner *sr;
+
+       s = screen_reader_suite();
+       sr = srunner_create(s);
+
+       srunner_run_all(sr, CK_VERBOSE);
+       number_failed = srunner_ntests_failed(sr);
+       srunner_free(sr);
+       return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}