Initial commit

master
Tait Hoyem 2 years ago
commit 2b4d06b37c

@ -0,0 +1,7 @@
CC=clang
CFLAGS=-O2 -g -Wall -I/usr/include -I/usr/X11R6/include `pkg-config --cflags glib-2.0 gobject-2.0 atk-bridge-2.0 atspi-2 speech-dispatcher`
LDFLAGS=-pthread -L/usr/X11R6/lib -lm `pkg-config --libs glib-2.0 gobject-2.0 atk-bridge-2.0 atspi-2 speech-dispatcher`
EXAMPLES = $(patsubst %.c,%,$(wildcard *.c))
default:
$(CC) -o speak-selection $< $(CFLAGS) $(LDFLAGS) speak-selection.c

@ -0,0 +1,18 @@
# Speak Selection (SSN)
This program is dead simple.
While running, it wil speak out newly selected pieces of text to a user.
It does not include any screen reading functionality.
For that, I would shamelessly recommend a project I work on called [Odilia}(https://odilia.app)
or a project I don't work on: [Orca](https://help.gnome.org/users/orca/stable/introduction.html.en).
## Credits
* Blatently ripped of the code from [at-spi2-examples](https://github.com/infapi00/at-spi2-examples/)
## Requirements
* `at-spi2-core`
* `libspeechd`
* `gcc` or `clang`

Binary file not shown.

@ -0,0 +1,205 @@
/*
* For each focus/selection change, it prints the following for each
* object getting the focus/selection:
* (appname, name, rolename, [state-set])
*
* You can specify if you want to filter by application. Use --help
* for more information.
*/
#include <atspi/atspi.h>
#include <atspi/atspi-accessible.h>
#include <speech-dispatcher/libspeechd.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
gchar *filter_name = NULL;
SPDConnection *spd_conn = NULL;
const static gchar*
atspi_state_get_name(gint state)
{
GTypeClass *type_class;
GEnumValue *value;
type_class = g_type_class_ref(ATSPI_TYPE_STATE_TYPE);
g_return_val_if_fail(G_IS_ENUM_CLASS (type_class), "");
value = g_enum_get_value(G_ENUM_CLASS (type_class), state);
return value->value_nick;
}
static gchar*
get_state_set(AtspiAccessible *accessible)
{
AtspiStateSet *state_set = atspi_accessible_get_state_set(accessible);
GArray *states = atspi_state_set_get_states(state_set);
gchar *result = g_strdup_printf("[");
gchar *aux = NULL;
gint i;
AtspiStateType state;
for(i = 0; i < states->len; i++) {
state = g_array_index(states, gint, i);
aux = result;
if(i < states->len -1)
result = g_strdup_printf("%s%s,", aux, atspi_state_get_name(state));
else
result = g_strdup_printf("%s%s", aux, atspi_state_get_name(state));
g_free(aux);
}
aux = result;
result = g_strconcat(aux, "]", NULL);
g_free(aux);
g_array_free(states, TRUE);
g_object_unref(state_set);
return result;
}
static gchar*
get_label(AtspiAccessible *accessible)
{
GArray *relations;
AtspiRelation *relation;
gint i;
gchar *result = "";
relations = atspi_accessible_get_relation_set(accessible, NULL);
if(relations == NULL) {
return "";
}
for(i = 0; i < relations->len; i++) {
relation = g_array_index(relations, AtspiRelation*, i);
if(atspi_relation_get_relation_type (relation) == ATSPI_RELATION_LABELLED_BY) {
result = atspi_accessible_get_name(atspi_relation_get_target (relation, 0), NULL);
}
}
if(relations != NULL)
g_array_free(relations, TRUE);
return result;
}
static void
print_info(AtspiAccessible *accessible,
gchar *app_name)
{
AtspiText *text = atspi_accessible_get_text_iface(accessible);
gchar *name = "NULL";
gchar *role_name = "NULL";
gchar *state_set = NULL;
gint length_of_string;
GError **error = NULL;
if(accessible != NULL) {
name = atspi_accessible_get_name(accessible, NULL);
if((name == NULL) || (g_strcmp0(name, "") == 0)) {
name = get_label(accessible);
if((name == NULL) || (g_strcmp0(name, "") == 0)) {
length_of_string = atspi_text_get_character_count(text, error);
name = atspi_text_get_text(text, 0, length_of_string, error);
}
}
role_name = atspi_accessible_get_role_name(accessible, NULL);
}
state_set = get_state_set(accessible);
g_print("(%s, %s, %s, %s)\n", app_name, name, role_name, state_set);
spd_sayf(spd_conn, SPD_TEXT, "%s, %s", name, role_name);
g_free(state_set);
}
static void
on_event(AtspiEvent *event,
void *data)
{
AtspiAccessible *application = NULL;
gchar *app_name = NULL;
if(event->source == NULL)
return;
/* We only care about focus/selection gain */
if(!event->detail1)
return;
application = atspi_accessible_get_application(event->source, NULL);
if(application == NULL)
return;
app_name = atspi_accessible_get_name(application, NULL);
if((filter_name != NULL) && (g_strcmp0 (app_name, filter_name) != 0))
goto clean;
print_info(event->source, app_name);
clean:
g_free(app_name);
}
static gchar*
parse_args(int *argc,
char ***argv)
{
GError *error = NULL;
GOptionContext *context;
static gchar *name = NULL;
static GOptionEntry entries [] =
{
{"application", 'a', 0, G_OPTION_ARG_STRING, &name, "Application name", NULL},
{NULL,},
};
context = g_option_context_new("");
g_option_context_add_main_entries(context, entries, NULL);
if(!g_option_context_parse (context, argc, argv, &error))
{
g_print("%s\n", error->message);
g_print("Use --help for more information.\n");
exit(0);
}
return name;
}
int main(int argc, gchar **argv)
{
AtspiEventListener *listener;
char username[64];
filter_name = parse_args(&argc, &argv);
if(!filter_name) {
g_print("NOTE: Application name to filter not specified. Showing "
"focus/selection changes for any application.\n");
}
if(getlogin_r(username, 64)) {
g_print("USERNAME not found. Error.\n");
exit(1);
}
spd_conn = spd_open("speak_selection", "main", username, SPD_MODE_SINGLE);
if(!spd_conn){
g_print("Error establishing speech dispatcher connection. Fatal error.");
exit(1);
}
atspi_init();
listener = atspi_event_listener_new(on_event, NULL, NULL);
//atspi_event_listener_register(listener, "object:state-changed:focused", NULL);
atspi_event_listener_register(listener, "object:text-caret-moved", NULL);
atspi_event_main();
spd_close(spd_conn);
return 0;
}
Loading…
Cancel
Save