+
+void
+check_property_change (AccessibleEvent *event, void *user_data)
+{
+ AccessibleSelection *selection = Accessible_getSelection (event->source);
+ int n_selections;
+ int i;
+ char *s;
+ if (selection)
+ {
+ n_selections = (int) AccessibleSelection_getNSelectedChildren (selection);
+ s = Accessible_getName (event->source);
+ fprintf (stderr, "(Property) %s event from %s, %d selected children\n",
+ event->type, s, n_selections);
+ SPI_freeString (s);
+ /* for now, speak entire selection set */
+ for (i=0; i<n_selections; ++i)
+ {
+ Accessible *obj = AccessibleSelection_getSelectedChild (selection, (long) i);
+ g_return_if_fail (obj);
+ s = Accessible_getName (obj);
+ fprintf (stderr, "Child %d, name=%s\n", i, s);
+ SPI_freeString (s);
+ report_focussed_accessible (obj, i==0);
+ }
+ }
+}
+
+static void
+simple_at_exit ()
+{
+ SPI_deregisterGlobalEventListenerAll (focus_listener);
+ AccessibleEventListener_unref (focus_listener);
+
+ SPI_deregisterGlobalEventListenerAll (property_listener);
+ AccessibleEventListener_unref (property_listener);
+
+ SPI_deregisterGlobalEventListenerAll (generic_listener);
+ AccessibleEventListener_unref (generic_listener);
+
+ SPI_deregisterGlobalEventListenerAll (button_listener);
+ AccessibleEventListener_unref (button_listener);
+
+ SPI_deregisterAccessibleKeystrokeListener (command_key_listener, SPI_KEYMASK_ALT);
+ AccessibleKeystrokeListener_unref (command_key_listener);
+
+ SPI_deregisterAccessibleKeystrokeListener (ordinary_key_listener, SPI_KEYMASK_UNMODIFIED);
+ SPI_deregisterAccessibleKeystrokeListener (ordinary_key_listener, SPI_KEYMASK_SHIFT);
+ AccessibleKeystrokeListener_unref (ordinary_key_listener);
+
+ SPI_freeAccessibleKeySet (spacebar_key_set);
+
+ SPI_event_quit ();
+}
+
+static SPIBoolean
+is_command_key (AccessibleKeystroke *key)
+{
+ switch (key->keyID)
+ {
+ case 'Q':
+ case 'q':
+ simple_at_exit();
+ return TRUE; /* not reached */
+ case 'M':
+ case 'm':
+ use_magnifier = ! use_magnifier;
+ return TRUE;
+ case 'F':
+ case 'f':
+ use_festival = ! use_festival;
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static SPIBoolean
+report_command_key_event (AccessibleKeystroke *key, void *user_data)
+{
+ fprintf (stderr, "Command KeyEvent %s%c (keycode %d)\n",
+ (key->modifiers & SPI_KEYMASK_ALT)?"Alt-":"",
+ ((key->modifiers & SPI_KEYMASK_SHIFT)^(key->modifiers & SPI_KEYMASK_SHIFTLOCK))?
+ (char) toupper((int) key->keyID) : (char) tolower((int) key->keyID),
+ (int) key->keycode);
+ return is_command_key (key);
+}
+
+
+static SPIBoolean
+report_ordinary_key_event (AccessibleKeystroke *key, void *user_data)
+{
+ fprintf (stderr, "Received key event:\tsym %ld\n\tmods %x\n\tcode %d\n\ttime %ld\n",
+ (long) key->keyID,
+ (unsigned int) key->modifiers,
+ (int) key->keycode,
+ (long int) key->timestamp);
+ return FALSE;
+}
+
+static int
+_festival_init ()
+{
+ int fd;
+ struct sockaddr_in name;
+ int tries = 2;
+
+ name.sin_family = AF_INET;
+ name.sin_port = htons (1314);
+ name.sin_addr.s_addr = htonl(INADDR_ANY);
+ fd = socket (PF_INET, SOCK_STREAM, 0);
+
+ while (connect(fd, (struct sockaddr *) &name, sizeof (name)) < 0) {
+ if (!tries--) {
+ perror ("connect");
+ return -1;
+ }
+ }
+
+ _festival_write ("(audio_mode'async)\n", fd);
+ _festival_write ("(Parameter.set 'Duration_Model 'Tree_ZScore)\n", fd);
+ _festival_write ("(Parameter.set 'Duration_Stretch 0.75)\n", fd);
+ return fd;
+}
+
+static void
+_festival_say (const char *text, const char *voice, SPIBoolean shutup)
+{
+ static int fd = 0;
+ gchar *quoted;
+ gchar *p;
+ gchar prefix[50];
+ static gchar voice_spec[32];
+
+ if (!fd)
+ {
+ fd = _festival_init ();
+ }
+
+ fprintf (stderr, "saying text: %s\n", text);
+
+ quoted = g_malloc(64+strlen(text)*2);
+
+ sprintf (prefix, "(SayText \"");
+
+ strncpy(quoted, prefix, 10);
+ p = quoted+strlen(prefix);
+ while (*text) {
+ if ( *text == '\\' || *text == '"' )
+ *p = '\\';
+ *p++ = *text++;
+ }
+ *p++ = '"';
+ *p++ = ')';
+ *p++ = '\n';
+ *p = 0;
+
+ if (shutup) _festival_write ("(audio_mode'shutup)\n", fd);
+ if (voice && (strncmp (voice, (char *) (voice_spec+1), strlen(voice))))
+ {
+ snprintf (voice_spec, 32, "(%s)\n", voice);
+ _festival_write (voice_spec, fd);
+ _festival_write ("(Parameter.set 'Duration_Model 'Tree_ZScore)\n", fd);
+ _festival_write ("(Parameter.set 'Duration_Stretch 0.75)\n", fd);
+ }
+
+ _festival_write (quoted, fd);
+
+ g_free(quoted);
+}
+
+static void
+_festival_write (const gchar *command_string, int fd)
+{
+ fprintf(stderr, command_string);
+ if (fd < 0) {
+ perror("socket");
+ return;
+ }
+ write(fd, command_string, strlen(command_string));
+}
+