forked from enlightenment/efl
this is... the beginning of accessibility supportin elm. it's direct
as in elm manages it itself - all it needs is a module to send text to. one is provided here that just execs espeak and handles a stream of things for it to say. this is only a start and is still being fleshed out. SVN revision: 62585
This commit is contained in:
parent
a8156d10c5
commit
430e7463f5
|
@ -582,6 +582,7 @@ src/lib/Makefile
|
|||
src/lib/Elementary.h
|
||||
src/bin/Makefile
|
||||
src/modules/Makefile
|
||||
src/modules/access_output/Makefile
|
||||
src/modules/test_entry/Makefile
|
||||
src/modules/test_map/Makefile
|
||||
src/edje_externals/Makefile
|
||||
|
|
|
@ -30,7 +30,6 @@ group { name: "elm/label/base/default";
|
|||
}
|
||||
part { name: "elm.text";
|
||||
type: TEXTBLOCK;
|
||||
mouse_events: 0;
|
||||
scale: 1;
|
||||
clip_to: "label.text.clip";
|
||||
description { state: "default" 0.0;
|
||||
|
@ -70,7 +69,6 @@ group { name: "elm/label/base/marker";
|
|||
}
|
||||
part { name: "elm.text";
|
||||
type: TEXTBLOCK;
|
||||
mouse_events: 0;
|
||||
scale: 1;
|
||||
description { state: "default" 0.0;
|
||||
text {
|
||||
|
@ -161,7 +159,6 @@ group { name: "elm/label/base/slide_long";
|
|||
}
|
||||
part { name: "elm.text";
|
||||
type: TEXTBLOCK;
|
||||
mouse_events: 0;
|
||||
scale: 1;
|
||||
clip_to: "label.text.clip";
|
||||
description { state: "default" 0.0;
|
||||
|
@ -287,7 +284,6 @@ group { name: "elm/label/base/slide_short";
|
|||
}
|
||||
part { name: "elm.text";
|
||||
type: TEXTBLOCK;
|
||||
mouse_events: 0;
|
||||
scale: 1;
|
||||
clip_to: "label.text.clip";
|
||||
description { state: "default" 0.0;
|
||||
|
@ -424,7 +420,6 @@ group { name: "elm/label/base/slide_bounce";
|
|||
}
|
||||
part { name: "elm.text";
|
||||
type: TEXTBLOCK;
|
||||
mouse_events: 0;
|
||||
scale: 1;
|
||||
clip_to: "label.text.clip";
|
||||
description { state: "default" 0.0;
|
||||
|
|
|
@ -51,6 +51,7 @@ elc_hoversel.c \
|
|||
elc_naviframe.c \
|
||||
elc_player.c \
|
||||
elc_scrolled_entry.c \
|
||||
elm_access.c \
|
||||
elm_actionslider.c \
|
||||
elm_bg.c \
|
||||
elm_box.c \
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
#include <Elementary.h>
|
||||
#include "elm_priv.h"
|
||||
|
||||
typedef struct _Mod_Api Mod_Api;
|
||||
|
||||
struct _Mod_Api
|
||||
{
|
||||
void (*out_read) (const char *txt);
|
||||
void (*out_read_done) (void);
|
||||
void (*out_cancel) (void);
|
||||
void (*out_done_callback_set) (void (*func) (void *data), const void *data);
|
||||
};
|
||||
|
||||
static int initted = 0;
|
||||
static Elm_Module *mod = NULL;
|
||||
static Mod_Api *mapi = NULL;
|
||||
|
||||
static void
|
||||
_access_init(void)
|
||||
{
|
||||
Elm_Module *m;
|
||||
initted++;
|
||||
if (initted > 1) return;
|
||||
if (!(m = _elm_module_find_as("access/api"))) return;
|
||||
mod = m;
|
||||
m->api = malloc(sizeof(Mod_Api));
|
||||
if (!m->api) return;
|
||||
m->init_func(m);
|
||||
((Mod_Api *)(m->api) )->out_read = // called to read out some text
|
||||
_elm_module_symbol_get(m, "out_read");
|
||||
((Mod_Api *)(m->api) )->out_read_done = // called to set a done marker so when it is reached the done callback is called
|
||||
_elm_module_symbol_get(m, "out_read_done");
|
||||
((Mod_Api *)(m->api) )->out_cancel = // called to read out some text
|
||||
_elm_module_symbol_get(m, "out_cancel");
|
||||
((Mod_Api *)(m->api) )->out_done_callback_set = // called when last read done
|
||||
_elm_module_symbol_get(m, "out_done_callback_set");
|
||||
mapi = m->api;
|
||||
}
|
||||
|
||||
static Elm_Access_Item *
|
||||
_access_add_set(Elm_Access_Info *ac, int type)
|
||||
{
|
||||
Elm_Access_Item *ai;
|
||||
Eina_List *l;
|
||||
|
||||
if (!ac) return NULL;
|
||||
EINA_LIST_FOREACH(ac->items, l, ai)
|
||||
{
|
||||
if (ai->type == type)
|
||||
{
|
||||
if (!ai->func)
|
||||
{
|
||||
if (ai->data) eina_stringshare_del(ai->data);
|
||||
}
|
||||
ai->func = NULL;
|
||||
ai->data = NULL;
|
||||
return ai;
|
||||
}
|
||||
}
|
||||
ai = calloc(1, sizeof(Elm_Access_Item));
|
||||
ai->type = type;
|
||||
ac->items = eina_list_prepend(ac->items, ai);
|
||||
return ai;
|
||||
}
|
||||
static Eina_Bool
|
||||
_access_obj_over_timeout_cb(void *data)
|
||||
{
|
||||
Elm_Access_Info *ac = evas_object_data_get(data, "_elm_access");
|
||||
if (!ac) return EINA_FALSE;
|
||||
_elm_access_read(ac, ELM_ACCESS_CANCEL, data, NULL);
|
||||
_elm_access_read(ac, ELM_ACCESS_TYPE, data, NULL);
|
||||
_elm_access_read(ac, ELM_ACCESS_INFO, data, NULL);
|
||||
_elm_access_read(ac, ELM_ACCESS_STATE, data, NULL);
|
||||
_elm_access_read(ac, ELM_ACCESS_DONE, data, NULL);
|
||||
ac->delay_timer = NULL;
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
_access_obj_mouse_in_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
|
||||
{
|
||||
Elm_Access_Info *ac = evas_object_data_get(data, "_elm_access");
|
||||
if (!ac) return;
|
||||
|
||||
if (ac->delay_timer)
|
||||
{
|
||||
ecore_timer_del(ac->delay_timer);
|
||||
ac->delay_timer = NULL;
|
||||
}
|
||||
ac->delay_timer = ecore_timer_add(0.2, _access_obj_over_timeout_cb, data);
|
||||
}
|
||||
|
||||
static void
|
||||
_access_obj_mouse_out_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
|
||||
{
|
||||
Elm_Access_Info *ac = evas_object_data_get(data, "_elm_access");
|
||||
if (!ac) return;
|
||||
if (ac->delay_timer)
|
||||
{
|
||||
ecore_timer_del(ac->delay_timer);
|
||||
ac->delay_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_access_obj_del_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
|
||||
{
|
||||
Elm_Access_Info *ac;
|
||||
|
||||
evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOUSE_IN,
|
||||
_access_obj_mouse_in_cb, data);
|
||||
evas_object_event_callback_del_full(obj, EVAS_CALLBACK_MOUSE_OUT,
|
||||
_access_obj_mouse_out_cb, data);
|
||||
evas_object_event_callback_del_full(obj, EVAS_CALLBACK_DEL,
|
||||
_access_obj_del_cb, data);
|
||||
ac = evas_object_data_get(data, "_elm_access");
|
||||
evas_object_data_del(data, "_elm_access");
|
||||
if (ac)
|
||||
{
|
||||
_elm_access_clear(ac);
|
||||
free(ac);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_access_read_done(void *data __UNUSED__)
|
||||
{
|
||||
printf("read done\n");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
EAPI void
|
||||
_elm_access_clear(Elm_Access_Info *ac)
|
||||
{
|
||||
Elm_Access_Item *ai;
|
||||
|
||||
if (!ac) return;
|
||||
if (ac->delay_timer)
|
||||
{
|
||||
ecore_timer_del(ac->delay_timer);
|
||||
ac->delay_timer = NULL;
|
||||
}
|
||||
EINA_LIST_FREE(ac->items, ai)
|
||||
{
|
||||
if (!ai->func)
|
||||
{
|
||||
if (ai->data) eina_stringshare_del(ai->data);
|
||||
}
|
||||
free(ai);
|
||||
}
|
||||
}
|
||||
|
||||
EAPI void
|
||||
_elm_access_text_set(Elm_Access_Info *ac, int type, const char *text)
|
||||
{
|
||||
Elm_Access_Item *ai = _access_add_set(ac, type);
|
||||
if (!ai) return;
|
||||
ai->data = eina_stringshare_add(text);
|
||||
}
|
||||
|
||||
EAPI void
|
||||
_elm_access_callback_set(Elm_Access_Info *ac, int type, Elm_Access_Content_Cb func, const void *data)
|
||||
{
|
||||
Elm_Access_Item *ai = _access_add_set(ac, type);
|
||||
if (!ai) return;
|
||||
ai->func = func;
|
||||
ai->data = data;
|
||||
}
|
||||
|
||||
EAPI char *
|
||||
_elm_access_text_get(Elm_Access_Info *ac, int type, Evas_Object *obj, Elm_Widget_Item *item)
|
||||
{
|
||||
Elm_Access_Item *ai;
|
||||
Eina_List *l;
|
||||
|
||||
if (!ac) return NULL;
|
||||
EINA_LIST_FOREACH(ac->items, l, ai)
|
||||
{
|
||||
if (ai->type == type)
|
||||
{
|
||||
if (ai->func) return ai->func(ai->data, obj, item);
|
||||
else if (ai->data) return strdup(ai->data);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EAPI void
|
||||
_elm_access_read(Elm_Access_Info *ac, int type, Evas_Object *obj, Elm_Widget_Item *item)
|
||||
{
|
||||
char *txt = _elm_access_text_get(ac, type, obj, item);
|
||||
|
||||
_access_init();
|
||||
if (mapi)
|
||||
{
|
||||
if (mapi->out_done_callback_set)
|
||||
mapi->out_done_callback_set(_access_read_done, NULL);
|
||||
if (type == ELM_ACCESS_DONE)
|
||||
{
|
||||
if (mapi->out_read_done) mapi->out_read_done();
|
||||
}
|
||||
else if (type == ELM_ACCESS_CANCEL)
|
||||
{
|
||||
if (mapi->out_cancel) mapi->out_cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (txt)
|
||||
{
|
||||
if (mapi->out_read) mapi->out_read(txt);
|
||||
if (mapi->out_read) mapi->out_read(".\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (txt) free(txt);
|
||||
}
|
||||
|
||||
EAPI Elm_Access_Info *
|
||||
_elm_access_object_get(Evas_Object *obj)
|
||||
{
|
||||
return evas_object_data_get(obj, "_elm_access");
|
||||
}
|
||||
|
||||
EAPI void
|
||||
_elm_access_object_register(Evas_Object *obj, Evas_Object *hoverobj)
|
||||
{
|
||||
Elm_Access_Info *ac;
|
||||
|
||||
evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_MOUSE_IN,
|
||||
_access_obj_mouse_in_cb, obj);
|
||||
evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_MOUSE_OUT,
|
||||
_access_obj_mouse_out_cb, obj);
|
||||
evas_object_event_callback_add(hoverobj, EVAS_CALLBACK_DEL,
|
||||
_access_obj_del_cb, obj);
|
||||
ac = calloc(1, sizeof(Elm_Access_Info));
|
||||
evas_object_data_set(obj, "_elm_access", ac);
|
||||
}
|
||||
|
||||
// XXX special version for items
|
||||
//EAPI void
|
||||
//_elm_access_item_hover_register(Elm_Widget_Item *item, Evas_Object *hoverobj)
|
||||
//{
|
||||
//}
|
|
@ -316,6 +316,14 @@ _elm_button_label_get(const Evas_Object *obj, const char *item)
|
|||
return wd->label;
|
||||
}
|
||||
|
||||
static char *
|
||||
_access_info_cb(const void *data, Evas_Object *obj, Elm_Widget_Item *item)
|
||||
{
|
||||
const char *txt = _elm_button_label_get(obj, NULL);
|
||||
if (txt) return strdup(txt);
|
||||
return txt;
|
||||
}
|
||||
|
||||
EAPI Evas_Object *
|
||||
elm_button_add(Evas_Object *parent)
|
||||
{
|
||||
|
@ -359,6 +367,12 @@ elm_button_add(Evas_Object *parent)
|
|||
// TODO: convert Elementary to subclassing of Evas_Smart_Class
|
||||
// TODO: and save some bytes, making descriptions per-class and not instance!
|
||||
evas_object_smart_callbacks_descriptions_set(obj, _signals);
|
||||
|
||||
_elm_access_object_register(obj, wd->btn);
|
||||
_elm_access_text_set(_elm_access_object_get(obj),
|
||||
ELM_ACCESS_TYPE, E_("Button"));
|
||||
_elm_access_callback_set(_elm_access_object_get(obj),
|
||||
ELM_ACCESS_INFO, _access_info_cb, obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -192,11 +192,45 @@ typedef struct _Elm_Tooltip Elm_Tooltip;
|
|||
typedef struct _Elm_Cursor Elm_Cursor;
|
||||
typedef struct _Elm_Widget_Item Elm_Widget_Item; /**< base structure for all widget items that are not Elm_Widget themselves */
|
||||
|
||||
typedef void (*Elm_Widget_On_Text_Set_Cb)(void *, const char *part, const char *text);
|
||||
typedef void (*Elm_Widget_On_Content_Set_Cb)(void *, const char *part, Evas_Object *content);
|
||||
typedef const char *(*Elm_Widget_On_Text_Get_Cb)(const void *, const char *part);
|
||||
typedef Evas_Object *(*Elm_Widget_On_Content_Get_Cb)(const void *, const char *part);
|
||||
typedef Evas_Object *(*Elm_Widget_On_Content_Unset_Cb)(const void *, const char *part);
|
||||
typedef struct _Elm_Access_Info Elm_Access_Info; /**< accessibility information to be able to set and get from the access API */
|
||||
typedef struct _Elm_Access_Item Elm_Access_Item; /**< accessibility info item */
|
||||
|
||||
typedef void (*Elm_Widget_On_Text_Set_Cb)(void *data, const char *part, const char *text);
|
||||
typedef void (*Elm_Widget_On_Content_Set_Cb)(void *data, const char *part, Evas_Object *content);
|
||||
typedef const char *(*Elm_Widget_On_Text_Get_Cb)(const void *data, const char *part);
|
||||
typedef Evas_Object *(*Elm_Widget_On_Content_Get_Cb)(const void *data, const char *part);
|
||||
typedef Evas_Object *(*Elm_Widget_On_Content_Unset_Cb)(const void *data, const char *part);
|
||||
|
||||
#define ELM_ACCESS_TYPE 0 // when reading out widget or item this is read first
|
||||
#define ELM_ACCESS_INFO 1 // next read is info - this is normally label
|
||||
#define ELM_ACCESS_STATE 2 // if there is a state (eg checkbox) then read state out
|
||||
#define ELM_ACCESS_CONTENT 3 // read ful content - eg all of the label, not a shortened version
|
||||
|
||||
#define ELM_ACCESS_DONE -1 // sentence done - send done event here
|
||||
#define ELM_ACCESS_CANCEL -2 // stop reading immediately
|
||||
|
||||
typedef char *(*Elm_Access_Content_Cb)(const void *data, Evas_Object *obj, Elm_Widget_Item *item);
|
||||
|
||||
struct _Elm_Access_Item
|
||||
{
|
||||
int type;
|
||||
const void *data;
|
||||
Elm_Access_Content_Cb func;
|
||||
};
|
||||
|
||||
struct _Elm_Access_Info
|
||||
{
|
||||
Eina_List *items;
|
||||
Ecore_Timer *delay_timer;
|
||||
};
|
||||
|
||||
EAPI void _elm_access_clear(Elm_Access_Info *ac);
|
||||
EAPI void _elm_access_text_set(Elm_Access_Info *ac, int type, const char *text);
|
||||
EAPI void _elm_access_callback_set(Elm_Access_Info *ac, int type, Elm_Access_Content_Cb func, const void *data);
|
||||
EAPI char *_elm_access_text_get(Elm_Access_Info *ac, int type, Evas_Object *obj, Elm_Widget_Item *item);
|
||||
EAPI void _elm_access_read(Elm_Access_Info *ac, int type, Evas_Object *obj, Elm_Widget_Item *item);
|
||||
EAPI Elm_Access_Info *_elm_access_object_get(Evas_Object *obj);
|
||||
EAPI void _elm_access_object_register(Evas_Object *obj, Evas_Object *hoverobj);
|
||||
|
||||
struct _Elm_Widget_Item
|
||||
{
|
||||
|
|
|
@ -3,4 +3,5 @@ MAINTAINERCLEANFILES = Makefile.in
|
|||
|
||||
SUBDIRS = \
|
||||
test_entry \
|
||||
test_map
|
||||
test_map \
|
||||
access_output
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I. \
|
||||
-I$(top_builddir) \
|
||||
-I$(top_srcdir) \
|
||||
-I$(top_srcdir)/src/lib \
|
||||
-I$(top_builddir)/src/lib \
|
||||
-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
|
||||
-DPACKAGE_LIB_DIR=\"$(libdir)\" \
|
||||
@ELEMENTARY_CFLAGS@ \
|
||||
@ELEMENTARY_X_CFLAGS@ \
|
||||
@ELEMENTARY_FB_CFLAGS@ \
|
||||
@ELEMENTARY_WIN32_CFLAGS@ \
|
||||
@ELEMENTARY_WINCE_CFLAGS@ \
|
||||
@ELEMENTARY_EDBUS_CFLAGS@ \
|
||||
@ELEMENTARY_EFREET_CFLAGS@ \
|
||||
@ELEMENTARY_ETHUMB_CFLAGS@ \
|
||||
@ELEMENTARY_EMAP_CFLAGS@
|
||||
|
||||
if ELEMENTARY_WINDOWS_BUILD
|
||||
AM_CPPFLAGS += -DELEMENTARY_BUILD
|
||||
endif
|
||||
|
||||
pkgdir = $(libdir)/elementary/modules/access_output/$(MODULE_ARCH)
|
||||
pkg_LTLIBRARIES = module.la
|
||||
|
||||
module_la_SOURCES = mod.c
|
||||
|
||||
module_la_LIBADD = $(top_builddir)/src/lib/libelementary.la
|
||||
module_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version
|
||||
module_la_LIBTOOLFLAGS = --tag=disable-static
|
|
@ -0,0 +1,116 @@
|
|||
#include <Elementary.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "elementary_config.h"
|
||||
#endif
|
||||
|
||||
/* to enable this module
|
||||
export ELM_MODULES="access_output>access/api"
|
||||
*/
|
||||
|
||||
static void (*cb_func) (void *data);
|
||||
static void *cb_data;
|
||||
static Ecore_Exe *espeak = NULL;
|
||||
static Ecore_Event_Handler *exe_exit_handler = NULL;
|
||||
static char *tmpf = NULL;
|
||||
static int tmpfd = -1;
|
||||
|
||||
static Eina_Bool
|
||||
_exe_del(void *data __UNUSED__, int type __UNUSED__, void *event)
|
||||
{
|
||||
Ecore_Exe_Event_Del *ev = event;
|
||||
|
||||
if ((espeak) && (ev->exe == espeak))
|
||||
{
|
||||
if (tmpf)
|
||||
{
|
||||
unlink(tmpf);
|
||||
free(tmpf);
|
||||
tmpf = NULL;
|
||||
close(tmpfd);
|
||||
}
|
||||
espeak = NULL;
|
||||
if (cb_func) cb_func(cb_data);
|
||||
}
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
// module api funcs needed
|
||||
EAPI int
|
||||
elm_modapi_init(void *m __UNUSED__)
|
||||
{
|
||||
exe_exit_handler =
|
||||
ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
|
||||
_exe_del, NULL);
|
||||
return 1; // succeed always
|
||||
}
|
||||
|
||||
EAPI int
|
||||
elm_modapi_shutdown(void *m __UNUSED__)
|
||||
{
|
||||
if (exe_exit_handler)
|
||||
{
|
||||
ecore_event_handler_del(exe_exit_handler);
|
||||
exe_exit_handler = NULL;
|
||||
}
|
||||
return 1; // succeed always
|
||||
}
|
||||
|
||||
// module fucns for the specific module type
|
||||
EAPI void
|
||||
out_read(const char *txt)
|
||||
{
|
||||
if (!tmpf)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
|
||||
snprintf(buf, sizeof(buf), "/tmp/.elm-speak-XXXXXX");
|
||||
tmpfd = mkstemp(buf);
|
||||
if (tmpfd >= 0) tmpf = strdup(buf);
|
||||
else return;
|
||||
}
|
||||
if (write(tmpfd, txt, strlen(txt)) < 0) perror("write to tmpfile (espeak)");
|
||||
}
|
||||
|
||||
EAPI void
|
||||
out_read_done(void)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if (espeak)
|
||||
{
|
||||
ecore_exe_interrupt(espeak);
|
||||
espeak = NULL;
|
||||
}
|
||||
if (tmpf)
|
||||
{
|
||||
close(tmpfd);
|
||||
snprintf(buf, sizeof(buf), "espeak -m -a 20 -f %s", tmpf);
|
||||
espeak = ecore_exe_pipe_run(buf,
|
||||
ECORE_EXE_NOT_LEADER,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
EAPI void
|
||||
out_cancel(void)
|
||||
{
|
||||
if (espeak)
|
||||
{
|
||||
ecore_exe_interrupt(espeak);
|
||||
espeak = NULL;
|
||||
}
|
||||
if (tmpf)
|
||||
{
|
||||
unlink(tmpf);
|
||||
free(tmpf);
|
||||
tmpf = NULL;
|
||||
close(tmpfd);
|
||||
}
|
||||
}
|
||||
|
||||
EAPI void
|
||||
out_done_callback_set(void (*func) (void *data), const void *data)
|
||||
{
|
||||
cb_func = func;
|
||||
cb_data = (void *)data;
|
||||
}
|
Loading…
Reference in New Issue