[dasher] Add AT-SPI 2 support.



commit e95c462e87378b0a9b9bfd46d0ba17040d89a38a
Author: Patrick Welche <prlw1 cam ac uk>
Date:   Tue Jan 15 09:59:04 2013 +0000

    Add AT-SPI 2 support.
    
    Direct mode may now use the DBus based accessibility bus with GTK3
    instead of using Xtst.

 INSTALL.Linux                             |   32 ++--
 Src/Gtk2/Makefile.am                      |    8 +
 Src/Gtk2/dasher_editor_external.h         |   11 +-
 Src/Gtk2/dasher_editor_external_atspi.cpp |  242 +++++++++++++++++++++++++++++
 Src/Gtk2/dasher_editor_external_cspi.cpp  |    9 +-
 Src/Gtk2/dasher_editor_private.h          |    2 +
 Src/Makefile.am                           |    3 +
 Src/main.cc                               |    4 +-
 configure.ac                              |   23 ++-
 9 files changed, 298 insertions(+), 36 deletions(-)
---
diff --git a/INSTALL.Linux b/INSTALL.Linux
index 555518e..2d99f29 100644
--- a/INSTALL.Linux
+++ b/INSTALL.Linux
@@ -1,4 +1,4 @@
-Copyright (C) 2008 The Dasher Project
+Copyright (C) 2013 The Dasher Project
  
 This file is free documentation; the Dasher Project gives unlimited
 permission to copy, distribute and modify it.
@@ -11,17 +11,15 @@ etc.), which are described in the 'INSTALL' file.  The following is
 specific to building and installing Dasher on Linux.  The Dasher
 maintainer documentation is at http://live.gnome.org/Dasher.
 
-If you are building sources from the Subversion then you must first
-install all the packages required to build Dasher.  On Debian based
-distributions the following can be used.
+If you are building sources from the Git repository then you must
+first install all the packages required to build Dasher.  On Debian
+based distributions the following can be used.
 
   packages="g++
-	    gnome-common
-	    gnome-doc-utils
-	    libatspi-dev
-	    libgconf2-dev
-	    libgtk2.0-dev
-	    libxtst-dev"
+            gnome-common
+            gnome-doc-utils
+            libatspi2.0-dev
+            libgtk-3-dev"
   sudo apt-get install $packages
 
 Then autogen:
@@ -35,14 +33,16 @@ following configure-time options are specific to Dasher (see section
 Options
 =======
 
-   --without-gnome     Disable features which require the GNOME core
-                       libraries (enabled by default).
+   --disable-speech    Disable speech support (speech dispatcher and
+                       GNOME speech).
 
-   --with-speech       Enable GNOME-Speech support (disabled by default).
+   --disable-a11y      Disable support for GNOME 2 accessibility features
+                       (enabled by default).
 
-   --disable-a11y      Disable support for GNOME accessibility features
-                       (enabled by default). You should probably specify
-                       this as well if you're using --without-gnome.
+   --disable-atspi     Disable support for GNOME 3 accessibility features
+                       (enabled by default). This flag is just useful
+                       for debugging, as accessibility is now built-in
+                       and doesn't bring in more dependencies.
 
    The following options include code which is significantly out of
 date and currently untested. It is likely that these options will not
diff --git a/Src/Gtk2/Makefile.am b/Src/Gtk2/Makefile.am
index 69aa1c3..626b034 100644
--- a/Src/Gtk2/Makefile.am
+++ b/Src/Gtk2/Makefile.am
@@ -82,6 +82,13 @@ libdashergtk_la_SOURCES = \
 		module_settings_window.cpp \
 		module_settings_window.h
 
+if USE_ATSPI
+libdashergtk_la_SOURCES += \
+		dasher_editor_external_atspi.cpp
+libdashergtk_la_CPPFLAGS = @ATSPI_CFLAGS@
+libdashergtk_la_LDFLAGS  = @ATSPI_LIBS@
+libdashergtk_la_LIBADD   = @ATSPI_LIBS@
+else
 if USE_CSPI
 libdashergtk_la_SOURCES += \
 		dasher_editor_external_cspi.cpp
@@ -92,5 +99,6 @@ else
 libdashergtk_la_SOURCES += \
 		dasher_editor_external_xtest.cpp
 endif
+endif
 
 AM_CXXFLAGS = -I$(srcdir)/../DasherCore -DPROGDATA=\"$(pkgdatadir)\" $(GTKBUILD_CFLAGS) 
diff --git a/Src/Gtk2/dasher_editor_external.h b/Src/Gtk2/dasher_editor_external.h
index 690dba6..719faf8 100644
--- a/Src/Gtk2/dasher_editor_external.h
+++ b/Src/Gtk2/dasher_editor_external.h
@@ -1,16 +1,13 @@
 #ifndef DASHER_EDITOR_EXTERNAL_H
 #define DASHER_EDITOR_EXTERNAL_H
 
-typedef struct _DasherEditor DasherEditor;
-struct _DasherEditor;
-typedef struct _GObject GObject;
-struct _GObject;
+#include "dasher_editor.h"
 
 void dasher_editor_external_finalize(GObject*);
 void dasher_editor_external_create_buffer(DasherEditor*); //  for dasher_editor_external_initialise, and calls focus bits
-void dasher_editor_external_output(DasherEditor *pSelf, const gchar *szText, int iOffset);
+void dasher_editor_external_output(DasherEditor *pSelf, const char *szText, int iOffset);
 void dasher_editor_external_delete(DasherEditor *pSelf, int iLength, int iOffset);
-const gchar * dasher_editor_external_get_context(DasherEditor *pSelf, int iOffset, int iLength);
-gint dasher_editor_external_get_offset(DasherEditor *pSelf);
+const char * dasher_editor_external_get_context(DasherEditor *pSelf, int iOffset, int iLength);
+int dasher_editor_external_get_offset(DasherEditor *pSelf);
 
 #endif
diff --git a/Src/Gtk2/dasher_editor_external_atspi.cpp b/Src/Gtk2/dasher_editor_external_atspi.cpp
new file mode 100644
index 0000000..fbdd15b
--- /dev/null
+++ b/Src/Gtk2/dasher_editor_external_atspi.cpp
@@ -0,0 +1,242 @@
+#include <X11/keysym.h>
+
+#include <atspi/atspi.h>
+
+#include "dasher_editor_external.h"
+#include "dasher_editor_private.h"
+
+typedef struct _DasherEditorExternalPrivate DasherEditorExternalPrivate;
+
+struct _DasherEditorExternalPrivate {
+  AtspiEventListener *pFocusListener;
+  AtspiEventListener *pCaretListener;
+  AtspiText *pAccessibleText;
+};
+
+void dasher_editor_external_handle_focus(DasherEditor *pSelf, const AtspiEvent *pEvent);
+void dasher_editor_external_handle_caret(DasherEditor *pSelf, const AtspiEvent *pEvent);
+
+void focus_listener(const AtspiEvent *pEvent, void *pUserData);
+void caret_listener(const AtspiEvent *pEvent, void *pUserData);
+
+bool
+initSPI() {
+#ifdef DEBUG_ATSPI
+  int ret = atspi_init();
+  printf("atspi_init() returned %d\n", ret);
+  return (ret <= 1);
+#else
+  return (atspi_init() <= 1);
+#endif
+}
+
+void
+dasher_editor_external_finalize(GObject *pSelf) {
+  DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+  DasherEditorExternalPrivate *p = pPrivate->pExtPrivate;
+
+#ifdef DEBUG_ATSPI
+  printf("dasher_editor_external_finalize()\n");
+#endif
+
+  atspi_event_listener_deregister(p->pFocusListener, "focus:", NULL);
+  atspi_event_listener_deregister(p->pCaretListener, "object:text-caret-moved", NULL);
+
+  g_object_unref(p->pFocusListener);
+  g_object_unref(p->pCaretListener);
+
+  delete p;
+}
+
+void
+dasher_editor_external_create_buffer(DasherEditor *pSelf) {
+  DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+  DasherEditorExternalPrivate *p;
+
+#ifdef DEBUG_ATSPI
+  printf("dasher_editor_external_create_buffer()\n");
+#endif
+
+  if(!initSPI()) {
+    g_message("Could not initialise SPI - accessibility options disabled");
+    return;
+  }
+  p = new DasherEditorExternalPrivate;
+  p->pFocusListener = atspi_event_listener_new(focus_listener, pSelf, NULL);
+  p->pCaretListener = atspi_event_listener_new(caret_listener, pSelf, NULL);
+  p->pAccessibleText = NULL;
+  pPrivate->pExtPrivate = p;
+
+#ifdef DEBUG_ATSPI
+  printf("dasher_editor_external_create_buffer: pPrivate=%p, pExtPrivate=%p\n", pPrivate, p);
+#endif
+
+  atspi_event_listener_register(p->pFocusListener, "focus:", NULL);
+  atspi_event_listener_register(p->pCaretListener, "object:text-caret-moved", NULL);
+}
+
+void
+dasher_editor_external_output(DasherEditor *pSelf, const char *szText, int iOffset /* unused */) {
+  if (!initSPI()) return;
+
+  atspi_generate_keyboard_event(0, szText, ATSPI_KEY_STRING, NULL);
+}
+
+void
+dasher_editor_external_delete(DasherEditor *pSelf, int iLength, int iOffset) {
+  if (!initSPI()) return;
+
+  atspi_generate_keyboard_event(XK_BackSpace, NULL, ATSPI_KEY_SYM, NULL);
+}
+
+const char *
+dasher_editor_external_get_context(DasherEditor *pSelf, int iOffset, int iLength) {
+  DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+
+#ifdef DEBUG_ATSPI
+  printf("dasher_editor_external_get_context(pPrivate=%p)\n", pPrivate);
+#endif
+
+  DASHER_ASSERT(pPrivate->pExtPrivate != NULL);
+  AtspiText *textobj = pPrivate->pExtPrivate->pAccessibleText;
+
+  if (textobj)
+    return atspi_text_get_text(textobj, iOffset, iOffset + iLength, NULL);
+  else
+    return "";
+}
+
+int
+dasher_editor_external_get_offset(DasherEditor *pSelf) {
+  DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+
+#ifdef DEBUG_ATSPI
+  printf("dasher_editor_external_get_offset()\n");
+#endif
+
+  DASHER_ASSERT(pPrivate->pExtPrivate != NULL);
+  AtspiText *textobj = pPrivate->pExtPrivate->pAccessibleText;
+  AtspiRange *range;
+  int ret;
+  
+  if (!textobj) {
+#ifdef DEBUG_ATSPI
+    printf("no textobj\n");
+#endif
+    return 0;
+  }
+
+  if (atspi_text_get_n_selections(textobj, NULL) == 0)
+    return atspi_text_get_caret_offset(textobj, NULL);
+
+  range = atspi_text_get_selection(textobj, 0, NULL);
+  ret = std::min(range->start_offset, range->end_offset);
+  g_free(range);
+
+  return ret;
+}
+
+void
+dasher_editor_external_handle_focus(DasherEditor *pSelf, const AtspiEvent *pEvent) {
+  DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+
+#ifdef DEBUG_ATSPI
+  printf("dasher_editor_external_handle_focus()\n");
+#endif
+
+  DASHER_ASSERT(pPrivate->pExtPrivate != NULL);
+  AtspiText *textobj = pPrivate->pExtPrivate->pAccessibleText;
+
+  if (textobj) {
+    g_object_unref(textobj);
+    textobj = NULL;
+  }
+
+  AtspiAccessible *acc = pEvent->source;
+  g_object_ref(acc);
+  
+#ifdef DEBUG_ATSPI
+  printf("%s from %s, %s, %s\n",
+    pEvent->type,
+    atspi_accessible_get_name(acc, NULL),
+    atspi_accessible_get_role_name(acc, NULL),
+    atspi_accessible_get_description(acc, NULL));
+#endif
+
+  textobj = atspi_accessible_get_text(acc);
+  pPrivate->pExtPrivate->pAccessibleText = textobj;
+  if (textobj) {
+    g_object_ref(textobj);
+
+    //ACL: in old code, external_buffer emitted signal, for which the editor_external had
+    // registered a callback, which then emitted the same/corresponding signal from the
+    // editor_external; so by combining buffer into editor, seems we don't need any of that
+    // and can just emit the signal from the editor directly. However, the callback also said:
+    //TODO: plumb signal back into control
+    // ...if that makes any sense?
+
+    g_signal_emit_by_name(G_OBJECT(pSelf), "buffer_changed", G_OBJECT(pSelf), NULL, NULL);
+  }
+
+  g_object_unref(acc);
+}
+
+void
+dasher_editor_external_handle_caret(DasherEditor *pSelf, const AtspiEvent *pEvent) {
+  DasherEditorPrivate *pPrivate = DASHER_EDITOR_GET_PRIVATE(pSelf);
+
+#ifdef DEBUG_ATSPI
+  printf("dasher_editor_external_handle_caret()\n");
+#endif
+
+  DASHER_ASSERT(pPrivate->pExtPrivate != NULL);
+  AtspiText *textobj = pPrivate->pExtPrivate->pAccessibleText;
+
+  if (textobj) {
+    g_object_unref(textobj);
+    textobj = NULL;
+  }
+
+  AtspiAccessible *acc = pEvent->source;
+  g_object_ref(acc);
+  
+#ifdef DEBUG_ATSPI
+  printf("%s from %s, %s, %s\n",
+    pEvent->type,
+    atspi_accessible_get_name(acc, NULL),
+    atspi_accessible_get_role_name(acc, NULL),
+    atspi_accessible_get_description(acc, NULL));
+#endif
+
+  textobj = atspi_accessible_get_text(acc);
+  pPrivate->pExtPrivate->pAccessibleText = textobj;
+  if (textobj) {
+    g_object_ref(textobj);
+
+    //ACL: in old code, external_buffer emitted signal, for which the editor_external had
+    // registered a callback, which then emitted the same/corresponding signal from the
+    // editor_external; so by combining buffer into editor, seems we don't need any of that
+    // and can just emit the signal from the editor directly. However, the callback also said:
+    //TODO: plumb signal back into control
+    // ...if that makes any sense?
+
+    g_signal_emit_by_name(G_OBJECT(pSelf), "context_changed", G_OBJECT(pSelf), NULL, NULL);
+  }
+#ifdef DEBUG_ATSPI
+  else {
+    printf("XXX Received text-caret-moved from source which doesn't implemenent AtspiText\n");
+  }
+#endif
+
+  g_object_unref(acc);
+}
+
+void
+focus_listener(const AtspiEvent *pEvent, void *pUserData) {
+  dasher_editor_external_handle_focus(DASHER_EDITOR(pUserData), pEvent);
+}
+
+void
+caret_listener(const AtspiEvent *pEvent, void *pUserData) {
+  dasher_editor_external_handle_caret(DASHER_EDITOR(pUserData), pEvent);
+}
diff --git a/Src/Gtk2/dasher_editor_external_cspi.cpp b/Src/Gtk2/dasher_editor_external_cspi.cpp
index 96554a4..6074c98 100644
--- a/Src/Gtk2/dasher_editor_external_cspi.cpp
+++ b/Src/Gtk2/dasher_editor_external_cspi.cpp
@@ -61,8 +61,7 @@ dasher_editor_external_create_buffer(DasherEditor *pSelf) {
     pPrivate->pExtPrivate = new DasherEditorExternalPrivate;
     pPrivate->pExtPrivate->pFocusListener = SPI_createAccessibleEventListener(focus_listener, pSelf);
     pPrivate->pExtPrivate->pCaretListener = SPI_createAccessibleEventListener(caret_listener, pSelf);
-    
-    // TODO: Need to deregister these on destruction
+    pPrivate->pExtPrivate->pAccessibleText = NULL;
     
     if(pPrivate->pExtPrivate->pFocusListener && pPrivate->pExtPrivate->pCaretListener) {
       SPI_registerGlobalEventListener(pPrivate->pExtPrivate->pFocusListener, "focus:");
@@ -71,8 +70,6 @@ dasher_editor_external_create_buffer(DasherEditor *pSelf) {
       g_message("Could not obtain an SPI listener");
     }
   }    
-
-  pPrivate->pExtPrivate->pAccessibleText = 0;
 }
 
 void
@@ -125,7 +122,7 @@ void dasher_editor_external_handle_focus(DasherEditor *pSelf, const AccessibleEv
   
   if(pPrivate->pExtPrivate->pAccessibleText) {
     AccessibleText_unref(pPrivate->pExtPrivate->pAccessibleText);
-    pPrivate->pExtPrivate->pAccessibleText = 0;
+    pPrivate->pExtPrivate->pAccessibleText = NULL;
   }
   
   Accessible *accessible = pEvent->source;
@@ -164,7 +161,7 @@ void dasher_editor_external_handle_caret(DasherEditor *pSelf, const AccessibleEv
   
   if(pPrivate->pExtPrivate->pAccessibleText) {
     AccessibleText_unref(pPrivate->pExtPrivate->pAccessibleText);
-    pPrivate->pExtPrivate->pAccessibleText = 0;
+    pPrivate->pExtPrivate->pAccessibleText = NULL;
   }
   
   Accessible *accessible = pEvent->source;
diff --git a/Src/Gtk2/dasher_editor_private.h b/Src/Gtk2/dasher_editor_private.h
index 1a1f424..9125940 100644
--- a/Src/Gtk2/dasher_editor_private.h
+++ b/Src/Gtk2/dasher_editor_private.h
@@ -1,6 +1,8 @@
 #ifndef DASHER_EDITOR_PRIVATE_H
 #define DASHER_EDITOR_PRIVATE_H
 
+#include <gtk/gtk.h>
+
 // Forward declarations
 typedef struct _DasherAppSettings DasherAppSettings;
 struct _DasherAppSettings;
diff --git a/Src/Makefile.am b/Src/Makefile.am
index 9d7df91..d4835f9 100644
--- a/Src/Makefile.am
+++ b/Src/Makefile.am
@@ -22,6 +22,9 @@ AM_CXXFLAGS = \
 	-DPROGDATA=\"$(pkgdatadir)\" \
 	-DSYSCONFDIR=\"$(sysconfdir)\" \
 	-DPACKAGE_LOCALE_DIR=\"$(datadir)/locale\"
+if USE_CSPI
+AM_CXXFLAGS += @CSPI_CFLAGS@
+endif
 
 dasher_LDADD = \
 	Common/libdashermisc.a \
diff --git a/Src/main.cc b/Src/main.cc
index 62e88d6..f53c0ae 100644
--- a/Src/main.cc
+++ b/Src/main.cc
@@ -18,7 +18,7 @@
 #endif
 
 // TODO: This shouldn't need to be here
-#if (defined GNOME_SPEECH || defined USE_CSPI)
+#if defined(GNOME_SPEECH) || defined(USE_CSPI)
 #include <libbonobo.h>
 #endif
 
@@ -201,7 +201,7 @@ int main(int argc, char *argv[]) {
   osso_context = osso_initialize("dasher", PACKAGE_VERSION, TRUE, NULL);
 #endif
 
-#if (defined GNOME_SPEECH || defined USE_CSPI)
+#if defined(GNOME_SPEECH) || defined(USE_CSPI)
   if(!bonobo_is_initialized()) {
     if(!bonobo_init(&argc, argv)) {
       g_error("Can't initialize Bonobo...\n");
diff --git a/configure.ac b/configure.ac
index 56b8c87..59d6dc2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -98,7 +98,10 @@ AC_ARG_WITH([gpe],
 
 AC_ARG_ENABLE([a11y],
 	[AS_HELP_STRING([--enable-a11y],
-		[use cspi rather than Xtst for direct entry mode if found])])
+		[with Gtk2 use cspi rather than Xtst for direct entry mode if found (default is YES)])])
+AC_ARG_ENABLE([atspi],
+	[AS_HELP_STRING([--enable-atspi],
+		[with Gtk3 use atspi rather than Xtst for direct entry mode if found (default is YES)])])
 
 AC_ARG_ENABLE([japanese],
 	 AS_HELP_STRING([--enable-japanese],[build with support for Japanese Kanji entry (experimental -- default is NO)]),
@@ -323,10 +326,15 @@ AC_CHECK_LIB(expat, XML_Parse,,[
 	fi
 ])
 
+PKG_CHECK_MODULES([ATSPI],
+	[atspi-2],
+	[have_libatspi=yes],
+	[have_libatspi=no])
+
 PKG_CHECK_MODULES([CSPI],
 	[bonobo-activation-2.0 libbonobo-2.0 ORBit-2.0 cspi-1.0 atk],
- 	[have_libcspi=yes],
- 	[have_libcspi=no])
+	[have_libcspi=yes],
+	[have_libcspi=no])
 
 AS_IF(	[test x$no_x = xyes],
 	[AC_MSG_WARN([X development libraries not found])],
@@ -337,7 +345,11 @@ AC_CHECK_LIB([Xtst], [XTestFakeKeyEvent],
 	[have_libxtst=no],
 	[$X_LIBS])
 
-AS_IF(	[test $have_libcspi = yes -a x$enable_a11y != xno],
+AS_IF(	[test $have_libatspi = yes -a x$enable_atspi != xno],
+		[connect_using=libatspi],
+	[test $have_libatspi = no -a x$enable_atspi = xyes],
+		[AC_MSG_ERROR([atspi 2 requested but not found])],
+	[test $have_libcspi = yes -a x$enable_a11y != xno],
 		[connect_using=libcspi
 		 AC_DEFINE([USE_CSPI], 1, [Use the libcspi for direct mode])],
 	[test $have_libcspi = no -a x$enable_a11y = xyes],
@@ -347,7 +359,8 @@ AS_IF(	[test $have_libcspi = yes -a x$enable_a11y != xno],
 		 X_LIBS="$X_LIBS -lXtst"],
 	[AC_MSG_ERROR([No method to send characters into another application found])])
 
-AM_CONDITIONAL(USE_CSPI, test $connect_using = libcspi)
+AM_CONDITIONAL(USE_CSPI,  test $connect_using = libcspi)
+AM_CONDITIONAL(USE_ATSPI, test $connect_using = libatspi)
 
 if test x"$WITHJAPANESE" = xtrue; then
 	AC_DEFINE([JAPANESE], 1, [Japanese support enabled])



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]