--- /dev/null
+## 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")
+
+## LINKER FLAGS
+SET(SLP_LINKER_FLAGS "")
--- /dev/null
+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")
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs REQUIRED
+ capi-appfw-application
+ bundle
+ atspi-2
+ gobject-2.0
+ dlog
+ vconf
+ tts
+ capi-system-device
+)
+
+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}")
+SET(CMAKE_CXX_FLAGS "${SLP_DEBUG_FLAGS} ${SLP_OPT_FLAGS} ${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} ${SLP_COMPILER_FLAGS}")
+
+MESSAGE(${CMAKE_C_FLAGS})
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS})
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} ${SLP_LD_PATH_FLAGS} ${SLP_LD_FLAGS} ${SLP_LINKER_FLAGS})
+
+# 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 /usr/share/packages)
+ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/tests)
+ADD_TEST(NAME screen_reader_test_suite COMMAND ${CMAKE_SOURCE_DIR}/tests/screen_reader_test_suite)
+# END OF A FILE
--- /dev/null
+#ifndef APP_TRACKER_H_
+#define APP_TRACKER_H_
+
+#include <atspi/atspi.h>
+
+/**
+ * @brief Application events
+ *
+ * @APP_TRACKER_EVENT_VIEW_CHANGE_STARTED - occuring when any of application's
+ * UI parts has change its visuals, including moving, resizing widgets, scrolling etc.
+ *
+ * @APP_TRACKER_EVENT_VIEW_CHANGED occuring when any of application's
+ * UI parts has changed its visuals and a prefined time period elapsed
+ *
+ */
+typedef enum {
+ APP_TRACKER_EVENT_VIEW_CHANGE_STARTED,
+ APP_TRACKER_EVENT_VIEW_CHANGED,
+ APP_TRACKER_EVENT_COUNT
+} AppTrackerEventType;
+
+/**
+ * @brief Callback
+ */
+typedef void (*AppTrackerEventCB)(AppTrackerEventType type, void *user_data);
+
+/**
+ * @brief Register listener on given event type.
+ *
+ * @param obj AtspiAccessible application object type
+ * @param event type of event to register on
+ * @param cb callback type
+ * @param user_data pointer passed to cb function.
+ */
+void app_tracker_callback_register(AtspiAccessible *app, AppTrackerEventType event, AppTrackerEventCB cb, void *user_data);
+
+/**
+ * @brief Unregister listener on given event type.
+ *
+ * @param obj AtspiAccessible application object type
+ * @param event type of event to register on
+ * @param cb callback type
+ * @param user_data pointer passed to cb function.
+ */
+void app_tracker_callback_unregister(AtspiAccessible *app, AppTrackerEventType event, AppTrackerEventCB cb, void *user_data);
+
+/**
+ * @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_ */
--- /dev/null
+#ifndef FLAT_NAVI_H_
+#define FLAT_NAVI_H_
+
+#include <atspi/atspi.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);
+
+/**
+ * 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
+ */
+gboolean flat_navi_context_current_set(FlatNaviContext *ctx, AtspiAccessible *target);
+
+/**
+ * Advances to previous line in natural reading order and returns
+ * new current element.
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @return AtspiAccessible* pointer to current object
+ *
+ * @note If current line is first one, function returns NULL
+ * @note current element will be first of line.
+ */
+AtspiAccessible *flat_navi_context_line_prev(FlatNaviContext *ctx);
+
+/**
+ * Advances to next line in natural reading order and returns
+ * new current element.
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @return AtspiAccessible* pointer to current object
+ *
+ * @note If current line is last one, function returns NULL
+ * @note current element will be first of line.
+ */
+AtspiAccessible *flat_navi_context_line_next(FlatNaviContext *ctx);
+
+/**
+ * Advances to first line.
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @return AtspiAccessible* pointer to current object
+ *
+ * @note current element will be first of line.
+ */
+AtspiAccessible *flat_navi_context_line_first(FlatNaviContext *ctx);
+
+/**
+ * Advances to last line.
+ *
+ * @param ctx FlatNaviContext
+ *
+ * @return AtspiAccessible* pointer to current object
+ *
+ * @note current element will be first of line.
+ */
+AtspiAccessible *flat_navi_context_line_last(FlatNaviContext *ctx);
+
+#endif /* end of include guard: FLAT_NAVI_H_ */
--- /dev/null
+/**
+ * @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);
--- /dev/null
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGGER_H_
+
+#include <dlog.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "SmartNavigator"
+
+#ifndef ERROR
+#define ERROR(fmt, arg...) LOGE("[%s:%d]:" fmt, __FILE__, __LINE__, ##arg)
+#endif
+
+#ifndef DEBUG
+#define DEBUG(fmt, arg...) LOGD("[%s:%d]:" fmt, __FILE__, __LINE__, ##arg)
+#endif
+
+#ifndef INFO
+#define INFO(fmt, arg...) LOGI("[%s:%d]:" fmt, __FILE__, __LINE__, ##arg)
+#endif
+
+
+#endif /* _LOGGER_H_ */
--- /dev/null
+#include <atspi/atspi.h>
+
+void navigator_init(void);
+void navigator_shutdown(void);
--- /dev/null
+#ifndef SMART_NAVI_OBJECT_CACHE_H_H
+#define SMART_NAVI_OBJECT_CACHE_H_H
+
+#include <atspi/atspi.h>
+
+typedef struct {
+ // Boundaries of object taken from atspi_component_get_extents function.
+ // NULL if object do not implemetn AtspiComponent interface
+ AtspiRect *bounds;
+} ObjectCache;
+
+typedef void (*ObjectCacheReadyCb)(void *data);
+
+/**
+ * @brief Recursivly build ObjectCache structures for root Accessible
+ * object from and its descendants. (Children's children also, etc.)
+ *
+ * @param root starting object.
+ *
+ * @remarks This function may block main-loop for significant ammount of time.
+ * Flushes all previously cached items.
+ */
+void object_cache_build(AtspiAccessible *root);
+
+/**
+ * @brief Recursivly build ObjectCache structures for ever Accessible
+ * object from root and its all descendants.
+ *
+ * @param root starting object.
+ * @param bulk_size Ammount of AtspiAccessible that will be cached on every
+ * ecore_main_loop entering into 'idle' state.
+ * @param cb function to be called after creating cache is done.
+ * @param user_data passed to cb
+ *
+ * @remarks Flushes all previously cached items.
+ */
+void object_cache_build_async(AtspiAccessible *root, int bulk_size, ObjectCacheReadyCb cb, void *user_data);
+
+/**
+ * @brief Gets and ObjectCache item from accessible object
+ *
+ * @param obj AtspiAccessible
+ *
+ * @remarks If obj is not in cache function will block main loop and build ObjectCache
+ * struct.
+ */
+const ObjectCache *object_cache_get(AtspiAccessible *obj);
+
+/**
+ * @brief Shoutdown cache.
+ *
+ * Function will free all gathered caches.
+ *
+ * @remarks Use it after You have used any of above object_cache_* functions.
+ */
+void object_cache_shutdown(void);
+
+#endif /* end of include guard: OBJECT_CACHE_H_H */
--- /dev/null
+#ifndef SMART_NAVI_PIVOT_CHOOSER_H_
+#define SMART_NAVI_PIVOT_CHOOSER_H_
+
+#include <atspi/atspi.h>
+
+/**
+ * @brief Heuristic choosing first element of the UI.
+ *
+ * @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_ */
--- /dev/null
+#ifndef SMART_NAVI_POSITION_SORT_H_
+#define SMART_NAVI_POSITION_SORT_H_
+
+#include <atspi/atspi.h>
+#include <glib.h>
+
+
+/**
+ * @brief Sort objects by position they appear on screen
+ *
+ * example:
+ *
+ * ------------------------------
+ * | _______ |
+ * | _______ | | |
+ * | | | ______ | | |
+ * | | A | | | | | |
+ * | |_____| | B | | C | |
+ * | | | | | |
+ * | |____| | | |
+ * | _____ | | | line 0: A, B, C
+ * | | | |_____| | line 1: D
+ * | | D | | ====> line 2: F
+ * | |___| _____ | line 3: G
+ * | | | |
+ * | | F | _____ |
+ * | |___| | | |
+ * | | G | |
+ * | |___| |
+ * | |
+ * ------------------------------
+ *
+ * @ret list List of lists
+ */
+GList *position_sort(const GList *obj);
+
+#endif /* end of include guard: POSITION_SORT_H_ */
--- /dev/null
+#ifndef __screen_reader_H__
+#define __screen_reader_H__
+
+#include <atspi/atspi.h>
+#include <tts.h>
+
+#define LAN_NAME 6
+
+#define MAX_REACHED ", maximum value reached"
+#define MIN_REACHED ", minimum value reached"
+#define MAX_POS_REACHED ", end of text reached"
+#define MIN_POS_REACHED ", begin of text reached"
+
+#define FOCUS_SIG "focused"
+
+#define EDITING_STARTED "Editing"
+
+#define FOCUS_CHANGED_SIG "object:state-changed:focused"
+#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
+ int information_level;
+ bool run_service;
+ char language[LAN_NAME];
+ int voice_type;
+ int reading_speed;
+ char *tracking_signal_name;
+
+ //Set by tts
+ tts_h tts;
+ GList *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 *spi_listener;
+
+ AtspiAccessible *currently_focused;
+ AtspiAccessible *mouse_down_widget;
+ AtspiAccessible *clicked_widget;
+
+ //Set by dbus
+ 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
--- /dev/null
+void haptic_module_init(void);
+void haptic_module_disconnect(void);
+void haptic_vibrate_start(void);
+void haptic_vibrate_stop(void);
--- /dev/null
+/*
+ * screen_reader_spi.h
+ *
+ * Created on: Feb 20, 2014
+ * Author: m.skorupinsk
+ */
+
+#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_ */
--- /dev/null
+void system_notifications_init(void);
+void system_notifications_shutdown(void);
\ No newline at end of file
--- /dev/null
+/*
+ * screen_reader_tts.h
+ *
+ * Created on: Feb 19, 2014
+ * Author: m.skorupinsk
+ */
+
+#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);
+gboolean tts_speak(char *text_to_speak, gboolean flush_switch);
+void spi_stop(void *data);
+
+#endif /* SCREEN_READER_TTS_H_ */
--- /dev/null
+/*
+ * screen_reader_vconf.h
+ *
+ * Created on: Feb 19, 2014
+ * Author: m.skorupinsk
+ */
+
+#ifndef SCREEN_READER_VCONF_H_
+#define SCREEN_READER_VCONF_H_
+
+#include "screen_reader.h"
+#include "logger.h"
+
+bool vconf_init(Service_Data *service_data);
+
+#endif /* SCREEN_READER_VCONF_H_ */
--- /dev/null
+#ifndef SMART_NOTIFICATION_H_
+#define SMART_NOTIFICATION_H_
+
+/**
+ * @brief Type of notification events.
+ *
+ * @FOCUS_CHAIN_END_NOTIFICATION_EVENT emitted when
+ * currnetly focued 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_ */
--- /dev/null
+#ifndef SMART_NAVI_STRUCTURAL_NAVI_H
+#define SMART_NAVI_STRUCTURAL_NAVI_H_
+
+#include <atspi/atspi.h>
+
+/**
+ * @brief Structural navigation submodule.
+ *
+ * @detail
+ * Structural navigation submodule can be used to traverse at-spi object tree
+ * hierarchy in unified, position sorted manner.
+ */
+
+/**
+ * @brief Gets next Accessible object in "structural navigation" hierarchy.
+ *
+ * @detail
+ * Gest next sibling object in accessibile objects tree which is considered as
+ * next by taking into account its position on screen. (see @position_sort for
+ * more details about visuall sorting)
+ *
+ * @param current AtspiAccessible instance from which sibling object will be
+ * searched.
+ *
+ * @retun 'Next' AtspiAccessible object pointer or NULL if not found.
+ */
+AtspiAccessible *structural_navi_same_level_next(AtspiAccessible *current);
+
+/**
+ * @brief Gets previous Accessible object in "structural navigation" hierarchy.
+ *
+ * @detail
+ * Gest previous sibling object in accessibile objects tree which is considered as
+ * previous by taking into account its position on screen. (see @position_sort for
+ * more details about visuall sorting)
+ *
+ * @param current AtspiAccessible instance from which sibling object will be
+ * searched.
+ *
+ * @retun 'Previous' AtspiAccessible object pointer or NULL if not found.
+ */
+AtspiAccessible *structural_navi_same_level_prev(AtspiAccessible *current);
+
+/**
+ * @brief Gests parent's object.
+ *
+ * @detail
+ * Gets parent object contained within same application. If there is no parent
+ * object contained in same application function should return NULL.
+ *
+ * @param object AtspiAccessible which parent will be searched.
+ * @return Parent AtspiAccessible object or NULL if not found or @current
+ * is an application object.
+ */
+AtspiAccessible *structural_navi_level_up(AtspiAccessible *current);
+
+/**
+ * @brief Gets child object.
+ *
+ * @detail
+ * Gets AtspiAccessible object considered as first child in structural
+ * navigation hierachy. Child will be sorted by its position on the screen.
+ * (see @position_sort for more details)
+ *
+ * @param object AtspiAccessible which child will be searched.
+ * @return child AtspiAccessible object or NULL if not found.
+ */
+AtspiAccessible *structural_navi_level_down(AtspiAccessible *current);
+
+/**
+ * @brief Gets next object.
+ *
+ * @detail
+ * Get next AtspiAccessible object from Atspi realation FLOWS_TO.
+ *
+ * @param object AtspiAccessible
+ * @return next AtspiAccessible object or NULL if not found.
+ */
+AtspiAccessible *structural_navi_app_chain_next(AtspiAccessible *current);
+
+/**
+ * @brief Gets previous object.
+ *
+ * @detail
+ * Get previous AtspiAccessible object from Atspi realation FLOWS_FROM.
+ *
+ * @param object AtspiAccessible
+ * @return next AtspiAccessible object or NULL if not found.
+ */
+AtspiAccessible *structural_navi_app_chain_prev(AtspiAccessible *current);
+
+#endif /* end of include guard: STRUCTURAL_NAVI_H_ */
--- /dev/null
+#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);
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" package="org.tizen.screen-reader" version="0.1.0" install-location="internal-only">
+ <label>screen-reader</label>
+ <description>SCREEN READER</description>
+ <ui-application appid="org.tizen.screen-reader" exec="/usr/apps/org.tizen.screen-reader/bin/screen-reader" nodisplay="false" multiple="false" type="capp" taskmanage="true">
+ <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>
+ </ui-application>
+</manifest>
+
--- /dev/null
+Name: org.tizen.screen-reader
+Summary: Empty app
+Version: 0.0.1
+Release: 1
+Group: Applications/Core Applications
+License: Apache-2.0
+Source0: %{name}-%{version}.tar.gz
+
+BuildRequires: at-spi2-core
+BuildRequires: at-spi2-core-devel
+BuildRequires: cmake
+BuildRequires: pkgconfig(capi-appfw-application)
+BuildRequires: pkgconfig(capi-system-device)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(bundle)
+BuildRequires: tts
+BuildRequires: tts-devel
+BuildRequires: vconf
+BuildRequires: pkgconfig(check)
+
+%define AppInstallPath /usr/apps/%{name}
+%define Exec screen-reader
+
+
+%description
+An utility library for developers of the menu screen.
+
+%prep
+%setup -q
+
+%build
+cmake . -DCMAKE_INSTALL_PREFIX="%{AppInstallPath}" -DCMAKE_TARGET="%{Exec}" -DCMAKE_PACKAGE="%{name}"
+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
+vconftool set -t string db/setting/accessibility/language "en_US"
+vconftool set -t int db/setting/accessibility/information_level 2
+vconftool set -t int db/setting/accessibility/voice 1
+vconftool set -t string db/setting/accessibility/tracking_signal "focused"
+
+%postun -p /sbin/ldconfig
+
+%files
+%manifest org.tizen.screen-reader.manifest
+%{AppInstallPath}/bin/screen-reader
+%{AppInstallPath}/res/icons/screen-reader.png
+/usr/share/packages/%{name}.xml
--- /dev/null
+#include "app_tracker.h"
+#include "logger.h"
+
+typedef struct {
+ AtspiAccessible *root;
+ GList *callbacks;
+ guint timer;
+} SubTreeRootData;
+
+typedef struct {
+ AppTrackerEventType type;
+ AppTrackerEventCB func;
+ void *user_data;
+} EventCallbackData;
+
+#define APP_TRACKER_INVACTIVITY_TIMEOUT 200
+
+static int _init_count;
+static GList *_roots;
+static AtspiEventListener *_listener;
+
+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 void
+_subtree_callbacks_call(AppTrackerEventType event, SubTreeRootData *std)
+{
+ GList *l;
+ EventCallbackData *ecd;
+
+ for (l = std->callbacks; l != NULL; l = l->next)
+ {
+ ecd = l->data;
+ if (ecd->type == event)
+ {
+ ecd->func(event, ecd->user_data);
+ }
+ }
+}
+
+static gboolean
+_on_timeout_cb(gpointer user_data)
+{
+ SubTreeRootData *std = user_data;
+
+ _subtree_callbacks_call(APP_TRACKER_EVENT_VIEW_CHANGED, std);
+
+ std->timer = 0;
+
+ return FALSE;
+}
+
+static void
+_on_atspi_event_cb(const AtspiEvent *event)
+{
+ GList *l;
+ SubTreeRootData *std;
+
+ if (!event || !event->source) return;
+
+ for (l = _roots; l != NULL; l = l->next)
+ {
+ std = l->data;
+ if (_is_descendant(std->root, event->source))
+ {
+ if (std->timer)
+ g_source_remove(std->timer);
+ else
+ _subtree_callbacks_call(APP_TRACKER_EVENT_VIEW_CHANGE_STARTED, std);
+
+ std->timer = g_timeout_add(APP_TRACKER_INVACTIVITY_TIMEOUT, _on_timeout_cb, std);
+ }
+ }
+}
+
+static int
+_app_tracker_init_internal(void)
+{
+ _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:children-changed", NULL);
+ atspi_event_listener_register(_listener, "object:bounds-changed", NULL);
+ atspi_event_listener_register(_listener, "object:visible-data-changed", 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:children-changed", NULL);
+ atspi_event_listener_deregister(_listener, "object:bounds-changed", NULL);
+ atspi_event_listener_deregister(_listener, "object:visible-data-changed", NULL);
+
+ g_object_unref(_listener);
+ _listener = NULL;
+
+ g_list_free_full(_roots, _free_rootdata);
+ _roots = NULL;
+}
+
+int app_tracker_init(void)
+{
+ 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, AppTrackerEventType event, AppTrackerEventCB cb, void *user_data)
+{
+ 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->callbacks = NULL;
+ rd->timer = 0;
+ _roots = g_list_append(_roots, rd);
+ }
+
+ cd = g_new(EventCallbackData, 1);
+ cd->type = event;
+ cd->func = cb;
+ cd->user_data = user_data;
+
+ rd->callbacks = g_list_append(rd->callbacks, cd);
+}
+
+void app_tracker_callback_unregister(AtspiAccessible *app, AppTrackerEventType event, AppTrackerEventCB cb, void *user_data)
+{
+ GList *l;
+ EventCallbackData *ecd;
+ SubTreeRootData *std = NULL;
+
+ for (l = _roots; l != NULL; l = l->next)
+ {
+ if (((SubTreeRootData*)l->data)->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);
+ }
+}
--- /dev/null
+#include "flat_navi.h"
+#include "position_sort.h"
+#include "object_cache.h"
+#include "logger.h"
+#include <glib.h>
+#include <stdlib.h>
+
+struct _FlatNaviContext {
+ AtspiAccessible *root;
+ GList *current;
+ GList *current_line;
+ GList *lines;
+ GList *candidates;
+};
+
+static const AtspiStateType required_states[] = {
+ ATSPI_STATE_VISIBLE,
+ ATSPI_STATE_SHOWING,
+ ATSPI_STATE_LAST_DEFINED
+};
+
+static const AtspiRole interesting_roles[] = {
+ ATSPI_ROLE_CHECK_BOX,
+ ATSPI_ROLE_COLOR_CHOOSER,
+ ATSPI_ROLE_COMBO_BOX,
+ ATSPI_ROLE_DATE_EDITOR,
+ ATSPI_ROLE_FILE_CHOOSER,
+ ATSPI_ROLE_FONT_CHOOSER,
+ ATSPI_ROLE_HEADER,
+ ATSPI_ROLE_HEADING,
+ ATSPI_ROLE_ICON,
+ ATSPI_ROLE_ENTRY,
+ ATSPI_ROLE_LABEL,
+ ATSPI_ROLE_LIST_ITEM,
+ ATSPI_ROLE_MENU_ITEM,
+ ATSPI_ROLE_PARAGRAPH,
+ ATSPI_ROLE_PASSWORD_TEXT,
+ 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
+};
+
+/**
+ * @brief Returns a list of all descendants objects
+ *
+ * @return GList of AtspiAccessible* type. List Should be
+ * be free with _accessible_list_free after usage
+ *
+ * @note obj parameter will also be included (in list head)
+ * @note every obj has increased ref. call g_object_unref on every
+ * object after usage.
+ */
+static GList*
+_descendants_list_get(AtspiAccessible *obj)
+{
+ GList *ret = NULL, *toprocess = NULL;
+ int i;
+
+ if (!obj) return NULL;
+
+ // to keep all refcounts in ret list +1
+ g_object_ref(obj);
+ toprocess = g_list_append(toprocess, obj);
+
+ while (toprocess)
+ {
+ AtspiAccessible *obj = toprocess->data;
+ toprocess = g_list_delete_link(toprocess, toprocess);
+ int n = atspi_accessible_get_child_count(obj, NULL);
+ for (i = 0; i < n; i++)
+ {
+ AtspiAccessible *child = atspi_accessible_get_child_at_index(obj, i, NULL);
+ if (child) toprocess = g_list_append(toprocess, child);
+ }
+
+ ret = g_list_append(ret, obj);
+ }
+
+ return ret;
+}
+
+static void
+_accessible_list_free(GList *d)
+{
+ for (; d != NULL; d = d->next)
+ g_object_unref(d->data);
+}
+
+typedef struct {
+ GList *success;
+ GList *failure;
+} FilterResult;
+
+typedef gboolean (*Filter_Each_Cb)(const void *container, void *data, void *fdata);
+
+static FilterResult
+_accessible_list_split_with_filter(GList *list, Filter_Each_Cb cb, void *user_data)
+{
+ FilterResult ret = { NULL, NULL};
+ GList *l;
+ AtspiAccessible *obj;
+
+ for (l = list; l; l = l->next)
+ {
+ obj = l->data;
+ gboolean res = cb(NULL, user_data, obj);
+ if (res)
+ ret.success = g_list_append(ret.success, obj);
+ else
+ ret.failure = g_list_append(ret.failure, obj);
+ }
+ g_list_free(list);
+
+ return ret;
+}
+
+static gboolean
+_filter_state_cb(const void *container, void *data, void *fdata)
+{
+ AtspiStateType *state = data;
+ gboolean ret = TRUE;
+ AtspiAccessible *obj = fdata;
+ AtspiStateSet *ss = atspi_accessible_get_state_set(obj);
+
+ while (*state != ATSPI_STATE_LAST_DEFINED)
+ {
+ if (!atspi_state_set_contains(ss, *state))
+ {
+ ret = FALSE;
+ break;
+ }
+ state++;
+ }
+
+ g_object_unref(ss);
+ return ret;
+}
+
+static gboolean
+_filter_role_cb(const void *container, void *data, void *fdata)
+{
+ AtspiRole *role = data;
+ gboolean ret = FALSE;
+ AtspiAccessible *obj = fdata;
+
+ while (*role != ATSPI_ROLE_LAST_DEFINED)
+ {
+ if (atspi_accessible_get_role(obj, NULL) == *role)
+ {
+ ret = TRUE;
+ break;
+ }
+ role++;
+ }
+
+ return ret;
+}
+
+static inline gboolean
+_rectangle_intersect(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2)
+{
+ return !(((y1 + h1) <= y2) || (y1 >= (y2 + h2)) || ((x1 + w1) <= x2) || (x1 >= (x2 + w2)));
+}
+
+static gboolean
+_filter_viewport_cb(const void *container, void *data, void *fdata)
+{
+ const ObjectCache *oc, *ocr = data;
+ AtspiAccessible *obj = fdata;
+
+ oc = object_cache_get(obj);
+ if (!oc || !oc->bounds || (oc->bounds->height < 0) || (oc->bounds->width < 0))
+ return FALSE;
+
+ // at least one pixel of child have to be in viewport
+ return _rectangle_intersect(ocr->bounds->x, ocr->bounds->y, ocr->bounds->width, ocr->bounds->height,
+ oc->bounds->x, oc->bounds->y, oc->bounds->width, oc->bounds->height);
+}
+
+static GList*
+_flat_review_candidates_get(AtspiAccessible *root)
+{
+ GList *desc, *ret = NULL;
+ FilterResult fr0;
+
+ desc = _descendants_list_get(root);
+
+ DEBUG("Descendants: %d", g_list_length(desc));
+
+ // remove object that are not in root's viewport
+ const ObjectCache *oc = object_cache_get(root);
+ if (!oc || !oc->bounds || (oc->bounds->height < 0) || (oc->bounds->width < 0))
+ {
+ ERROR("Invalid root object bounds. Unable to filter descendants by position");
+ fr0.success = desc;
+ }
+ else
+ {
+ fr0 = _accessible_list_split_with_filter(desc, _filter_viewport_cb, (void*)oc);
+ _accessible_list_free(fr0.failure);
+ }
+
+ // remove objects without required states
+ FilterResult fr1 = _accessible_list_split_with_filter(fr0.success, _filter_state_cb, (void*)required_states);
+ _accessible_list_free(fr1.failure);
+
+ // get 'interesting' objects
+ FilterResult fr2 = _accessible_list_split_with_filter(fr1.success, _filter_role_cb, (void*)interesting_roles);
+ ret = g_list_concat(ret, fr2.success);
+ _accessible_list_free(fr2.failure);
+
+ DEBUG("Candidates: %d", g_list_length(ret));
+
+ return ret;
+}
+
+static void
+debug(FlatNaviContext *ctx)
+{
+ GList *l1, *l2, *line;
+ AtspiAccessible *obj;
+ int i, l = 0;
+
+ DEBUG("===============================");
+ for (l1 = ctx->lines; l1 != NULL; l1 = l1->next)
+ {
+ line = l1->data;
+ i = 0;
+ DEBUG("==== Line %d ====", l);
+ for (l2 = line; l2 != NULL; l2 = l2->next)
+ {
+ obj = l2->data;
+ char *name = atspi_accessible_get_name(obj, NULL);
+ char *role = atspi_accessible_get_role_name(obj, NULL);
+ const ObjectCache *oc = object_cache_get(obj);
+ if (oc) {
+ DEBUG("%d %s %s, (%d %d %d %d)", i++, name, role,
+ oc->bounds->x, oc->bounds->y, oc->bounds->width, oc->bounds->height);
+ }
+ if (name) g_free(name);
+ if (role) g_free(role);
+ }
+ l++;
+ }
+ DEBUG("===============================");
+}
+
+FlatNaviContext *flat_navi_context_create(AtspiAccessible *root)
+{
+ FlatNaviContext *ret;
+
+ ret = calloc(1, sizeof(FlatNaviContext));
+ if (!ret) return NULL;
+
+ ret->root = root;
+ ret->candidates = _flat_review_candidates_get(root);
+ ret->lines = position_sort(ret->candidates);
+ ret->current_line = ret->lines;
+ ret->current = ret->current_line ? ret->current_line->data : NULL;
+
+ debug(ret);
+
+ return ret;
+}
+
+void flat_navi_context_free(FlatNaviContext *ctx)
+{
+ GList *l;
+ for (l = ctx->lines; l != NULL; l = l->next)
+ g_list_free((GList*)l->data);
+ _accessible_list_free(ctx->candidates);
+ free(ctx);
+}
+
+AtspiAccessible *flat_navi_context_current_get(FlatNaviContext *ctx)
+{
+ if (!ctx || !ctx->lines) return NULL;
+ return ctx->current->data;
+}
+
+gboolean flat_navi_context_current_set(FlatNaviContext *ctx, AtspiAccessible *target)
+{
+ if(!ctx || !target) return FALSE;
+
+ GList *l, *l2, *line;
+ AtspiAccessible *obj;
+ gboolean found = FALSE;
+
+ for (l = ctx->lines; l; l = l->next)
+ {
+ line = l->data;
+ for (l2 = line; l2; l2 = l2->next)
+ {
+ obj = l2->data;
+ if (obj == target)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found)
+ {
+ ctx->current_line = l;
+ ctx->current = l2;
+ break;
+ }
+ }
+
+ return found;
+}
+
+AtspiAccessible *flat_navi_context_next(FlatNaviContext *ctx)
+{
+ if (!ctx || !ctx->lines) return NULL;
+ GList *ret = ctx->current->next;
+
+ if (ret)
+ {
+ ctx->current = ret;
+ return ctx->current->data;
+ }
+ return NULL;
+}
+
+AtspiAccessible *flat_navi_context_prev(FlatNaviContext *ctx)
+{
+ if (!ctx || !ctx->lines) return NULL;
+ GList *ret = ctx->current->prev;
+
+ if (ret)
+ {
+ ctx->current = ret;
+ return ctx->current->data;
+ }
+ return NULL;
+}
+
+AtspiAccessible *flat_navi_context_first(FlatNaviContext *ctx)
+{
+ if (!ctx || !ctx->lines) return NULL;
+ GList *ret = ctx->current_line->data;
+
+ if (ret)
+ {
+ ctx->current = ret;
+ return ctx->current->data;
+ }
+ return NULL;
+}
+
+AtspiAccessible *flat_navi_context_last(FlatNaviContext *ctx)
+{
+ if (!ctx || !ctx->lines) return NULL;
+ GList *ret = g_list_last(ctx->current);
+
+ if (ret)
+ {
+ ctx->current = ret;
+ return ctx->current->data;
+ }
+ return NULL;
+}
+
+AtspiAccessible *flat_navi_context_line_next(FlatNaviContext *ctx)
+{
+ if (!ctx || !ctx->lines) return NULL;
+
+ GList *ret = ctx->current_line->next;
+ if (!ret) return NULL;
+
+ ctx->current_line = ret;
+ ctx->current = ctx->current_line->data;
+
+ return ctx->current->data;
+}
+
+AtspiAccessible *flat_navi_context_line_prev(FlatNaviContext *ctx)
+{
+ if (!ctx || !ctx->lines) return NULL;
+
+ GList *ret = ctx->current_line->prev;
+ if (!ret) return NULL;
+
+ ctx->current_line = ret;
+ ctx->current = ctx->current_line->data;
+
+ return ctx->current->data;
+}
+
+AtspiAccessible *flat_navi_context_line_first(FlatNaviContext *ctx)
+{
+ if (!ctx || !ctx->lines) return NULL;
+
+ ctx->current_line = ctx->lines;
+ ctx->current = ctx->current_line->data;
+
+ return ctx->current->data;
+}
+
+AtspiAccessible *flat_navi_context_line_last(FlatNaviContext *ctx)
+{
+ if (!ctx || !ctx->lines) return NULL;
+
+ ctx->current_line = g_list_last(ctx->current_line);
+ ctx->current = ctx->current_line->data;
+
+ return ctx->current->data;
+}
--- /dev/null
+#include <string.h>
+#include <atspi/atspi.h>
+#include "keyboard_tracker.h"
+#include "logger.h"
+
+static AtspiDeviceListener *listener;
+static Keyboard_Tracker_Cb user_cb;
+static void *user_data;
+
+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)
+{
+ atspi_init();
+ 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);
+ 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);
+ DEBUG("keyboard tracker shutdown");
+}
--- /dev/null
+#include <app.h>
+#include "navigator.h"
+#include "window_tracker.h"
+#include "logger.h"
+#include "screen_reader.h"
+
+
+static bool app_create(void *data)
+{
+ atspi_init();
+ navigator_init();
+ screen_reader_create_service(data);
+
+ return true;
+}
+
+static void app_terminate(void *data)
+{
+ screen_reader_terminate_service(data);
+ navigator_shutdown();
+}
+
+int main(int argc, char **argv)
+{
+ app_create(get_pointer_to_service_data_struct());
+
+ GMainLoop *ml = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run (ml);
+
+ app_terminate(get_pointer_to_service_data_struct());
+
+ return 0;
+}
--- /dev/null
+#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 "structural_navi.h"
+#include "object_cache.h"
+#include "flat_navi.h"
+#include "app_tracker.h"
+#include "smart_notification.h"
+#include "screen_reader_system.h"
+#include "screen_reader_haptic.h"
+
+#define QUICKPANEL_DOWN TRUE
+#define QUICKPANEL_UP FALSE
+
+#define DISTANCE_NB 8
+
+#define DEBUG_MODE
+
+#define GERROR_CHECK(error)\
+ if (error)\
+ {\
+ ERROR("Error_log:%s",error->message);\
+ g_error_free(error);\
+ error = NULL;\
+ }
+
+static AtspiAccessible *top_window;
+static gboolean _window_cache_builded;
+static FlatNaviContext *context;
+
+static void
+_current_highlight_object_set(AtspiAccessible *obj)
+{
+ //TODO
+ //speak virtually focused widget
+}
+
+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)
+ 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)
+ }
+}
+
+static void _focus_next(void)
+{
+ AtspiAccessible *obj;
+ if (!context)
+ {
+ ERROR("No navigation context created");
+ return;
+ }
+ obj = flat_navi_context_next(context);
+ // try next line
+ if (!obj)
+ obj = flat_navi_context_line_next(context);
+ // try 'cycle' objects in context
+ if (!obj)
+ {
+ flat_navi_context_line_first(context);
+ obj = flat_navi_context_first(context);
+ smart_notification(FOCUS_CHAIN_END_NOTIFICATION_EVENT, 0, 0);
+ }
+ if (obj)
+ _current_highlight_object_set(obj);
+ else
+ DEBUG("Next 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);
+ // try previous line
+ if (!obj)
+ {
+ obj = flat_navi_context_line_prev(context);
+ if (obj)
+ obj = flat_navi_context_last(context);
+ }
+ // try 'cycle' objects in context
+ if (!obj)
+ {
+ flat_navi_context_line_last(context);
+ obj = flat_navi_context_last(context);
+ smart_notification(FOCUS_CHAIN_END_NOTIFICATION_EVENT, 0, 0);
+ }
+ if (obj)
+ _current_highlight_object_set(obj);
+ else
+ DEBUG("Previous widget not found. Abort");
+}
+
+static void
+_on_cache_builded(void *data)
+{
+ DEBUG("Cache building");
+ _window_cache_builded = TRUE;
+ AtspiAccessible *pivot = NULL;
+ if (context)
+ {
+ pivot = flat_navi_context_current_get(context);
+ flat_navi_context_free(context);
+ }
+ context = flat_navi_context_create(top_window);
+
+ // try to set previous object in new context
+ if (flat_navi_context_current_set(context, pivot))
+ _current_highlight_object_set(pivot);
+ else
+ _current_highlight_object_set(flat_navi_context_current_get(context));
+ DEBUG("Cache building finished");
+}
+
+static void
+_view_content_changed(AppTrackerEventType type, void *user_data)
+{
+ DEBUG("View content changed");
+ _window_cache_builded = FALSE;
+ if (top_window)
+ object_cache_build_async(top_window, 5, _on_cache_builded, NULL);
+}
+
+static void on_window_activate(void *data, AtspiAccessible *window)
+{
+ gchar *name;
+ DEBUG("... on window activate ...");
+
+ app_tracker_callback_unregister(top_window, APP_TRACKER_EVENT_VIEW_CHANGED, _view_content_changed, NULL);
+
+ if(window)
+ {
+ app_tracker_callback_register(window, APP_TRACKER_EVENT_VIEW_CHANGED, _view_content_changed, NULL);
+ name = atspi_accessible_get_name(window, NULL);
+ DEBUG("Window name: %s", name);
+ _window_cache_builded = FALSE;
+ object_cache_build_async(window, 5, _on_cache_builded, NULL);
+ g_free(name);
+ }
+ else
+ {
+ ERROR("No top window found!");
+ }
+ top_window = window;
+}
+
+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)
+{
+ window_tracker_init();
+ window_tracker_register(on_window_activate, NULL);
+ window_tracker_active_window_request();
+ app_tracker_init();
+ smart_notification_init();
+ system_notifications_init();
+ keyboard_tracker_init();
+ keyboard_tracker_register(kb_tracker, NULL);
+}
+
+void navigator_shutdown(void)
+{
+ if (context)
+ {
+ flat_navi_context_free(context);
+ context = NULL;
+ }
+ object_cache_shutdown();
+ app_tracker_shutdown();
+ window_tracker_shutdown();
+ smart_notification_shutdown();
+ system_notifications_shutdown();
+ keyboard_tracker_shutdown();
+}
--- /dev/null
+#include "object_cache.h"
+#include "logger.h"
+
+#include <glib.h>
+
+static GHashTable *cache;
+static int idler = -1;
+static GList *toprocess;
+static void *user_data;
+static ObjectCacheReadyCb callback;
+static int bulk;
+
+static void
+_cache_item_free_cb(gpointer data)
+{
+ if (!data) return;
+ ObjectCache *co = data;
+ g_free(co->bounds);
+ g_free(co);
+}
+
+static void
+_list_obj_unref_and_free(GList *toprocess)
+{
+ for (; toprocess; toprocess = toprocess->next)
+ g_object_unref(toprocess->data);
+}
+
+static void
+_object_cache_free_internal(void)
+{
+ if (idler > 0)
+ {
+ g_source_remove(idler);
+ idler = -1;
+ }
+ if (toprocess)
+ {
+ _list_obj_unref_and_free(toprocess);
+ toprocess = NULL;
+ }
+ if (cache)
+ {
+ g_hash_table_destroy(cache);
+ cache = NULL;
+ }
+}
+
+/**
+ * Returnes a list of all accessible object implementing AtkCompoment interface
+ * GList should be free with g_list_Free function.
+ * Every AtspiAccessible in list should be unrefed with g_object_unref.
+ */
+static GList*
+_cache_candidates_list_prepare(AtspiAccessible *root)
+{
+ GList *toprocess = NULL, *ret = NULL;
+ int n, i;
+
+ if (!root) return NULL;
+
+ // Keep ref counter +1 on every object in returned list
+ g_object_ref(root);
+ toprocess = g_list_append(toprocess, root);
+
+ while (toprocess)
+ {
+ AtspiAccessible *obj = toprocess->data;
+ toprocess = g_list_delete_link(toprocess, toprocess);
+ if (!obj)
+ continue;
+ n = atspi_accessible_get_child_count(obj, NULL);
+ for (i = 0; i < n; i++)
+ {
+ AtspiAccessible *cand = atspi_accessible_get_child_at_index(obj, i, NULL);
+ if(cand)
+ toprocess = g_list_append(toprocess, cand);
+ }
+ ret = g_list_append(ret, obj);
+ }
+
+ return ret;
+}
+
+static ObjectCache*
+_cache_item_construct(AtspiAccessible *obj)
+{
+ ObjectCache *ret;
+ AtspiComponent *comp;
+
+ if (!obj)
+ return NULL;
+
+ ret = g_new0(ObjectCache, 1);
+ if (!ret)
+ {
+ ERROR("out-of memory");
+ return NULL;
+ }
+
+ comp = atspi_accessible_get_component(obj);
+ if (!comp) {
+ ERROR("Cached Object do not implement Component interface");
+ }
+ else {
+ ret->bounds = atspi_component_get_extents(comp, ATSPI_COORD_TYPE_SCREEN, NULL);
+ g_object_unref(comp);
+ }
+
+ return ret;
+}
+
+static GList*
+_cache_item_n_cache(GList *objs, int count)
+{
+ int i = 0;
+ GList *ret = objs;
+ ObjectCache *oc;
+ AtspiAccessible *obj;
+
+ for (i = 0; (i < count) && ret; i++)
+ {
+ obj = ret->data;
+ ret = g_list_delete_link(ret, ret);
+
+ oc = _cache_item_construct(obj);
+ if (!oc) break;
+
+ g_hash_table_insert (cache, obj, oc);
+
+ g_object_unref(obj);
+ }
+
+ return ret;
+}
+
+static GHashTable*
+_object_cache_new(void)
+{
+ return g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, _cache_item_free_cb);
+}
+
+void
+object_cache_build(AtspiAccessible *root)
+{
+ GList *objs;
+
+ _object_cache_free_internal();
+ cache = _object_cache_new();
+ if (!cache)
+ {
+ ERROR("ObjectCache: hash table creation failed");
+ return;
+ }
+
+ objs = _cache_candidates_list_prepare(root);
+ _cache_item_n_cache(objs, g_list_length(objs));
+
+ return;
+}
+
+static gboolean
+_do_cache(void *data)
+{
+ toprocess = _cache_item_n_cache(toprocess, bulk);
+ idler = -1;
+
+ if (toprocess)
+ idler = g_idle_add(_do_cache, NULL);
+ else if (callback) callback(user_data);
+
+ return FALSE;
+}
+
+void
+object_cache_build_async(AtspiAccessible *root, int bulk_size, ObjectCacheReadyCb cb, void *ud)
+{
+ _object_cache_free_internal();
+
+ callback = cb;
+ user_data = ud;
+ bulk = bulk_size;
+
+ cache = _object_cache_new();
+ if (!cache)
+ {
+ ERROR("ObjectCache: hash table creation failed");
+ return;
+ }
+
+ toprocess = _cache_candidates_list_prepare(root);
+ idler = g_idle_add(_do_cache, NULL);
+
+ return;
+}
+
+void
+object_cache_shutdown(void)
+{
+ _object_cache_free_internal();
+}
+
+const ObjectCache*
+object_cache_get(AtspiAccessible *obj)
+{
+ ObjectCache *ret = NULL;
+ if (idler > 0)
+ {
+ ERROR("Invalid usage. Async cache build is ongoing...");
+ return NULL;
+ }
+
+ if (!cache)
+ {
+ cache = _object_cache_new();
+ if (!cache)
+ {
+ ERROR("ObjectCache: hash table creation failed");
+ return NULL;
+ }
+ }
+
+ ret = g_hash_table_lookup(cache, obj);
+ if (!ret)
+ {
+ // fallback to blocking d-bus call
+ ret = _cache_item_construct(obj);
+ g_hash_table_insert(cache, obj, ret);
+ }
+
+ return ret;
+}
--- /dev/null
+#include <atspi/atspi.h>
+#include "logger.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)
+{
+ GList *candidates = NULL, *queue = NULL;
+
+ // ref object to keep same ref count
+ g_object_ref(parent);
+ queue = g_list_append(queue, parent);
+
+ while (queue)
+ {
+ AtspiAccessible *obj = queue->data;
+ queue = g_list_delete_link(queue, queue);
+
+ int n = atspi_accessible_get_child_count(obj, NULL);
+ if (n == 0)
+ candidates = g_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 = g_list_append(queue, child);
+ }
+ g_object_unref(obj);
+ }
+ }
+
+ // FIXME sort by (x,y) first ??
+ while (candidates)
+ {
+ AtspiAccessible *obj = candidates->data;
+ candidates = g_list_delete_link(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);
+ g_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;
+}
--- /dev/null
+#include "logger.h"
+#include "object_cache.h"
+#include "position_sort.h"
+
+
+static int
+_sort_vertically(const void *a, const void *b)
+{
+ AtspiAccessible *objA, *objB;
+ const ObjectCache *cA, *cB;
+
+ objA = (AtspiAccessible*)a;
+ objB = (AtspiAccessible*)b;
+
+ cA = object_cache_get(objA);
+ cB = object_cache_get(objB);
+
+ if (cA->bounds->y == cB->bounds->y)
+ return 0;
+ else if (cA->bounds->y > cB->bounds->y)
+ return 1;
+ else
+ return -1;
+}
+
+static int
+_sort_horizontally(const void *a, const void *b)
+{
+ AtspiAccessible *objA, *objB;
+ const ObjectCache *cA, *cB;
+
+ objA = (AtspiAccessible*)a;
+ objB = (AtspiAccessible*)b;
+
+ cA = object_cache_get(objA);
+ cB = object_cache_get(objB);
+
+ if (cA->bounds->x == cB->bounds->x)
+ {
+ if (cA->bounds->y > cB->bounds->y)
+ return 1;
+ if (cA->bounds->y < cB->bounds->y)
+ return -1;
+ return 0;
+ }
+ else if (cA->bounds->x > cB->bounds->x)
+ return 1;
+ else
+ return -1;
+}
+
+static GList*
+_get_zones(const GList *objs)
+{
+ GList *candidates = NULL;
+ const GList *l;
+ AtspiAccessible *obj;
+ AtspiComponent *comp;
+ const ObjectCache *oc;
+
+ for (l = objs; l; l = l->next)
+ {
+ obj = l->data;
+ if ((comp = atspi_accessible_get_component(obj)) != NULL)
+ {
+ oc = object_cache_get(obj);
+
+ // some objects may implement AtspiCompoment interface, however
+ // they do not have valid sizes.
+ if (!oc->bounds || (oc->bounds->width < 0) || oc->bounds->height < 0)
+ {
+ DEBUG("Invalid bounds. skipping from zone list: %s %s",
+ atspi_accessible_get_name(obj, NULL),
+ atspi_accessible_get_role_name(obj, NULL));
+ continue;
+ }
+ candidates = g_list_append(candidates, obj);
+ }
+ else
+ DEBUG("No component interface: skipping %s %s",
+ atspi_accessible_get_name(obj, NULL),
+ atspi_accessible_get_role_name(obj, NULL));
+ }
+
+ // Sort object by y - coordinate
+ return g_list_sort(candidates, _sort_vertically);
+}
+
+static GList*
+_get_lines(const GList *objs)
+{
+ GList *line = NULL, *lines = NULL;
+ const GList *l;
+ AtspiAccessible *obj;
+ const ObjectCache *line_beg;
+
+ for (l = objs; l; l = l->next)
+ {
+ obj = l->data;
+ if (!line) {
+ // set first object in line
+ line = g_list_append(line, obj);
+ line_beg = object_cache_get(obj);
+ continue;
+ }
+
+ const ObjectCache *oc = object_cache_get(obj);
+ // Object are considered as present in same line, if
+ // its y coordinate begins maximum 25% below
+ // y coordinate% of first object in line.
+ if ((line_beg->bounds->y + (int)(0.25 * (double)line_beg->bounds->height)) >
+ oc->bounds->y)
+ {
+ line = g_list_append(line, obj);
+ continue;
+ }
+ else
+ {
+ //finish line & set new line leader
+ lines = g_list_append(lines, line);
+ line = NULL;
+ line = g_list_append(line, obj);
+ line_beg = object_cache_get(obj);
+ }
+ }
+
+ // finish last line
+ if (line) lines = g_list_append(lines, line);
+
+ return lines;
+}
+
+GList *position_sort(const GList *objs)
+{
+ GList *l, *line, *zones, *lines = NULL;
+ int i = 0;
+
+ // Get list of objects occupying place on the screen
+ DEBUG("PositionSort: Candidates; %d", g_list_length((GList*)objs));
+ zones = _get_zones(objs);
+
+ // Cluster all zones into lines - verticaly
+ DEBUG("PositionSort: Zones; %d", g_list_length(zones));
+ lines = _get_lines(zones);
+
+ // sort all zones in line - horizontaly
+ DEBUG("PositionSort: Lines; %d", g_list_length(lines));
+ for (l = lines; l; l = l->next)
+ {
+ line = l->data;
+ DEBUG("PositionSort: Line %d: %d items", i++, g_list_length(line));
+ line = g_list_sort(line, _sort_horizontally);
+ l->data = line;
+ }
+
+ if (zones) g_list_free(zones);
+
+ return lines;
+}
--- /dev/null
+#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
+ .information_level = 1,
+ .run_service = 1,
+ .language = "en_US",
+ .voice_type = TTS_VOICE_TYPE_FEMALE,
+ .reading_speed = 2,
+ .tracking_signal_name = FOCUS_CHANGED_SIG,
+
+
+ //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)
+{
+ DEBUG("Service Create Callback \n");
+
+ Service_Data *service_data = data;
+
+ tts_init(service_data);
+ vconf_init(service_data);
+ spi_init(service_data);
+
+
+ /* 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;
+
+ int vconf_ret = vconf_set_bool("db/setting/accessibility/screen_reader", FALSE);
+ if(vconf_ret == 0)
+ {
+ DEBUG("TTS key set to false");
+ }
+ else
+ {
+ DEBUG("COULD NOT SET tts key to 0");
+ }
+
+
+ vconf_ret = vconf_set_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, FALSE);
+ if(vconf_ret == 0)
+ {
+ DEBUG("TTS key set to false");
+ }
+ else
+ {
+ DEBUG("COULD NOT SET tts key to 0");
+ }
+
+ 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;
+}
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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
+#include <stdlib.h>
+
+#define MEMORY_ERROR "Memory allocation failed"
+#define NO_VALUE_INTERFACE "No value interface present"
+#define NO_TEXT_INTERFACE "No text interface present"
+
+#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_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("--------------------------------------------------------");
+}
+
+gboolean double_click_timer_cb(void *data)
+{
+ Service_Data *sd = data;
+ sd->clicked_widget = NULL;
+
+ return 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)
+{
+ if (!allow_recursive_name(obj))
+ return strdup("");
+
+ if (!obj)
+ return strdup("");
+ int child_count = atspi_accessible_get_child_count(obj, NULL);
+
+ 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);
+ if (strncmp(name, "\0", 1)) {
+ strncat(ret, name, sizeof(ret) - strlen(ret) - 1);
+ }
+ strncat(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_get_atspi_accessible_basic_text_description(AtspiAccessible *obj)
+{
+ if(!obj) return NULL;
+
+ char *name;
+ char *description;
+ char *role_name;
+ char *other;
+ char *return_text = NULL;
+
+ description = atspi_accessible_get_description(obj, NULL);
+ name = atspi_accessible_get_name(obj, NULL);
+ role_name = atspi_accessible_get_role_name(obj, NULL);
+ other = generate_description_for_subtree(obj);
+
+ if (strncmp(description, "\0", 1))
+ return_text = strdup(description);
+ else if (strncmp(name, "\0", 1))
+ return_text = strdup(name);
+ else if (strncmp(other, "\0", 1))
+ return_text = strdup(other);
+ else
+ return_text = strdup(role_name);
+
+ free(name);
+ free(description);
+ free(role_name);
+ free(other);
+
+ return return_text;
+}
+
+static char *spi_on_state_changed_get_text(AtspiEvent *event, void *user_data)
+{
+ Service_Data *sd = (Service_Data*)user_data;
+ char *return_text = NULL;
+ sd->currently_focused = event->source;
+
+ DEBUG("->->->->->-> WIDGET GAINED HIGHLIGHT <-<-<-<-<-<-<-");
+ return_text = spi_get_atspi_accessible_basic_text_description(sd->currently_focused);
+
+ return return_text;
+}
+
+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(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), MIN_POS_REACHED) < 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), MAX_POS_REACHED) < 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;
+
+ sd->currently_focused = event->source;
+
+ AtspiValue *value_interface = atspi_accessible_get_value(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, MAX_REACHED) < 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, MIN_REACHED) < 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;
+}
+
+static void spi_object_append_relations_to_text_to_read(AtspiAccessible *obj,
+ char **text_to_read)
+{
+ if(!obj || !text_to_read || !*text_to_read)
+ {
+ ERROR("Invalid parameter");
+ return;
+ }
+
+ GError *error = NULL;
+
+ GArray *relations = atspi_accessible_get_relation_set(obj, &error);
+ if(error != NULL)
+ {
+ ERROR("Can not get atspi object relations: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ if(!relations)
+ {
+ ERROR("Can not get accessible object relations");
+ return;
+ }
+
+ int i = 0;
+ for(; i < relations->len; ++i)
+ {
+ AtspiRelation *relation = g_array_index(relations, AtspiRelation*, i);
+ if(!relation) continue;
+
+ AtspiRelationType relation_type = atspi_relation_get_relation_type(relation);
+ if(relation_type == ATSPI_RELATION_LABEL_FOR ||
+ relation_type == ATSPI_RELATION_LABELLED_BY)
+ {
+ int relation_objects = atspi_relation_get_n_targets(relation);
+ int j;
+ for(j = 0; j < relation_objects; ++j)
+ {
+ AtspiAccessible *relation_object = atspi_relation_get_target(relation, j);
+ if(!relation_object || obj == relation_object) continue;
+
+ char *relation_obj_text = spi_get_atspi_accessible_basic_text_description(relation_object);
+
+ char *text = NULL;
+ if(asprintf(&text, "%s %s", *text_to_read, relation_obj_text) == -1)
+ continue;
+ free(relation_obj_text);
+ free(*text_to_read);
+ *text_to_read = text;
+ g_object_unref(relation_object);
+ }
+ }
+ }
+
+ g_array_free(relations, TRUE);
+}
+
+char *spi_event_get_text_to_read(AtspiEvent *event, void *user_data)
+{
+ Service_Data *sd = (Service_Data*)user_data;
+ char *text_to_read;
+
+ 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
+ {
+ DEBUG("Unhandled event type: %s %d:%d", event->type, event->detail1, event->detail2);
+ return NULL;
+ }
+
+ spi_object_append_relations_to_text_to_read(sd->currently_focused, &text_to_read);
+ DEBUG("%p %s", sd->currently_focused, text_to_read);
+
+ return text_to_read;
+}
+
+void spi_event_listener_cb(AtspiEvent *event, void *user_data)
+{
+ 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;
+ }
+ tts_speak(text_to_read, FALSE);
+ free(text_to_read);
+}
+
+/**
+ * @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");
+ }
+
+ // ---------------------------------------------------------------------------------------------------
+
+ 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");
+ }
+
+ gboolean ret2 = atspi_event_listener_register(sd->spi_listener, CARET_MOVED_SIG, NULL);
+ if(ret2 == false)
+ {
+ DEBUG("FAILED TO REGISTER spi caret moved listener");
+ }
+
+ gboolean ret3 = atspi_event_listener_register(sd->spi_listener, VALUE_CHANGED_SIG, NULL);
+ if(ret3 == false)
+ {
+ DEBUG("FAILED TO REGISTER spi value changed listener");
+ }
+
+
+ if(ret1 == true && ret2 == true && ret3 == true)
+ {
+ DEBUG("spi listener REGISTERED");
+ }
+
+ DEBUG( "---------------------- SPI_init END ----------------------\n\n");
+}
--- /dev/null
+#include <device/battery.h>
+#include <device/display.h>
+#include <device/callback.h>
+#include "screen_reader.h"
+#include "screen_reader_tts.h"
+#include "smart_notification.h"
+#include "logger.h"
+
+#define CHARGING "Battery charger connected"
+#define NOT_CHARGING "Battery charger disconnected"
+#define SCREEN_ON "Screen is on"
+#define SCREEN_OFF "Screen is off"
+#define BATTERY_LOW "Battery level is low"
+#define BATTERY_FULL "Battery level is full"
+#define BATTERY_CRITICAL "Battery level is critical"
+
+static void device_system_cb(device_callback_e type, void *value, void *user_data);
+
+/**
+ * @brief Initializer for smart notifications
+ *
+ */
+void system_notifications_init(void)
+{
+ // 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);
+}
+
+/**
+ * @brief Initializer for smart notifications
+ *
+ */
+void system_notifications_shutdown(void)
+{
+ // 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);
+}
+
+/**
+ * @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(BATTERY_LOW, TRUE);
+ }
+ else if(status == DEVICE_BATTERY_LEVEL_CRITICAL)
+ {
+ tts_speak(BATTERY_CRITICAL, TRUE);
+ }
+ else if(status == DEVICE_BATTERY_LEVEL_FULL)
+ {
+ tts_speak(BATTERY_FULL, 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(CHARGING, FALSE);
+ }
+ else
+ {
+ tts_speak(NOT_CHARGING, 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(SCREEN_ON, FALSE);
+ }
+ else if(state == DISPLAY_STATE_SCREEN_OFF)
+ {
+ tts_speak(SCREEN_OFF, FALSE);
+ }
+ }
+}
--- /dev/null
+#define _GNU_SOURCE
+
+#include "screen_reader_tts.h"
+#include "logger.h"
+#include <stdlib.h>
+#include <stdio.h>
+#define MEMORY_ERROR "Memory allocation failed"
+
+// ---------------------------- DEBUG HELPERS ------------------------------
+
+#define FLUSH_LIMIT 1
+
+static int last_utt_id;
+static gboolean pause_state = FALSE;
+static gboolean flush_flag = FALSE;
+
+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";
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------
+
+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(asprintf(&vi->language, "%s",language) < 0)
+ {
+ free(vi);
+ ERROR(MEMORY_ERROR);
+ return FALSE;
+ }
+
+ vi->voice_type = voice_type;
+
+ sd->available_languages = g_list_append(sd->available_languages, vi);
+
+ return TRUE;
+}
+
+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 = TRUE;
+ else
+ {
+ if(flush_flag)
+ flush_flag = FALSE;
+ }
+ 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_DEFAULT );
+ 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");
+ return true;
+}
+
+gboolean tts_pause_set(gboolean pause_switch)
+{
+ Service_Data *sd = get_pointer_to_service_data_struct();
+ if(!sd)
+ return FALSE;
+
+ if(pause_switch)
+ {
+ if(!tts_pause(sd->tts))
+ pause_state = TRUE;
+ else
+ return FALSE;
+ }
+ else if(!pause_switch)
+ {
+ if(!tts_play(sd->tts))
+ pause_state = FALSE;
+ else
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean tts_speak(char *text_to_speak, gboolean flush_switch)
+{
+ Service_Data *sd = get_pointer_to_service_data_struct();
+ int speak_id;
+
+ if(!sd)
+ return FALSE;
+
+ if(flush_flag || flush_switch)
+ tts_stop(sd->tts);
+
+ DEBUG( "tts_speak\n");
+ DEBUG( "text to say:%s\n", text_to_speak);
+ if ( !text_to_speak ) return FALSE;
+ if ( !text_to_speak[0] ) return FALSE;
+
+ if(tts_add_text( sd->tts, text_to_speak, sd->language, TTS_VOICE_TYPE_AUTO, TTS_SPEED_AUTO, &speak_id))
+ return FALSE;
+
+ DEBUG("added id to:%d\n", speak_id);
+ last_utt_id = speak_id;
+
+ return TRUE;
+}
+
+gboolean 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 FALSE;
+ }
+
+ if(state == TTS_STATE_READY)
+ {
+ tts_foreach_supported_voices(sd->tts, get_supported_voices_cb, sd);
+ }
+ else
+ {
+ sd->update_language_list = TRUE;
+ }
+
+ DEBUG("END");
+ return 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(current == TTS_STATE_READY || current == TTS_STATE_PAUSED)
+ {
+ DEBUG("TTS state == %s!", get_tts_state(current));
+ tts_play(sd->tts);
+ }
+ else
+ {
+ DEBUG("TTS state != ready or paused!\n");
+ }
+}
+
+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);
+}
--- /dev/null
+#include <vconf.h>
+#include "screen_reader_vconf.h"
+#include "screen_reader_spi.h"
+#include "logger.h"
+#include <stdio.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;
+
+bool set_langauge(Service_Data *sd, const char *new_language, int new_voice)
+{
+ DEBUG("START");
+
+ GList *l;
+ Voice_Info *vi;
+
+ if(strncmp(sd->language, new_language, LAN_NAME - 1) == 0 && sd->voice_type == new_voice)
+ {
+ DEBUG("No need to change accessibility language: %s(%d) -> %s(%d)",
+ sd->language, sd->voice_type, new_language, new_voice);
+
+ return true;
+ }
+
+ for (l = sd->available_languages; l; l = l->next)
+ {
+ vi = l->data;
+ DEBUG("foreach %s <- %s", vi->language, new_language);
+ if(strncmp(vi->language, new_language, LAN_NAME - 1) == 0 &&
+ vi->voice_type == new_voice)
+ {
+ DEBUG("str_cpy %s (%d) -> %s (%d)", sd->language, sd->voice_type, vi->language, vi->voice_type);
+ snprintf(sd->language, LAN_NAME, "%s", vi->language);
+ sd->voice_type = vi->voice_type;
+ DEBUG("after_str_cpy");
+
+ DEBUG("ACCESSIBILITY LANGUAGE CHANGED");
+ DEBUG("END");
+ return true;
+ }
+ }
+
+ DEBUG("ACCESSIBILITY LANGUAGE FAILED TO CHANGED");
+
+ vconf_set_str("db/setting/accessibility/language", sd->language);
+ vconf_set_int("db/setting/accessibility/voice", sd->voice_type);
+
+ DEBUG("END");
+ return false;
+}
+
+char *fold_tracker_signal(const char *signal_name)
+{
+ if(strcmp(signal_name, FOCUS_SIG) == 0)
+ {
+ return FOCUS_CHANGED_SIG;
+ }
+ return NULL;
+}
+
+bool set_tracking_listener(Service_Data *sd, const char *signal_name)
+{
+ DEBUG("START: %s", sd->tracking_signal_name);
+
+ char *new_tracking_signal;
+
+ new_tracking_signal = fold_tracker_signal(signal_name);
+
+ if(strcmp(sd->tracking_signal_name, new_tracking_signal) == 0)
+ {
+ DEBUG("No need to change accessibility tracking signal: %s == %s",
+ sd->tracking_signal_name, new_tracking_signal);
+
+ return true;
+ }
+
+ gboolean ret = atspi_event_listener_deregister(sd->spi_listener, sd->tracking_signal_name, NULL);
+ if(ret == false)
+ {
+ DEBUG("Tracking signal listerner deregister successful");
+ }
+
+ sd->tracking_signal_name = strdup(new_tracking_signal);
+
+ gboolean ret1 = atspi_event_listener_register(sd->spi_listener, sd->tracking_signal_name, NULL);
+ if(ret1 == false)
+ {
+ DEBUG("FAILED TO REGISTER spi focus listener");
+ return false;
+ }
+ else
+ {
+ DEBUG("Tracking listener register for new signal");
+ }
+ return true;
+}
+
+// ------------------------------ vconf callbacks----------------------
+
+void information_level_cb(keynode_t *node, void *user_data)
+{
+ DEBUG("START");
+ DEBUG("Information level set: %d", vconf_keynode_get_int(node));
+
+ Service_Data *service_data = user_data;
+ service_data->information_level = vconf_keynode_get_int(node);
+
+ DEBUG("END");
+}
+
+void app_termination_cb(keynode_t *node, void *user_data)
+{
+ DEBUG("START");
+ DEBUG("Application terminate %d", !vconf_keynode_get_int(node));
+
+ Service_Data *service_data = user_data;
+ service_data->run_service = vconf_keynode_get_int(node);
+
+ if(service_data->run_service == 0)
+ {
+ atspi_exit();
+ }
+
+ DEBUG("END");
+}
+
+void language_cb(keynode_t *node, void *user_data)
+{
+ DEBUG("START");
+ DEBUG("Trying to set language to: %s", vconf_keynode_get_str(node));
+
+ Service_Data *sd = user_data;
+
+ int voice_type;
+
+ vconf_get_int("db/setting/accessibility/voice", (int*)(&voice_type));
+ set_langauge(sd, vconf_keynode_get_str(node), voice_type);
+
+ DEBUG("END");
+}
+
+void voice_cb(keynode_t *node, void *user_data)
+{
+ DEBUG("START");
+ DEBUG("Voice set to: %d", vconf_keynode_get_int(node));
+
+ Service_Data *sd = user_data;
+
+ const char *lang = vconf_get_str("db/setting/accessibility/language");
+ set_langauge(sd, lang, vconf_keynode_get_int(node));
+
+ DEBUG("END");
+}
+
+void reading_speed_cb(keynode_t *node, void *user_data)
+{
+ DEBUG("START");
+ DEBUG("Reading speed set to: %d", vconf_keynode_get_int(node));
+
+ Service_Data *service_data = user_data;
+ service_data->reading_speed = vconf_keynode_get_int(node);
+
+ DEBUG("END");
+}
+
+void tracking_signal_changed_cb(keynode_t *node, void *user_data)
+{
+ DEBUG("START");
+ DEBUG("Tracking signal set to: %s", vconf_keynode_get_str(node));
+
+ Service_Data *sd = user_data;
+ const char *tracking_signal = vconf_get_str("db/setting/accessibility/tracking_signal");
+ set_tracking_listener(sd, tracking_signal);
+}
+
+// --------------------------------------------------------------------
+
+int get_key_values(Service_Data *sd)
+{
+ DEBUG("START");
+
+ char *language = vconf_get_str("db/setting/accessibility/language");
+
+ if(sd->language == NULL)
+ {
+ DEBUG("FAILED TO SET LANGUAGE");
+ }
+
+ int ret = -1;
+ int to_ret = 0;
+
+
+ int voice;
+ ret = vconf_get_int("db/setting/accessibility/voice", &voice);
+ if(ret != 0)
+ {
+ to_ret -= -1;
+ DEBUG("FAILED TO SET VOICE TYPE: %d", ret);
+ }
+
+ set_langauge(sd, language, voice);
+
+ ret = vconf_get_int("db/setting/accessibility/speech_rate", &sd->reading_speed);
+ if(ret != 0)
+ {
+ to_ret -= -2;
+ DEBUG("FAILED TO SET READING SPEED: %d", ret);
+ }
+
+ ret = vconf_get_int("db/setting/accessibility/information_level", &sd->information_level);
+ if(ret != 0)
+ {
+ to_ret -= -4;
+ DEBUG("FAILED TO SET INFORMATION LEVEL: %d", ret);
+ }
+
+ DEBUG("SCREEN READER DATA SET TO: Language: %s; Voice: %d, Reading_Speed: %d, Information_Level: %d, Tracking signal: %s;",
+ sd->language, sd->voice_type, sd->reading_speed, sd->information_level, sd->tracking_signal_name);
+
+ DEBUG("END");
+ return to_ret;
+}
+
+bool vconf_init(Service_Data *service_data)
+{
+ DEBUG( "--------------------- VCONF_init START ---------------------");
+ int ret = 0;
+
+ //TODO: Remove when adequate keys in the settings(control?) panel are added
+ // ----------------------- TEST ONLY -----------------------------------------------
+ DEBUG("TESTS START");
+ keys = vconf_keylist_new();
+ vconf_keylist_add_int(keys, "db/setting/accessibility/information_level", 2);
+ vconf_keylist_add_str(keys, "db/setting/accessibility/language", "en_US");
+ vconf_keylist_add_int(keys, "db/setting/accessibility/voice", 1);
+ vconf_keylist_add_str(keys, "db/setting/accessibility/tracking_signal", FOCUS_SIG);
+ //-----------------------------------------------------------------------------------
+ //vconf_set_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, EINA_TRUE);
+
+ 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);
+ }
+
+ ret = vconf_notify_key_changed("db/setting/accessibility/information_level", information_level_cb, service_data);
+ if(ret != 0)
+ {
+ DEBUG("Could not add information level callback");
+ return false;
+ }
+
+ ret = vconf_notify_key_changed("db/setting/accessibility/screen_reader", app_termination_cb, service_data);
+ if(ret != 0)
+ {
+ DEBUG("Could not add app termination callback");
+ return false;
+ }
+
+ ret = vconf_notify_key_changed("db/menu_widget/language", language_cb, service_data);
+ if(ret != 0)
+ {
+ DEBUG("Could not add language callback");
+ return false;
+ }
+
+ ret = vconf_notify_key_changed("db/setting/accessibility/language", language_cb, service_data);
+ if(ret != 0)
+ {
+ DEBUG("Could not add language callback");
+ return false;
+ }
+
+ ret = vconf_notify_key_changed("db/setting/accessibility/voice", voice_cb, service_data);
+ if(ret != 0)
+ {
+ DEBUG("Could not add voice callback");
+ return false;
+ }
+
+ ret = vconf_notify_key_changed("db/setting/accessibility/speech_rate", reading_speed_cb, service_data);
+ if(ret != 0)
+ {
+ DEBUG("Could not add reading speed callback callback");
+ return false;
+ }
+
+ ret = vconf_notify_key_changed("db/setting/accessibility/tracking_signal", tracking_signal_changed_cb, service_data);
+ if(ret != 0)
+ {
+ DEBUG("Could not add reading speed callback callback");
+ return false;
+ }
+
+ DEBUG("ALL CALBACKS ADDED");
+
+ DEBUG( "---------------------- VCONF_init END ----------------------\n\n");
+ return true;
+}
--- /dev/null
+#include <atspi/atspi.h>
+#include <stdio.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"
+
+#define ITEMS_NOTIFICATION "Visible items from %d to %d"
+
+static gboolean status = 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)
+{
+ 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)
+{
+ 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);
+
+ 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, void *user_data)
+{
+ if(!status)
+ return;
+
+ int start_index, end_index;
+ start_index = 0;
+ end_index = 0;
+
+ DEBUG("Event: %s: %d, obj: %p: role: %s\n", event->type, event->detail1, event->source,
+ atspi_accessible_get_role_name(event->source, NULL));
+
+ if (!strcmp(event->type, "object:scroll-start"))
+ {
+ DEBUG("Scrolling started");
+ tts_speak("Scrolling started", TRUE);
+ }
+ else if (!strcmp(event->type, "object:scroll-end"))
+ {
+ DEBUG("Scrolling finished");
+ tts_speak("Scrolling finished", 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 = TRUE;
+}
+
+/**
+ * @brief Smart notifications shutdown
+ *
+ */
+void smart_notification_shutdown(void)
+{
+ status = 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);
+}
+
+/**
+ * @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), ITEMS_NOTIFICATION, start_idx, end_idx);
+
+ tts_speak(strdup(buf), FALSE);
+}
--- /dev/null
+#include "structural_navi.h"
+#include "position_sort.h"
+#include "logger.h"
+
+static GList*
+_atspi_children_list_get(AtspiAccessible *parent)
+{
+ GList *ret = NULL;
+ int count = atspi_accessible_get_child_count(parent, NULL);
+ int i;
+
+ for (i = 0; i < count; i++)
+ {
+ AtspiAccessible *child = atspi_accessible_get_child_at_index(parent, i, NULL);
+ if (child) ret = g_list_append(ret, child);
+ }
+
+ return ret;
+}
+
+static void
+_atspi_children_list_free(GList *children)
+{
+ for (; children; children = children->next)
+ g_object_unref(children->data);
+}
+
+static GList*
+_flat_review_get(GList *tosort)
+{
+ GList *ret = NULL, *lines, *l, *line;
+
+ lines = position_sort(tosort);
+
+ for (l = lines; l; l = l->next)
+ {
+ line = l->data;
+ ret = g_list_concat(ret, line);
+ }
+
+ g_list_free(lines);
+
+ return ret;
+}
+
+AtspiAccessible *structural_navi_same_level_next(AtspiAccessible *current)
+{
+ AtspiAccessible *parent, *ret = NULL;
+ AtspiRole role;
+
+ parent = atspi_accessible_get_parent(current, NULL);
+ if (!parent) return NULL;
+
+ role = atspi_accessible_get_role(parent, NULL);
+ if (role != ATSPI_ROLE_DESKTOP_FRAME)
+ {
+ GList *children = _atspi_children_list_get(parent);
+ GList *sorted = _flat_review_get(children);
+ GList *me = g_list_find(sorted, current);
+ if (me)
+ ret = me->next ? me->next->data : NULL;
+
+ if (sorted) g_list_free(sorted);
+ _atspi_children_list_free(children);
+
+ return ret;
+ }
+ return NULL;
+}
+
+AtspiAccessible *structural_navi_same_level_prev(AtspiAccessible *current)
+{
+ AtspiAccessible *parent, *ret = NULL;
+ AtspiRole role;
+
+ parent = atspi_accessible_get_parent(current, NULL);
+ if (!parent) return NULL;
+
+ role = atspi_accessible_get_role(parent, NULL);
+ if (role != ATSPI_ROLE_DESKTOP_FRAME)
+ {
+ GList *children = _atspi_children_list_get(parent);
+ GList *sorted = _flat_review_get(children);
+ GList *me = g_list_find(sorted, current);
+ if (me)
+ ret = me->prev ? me->prev->data : NULL;
+
+ if (sorted) g_list_free(sorted);
+ _atspi_children_list_free(children);
+
+ return ret;
+ }
+ return NULL;
+}
+
+AtspiAccessible *structural_navi_level_up(AtspiAccessible *current)
+{
+ AtspiAccessible *parent;
+ AtspiRole role;
+
+ parent = atspi_accessible_get_parent(current, NULL);
+ if (!parent) return NULL;
+
+ role = atspi_accessible_get_role(parent, NULL);
+ if (role != ATSPI_ROLE_DESKTOP_FRAME)
+ {
+ return parent;
+ }
+ return NULL;
+}
+
+AtspiAccessible *structural_navi_level_down(AtspiAccessible *current)
+{
+ AtspiAccessible *ret;
+
+ GList *children = _atspi_children_list_get(current);
+ GList *sorted = _flat_review_get(children);
+
+ ret = sorted ? sorted->data : NULL;
+
+ if (sorted) g_list_free(sorted);
+ _atspi_children_list_free(children);
+
+ return ret;
+}
+
+static AtspiAccessible*
+_navi_app_chain_next(AtspiAccessible *current, AtspiRelationType search_type)
+{
+ GArray *relations;
+ AtspiAccessible *ret = NULL;
+ AtspiRelation *relation;
+ AtspiRelationType type;
+ int i;
+
+ relations = atspi_accessible_get_relation_set(current, NULL);
+
+ for (i = 0; i < relations->len; i++)
+ {
+ relation = g_array_index (relations, AtspiRelation*, i);
+ type = atspi_relation_get_relation_type(relation);
+
+ if (type == search_type)
+ {
+ ret = atspi_relation_get_target(relation, 0);
+ break;
+ }
+ }
+
+ g_array_free(relations, TRUE);
+ return ret;
+}
+
+AtspiAccessible *structural_navi_app_chain_next(AtspiAccessible *current)
+{
+ return _navi_app_chain_next(current, ATSPI_RELATION_FLOWS_TO);
+}
+
+AtspiAccessible *structural_navi_app_chain_prev(AtspiAccessible *current)
+{
+ return _navi_app_chain_next(current, ATSPI_RELATION_FLOWS_FROM);
+}
--- /dev/null
+#include <string.h>
+#include "window_tracker.h"
+#include "logger.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)
+{
+ if (!strcmp(event->type, "window:activate") ||
+ !strcmp(event->type, "window:restore"))
+ {
+ if (last_active_win != event->source)
+ {
+ if (user_cb) user_cb(user_data, event->source);
+ last_active_win = event->source;
+ }
+ }
+ else if (!strcmp(event->type, "window:deactivate") ||
+ !strcmp(event->type, "window:destroy"))
+ {
+ if ((last_active_win == event->source) &&
+ user_cb)
+ user_cb(user_data, NULL);
+ last_active_win = NULL;
+ }
+}
+
+static AtspiAccessible*
+_get_active_win(void)
+{
+ int i, j;
+ last_active_win = NULL;
+ AtspiAccessible *desktop = atspi_get_desktop(0);
+
+ for (i = 0; i < atspi_accessible_get_child_count(desktop, NULL); i++) {
+ AtspiAccessible *app = atspi_accessible_get_child_at_index(desktop, i, NULL);
+ for (j = 0; j < atspi_accessible_get_child_count(app, NULL); 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))
+ {
+ g_object_unref(states);
+ last_active_win = win;
+ break;
+ }
+ g_object_unref(states);
+ }
+ }
+ return last_active_win;
+}
+
+void window_tracker_init(void)
+{
+ listener = atspi_event_listener_new_simple(_on_atspi_window_cb, NULL);
+ atspi_event_listener_register(listener, "window:activate", NULL);
+ atspi_event_listener_register(listener, "window:deactivate", NULL);
+ atspi_event_listener_register(listener, "window:restore", NULL);
+ atspi_event_listener_register(listener, "window:destroy", NULL);
+}
+
+void window_tracker_shutdown(void)
+{
+ atspi_event_listener_deregister(listener, "window:activate", NULL);
+ atspi_event_listener_deregister(listener, "window:deactivate", NULL);
+ atspi_event_listener_deregister(listener, "window:restore", NULL);
+ atspi_event_listener_deregister(listener, "window:destroy", 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)
+{
+ user_cb = cb;
+ user_data = data;
+}
+
+void window_tracker_active_window_request(void)
+{
+ _get_active_win();
+ if (user_cb) user_cb(user_data, last_active_win);
+}
--- /dev/null
+## PROJECT NAME
+PROJECT(smart-navigator-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 "")
--- /dev/null
+
+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
+ tts
+ check
+ vconf
+)
+
+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
+ ${CMAKE_SOURCE_DIR}/src/position_sort.c
+ ${CMAKE_SOURCE_DIR}/src/object_cache.c)
+
+ADD_EXECUTABLE(screen_reader_test_suite screen_reader_test_suite.c ${TESTED_SRCS})
+TARGET_LINK_LIBRARIES(screen_reader_test_suite atspi ${tests_LDFLAGS} ${SLP_LD_PATH_FLAGS} ${SLP_LD_FLAGS} ${SLP_LINKER_FLAGS})
--- /dev/null
+
+#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 AtspiComponent *component = NULL;
+
+G_DEFINE_TYPE(AtspiAccessible, atspi_accessible, 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");
+ 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);
+}
+
+AtspiText * atspi_accessible_get_text (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 (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;
+}
+
+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 (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 = 0;
+ rect.y = 0;
+ 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
+ {
+ 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;
+
+ GArray *states = g_array_new (TRUE, TRUE, sizeof (AtspiStateType));
+ AtspiStateType s[] = {
+ ATSPI_STATE_VISIBLE,
+ ATSPI_STATE_SHOWING,
+ ATSPI_STATE_LAST_DEFINED
+ };
+ int i;
+ for (i=0; i<sizeof(s); 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)
+{
+ obj->children = g_list_append(obj->children, child);
+}
+
+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_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 (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;
+}
+
+int atspi_exit(void)
+{
+ return 1;
+}
--- /dev/null
+
+
+#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_ACESSIBLE_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_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 _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 _AtspiAccessibleClass AtspiAccessibleClass;
+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,
+} 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;
+};
+
+struct _AtspiAccessibleClass
+{
+ 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 _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_toolkit_name (AtspiAccessible *obj, GError **error);
+gchar * atspi_accessible_get_description (AtspiAccessible *obj, GError **error);
+AtspiText * atspi_accessible_get_text (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 (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 (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 (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);
+int atspi_exit(void);
+
+#endif /*__ATSPI_H__*/
--- /dev/null
+
+#include "screen_reader_spi.h"
+#include "flat_navi.h"
+#include <check.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <atspi/atspi.h>
+
+static AtspiAccessible *root;
+static AtspiAccessible *child1;
+static AtspiAccessible *child2;
+static AtspiAccessible *child3;
+static AtspiAccessible *child4;
+static FlatNaviContext *ctx;
+
+void setup(void)
+{
+ Service_Data *data = get_pointer_to_service_data_struct();
+ data->information_level = 1;
+ data->run_service = 1;
+ data->voice_type = TTS_VOICE_TYPE_FEMALE;
+ data->reading_speed = 2;
+ data->tracking_signal_name = "test_event";
+
+ //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);
+ atspi_alloc_memory();
+ ctx = flat_navi_context_create(root);
+}
+
+void teardown_flat_navi()
+{
+ flat_navi_context_free(ctx);
+ atspi_free_memory();
+ atspi_delete_accessible(root);
+ 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 = atspi_create_accessible();
+ event.type = "test_event";
+ sd->tracking_signal_name = "test_event";
+ event.detail1 = 1;
+ accessible->name = "test_name";
+ event.source = accessible;
+ char *return_value = spi_event_get_text_to_read(&event, sd);
+ fail_if(!return_value || strcmp(return_value, "test_name"));
+ free(return_value);
+ atspi_delete_accessible(accessible);
+}
+END_TEST
+
+START_TEST(spi_on_state_change_name_tree)
+{
+ Service_Data *sd = get_pointer_to_service_data_struct();
+ AtspiEvent event;
+ AtspiAccessible *accessible = atspi_create_accessible();
+ AtspiAccessible *child1 = atspi_create_accessible();
+ AtspiAccessible *child11 = atspi_create_accessible();
+ AtspiAccessible *child12 = atspi_create_accessible();
+ AtspiAccessible *child2 = atspi_create_accessible();
+ accessible->role = ATSPI_ROLE_FILLER;
+ child1->role = ATSPI_ROLE_FILLER;
+ child11->role = ATSPI_ROLE_FILLER;
+ child12->role = ATSPI_ROLE_FILLER;
+ child2->role = ATSPI_ROLE_FILLER;
+ child1->name = "one";
+ child11->name = "two";
+ child12->name = "two1";
+ child2->name = "three";
+ atspi_accessible_add_child(accessible, child1);
+ atspi_accessible_add_child(child1, child11);
+ atspi_accessible_add_child(child1, child12);
+ atspi_accessible_add_child(accessible, child2);
+ event.type = "test_event";
+ sd->tracking_signal_name = "test_event";
+ event.detail1 = 1;
+ event.source = accessible;
+ char *return_value = spi_event_get_text_to_read(&event, sd);
+ fail_if(!return_value || strcmp(return_value, "one two two1 three "));
+ free(return_value);
+ atspi_delete_accessible(accessible);
+}
+END_TEST
+START_TEST(spi_on_state_change_description)
+{
+ Service_Data *sd = get_pointer_to_service_data_struct();
+ AtspiEvent event;
+ AtspiAccessible *accessible = atspi_create_accessible();
+ event.type = "test_event";
+ sd->tracking_signal_name = "test_event";
+ event.detail1 = 1;
+ accessible->name = "test_name";
+ accessible->description = "test description";
+ event.source = accessible;
+ char *return_value = spi_event_get_text_to_read(&event, sd);
+ fail_if(!return_value || strcmp(return_value, "test description"));
+ free(return_value);
+ atspi_delete_accessible(accessible);
+}
+END_TEST
+
+START_TEST(spi_on_state_change_role)
+{
+ Service_Data *sd = get_pointer_to_service_data_struct();
+ AtspiEvent event;
+ AtspiAccessible *accessible = atspi_create_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);
+ fail_if(!return_value || strcmp(return_value, atspi_accessible_get_role_name(accessible, NULL)));
+ free(return_value);
+ atspi_delete_accessible(accessible);
+}
+END_TEST
+
+START_TEST(spi_on_caret_move)
+{
+ Service_Data *sd = get_pointer_to_service_data_struct();
+ AtspiEvent event;
+ AtspiAccessible *accessible = atspi_create_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);
+ atspi_delete_accessible(accessible);
+}
+END_TEST
+
+START_TEST(spi_on_value_changed)
+{
+ Service_Data *sd = get_pointer_to_service_data_struct();
+ AtspiEvent event;
+ AtspiAccessible *accessible = atspi_create_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);
+ atspi_delete_accessible(accessible);
+}
+END_TEST
+
+START_TEST(spi_on_editing)
+{
+ Service_Data *sd = get_pointer_to_service_data_struct();
+ AtspiEvent event;
+ AtspiAccessible *accessible = atspi_create_accessible();
+ event.type = FOCUS_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, EDITING_STARTED));
+ free(return_value);
+ atspi_delete_accessible(accessible);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_create_null_parameter)
+{
+ FlatNaviContext *test_ctx = flat_navi_context_create(NULL);
+ fail_if(!test_ctx);
+ free(test_ctx);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_create_valid_parameter)
+{
+ FlatNaviContext *test_ctx = flat_navi_context_create(root);
+ 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 != child1);
+}
+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 != 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);
+ flat_navi_context_current_set(ctx, child4);
+ prev = flat_navi_context_prev(ctx);
+ fail_if(!prev || prev != child3);
+}
+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 != child2);
+}
+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 != child1);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_current_set_null_parameters)
+{
+ gboolean ret = flat_navi_context_current_set(NULL, NULL);
+ fail_if(ret != FALSE);
+ ret = flat_navi_context_current_set(ctx, NULL);
+ fail_if(ret != FALSE);
+ AtspiAccessible *last = flat_navi_context_last(ctx);
+ ret = flat_navi_context_current_set(NULL, last);
+ fail_if(ret != FALSE);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_current_set_valid_parameters)
+{
+ AtspiAccessible *last = flat_navi_context_last(ctx);
+ gboolean ret = flat_navi_context_current_set(ctx, last);
+ AtspiAccessible *current = flat_navi_context_current_get(ctx);
+ fail_if(ret != TRUE || current != last);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_line_prev_null_parameter)
+{
+ AtspiAccessible *prev = flat_navi_context_line_prev(NULL);
+ fail_if(prev);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_line_prev_valid_parameter)
+{
+ AtspiAccessible *prev = flat_navi_context_line_prev(ctx);
+ fail_if(prev);
+ flat_navi_context_current_set(ctx, child4);
+ prev = flat_navi_context_line_prev(ctx);
+ fail_if(!prev || prev != child1);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_line_next_null_parameter)
+{
+ AtspiAccessible *next = flat_navi_context_line_next(NULL);
+ fail_if(next);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_line_next_valid_parameter)
+{
+ AtspiAccessible *next = flat_navi_context_line_next(ctx);
+ fail_if(!next || next != child3);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_line_first_null_parameter)
+{
+ AtspiAccessible *first = flat_navi_context_line_first(NULL);
+ fail_if(first);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_line_first_valid_parameter)
+{
+ AtspiAccessible *first = flat_navi_context_line_first(ctx);
+ fail_if(!first || first != child1);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_line_last_null_parameter)
+{
+ AtspiAccessible *last = flat_navi_context_line_last(NULL);
+ fail_if(last);
+}
+END_TEST
+
+START_TEST(spi_flat_navi_context_line_last_valid_parameter)
+{
+ AtspiAccessible *last = flat_navi_context_line_last(ctx);
+ fail_if(!last || last != child3);
+}
+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_on_editing;
+ TCase *tc_spi_screen_reader_flat_navi;
+
+ 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_on_editing = tcase_create("tc_spi_screen_reader_on_editing");
+ tc_spi_screen_reader_flat_navi = tcase_create("tc_scpi_screen_reader_flat_navi");
+
+ 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_on_editing, setup, teardown);
+
+ tcase_add_checked_fixture(tc_spi_screen_reader_flat_navi, setup_flat_navi, 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_name_tree);
+ 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_on_editing, spi_on_editing);
+ 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_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_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);
+ tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_line_prev_null_parameter);
+ tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_line_prev_valid_parameter);
+ tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_line_next_null_parameter);
+ tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_line_next_valid_parameter);
+ tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_line_first_null_parameter);
+ tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_line_first_valid_parameter);
+ tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_line_last_null_parameter);
+ tcase_add_test(tc_spi_screen_reader_flat_navi, spi_flat_navi_context_line_last_valid_parameter);
+
+ 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);
+
+ return s;
+}
+
+int main()
+{
+ int number_failed;
+ Suite *s;
+ SRunner *sr;
+
+ s = screen_reader_suite();
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_NORMAL);
+ number_failed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}