f387e5af22438714cf196ad284b62893c1752fdc
[platform/core/uifw/at-spi2-atk.git] / atk-bridge / bridge.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.,
6  * Copyright 2001, 2002, 2003 Ximian, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include "config.h"
25
26 #include <X11/Xlib.h>
27 #include <X11/Xatom.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <libbonobo.h>
34 #include <orbit/orbit.h>
35 #include <atk/atk.h>
36 #include <atk/atkobject.h>
37 #include <atk/atknoopobject.h>
38 #include <libspi/Accessibility.h>
39 #include <libspi/spi-private.h>
40 #include "remoteobject.h"
41 #include "accessible.h"
42 #include "application.h"
43 #include <bonobo-activation/bonobo-activation-register.h>
44
45 #undef SPI_BRIDGE_DEBUG
46
47 #define DBG(a,b) if(_dbg>=(a))b
48
49 int _dbg = 0;
50 static CORBA_Environment ev;
51 static Accessibility_Registry registry = CORBA_OBJECT_NIL;
52 static Accessibility_DeviceEventController device_event_controller = CORBA_OBJECT_NIL;
53 static SpiApplication *this_app = NULL;
54 static gboolean registry_died = FALSE;
55 static gboolean atk_listeners_registered = FALSE;
56 static gint toplevels = 0;
57 static gboolean exiting = FALSE;
58 static AtkMisc *misc = NULL;
59
60 static guint atk_signal_text_changed;
61 static guint atk_signal_children_changed;
62 static guint atk_signal_active_descendant_changed;
63 static guint atk_signal_text_selection_changed;
64
65 /* NOT YET USED
66    static guint atk_signal_row_reordered;
67    static guint atk_signal_row_inserted;
68    static guint atk_signal_row_deleted;
69    static guint atk_signal_column_reordered;
70    static guint atk_signal_column_inserted;
71    static guint atk_signal_column_deleted;
72 */
73
74 static guint atk_signal_link_selected;
75 static guint atk_signal_bounds_changed;
76
77 static Accessibility_Registry spi_atk_bridge_get_registry (void);
78 static gboolean spi_atk_bridge_do_registration         (void);
79 static void     spi_atk_bridge_toplevel_added          (AtkObject             *object,
80                                                         guint                 index,
81                                                         AtkObject             *child);
82 static void     spi_atk_bridge_toplevel_removed        (AtkObject             *object,
83                                                         guint                 index,
84                                                         AtkObject             *child);
85
86 static void     spi_atk_bridge_exit_func               (void);
87 static void     spi_atk_register_event_listeners       (void);
88 static void     spi_atk_bridge_focus_tracker           (AtkObject             *object);
89 static gchar   *spi_atk_bridge_get_registry_ior        (void);
90 static void     spi_atk_bridge_register_application    (Accessibility_Registry registry);
91 static gboolean spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
92                                                         guint                  n_param_values,
93                                                         const GValue          *param_values,
94                                                         gpointer               data);
95
96 static void     spi_atk_bridge_init_nil                (CORBA_any *any, 
97                                                         AtkObject *obj);
98 static void     spi_atk_bridge_init_object             (CORBA_any *any, 
99                                                         AtkObject *obj,
100                                                         CORBA_Object *c_obj);
101 static void     spi_atk_bridge_init_string             (CORBA_any *any, 
102                                                         AtkObject *obj, 
103                                                         gchar **string);
104 static void     spi_atk_bridge_init_rect               (CORBA_any *any, 
105                                                         AtkObject *obj, 
106                                                         AtkRectangle *rect);
107
108 static gboolean
109 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
110                                 guint n_param_values,
111                                 const GValue *param_values,
112                                 gpointer data);
113 static gboolean
114 spi_atk_bridge_document_event_listener (GSignalInvocationHint *signal_hint,
115                                 guint n_param_values,
116                                 const GValue *param_values,
117                                 gpointer data);
118 static gboolean
119 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
120                                      guint n_param_values,
121                                      const GValue *param_values,
122                                      gpointer data);
123 static gboolean spi_atk_bridge_signal_listener         (GSignalInvocationHint *signal_hint,
124                                                         guint                  n_param_values,
125                                                         const GValue          *param_values,
126                                                         gpointer               data);
127 static gint     spi_atk_bridge_key_listener            (AtkKeyEventStruct     *event,
128                                                         gpointer               data);
129 static void     spi_atk_tidy_windows                   (void);
130 static void     deregister_application                 (BonoboObject          *app);
131 static void     reinit_register_vars                   (void);
132
133 /* For automatic libgnome init */
134 extern void gnome_accessibility_module_init     (void);
135 extern void gnome_accessibility_module_shutdown (void);
136
137 static int     atk_bridge_initialized = FALSE;
138 static pid_t   atk_bridge_pid = 0;
139 static guint   atk_bridge_focus_tracker_id = 0;
140 static guint   atk_bridge_key_event_listener_id = 0;
141 static GArray *listener_ids = NULL;
142
143 /*
144  *   These exported symbols are hooked by gnome-program
145  * to provide automatic module initialization and shutdown.
146  */
147 extern void gnome_accessibility_module_init     (void);
148 extern void gnome_accessibility_module_shutdown (void);
149
150 static void
151 spi_atk_bridge_init_event_type_consts ()
152 {
153   static gboolean done = FALSE;
154
155   if (done)
156     return;
157
158   atk_signal_children_changed = g_signal_lookup ("children_changed", 
159                                               ATK_TYPE_OBJECT);
160   atk_signal_text_changed = g_signal_lookup ("text_changed", 
161                                              ATK_TYPE_TEXT);
162   atk_signal_bounds_changed = g_signal_lookup ("bounds_changed", 
163                                               ATK_TYPE_COMPONENT);
164   atk_signal_active_descendant_changed = 
165          g_signal_lookup ("active_descendant_changed", 
166                           ATK_TYPE_OBJECT); 
167   atk_signal_link_selected = g_signal_lookup ("link_selected", 
168                                               ATK_TYPE_HYPERTEXT);
169   atk_signal_text_selection_changed = g_signal_lookup ("text_selection_changed", 
170                                               ATK_TYPE_TEXT);
171   done = TRUE;
172 }
173
174 static int
175 atk_bridge_init (gint *argc, gchar **argv[])
176 {
177   const char *debug_env_string = g_getenv ("AT_SPI_DEBUG");
178   gchar *fname;
179   gboolean success = FALSE;
180
181   if (atk_bridge_initialized)
182     {
183       return 0;
184     }
185   atk_bridge_initialized = TRUE;
186   atk_bridge_pid = getpid ();
187
188   misc = atk_misc_get_instance();
189
190   if (g_getenv ("ATK_BRIDGE_REDIRECT_LOG"))
191   {
192       fname = g_strconcat ("/tmp/", g_get_prgname (), ".at-spi-log", NULL);
193       /* make sure we're not being redirected - security issue */
194       if (!g_file_test (fname, G_FILE_TEST_IS_SYMLINK))
195           freopen (fname, "w", stderr);
196       g_free (fname);
197   }
198
199   if (debug_env_string) 
200       _dbg = (int) g_ascii_strtod (debug_env_string, NULL);
201
202   if (!bonobo_init (argc, argv ? *argv : NULL))
203     {
204       g_error ("Could not initialize Bonobo");
205     }
206
207   /* Create the accessible application server object */
208   this_app = spi_application_new (atk_get_root ());
209   /*
210    * We only want to enable the bridge for top level
211    * applications, we detect bonobo components by seeing
212    * if they were activated with the intention of extracting
213    * an impl. by IID - very solid.
214    */
215   if (bonobo_activation_iid_get ())
216     {
217       DBG (1, g_message ("Found Bonobo component\n"));
218       g_signal_connect (atk_get_root (), 
219                         "children-changed::add",
220                         (GCallback) spi_atk_bridge_toplevel_added, 
221                         NULL);
222       g_signal_connect (atk_get_root (), 
223                         "children-changed::remove",
224                         (GCallback) spi_atk_bridge_toplevel_removed, 
225                         NULL);
226     }
227   else
228     {
229       success = spi_atk_bridge_do_registration ();
230     }
231   if (success) 
232     {
233       spi_atk_register_event_listeners ();
234       spi_atk_bridge_init_event_type_consts ();
235     }
236   else
237     {
238       atk_bridge_initialized = FALSE;
239     }
240   return 0;
241 }
242
243
244 static gboolean
245 spi_atk_bridge_do_registration (void)
246 {
247   CORBA_Environment ev;
248
249   CORBA_exception_init(&ev);
250
251   if (spi_atk_bridge_get_registry () == CORBA_OBJECT_NIL)
252     {
253       g_warning ("Could not locate registry");
254       return FALSE;
255     }
256
257   bonobo_activate ();
258
259   /* Create the accessible application server object */
260   if (this_app == NULL)
261     this_app = spi_application_new (atk_get_root ());
262
263   DBG (1, g_message ("About to register application\n"));
264
265   spi_atk_bridge_register_application (spi_atk_bridge_get_registry ());
266   
267   g_atexit (spi_atk_bridge_exit_func);
268
269   DBG (1, g_message ("Application registered & listening\n"));
270   return TRUE;
271 }
272
273 static void
274 spi_atk_bridge_toplevel_added (AtkObject *object,
275                                guint     index,
276                                AtkObject *child)
277 {
278   if (toplevels == 0)
279     {
280       spi_atk_bridge_do_registration ();
281     }
282   toplevels++;
283 }
284
285 static void
286 spi_atk_bridge_toplevel_removed (AtkObject *object,
287                                  guint     index,
288                                  AtkObject *child)
289 {
290   BonoboObject *app = (BonoboObject *) this_app;
291
292   toplevels--;
293   if (toplevels == 0)
294     {
295       deregister_application (app);
296       reinit_register_vars ();
297     }
298   if (toplevels < 0)
299     {
300       g_warning ("More toplevels removed than added\n");
301       toplevels = 0;
302     }
303 }
304
305 static void
306 spi_atk_bridge_register_application (Accessibility_Registry registry)
307 {
308   atk_misc_threads_leave (misc);
309   Accessibility_Registry_registerApplication (spi_atk_bridge_get_registry (),
310                                               BONOBO_OBJREF (this_app),
311                                               &ev);
312   atk_misc_threads_enter (misc);
313   if (ev._major != CORBA_NO_EXCEPTION)
314     CORBA_exception_free (&ev);
315 }
316
317 /* 
318  * Returns a 'canonicalized' value for DISPLAY,
319  * with the screen number stripped off if present.
320  */
321 static const gchar*
322 spi_display_name (void)
323 {
324     static const char *canonical_display_name = NULL;
325     if (!canonical_display_name)
326     {
327         const gchar *display_env = g_getenv ("AT_SPI_DISPLAY");
328         if (!display_env)
329         {
330             display_env = g_getenv ("DISPLAY");
331             if (!display_env || !display_env[0]) 
332                 canonical_display_name = ":0";
333             else
334             {
335                 gchar *display_p, *screen_p;
336                 canonical_display_name = g_strdup (display_env);
337                 display_p = strrchr (canonical_display_name, ':');
338                 screen_p = strrchr (canonical_display_name, '.');
339                 if (screen_p && display_p && (screen_p > display_p))
340                 {
341                     *screen_p = '\0';
342                 }
343             }
344         }
345         else
346         {
347             canonical_display_name = display_env;
348         }
349     }
350     return canonical_display_name;
351 }
352
353 static     Display *bridge_display = NULL;
354
355 static gchar *
356 spi_atk_bridge_get_registry_ior (void)
357 {
358      
359      Atom AT_SPI_IOR;
360      Atom actual_type;
361      int actual_format;
362      unsigned char *data = NULL;  
363      unsigned long nitems;
364      unsigned long leftover;
365      if (!bridge_display) 
366        bridge_display = XOpenDisplay (spi_display_name ());
367
368      AT_SPI_IOR = XInternAtom (bridge_display, "AT_SPI_IOR", False); 
369      XGetWindowProperty(bridge_display, 
370                         XDefaultRootWindow (bridge_display),
371                         AT_SPI_IOR, 0L, 
372                         (long)BUFSIZ, False, 
373                         (Atom) 31, &actual_type, &actual_format,
374                         &nitems, &leftover, &data);
375      if (data == NULL)
376           g_warning (_("AT_SPI_REGISTRY was not started at session startup."));
377      
378      return (gchar *) data;
379      
380 }
381
382
383 static Accessibility_Registry
384 spi_atk_bridge_get_registry (void)
385 {
386   CORBA_Environment ev;
387   char *ior =  NULL;
388
389   if (registry_died || (registry == CORBA_OBJECT_NIL)) {
390           CORBA_exception_init (&ev);
391           if (registry_died) 
392             {
393               if (exiting)
394                 return CORBA_OBJECT_NIL;
395               else
396                 DBG (1, g_warning ("registry died! restarting..."));
397             }
398
399           /* XXX: This presumes that the registry has successfully restarted itself already...*/
400           ior = (char *) spi_atk_bridge_get_registry_ior ();
401
402           if (ior != NULL)
403                registry = CORBA_ORB_string_to_object (bonobo_activation_orb_get (), 
404                                                       ior, &ev);
405           else {
406                g_warning ("IOR not set.");  
407                registry = CORBA_OBJECT_NIL;
408           }
409           
410           if (ev._major != CORBA_NO_EXCEPTION)
411           {
412                   g_error ("Accessibility app error: exception during "
413                            "registry activation from id: %s\n",
414                            CORBA_exception_id (&ev));
415                   CORBA_exception_free (&ev);
416           }
417           
418           if (registry_died && registry) {
419                   registry_died = FALSE;
420                   spi_atk_bridge_register_application (registry);
421           }
422   }
423   return registry;
424 }
425
426 static Accessibility_DeviceEventController
427 spi_atk_bridget_get_dec (void)
428 {
429   CORBA_Environment ev;
430
431   if (device_event_controller != CORBA_OBJECT_NIL)
432     {
433       if (ORBit_small_get_connection_status (device_event_controller)
434           == ORBIT_CONNECTION_CONNECTED)
435         return device_event_controller;
436     }
437
438   CORBA_exception_init (&ev);
439
440   device_event_controller =
441     Accessibility_Registry_getDeviceEventController (
442             spi_atk_bridge_get_registry (), &ev);
443
444   if (BONOBO_EX (&ev))
445     {
446       g_warning (_("failure: no device event controller found.\n"));
447       registry_died = TRUE;
448       device_event_controller = CORBA_OBJECT_NIL;
449     }
450
451   return device_event_controller;
452 }
453
454 int
455 gtk_module_init (gint *argc, gchar **argv[])
456 {
457         return atk_bridge_init (argc, argv);
458 }
459
460 static void
461 add_signal_listener (const char *signal_name)
462 {
463   guint id;
464
465   id = atk_add_global_event_listener (
466     spi_atk_bridge_signal_listener, signal_name);
467
468   g_array_append_val (listener_ids, id);
469 }
470
471 static void
472 spi_atk_register_event_listeners (void)
473 {
474   /*
475    * kludge to make sure the Atk interface types are registered, otherwise
476    * the AtkText signal handlers below won't get registered
477    */
478   guint      id;
479   GObject   *ao = g_object_new (ATK_TYPE_OBJECT, NULL);
480   AtkObject *bo = atk_no_op_object_new (ao);
481
482
483   if (atk_listeners_registered) return;
484
485   atk_listeners_registered = TRUE;
486
487   /* Register for focus event notifications, and register app with central registry  */
488
489   listener_ids = g_array_sized_new (FALSE, TRUE, sizeof (guint), 16);
490
491   atk_bridge_focus_tracker_id = atk_add_focus_tracker (spi_atk_bridge_focus_tracker);
492
493   id = atk_add_global_event_listener (spi_atk_bridge_property_event_listener,
494                                       "Gtk:AtkObject:property-change");
495   g_array_append_val (listener_ids, id);
496   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
497                                       "window:create");
498   g_array_append_val (listener_ids, id);
499   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
500                                       "window:destroy");
501   g_array_append_val (listener_ids, id);
502   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
503                                       "window:minimize");
504   g_array_append_val (listener_ids, id);
505   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
506                                       "window:maximize");
507   g_array_append_val (listener_ids, id);
508   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
509                                       "window:restore");
510   g_array_append_val (listener_ids, id);
511   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
512                                       "window:activate");
513   g_array_append_val (listener_ids, id);
514   id = atk_add_global_event_listener (spi_atk_bridge_window_event_listener,
515                                       "window:deactivate");
516   g_array_append_val (listener_ids, id);
517   id = atk_add_global_event_listener (spi_atk_bridge_document_event_listener,
518                                       "Gtk:AtkDocument:load-complete");
519   g_array_append_val (listener_ids, id);
520   id = atk_add_global_event_listener (spi_atk_bridge_document_event_listener,
521                                       "Gtk:AtkDocument:reload");
522   g_array_append_val (listener_ids, id);
523   id = atk_add_global_event_listener (spi_atk_bridge_document_event_listener,
524                                       "Gtk:AtkDocument:load-stopped");
525   g_array_append_val (listener_ids, id);
526   id = atk_add_global_event_listener (spi_atk_bridge_state_event_listener,
527                                       "Gtk:AtkObject:state-change");
528   g_array_append_val (listener_ids, id);
529
530   add_signal_listener ("Gtk:AtkObject:children-changed");
531   add_signal_listener ("Gtk:AtkObject:visible-data-changed");
532   add_signal_listener ("Gtk:AtkObject:active-descendant-changed");
533   add_signal_listener ("Gtk:AtkComponent:bounds-changed");
534   add_signal_listener ("Gtk:AtkSelection:selection-changed");
535   add_signal_listener ("Gtk:AtkText:text-selection-changed");
536   add_signal_listener ("Gtk:AtkText:text-changed");
537   add_signal_listener ("Gtk:AtkText:text-caret-moved");
538   add_signal_listener ("Gtk:AtkTable:row-inserted");
539   add_signal_listener ("Gtk:AtkTable:row-reordered");
540   add_signal_listener ("Gtk:AtkTable:row-deleted");
541   add_signal_listener ("Gtk:AtkTable:column-inserted");
542   add_signal_listener ("Gtk:AtkTable:column-reordered");
543   add_signal_listener ("Gtk:AtkTable:column-deleted");
544   add_signal_listener ("Gtk:AtkTable:model-changed");
545   add_signal_listener ("Gtk:AtkHypertext:link-selected");
546 /*
547  * May add the following listeners to implement preemptive key listening for GTK+
548  *
549  * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-press-event");
550  * atk_add_global_event_listener (spi_atk_bridge_widgetkey_listener, "Gtk:GtkWidget:key-release-event");
551  */
552   atk_bridge_key_event_listener_id = atk_add_key_event_listener (
553     spi_atk_bridge_key_listener, NULL);
554   
555   g_object_unref (G_OBJECT (bo));
556   g_object_unref (ao);
557 }
558
559 static void
560 deregister_application (BonoboObject *app)
561 {
562   Accessibility_Registry registry = spi_atk_bridge_get_registry ();
563   atk_misc_threads_leave (misc);
564   Accessibility_Registry_deregisterApplication (registry, BONOBO_OBJREF (app), &ev);
565   atk_misc_threads_enter (misc);
566
567   device_event_controller = bonobo_object_release_unref (device_event_controller, &ev);
568   registry = bonobo_object_release_unref (registry, &ev);
569   
570   app = bonobo_object_unref (app);
571 }
572
573 static void
574 spi_atk_bridge_exit_func (void)
575 {
576   BonoboObject *app = (BonoboObject *) this_app;
577
578   DBG (1, g_message ("exiting bridge\n"));
579
580   if (!app)
581     {
582       return;
583     }
584   this_app = NULL;
585   if (atk_bridge_pid != getpid ())
586     {
587       _exit (0);
588     }
589
590   exiting = TRUE;
591   /*
592    * Check whether we still have windows which have not been deleted.
593    */
594   spi_atk_tidy_windows ();
595   /*
596    *  FIXME: this may be incorrect for apps that do their own bonobo
597    *  shutdown, until we can explicitly shutdown to get the ordering
598    *  right.
599    */
600   if (!bonobo_is_initialized ())
601     {
602       DBG (1, g_warning ("Re-initializing bonobo\n"));
603       g_assert (bonobo_init (0, NULL));
604       g_assert (bonobo_activate ());
605     }
606   
607   if (!registry_died)
608     deregister_application (app);
609
610   DBG (1, g_message ("bridge exit func complete.\n"));
611
612   if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
613     {
614       g_assert (!bonobo_debug_shutdown ());
615     }
616   if (bridge_display)
617     XCloseDisplay (bridge_display);
618 }
619
620 void
621 gnome_accessibility_module_init (void)
622 {
623   atk_bridge_init (NULL, NULL);
624
625   if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
626     {
627         g_print("Atk Accessibility bridge initialized\n");
628     }
629 }
630
631 void
632 gnome_accessibility_module_shutdown (void)
633 {
634   BonoboObject *app = (BonoboObject *) this_app;
635   int     i;
636   GArray *ids = listener_ids;
637   
638   if (!atk_bridge_initialized)
639     {
640       return;
641     }
642   atk_bridge_initialized = FALSE;
643   this_app = NULL;
644
645   if (g_getenv ("AT_BRIDGE_SHUTDOWN"))
646     {
647         g_print("Atk Accessibility bridge shutdown\n");
648     }
649
650   listener_ids = NULL;
651   if (atk_bridge_focus_tracker_id)
652         atk_remove_focus_tracker (atk_bridge_focus_tracker_id);
653   
654   for (i = 0; ids && i < ids->len; i++)
655   {
656           atk_remove_global_event_listener (g_array_index (ids, guint, i));
657   }
658   
659   if (atk_bridge_key_event_listener_id)
660           atk_remove_key_event_listener (atk_bridge_key_event_listener_id);
661
662   deregister_application (app);
663 }
664
665 static void
666 spi_atk_bridge_focus_tracker (AtkObject *object)
667 {
668   SpiAccessible *source;
669   Accessibility_Event e;
670   
671   source = spi_accessible_new (object);
672   
673   CORBA_exception_init (&ev);
674   
675   e.type = "focus:";
676   e.source = BONOBO_OBJREF (source);
677   e.detail1 = 0;
678   e.detail2 = 0;
679   spi_atk_bridge_init_nil (&e.any_data, object);
680   if (BONOBO_EX (&ev))
681       registry_died = TRUE;
682   else {
683       atk_misc_threads_leave (misc);   
684       Accessibility_Registry_notifyEvent (spi_atk_bridge_get_registry (), 
685                                           &e, &ev);
686       atk_misc_threads_enter (misc);
687   }
688   if (BONOBO_EX (&ev))
689     registry_died = TRUE;
690
691   bonobo_object_unref (source);
692   
693   CORBA_exception_free (&ev);
694 }
695
696 static void
697 spi_atk_emit_eventv (const GObject         *gobject,
698                      long                   detail1,
699                      long                   detail2,
700                      CORBA_any             *any,
701                      const char            *format, ...)
702 {
703   va_list             args;
704   Accessibility_Event e;
705   AtkObject          *aobject;
706   SpiAccessible      *source = NULL;
707   Accessibility_Registry registry;
708   const gchar *name;
709 #ifdef SPI_BRIDGE_DEBUG
710   CORBA_string s = NULL;
711 #endif
712   
713   va_start (args, format);
714   
715   if (ATK_IS_IMPLEMENTOR (gobject))
716     {
717       aobject = atk_implementor_ref_accessible (ATK_IMPLEMENTOR (gobject));
718       source  = spi_accessible_new (aobject);
719       g_object_unref (G_OBJECT (aobject));
720     }
721   else if (ATK_IS_OBJECT (gobject))
722     {
723       aobject = ATK_OBJECT (gobject);
724       if (SPI_IS_REMOTE_OBJECT (aobject))
725          e.source = spi_remote_object_get_accessible (SPI_REMOTE_OBJECT (aobject));
726       else
727          source  = spi_accessible_new (aobject);
728     }
729   else
730     {
731       aobject = NULL;
732       DBG (0, g_warning ("received property-change event from non-AtkImplementor"));
733       va_end (args);
734       return;
735     }
736   name = atk_object_get_name (aobject);
737   e.type = g_strdup_vprintf (format, args);
738   if (source) e.source = BONOBO_OBJREF (source);
739   e.detail1 = detail1;
740   e.detail2 = detail2;
741   if (any) e.any_data = *any;
742   else spi_atk_bridge_init_nil (&e.any_data, aobject);
743
744 #ifdef SPI_BRIDGE_DEBUG
745   if (e.source != CORBA_OBJECT_NIL)
746       s = Accessibility_Accessible__get_name (e.source, &ev);
747   g_message ("Emitting event '%s' (%lu, %lu) on %s",
748              e.type, e.detail1, e.detail2, s);
749   CORBA_free (s);
750 #endif
751   CORBA_exception_init (&ev);
752   registry = spi_atk_bridge_get_registry ();
753   if (!registry_died)
754   {
755     atk_misc_threads_leave (misc); 
756     Accessibility_Registry_notifyEvent (registry, 
757                                         &e, &ev);
758     atk_misc_threads_enter (misc);
759 #ifdef SPI_BRIDGE_DEBUG
760     if (ev._major != CORBA_NO_EXCEPTION)
761         g_message ("error emitting event %s, (%d) %s",
762                    e.type,
763                    ev._major,
764                    CORBA_exception_id(&ev));
765 #endif        
766     if (BONOBO_EX (&ev)) registry_died = TRUE;
767   }
768
769   if (source)
770       bonobo_object_unref (BONOBO_OBJECT (source));
771   else
772       Bonobo_Unknown_unref (e.source, &ev);
773   
774   CORBA_exception_free (&ev);
775   
776   g_free (e.type);
777
778   if (!any && e.any_data._release) CORBA_free (e.any_data._value);
779
780   if (any && any->_release) CORBA_free (any->_value);
781   
782   va_end (args);
783
784 }
785
786 static gboolean
787 spi_atk_bridge_property_event_listener (GSignalInvocationHint *signal_hint,
788                                         guint n_param_values,
789                                         const GValue *param_values,
790                                         gpointer data)
791 {
792   AtkPropertyValues *values;
793   GObject *gobject;
794   const gchar *prop_name;
795   CORBA_any any;
796   const gchar *sp = NULL;
797   AtkObject *ao;
798   SpiAccessible *s_ao = NULL;
799   CORBA_Object c_obj;
800   gint i;
801   const gchar *name = NULL;
802
803 #ifdef SPI_BRIDGE_DEBUG
804   GSignalQuery signal_query;
805   const gchar *signame;
806   const gchar *s, *s2;
807   
808   g_signal_query (signal_hint->signal_id, &signal_query);
809   signame = signal_query.signal_name;
810
811   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
812   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
813   values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
814   DBG (2, g_message ("Received (property) signal %s:%s:%s from object %s (gail %s)\n",
815            g_type_name (signal_query.itype), signame, values->property_name, s, s2));
816   
817 #endif
818
819   gobject = g_value_get_object (param_values + 0);
820   name = atk_object_get_name (ATK_OBJECT (gobject));
821   values = (AtkPropertyValues*) g_value_get_pointer (param_values + 1);
822
823   prop_name = values->property_name;
824   if (strcmp (prop_name, "accessible-name") == 0)
825     {
826       spi_atk_bridge_init_string (&any, 
827                                   ATK_OBJECT (gobject),
828                                   (gchar **)&name);
829     }
830   else if (strcmp (prop_name, "accessible-description") == 0)
831     {
832       sp = atk_object_get_description (ATK_OBJECT (gobject));
833       spi_atk_bridge_init_string (&any, 
834                                   ATK_OBJECT (gobject),
835                                   (gchar **)&sp);
836     }
837   else if (strcmp (prop_name, "accessible-parent") == 0)
838     {
839       ao = atk_object_get_parent (ATK_OBJECT (gobject));
840       if (ao) 
841         {
842           s_ao = spi_accessible_new (ao);
843           c_obj = BONOBO_OBJREF (s_ao);
844           spi_atk_bridge_init_object (&any, 
845                                       ATK_OBJECT (gobject),
846                                       &c_obj);
847         }
848       else
849         {
850           spi_atk_bridge_init_nil (&any,
851                                    ATK_OBJECT (gobject));
852         }
853     }
854   else if (strcmp (prop_name, "accessible-table-summary") == 0)
855     {
856       ao = atk_table_get_summary (ATK_TABLE (gobject));
857       if (ao) 
858         {
859           s_ao = spi_accessible_new (ao);
860           c_obj = BONOBO_OBJREF (s_ao);
861           spi_atk_bridge_init_object (&any, 
862                                       ATK_OBJECT (gobject),
863                                       &c_obj);
864         }
865       else
866         {
867           spi_atk_bridge_init_nil (&any,
868                                    ATK_OBJECT (gobject));
869         }
870     }
871   else if (strcmp (prop_name, "accessible-table-column-header") == 0)
872     {
873       i = g_value_get_int (&(values->new_value));
874       ao = atk_table_get_column_header (ATK_TABLE (gobject), i);
875       if (ao) 
876         {
877           s_ao = spi_accessible_new (ao);
878           c_obj = BONOBO_OBJREF (s_ao);
879           spi_atk_bridge_init_object (&any, 
880                                       ATK_OBJECT (gobject),
881                                       &c_obj);
882         }
883       else
884         {
885           spi_atk_bridge_init_nil (&any, ATK_OBJECT (gobject));
886         }
887     }
888   else if (strcmp (prop_name, "accessible-table-row-header") == 0)
889     {
890       i = g_value_get_int (&(values->new_value));
891       ao = atk_table_get_row_header (ATK_TABLE (gobject), i);
892       if (ao) 
893         {
894           s_ao = spi_accessible_new (ao);
895           c_obj = BONOBO_OBJREF (s_ao);
896           spi_atk_bridge_init_object (&any, ATK_OBJECT (gobject), &c_obj);
897         }
898       else
899         {
900           spi_atk_bridge_init_nil (&any, ATK_OBJECT (gobject));
901         }
902     }
903   else if (strcmp (prop_name, "accessible-table-row-description") == 0)
904     {
905       i = g_value_get_int (&(values->new_value));
906       sp = atk_table_get_row_description (ATK_TABLE (gobject), i);
907       spi_atk_bridge_init_string (&any, ATK_OBJECT (gobject), 
908                                   (gchar **)&sp);
909     }
910   else if (strcmp (prop_name, "accessible-table-column-description") == 0)
911     {
912       i = g_value_get_int (&(values->new_value));
913       sp = atk_table_get_column_description (ATK_TABLE (gobject), i);
914       spi_atk_bridge_init_string (&any, ATK_OBJECT (gobject), 
915                                   (gchar **)&sp);
916     }
917   else if (strcmp (prop_name, "accessible-table-caption-object") == 0)
918     {
919       ao = atk_table_get_caption (ATK_TABLE (gobject));
920       sp = atk_object_get_name (ao);
921       spi_atk_bridge_init_string (&any, ATK_OBJECT (gobject), 
922                                   (gchar **)&sp);
923     }
924   else
925     {
926       spi_atk_bridge_init_nil (&any, ATK_OBJECT (gobject));
927     }
928
929   spi_atk_emit_eventv (gobject, 0, 0, &any,
930                        "object:property-change:%s", prop_name);
931
932   if (s_ao) 
933     bonobo_object_unref (BONOBO_OBJECT (s_ao));
934
935   return TRUE;
936 }
937
938 static gboolean
939 spi_atk_bridge_state_event_listener (GSignalInvocationHint *signal_hint,
940                                      guint n_param_values,
941                                      const GValue *param_values,
942                                      gpointer data)
943 {
944   GObject *gobject;
945   gchar *property_name;
946   gchar *type;
947   unsigned long detail1;
948 #ifdef SPI_BRIDGE_DEBUG
949   GSignalQuery signal_query;
950   const gchar *name;
951   
952   g_signal_query (signal_hint->signal_id, &signal_query);
953   name = signal_query.signal_name;
954   fprintf (stderr, "Received (state) signal %s:%s\n",
955            g_type_name (signal_query.itype), name);
956 #endif
957
958   gobject = g_value_get_object (param_values + 0);
959   property_name = g_strdup (g_value_get_string (param_values + 1));
960   detail1 = (g_value_get_boolean (param_values + 2))
961     ? 1 : 0;
962   type = g_strdup_printf ("object:state-changed:%s", property_name);
963   spi_atk_emit_eventv (gobject, 
964                        detail1,
965                        0,
966                        NULL,
967                        type);
968   g_free (property_name);
969   g_free (type);
970   return TRUE;
971 }
972
973 static void
974 spi_init_keystroke_from_atk_key_event (Accessibility_DeviceEvent  *keystroke,
975                                        AtkKeyEventStruct          *event)
976 {
977 #ifdef SPI_DEBUG
978   if (event)
979     {
980       g_print ("event %c (%d)\n", (int) event->keyval, (int) event->keycode);
981     }
982   else
983 #endif
984   if (!event)
985     { /* this doesn't really need translating */
986       g_print (_("WARNING: NULL key event reported."));
987     }
988   
989   keystroke->id        = (CORBA_long) event->keyval;
990   keystroke->hw_code   = (CORBA_short) event->keycode;
991   keystroke->timestamp = (CORBA_unsigned_long) event->timestamp;
992   keystroke->modifiers = (CORBA_unsigned_short) (event->state & 0xFFFF);
993   if (event->string)
994     {
995       gunichar c;
996
997       keystroke->event_string = CORBA_string_dup (event->string);
998       c = g_utf8_get_char_validated (event->string, -1);
999       if (c > 0 && g_unichar_isprint (c))
1000         keystroke->is_text = CORBA_TRUE;
1001       else
1002         keystroke->is_text = CORBA_FALSE;
1003     }
1004   else
1005     {
1006       keystroke->event_string = CORBA_string_dup ("");
1007       keystroke->is_text = CORBA_FALSE;
1008     }
1009   switch (event->type)
1010     {
1011     case (ATK_KEY_EVENT_PRESS):
1012       keystroke->type = Accessibility_KEY_PRESSED_EVENT;
1013       break;
1014     case (ATK_KEY_EVENT_RELEASE):
1015       keystroke->type = Accessibility_KEY_RELEASED_EVENT;
1016       break;
1017     default:
1018       keystroke->type = 0;
1019       break;
1020     }
1021 #if 0  
1022   g_print ("key_event type %d; val=%d code=%d modifiers=%x name=%s is_text=%d, time=%lx\n",
1023            (int) keystroke->type, (int) keystroke->id, (int) keystroke->hw_code,
1024            (int) keystroke->modifiers,
1025            keystroke->event_string, (int) keystroke->is_text, (unsigned long) keystroke->timestamp);
1026 #endif
1027 }
1028
1029 static gint
1030 spi_atk_bridge_key_listener (AtkKeyEventStruct *event, gpointer data)
1031 {
1032   CORBA_boolean             result;
1033   Accessibility_DeviceEvent key_event;
1034
1035   CORBA_exception_init (&ev);
1036
1037   spi_init_keystroke_from_atk_key_event (&key_event, event);
1038
1039   result = Accessibility_DeviceEventController_notifyListenersSync (
1040           spi_atk_bridget_get_dec (), &key_event, &ev);
1041
1042   if (key_event.event_string) CORBA_free (key_event.event_string);
1043
1044   if (BONOBO_EX(&ev)) {
1045       result = FALSE;
1046       CORBA_exception_free (&ev);
1047   }
1048
1049   return result;
1050 }
1051
1052 static gboolean
1053 spi_atk_bridge_signal_listener (GSignalInvocationHint *signal_hint,
1054                                 guint n_param_values,
1055                                 const GValue *param_values,
1056                                 gpointer data)
1057 {
1058   GObject *gobject;
1059   GSignalQuery signal_query;
1060   const gchar *name;
1061   const gchar *detail;
1062   CORBA_any any;
1063   CORBA_Object c_obj;
1064   char *sp = NULL;
1065   AtkObject *ao;
1066   gint detail1 = 0, detail2 = 0;
1067   SpiAccessible *s_ao = NULL;
1068 #ifdef SPI_BRIDGE_DEBUG
1069   const gchar *s, *s2;
1070 #endif 
1071   
1072   g_signal_query (signal_hint->signal_id, &signal_query);
1073
1074   name = signal_query.signal_name;
1075   if (signal_hint->detail)
1076     detail = g_quark_to_string (signal_hint->detail);
1077   else
1078     detail = NULL;
1079
1080 #ifdef SPI_BRIDGE_DEBUG
1081   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
1082   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
1083   fprintf (stderr, "Received signal %s:%s detail: %s from object %s (gail %s)\n",
1084            g_type_name (signal_query.itype), name, 
1085            detail ? detail : "<NULL>", s ? s : "<NULL>" , s2);
1086 #endif
1087   
1088   gobject = g_value_get_object (param_values + 0);
1089
1090   if (signal_query.signal_id == atk_signal_active_descendant_changed)
1091     {
1092       gpointer child = g_value_get_pointer (param_values + 1);
1093
1094       g_return_val_if_fail (ATK_IS_OBJECT (child), TRUE);
1095
1096       ao = ATK_OBJECT (child);
1097
1098       detail1 = atk_object_get_index_in_parent (ao);
1099       s_ao = spi_accessible_new (ao);
1100       c_obj = BONOBO_OBJREF (s_ao);
1101       spi_atk_bridge_init_object (&any, ATK_OBJECT (gobject), &c_obj);
1102     }
1103   else if (signal_query.signal_id == atk_signal_link_selected)
1104     {
1105       if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
1106         detail1 = g_value_get_int (param_values + 1);
1107       spi_atk_bridge_init_nil (&any, ATK_OBJECT (gobject));
1108     }
1109   else if (signal_query.signal_id == atk_signal_bounds_changed)
1110     {
1111       AtkRectangle *atk_rect = NULL;
1112
1113       if (G_VALUE_HOLDS_BOXED (param_values + 1))
1114           atk_rect = g_value_get_boxed (param_values + 1);
1115       spi_atk_bridge_init_rect (&any, ATK_OBJECT (gobject), atk_rect);
1116     }
1117   else if ((signal_query.signal_id == atk_signal_children_changed) && gobject)
1118     {
1119       detail1 = g_value_get_uint (param_values + 1);
1120       ao = atk_object_ref_accessible_child (ATK_OBJECT (gobject), 
1121                                             detail1);
1122       if (ao) 
1123         {
1124           s_ao = spi_accessible_new (ao);
1125           c_obj = BONOBO_OBJREF (s_ao);
1126           spi_atk_bridge_init_object (&any, ATK_OBJECT (gobject), &c_obj);
1127           g_object_unref (ao);
1128         }
1129       else
1130         {
1131           spi_atk_bridge_init_nil (&any, ATK_OBJECT (gobject));
1132         }
1133     }
1134   else
1135     {
1136       if (n_param_values >= 2)
1137         {
1138           if (G_VALUE_TYPE (param_values + 1) == G_TYPE_INT)
1139             detail1 = g_value_get_int (param_values + 1);
1140           if (n_param_values >= 3)
1141             {
1142               if (G_VALUE_TYPE (param_values + 2) == G_TYPE_INT)
1143                 detail2 = g_value_get_int (param_values + 2);
1144             }
1145         }
1146
1147       if (signal_query.signal_id == atk_signal_text_changed)
1148         {
1149           sp = atk_text_get_text (ATK_TEXT (gobject),
1150                                   detail1,
1151                                   detail1+detail2);
1152           spi_atk_bridge_init_string (&any, ATK_OBJECT (gobject), 
1153                                       (gchar **) &sp);
1154         }
1155       else if (signal_query.signal_id == atk_signal_text_selection_changed)
1156         {
1157           /* Return NULL as the selected string */
1158           spi_atk_bridge_init_nil (&any, ATK_OBJECT (gobject));
1159         }
1160       else
1161         {
1162           spi_atk_bridge_init_nil (&any, ATK_OBJECT (gobject));
1163         }
1164     }
1165
1166   if (detail)
1167     spi_atk_emit_eventv (gobject, detail1, detail2, &any,
1168                          "object:%s:%s", name, detail);
1169   else
1170     spi_atk_emit_eventv (gobject, detail1, detail2, &any,
1171                          "object:%s", name);
1172
1173   if (sp) 
1174     g_free (sp);
1175
1176   if (s_ao)
1177      bonobo_object_unref (BONOBO_OBJECT (s_ao));
1178
1179   return TRUE;
1180 }
1181
1182 static gboolean
1183 spi_atk_bridge_window_event_listener (GSignalInvocationHint *signal_hint,
1184                                       guint n_param_values,
1185                                       const GValue *param_values,
1186                                       gpointer data)
1187 {
1188   GObject *gobject;
1189   GSignalQuery signal_query;
1190   CORBA_any any;
1191   const gchar *name, *s;
1192 #ifdef SPI_BRIDGE_DEBUG
1193   const gchar *s2;
1194 #endif
1195   
1196   g_signal_query (signal_hint->signal_id, &signal_query);
1197
1198   name = signal_query.signal_name;
1199
1200 #ifdef SPI_BRIDGE_DEBUG
1201   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
1202   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
1203   fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
1204            g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
1205 #endif
1206   
1207   gobject = g_value_get_object (param_values + 0);
1208
1209   s = atk_object_get_name (ATK_OBJECT (gobject));
1210   spi_atk_bridge_init_string (&any, ATK_OBJECT (gobject), (gchar **) &s);
1211   
1212   spi_atk_emit_eventv (gobject, 0, 0, &any,
1213                        "window:%s", name);
1214   return TRUE;
1215 }
1216
1217 static gboolean
1218 spi_atk_bridge_document_event_listener (GSignalInvocationHint *signal_hint,
1219                                       guint n_param_values,
1220                                       const GValue *param_values,
1221                                       gpointer data)
1222 {
1223   GObject *gobject;
1224   GSignalQuery signal_query;
1225   CORBA_any any;
1226   const gchar *name, *s;
1227 #ifdef SPI_BRIDGE_DEBUG
1228   const gchar *s2;
1229 #endif
1230
1231   g_signal_query (signal_hint->signal_id, &signal_query);
1232
1233   name = signal_query.signal_name;
1234
1235 #ifdef SPI_BRIDGE_DEBUG
1236   s2 = g_type_name (G_OBJECT_TYPE (g_value_get_object (param_values + 0)));
1237   s = atk_object_get_name (ATK_OBJECT (g_value_get_object (param_values + 0)));
1238   fprintf (stderr, "Received signal %s:%s from object %s (gail %s)\n",
1239            g_type_name (signal_query.itype), name, s ? s : "<NULL>" , s2);
1240 #endif
1241
1242   gobject = g_value_get_object (param_values + 0);
1243
1244   s = atk_object_get_name (ATK_OBJECT (gobject));
1245   spi_atk_bridge_init_string (&any, ATK_OBJECT (gobject), (gchar **) &s);
1246
1247   spi_atk_emit_eventv (gobject, 0, 0, &any,
1248                        "document:%s", name);
1249   return TRUE;
1250 }
1251
1252 static void
1253 spi_atk_tidy_windows (void)
1254 {
1255   AtkObject *root;
1256   gint n_children;
1257   gint i;
1258
1259   root = atk_get_root ();
1260   n_children = atk_object_get_n_accessible_children (root);
1261   for (i = 0; i < n_children; i++)
1262     {
1263       AtkObject *child;
1264       AtkStateSet *stateset;
1265       CORBA_any any;
1266       const gchar *name;
1267      
1268       child = atk_object_ref_accessible_child (root, i);
1269       stateset = atk_object_ref_state_set (child);
1270       
1271       name = atk_object_get_name (child);
1272       if (atk_state_set_contains_state (stateset, ATK_STATE_ACTIVE))
1273         {
1274           spi_atk_bridge_init_string (&any, child, (gchar**) &name);
1275           spi_atk_emit_eventv (G_OBJECT (child), 0, 0, &any, "window:deactivate");
1276           if (registry_died)
1277             return;
1278         }
1279       g_object_unref (stateset);
1280
1281       spi_atk_bridge_init_string (&any, child, (gchar**) &name);
1282       spi_atk_emit_eventv (G_OBJECT (child), 0, 0, &any, "window:destroy");
1283       g_object_unref (child);
1284     }
1285 }
1286
1287 static void
1288 reinit_register_vars (void)
1289 {
1290   registry = CORBA_OBJECT_NIL;
1291   device_event_controller = CORBA_OBJECT_NIL;
1292   this_app = NULL;
1293 }
1294
1295 static void
1296 spi_atk_bridge_init_base (CORBA_any *any, AtkObject *obj, 
1297                           Accessibility_Application *app, Accessibility_Role *role,
1298                           CORBA_string *name)
1299 {
1300     const gchar *s = atk_object_get_name (obj);
1301     *app = spi_accessible_new_return (atk_get_root (), FALSE, NULL);
1302     *role = spi_role_from_atk_role (atk_object_get_role (obj));
1303     *name = s ? s : ""; /* string gets dup-ed in util.c spi_init_any_* */
1304 }
1305
1306 static void     
1307 spi_atk_bridge_init_nil  (CORBA_any *any, AtkObject *obj)
1308 {
1309     Accessibility_Application app = CORBA_OBJECT_NIL;
1310     Accessibility_Role role = Accessibility_ROLE_UNKNOWN;
1311     CORBA_string name;
1312     spi_atk_bridge_init_base (any, obj, &app, &role, &name);
1313     spi_init_any_nil (any, app, role, name);
1314 }
1315
1316 static void     
1317 spi_atk_bridge_init_object (CORBA_any *any, AtkObject *obj, CORBA_Object *c_obj)
1318 {
1319     Accessibility_Application app = CORBA_OBJECT_NIL;
1320     Accessibility_Role role = Accessibility_ROLE_UNKNOWN;
1321     CORBA_string name;
1322     spi_atk_bridge_init_base (any, obj, &app, &role, &name);
1323     spi_init_any_object (any, app, role, name, c_obj);
1324 }
1325
1326 static void     
1327 spi_atk_bridge_init_string (CORBA_any *any, AtkObject *obj, gchar **string)
1328 {
1329     Accessibility_Application app = CORBA_OBJECT_NIL;
1330     Accessibility_Role role = Accessibility_ROLE_UNKNOWN;
1331     CORBA_string name;
1332     spi_atk_bridge_init_base (any, obj, &app, &role, &name);
1333     spi_init_any_string (any, app, role, name, string);
1334 }
1335
1336 static void     
1337 spi_atk_bridge_init_rect (CORBA_any *any, AtkObject *obj, AtkRectangle *rect)
1338 {
1339     Accessibility_Application app = CORBA_OBJECT_NIL;
1340     Accessibility_Role role = Accessibility_ROLE_UNKNOWN;
1341     CORBA_string name;
1342     spi_atk_bridge_init_base (any, obj, &app, &role, &name);
1343     spi_init_any_rect (any, app, role, name, rect);
1344 }
1345