Back to index

unity  6.0.0
unitya11y.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  */
00018 
00019 #include <glib.h>
00020 #include <gio/gio.h>
00021 #include <gmodule.h>
00022 #include <stdio.h>
00023 
00024 #include "unitya11y.h"
00025 #include "unitya11ytests.h"
00026 #include "unity-util-accessible.h"
00027 
00028 /* nux accessible objects */
00029 #include "nux-view-accessible.h"
00030 #include "nux-base-window-accessible.h"
00031 #include "nux-layout-accessible.h"
00032 #include "nux-text-entry-accessible.h"
00033 
00034 /* unity accessible objects */
00035 #include "Launcher.h"
00036 #include "LauncherIcon.h"
00037 #include "SimpleLauncherIcon.h"
00038 #include "PanelView.h"
00039 #include "DashView.h"
00040 #include "PlacesGroup.h"
00041 #include "QuicklistView.h"
00042 #include "QuicklistMenuItem.h"
00043 #include "SwitcherView.h"
00044 #include "unity-launcher-accessible.h"
00045 #include "unity-launcher-icon-accessible.h"
00046 #include "unity-panel-view-accessible.h"
00047 #include "unity-dash-view-accessible.h"
00048 #include "unity-search-bar-accessible.h"
00049 #include "unity-sctext-accessible.h"
00050 #include "unity-rvgrid-accessible.h"
00051 #include "unity-places-group-accessible.h"
00052 #include "unity-quicklist-accessible.h"
00053 #include "unity-quicklist-menu-item-accessible.h"
00054 #include "unity-switcher-accessible.h"
00055 
00056 using namespace unity;
00057 using namespace unity::dash;
00058 using namespace unity::launcher;
00059 
00060 static GHashTable* accessible_table = NULL;
00061 /* FIXME: remove accessible objects when not required anymore */
00062 
00063 static gboolean a11y_initialized = FALSE;
00064 
00065 #define INIT_METHOD "gnome_accessibility_module_init"
00066 #define DESKTOP_SCHEMA "org.gnome.desktop.interface"
00067 #define ACCESSIBILITY_ENABLED_KEY "toolkit-accessibility"
00068 #define AT_SPI_SCHEMA "org.a11y.atspi"
00069 #define ATK_BRIDGE_LOCATION_KEY "atk-bridge-location"
00070 
00071 static void
00072 unity_a11y_restore_environment(void)
00073 {
00074   g_unsetenv("NO_AT_BRIDGE");
00075   g_unsetenv("NO_GAIL");
00076 }
00077 
00078 static void
00079 load_unity_atk_util(nux::WindowThread* wt)
00080 {
00081   unity_util_accessible_set_window_thread(wt);
00082   g_type_class_unref(g_type_class_ref(UNITY_TYPE_UTIL_ACCESSIBLE));
00083 }
00084 
00085 /* This method is required because g_setting_new abort if the schema
00086    is not present. */
00087 static gboolean
00088 has_gsettings_schema(const gchar* schema)
00089 {
00090   const char* const* list_schemas = NULL;
00091   gboolean found = FALSE;
00092   int i = 0;
00093 
00094   list_schemas = g_settings_list_schemas();
00095   for (i = 0; list_schemas [i]; i++)
00096   {
00097     if (!g_strcmp0(list_schemas[i], schema))
00098     {
00099       found = TRUE;
00100       break;
00101     }
00102   }
00103 
00104   return found;
00105 }
00106 
00107 static gboolean
00108 should_enable_a11y(void)
00109 {
00110   GSettings* desktop_settings = NULL;
00111   gboolean value = FALSE;
00112 
00113   if (!has_gsettings_schema(DESKTOP_SCHEMA))
00114     return FALSE;
00115 
00116   desktop_settings = g_settings_new(DESKTOP_SCHEMA);
00117   value = g_settings_get_boolean(desktop_settings, ACCESSIBILITY_ENABLED_KEY);
00118 
00119   g_object_unref(desktop_settings);
00120 
00121   return value;
00122 }
00123 
00124 static gchar*
00125 get_atk_bridge_path(void)
00126 {
00127   GSettings* atspi_settings = NULL;
00128   GVariant *variant = NULL;
00129   char* value = NULL;
00130 
00131   if (!has_gsettings_schema(AT_SPI_SCHEMA))
00132     return NULL;
00133 
00134   atspi_settings = g_settings_new(AT_SPI_SCHEMA);
00135   variant = g_settings_get_value (atspi_settings, ATK_BRIDGE_LOCATION_KEY);
00136   value = g_variant_dup_bytestring (variant, NULL);
00137 
00138   g_variant_unref (variant);
00139   g_object_unref(atspi_settings);
00140 
00141   return value;
00142 }
00143 
00144 static gboolean
00145 a11y_invoke_module(const char* module_path)
00146 {
00147   GModule*    handle;
00148   void (*invoke_fn)(void);
00149 
00150   if (!module_path)
00151   {
00152     g_warning("Accessibility: invalid module path (NULL)");
00153 
00154     return FALSE;
00155   }
00156 
00157   if (!(handle = g_module_open(module_path, (GModuleFlags)0)))
00158   {
00159     g_warning("Accessibility: failed to load module '%s': '%s'",
00160               module_path, g_module_error());
00161 
00162     return FALSE;
00163   }
00164 
00165   if (!g_module_symbol(handle, INIT_METHOD, (gpointer*)&invoke_fn))
00166   {
00167     g_warning("Accessibility: error library '%s' does not include "
00168               "method '%s' required for accessibility support",
00169               module_path, INIT_METHOD);
00170     g_module_close(handle);
00171 
00172     return FALSE;
00173   }
00174 
00175   invoke_fn();
00176 
00177   return TRUE;
00178 }
00179 
00180 /********************************************************************************/
00181 /*
00182  * In order to avoid the atk-bridge loading and the GAIL
00183  * initialization during the gtk_init, it is required to set some
00184  * environment vars.
00185  *
00186  */
00187 void
00188 unity_a11y_preset_environment(void)
00189 {
00190   g_setenv("NO_AT_BRIDGE", "1", TRUE);
00191   g_setenv("NO_GAIL", "1", TRUE);
00192 }
00193 
00194 /*
00195  * Initializes the accessibility (ATK) support on Unity
00196  *
00197  * It loads the atk-bridge if required. It checks:
00198  *  * If the proper gsettings keys are set
00199  *  * Loads the proper AtkUtil implementation
00200  */
00201 void
00202 unity_a11y_init(nux::WindowThread* wt)
00203 {
00204   gchar* bridge_path = NULL;
00205 
00206   unity_a11y_restore_environment();
00207 
00208   if (!should_enable_a11y())
00209     return;
00210 
00211   load_unity_atk_util(wt);
00212 
00213   bridge_path = get_atk_bridge_path();
00214 
00215   if (a11y_invoke_module(bridge_path))
00216   {
00217     g_debug("Unity Oneiric accessibility started, using bridge on %s",
00218             bridge_path);
00219 
00220     atk_get_root();
00221 
00222     a11y_initialized = TRUE;
00223   }
00224 
00225   g_free(bridge_path);
00226 
00227 // NOTE: we run manually the unit tests while developing by
00228 // uncommenting this. Take a look to the explanation on
00229 // unitya11ytests.h header for more information
00230 
00231 //  unity_run_a11y_unit_tests ();
00232 }
00233 
00234 /*
00235  * Finalize the related issues related with the accessibility.
00236  *
00237  * It mainly clean the resources related with the accessibility
00238  */
00239 void
00240 unity_a11y_finalize(void)
00241 {
00242   if (accessible_table != NULL)
00243   {
00244     g_hash_table_unref(accessible_table);
00245     accessible_table = NULL;
00246   }
00247   a11y_initialized = FALSE;
00248 }
00249 
00250 
00251 /*
00252  * Creates the accessible object for a nux::Area object
00253  *
00254  * Method factory, equivalent to
00255  * atk_object_factory_creeate_accessible, but required because
00256  * AtkObjectFactory gives only support for GObject classes.
00257  *
00258  * FIXME: this should be a temporal method. The best way to implement
00259  * that would be add a ->get_accessible method on the nux::View
00260  * subclasses itself.
00261  *
00262  * WARNING: as a reason the previous comment it is true. Take into
00263  * account that you should be careful with the order you add those
00264  * defines. The order will be from more specific classes to more
00265  * abstracted classes.
00266  *
00267  */
00268 
00269 static AtkObject*
00270 unity_a11y_create_accessible(nux::Object* object)
00271 {
00272   /* UNITY classes*/
00273   if (object->Type().IsDerivedFromType(Launcher::StaticObjectType))
00274     return unity_launcher_accessible_new(object);
00275 
00276   if (object->Type().IsDerivedFromType(LauncherIcon::StaticObjectType))
00277     return unity_launcher_icon_accessible_new(object);
00278 
00279   if (object->Type().IsDerivedFromType(PanelView::StaticObjectType))
00280     return unity_panel_view_accessible_new(object);
00281 
00282   if (object->Type().IsDerivedFromType(DashView::StaticObjectType))
00283     return unity_dash_view_accessible_new(object);
00284 
00285   if (object->Type().IsDerivedFromType(PlacesGroup::StaticObjectType))
00286     return unity_places_group_accessible_new(object);
00287 
00288   if (object->Type().IsDerivedFromType(QuicklistView::StaticObjectType))
00289     return unity_quicklist_accessible_new(object);
00290 
00291   if (object->Type().IsDerivedFromType(QuicklistMenuItem::StaticObjectType))
00292     return unity_quicklist_menu_item_accessible_new(object);
00293 
00294   if (object->Type().IsDerivedFromType(nux::StaticCairoText::StaticObjectType))
00295     return unity_sctext_accessible_new(object);
00296 
00297   if (object->Type().IsDerivedFromType(unity::dash::ResultViewGrid::StaticObjectType))
00298     return unity_rvgrid_accessible_new(object);
00299 
00300   if (object->Type().IsDerivedFromType(unity::SearchBar::StaticObjectType))
00301     return unity_search_bar_accessible_new(object);
00302 
00303   if (object->Type().IsDerivedFromType(unity::switcher::SwitcherView::StaticObjectType))
00304     return unity_switcher_accessible_new(object);
00305 
00306   /* NUX classes  */
00307   if (object->Type().IsDerivedFromType(nux::TextEntry::StaticObjectType))
00308     return nux_text_entry_accessible_new(object);
00309 
00310   if (object->Type().IsDerivedFromType(nux::BaseWindow::StaticObjectType))
00311     return nux_base_window_accessible_new(object);
00312 
00313   if (object->Type().IsDerivedFromType(nux::View::StaticObjectType))
00314     return nux_view_accessible_new(object);
00315 
00316   if (object->Type().IsDerivedFromType(nux::Layout::StaticObjectType))
00317     return nux_layout_accessible_new(object);
00318 
00319   if (object->Type().IsDerivedFromType(nux::Area::StaticObjectType))
00320     return nux_area_accessible_new(object);
00321 
00322   return nux_object_accessible_new(object);
00323 }
00324 
00325 static void
00326 on_object_destroy_cb(nux::Object* base_object,
00327                      AtkObject* accessible_object)
00328 {
00329   /* NOTE: the pair key:value (base_object:accessible_object) could be
00330      already removed on on_accessible_destroy_cb. That just mean that
00331      g_hash_table_remove would return FALSE. We don't add a
00332      debug/warning message to avoid being too verbose */
00333 
00334   g_hash_table_remove(accessible_table, base_object);
00335 }
00336 
00337 static void
00338 on_accessible_destroy_cb(gpointer data,
00339                          GObject* where_the_object_was)
00340 {
00341   /* NOTE: the pair key:value (base_object:accessible_object) could be
00342      already removed on on_object_destroy_cb. That just mean that
00343      g_hash_table_remove would return FALSE. We don't add a
00344      debug/warning message to avoid being too verbose */
00345 
00346   g_hash_table_remove(accessible_table, data);
00347 }
00348 
00349 /*
00350  * Returns the accessible object of a nux::View object
00351  *
00352  * This method tries to:
00353  *   * Check if area has already a accessibility object
00354  *   * If this is the case, returns that
00355  *   * If not, creates it and return the object
00356  *
00357  * FIXME: this should be a temporal method. The best way to implement
00358  * that would be add a ->get_accessible method on the nux::View
00359  * subclasses itself.
00360  *
00361  */
00362 AtkObject*
00363 unity_a11y_get_accessible(nux::Object* object)
00364 {
00365   AtkObject* accessible_object = NULL;
00366 
00367   g_return_val_if_fail(object != NULL, NULL);
00368 
00369   if (accessible_table == NULL)
00370   {
00371     accessible_table = g_hash_table_new(g_direct_hash, g_direct_equal);
00372   }
00373 
00374   accessible_object = ATK_OBJECT(g_hash_table_lookup(accessible_table, object));
00375   if (accessible_object == NULL)
00376   {
00377     accessible_object = unity_a11y_create_accessible(object);
00378 
00379     g_hash_table_insert(accessible_table, object, accessible_object);
00380 
00381     /* there are two reasons the object should be removed from the
00382      * table: base object destroyed, or accessible object
00383      * destroyed
00384      */
00385     g_object_weak_ref(G_OBJECT(accessible_object),
00386                       on_accessible_destroy_cb,
00387                       object);
00388 
00389     object->OnDestroyed.connect(sigc::bind(sigc::ptr_fun(on_object_destroy_cb),
00390                                            accessible_object));
00391   }
00392 
00393   return accessible_object;
00394 }
00395 
00396 /*
00397  * Returns if the accessibility support is properly initialized
00398  */
00399 gboolean unity_a11y_initialized(void)
00400 {
00401   return a11y_initialized;
00402 }
00403 
00404 /* Returns the accessible_table. Just for unit testing purposes, you
00405    should not require to use it */
00406 GHashTable* _unity_a11y_get_accessible_table()
00407 {
00408   return accessible_table;
00409 }