Back to index

unity  6.0.0
unity-util-accessible.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2011 Canonical Ltd
00003  *
00004  * This program is free software: you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License version 3 as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Authored by: Alejandro PiƱeiro Iglesias <apinheiro@igalia.com>
00017  *              Rodrigo Moya <rodrigo.moya@canonical.com>
00018  */
00019 
00020 #include <gdk/gdk.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 
00024 #include "unity-util-accessible.h"
00025 #include "unity-root-accessible.h"
00026 
00027 #include "nux-base-window-accessible.h"
00028 
00029 static void unity_util_accessible_class_init(UnityUtilAccessibleClass*  klass);
00030 static void unity_util_accessible_init(UnityUtilAccessible* unity_util_accessible);
00031 
00032 /* atkutil.h */
00033 
00034 static guint         unity_util_accessible_add_global_event_listener(GSignalEmissionHook listener,
00035                                                                      const gchar*        event_type);
00036 static void          unity_util_accessible_remove_global_event_listener(guint remove_listener);
00037 static AtkObject*    unity_util_accessible_get_root(void);
00038 static const gchar* unity_util_accessible_get_toolkit_name(void);
00039 static const gchar* unity_util_accessible_get_toolkit_version(void);
00040 static guint         unity_util_accessible_add_key_event_listener(AtkKeySnoopFunc listener,
00041                                                                   gpointer data);
00042 static void          unity_util_accessible_remove_key_event_listener(guint remove_listener);
00043 
00044 typedef struct
00045 {
00046   guint idx;
00047   gulong hook_id;
00048   guint signal_id;
00049 } UnityUtilListenerInfo;
00050 
00051 typedef struct
00052 {
00053   AtkKeySnoopFunc func;
00054   gpointer        data;
00055   guint           key;
00056 } UnityKeyEventListener;
00057 
00058 /* FIXME: move this to a private structure on UnityUtilAccessible? */
00059 static GHashTable* listener_list = NULL;
00060 static AtkObject* root = NULL;
00061 static GSList* key_listener_list = NULL;
00062 static guint event_inspector_id = 0;
00063 static nux::WindowThread* unity_window_thread = NULL;
00064 
00065 G_DEFINE_TYPE(UnityUtilAccessible, unity_util_accessible, ATK_TYPE_UTIL);
00066 
00067 static void
00068 unity_util_accessible_class_init(UnityUtilAccessibleClass* klass)
00069 {
00070   AtkUtilClass* atk_class;
00071   gpointer data;
00072 
00073   data = g_type_class_peek(ATK_TYPE_UTIL);
00074   atk_class = ATK_UTIL_CLASS(data);
00075 
00076   atk_class->add_global_event_listener    = unity_util_accessible_add_global_event_listener;
00077   atk_class->remove_global_event_listener = unity_util_accessible_remove_global_event_listener;
00078   atk_class->add_key_event_listener       = unity_util_accessible_add_key_event_listener;
00079   atk_class->remove_key_event_listener    = unity_util_accessible_remove_key_event_listener;
00080   atk_class->get_root                     = unity_util_accessible_get_root;
00081   atk_class->get_toolkit_name             = unity_util_accessible_get_toolkit_name;
00082   atk_class->get_toolkit_version          = unity_util_accessible_get_toolkit_version;
00083 }
00084 
00085 static void
00086 unity_util_accessible_init(UnityUtilAccessible* unity_util_accessible)
00087 {
00088 }
00089 
00090 static AtkObject*
00091 unity_util_accessible_get_root(void)
00092 {
00093   if (!root)
00094     root = unity_root_accessible_new();
00095 
00096   return root;
00097 }
00098 
00099 static const gchar*
00100 unity_util_accessible_get_toolkit_name(void)
00101 {
00102   return "UNITY";
00103 }
00104 
00105 static const gchar*
00106 unity_util_accessible_get_toolkit_version(void)
00107 {
00108   /*
00109    * FIXME:
00110    * Version is passed in as a -D flag when this file is
00111    * compiled.
00112    */
00113   return "0.1";
00114 }
00115 
00116 static guint
00117 add_listener(GSignalEmissionHook listener,
00118              const gchar*        object_type,
00119              const gchar*        signal_name,
00120              const gchar*        hook_data)
00121 {
00122   GType type;
00123   guint signal_id;
00124   guint rc = 0;
00125   static guint listener_idx = 1;
00126 
00127   if (!listener_list)
00128     listener_list =  g_hash_table_new_full(g_int_hash, g_int_equal, NULL, g_free);
00129 
00130   type = g_type_from_name(object_type);
00131   if (type)
00132   {
00133     signal_id = g_signal_lookup(signal_name, type);
00134     if (signal_id > 0)
00135     {
00136       UnityUtilListenerInfo* listener_info;
00137 
00138       rc = listener_idx;
00139       listener_info = g_new0(UnityUtilListenerInfo, 1);
00140       listener_info->idx = listener_idx;
00141       listener_info->hook_id = g_signal_add_emission_hook(signal_id, 0, listener,
00142                                                           g_strdup(hook_data),
00143                                                           (GDestroyNotify) g_free);
00144       listener_info->signal_id = signal_id;
00145 
00146       g_hash_table_insert(listener_list, &(listener_info->idx), listener_info);
00147 
00148       listener_idx++;
00149     }
00150     else
00151     {
00152       /* Mainly becase some "window::xxx" methods not implemented
00153          on NuxBaseWindowAccessible */
00154       g_debug("Signal type %s not supported\n", signal_name);
00155     }
00156   }
00157   else
00158     g_warning("Invalid object type %s\n", object_type);
00159 
00160   return rc;
00161 }
00162 
00163 static void
00164 do_window_event_initialization(void)
00165 {
00166   /*
00167    * Ensure that NuxBaseWindowClass exists
00168    */
00169   g_type_class_unref(g_type_class_ref(NUX_TYPE_BASE_WINDOW_ACCESSIBLE));
00170 }
00171 
00172 static guint
00173 unity_util_accessible_add_global_event_listener(GSignalEmissionHook listener,
00174                                                 const gchar*        event_type)
00175 {
00176   gchar** split_string;
00177   guint rc = 0;
00178 
00179   split_string = g_strsplit(event_type, ":", 3);
00180   if (split_string)
00181   {
00182     if (g_str_equal("window", split_string[0]))
00183     {
00184       /* Using NuxBaseWindow as the toplevelwindow */
00185       static gboolean initialized = FALSE;
00186 
00187       if (initialized == FALSE)
00188       {
00189         do_window_event_initialization();
00190         initialized = TRUE;
00191       }
00192 
00193       rc = add_listener(listener, "NuxBaseWindowAccessible", split_string [1], event_type);
00194     }
00195     else
00196     {
00197       rc = add_listener(listener, split_string[1], split_string[2], event_type);
00198     }
00199 
00200     g_strfreev(split_string);
00201   }
00202 
00203   return rc;
00204 }
00205 
00206 static void
00207 unity_util_accessible_remove_global_event_listener(guint remove_listener)
00208 {
00209   if (remove_listener > 0)
00210   {
00211     UnityUtilListenerInfo* listener_info;
00212 
00213     listener_info = (UnityUtilListenerInfo*) g_hash_table_lookup(listener_list, &remove_listener);
00214     if (listener_info != NULL)
00215     {
00216       if (listener_info->hook_id != 0 && listener_info->signal_id != 0)
00217       {
00218         g_signal_remove_emission_hook(listener_info->signal_id,
00219                                       listener_info->hook_id);
00220         g_hash_table_remove(listener_list, &remove_listener);
00221       }
00222       else
00223       {
00224         g_warning("Invalid listener hook_id %ld or signal_id %d",
00225                   listener_info->hook_id, listener_info->signal_id);
00226       }
00227     }
00228     else
00229       g_warning("No listener with the specified ID: %d", remove_listener);
00230   }
00231   else
00232     g_warning("Invalid listener_id: %d", remove_listener);
00233 }
00234 
00235 static guint
00236 translate_nux_modifiers_to_gdk_state(unsigned long nux_modifier)
00237 {
00238   guint result = 0;
00239 
00240   if (nux_modifier & nux::NUX_STATE_SHIFT)
00241     result |= GDK_SHIFT_MASK;
00242 
00243   if (nux_modifier & nux::NUX_STATE_CAPS_LOCK)
00244     result |= GDK_LOCK_MASK;
00245 
00246   if (nux_modifier & nux::NUX_STATE_CTRL)
00247     result |= GDK_CONTROL_MASK;
00248 
00249   /* From gdk documentation
00250 
00251      GDK_MOD1_MASK : the fourth modifier key (it depends on the
00252      modifier mapping of the X server which key is interpreted as this
00253      modifier, but normally it is the Alt key).
00254    */
00255 
00256   if (nux_modifier & nux::NUX_STATE_ALT)
00257     result |= GDK_MOD1_MASK;
00258 
00259   /* FIXME: not sure how to translate this ones */
00260   // if (nux_modifier & NUX_STATE_SCROLLLOCK)
00261   // if (nux_modifier & NUX_STATE_NUMLOCK)
00262 
00263   return result;
00264 }
00265 
00266 static AtkKeyEventStruct*
00267 atk_key_event_from_nux_event_key(nux::Event* event)
00268 {
00269   AtkKeyEventStruct* atk_event = g_new0(AtkKeyEventStruct, 1);
00270   gunichar key_unichar;
00271   static GdkDisplay* display = gdk_display_get_default();
00272   static GdkKeymap* keymap = gdk_keymap_get_for_display(display);
00273   GdkKeymapKey* keys = NULL;
00274   gint n_keys = 0;
00275   gboolean success = FALSE;
00276 
00277   switch (event->type)
00278   {
00279     case nux::NUX_KEYDOWN:
00280       atk_event->type = ATK_KEY_EVENT_PRESS;
00281       break;
00282     case nux::NUX_KEYUP:
00283       atk_event->type = ATK_KEY_EVENT_RELEASE;
00284       break;
00285     default:
00286       /* we don't call atk_key_event_from_nux_event_key if the event
00287          is different to keydown or keyup */
00288       g_assert_not_reached();
00289       return NULL;
00290   }
00291 
00292   atk_event->state = translate_nux_modifiers_to_gdk_state(event->key_modifiers);
00293 
00294   atk_event->keyval = event->x11_keysym;
00295   atk_event->keycode = event->x11_keycode;
00296 
00297   /* GDK applies the modifiers to the keyval, and ATK expects that, so
00298    * we need to do this also here, as it is not done on the release  */
00299 
00300   success = gdk_keymap_get_entries_for_keyval(keymap, atk_event->keyval,
00301                                               &keys, &n_keys);
00302   success &= n_keys > 0;
00303 
00304   if (success)
00305   {
00306     gint group;
00307     guint new_keyval;
00308     gint effective_group;
00309     gint level;
00310     GdkModifierType consumed;
00311 
00312     group = keys [0].group;
00313 
00314     success = gdk_keymap_translate_keyboard_state(keymap,
00315                                                   atk_event->keycode,
00316                                                   (GdkModifierType) atk_event->state,
00317                                                   group,
00318                                                   &new_keyval,
00319                                                   &effective_group,
00320                                                   &level,
00321                                                   &consumed);
00322     if (success)
00323       atk_event->keyval = new_keyval;
00324   }
00325 
00326   atk_event->string = NULL;
00327   if (event->text && event->text[0])
00328   {
00329     key_unichar = g_utf8_get_char(event->text);
00330 
00331     if (g_unichar_validate(key_unichar) && g_unichar_isgraph(key_unichar))
00332     {
00333       GString* new_string = NULL;
00334 
00335       new_string = g_string_new("");
00336       new_string = g_string_insert_unichar(new_string, 0, key_unichar);
00337       atk_event->string = new_string->str;
00338       g_string_free(new_string, FALSE);
00339     }
00340   }
00341 
00342   /* If ->string is still NULL we compute it from the keyval*/
00343   if (atk_event->string == NULL)
00344     atk_event->string = g_strdup(gdk_keyval_name(atk_event->keyval));
00345 
00346   /* e_x11_timestamp is zero, see bug LB#735645*/
00347   atk_event->timestamp = g_get_real_time() / 1000;
00348 
00349 #ifdef DEBUG_ANY_KEY_EVENT
00350   g_debug("[a11y] AtkKeyEvent:\n\t\tsym 0x%x\n\t\tmods %x\n\t\tcode %u\n\t\ttime %lx \n\t\tstring %s\n",
00351           (unsigned int) atk_event->keyval,
00352           (unsigned int) atk_event->state,
00353           (unsigned int) atk_event->keycode,
00354           (unsigned long int) atk_event->timestamp,
00355           atk_event->string);
00356 #endif
00357 
00358   return atk_event;
00359 }
00360 
00361 static int
00362 unity_util_event_inspector(nux::Area* area,
00363                            nux::Event* event,
00364                            void* data)
00365 {
00366   GSList* list = NULL;
00367   AtkKeyEventStruct* atk_key_event = NULL;
00368   gint result = 0;
00369 
00370   if ((event->type != nux::NUX_KEYDOWN) && (event->type != nux::NUX_KEYUP))
00371     return 0;
00372 
00373   atk_key_event = atk_key_event_from_nux_event_key(event);
00374 
00375   for (list = key_listener_list; list; list = list->next)
00376   {
00377     UnityKeyEventListener* listener = (UnityKeyEventListener*) list->data;
00378 
00379     result |= listener->func(atk_key_event, listener->data);
00380   }
00381 
00382   g_free(atk_key_event->string);
00383   g_free(atk_key_event);
00384 
00385   return result;
00386 }
00387 
00388 static guint
00389 unity_util_accessible_add_key_event_listener(AtkKeySnoopFunc listener_func,
00390                                              gpointer data)
00391 {
00392   static guint key = 0;
00393   UnityKeyEventListener* listener;
00394 
00395   if (event_inspector_id == 0)
00396   {
00397     if (unity_window_thread == NULL)
00398       return 0;
00399 
00400     event_inspector_id = unity_window_thread->InstallEventInspector(unity_util_event_inspector, NULL);
00401   }
00402 
00403   key++;
00404 
00405   listener = g_slice_new0(UnityKeyEventListener);
00406   listener->func = listener_func;
00407   listener->data = data;
00408   listener->key = key;
00409 
00410   key_listener_list = g_slist_append(key_listener_list, listener);
00411 
00412   return key;
00413 }
00414 
00415 static void
00416 unity_util_accessible_remove_key_event_listener(guint remove_listener)
00417 {
00418   GSList* l;
00419 
00420   for (l = key_listener_list; l; l = l->next)
00421   {
00422     UnityKeyEventListener* listener = (UnityKeyEventListener*) l->data;
00423 
00424     if (listener->key == remove_listener)
00425     {
00426       g_slice_free(UnityKeyEventListener, listener);
00427       key_listener_list = g_slist_delete_link(key_listener_list, l);
00428 
00429       break;
00430     }
00431   }
00432 
00433   if (key_listener_list == NULL)
00434   {
00435     if (unity_window_thread == NULL)
00436       return;
00437 
00438     unity_window_thread->RemoveEventInspector(event_inspector_id);
00439     event_inspector_id = 0;
00440   }
00441 }
00442 
00443 /* Public */
00444 void
00445 unity_util_accessible_set_window_thread(nux::WindowThread* wt)
00446 {
00447   unity_window_thread = wt;
00448 }