summaryrefslogtreecommitdiff
path: root/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/a11y-keyboard/msd-a11y-keyboard-manager.c')
-rw-r--r--plugins/a11y-keyboard/msd-a11y-keyboard-manager.c1347
1 files changed, 1347 insertions, 0 deletions
diff --git a/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c b/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c
new file mode 100644
index 0000000..7505b14
--- /dev/null
+++ b/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c
@@ -0,0 +1,1347 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright © 2001 Ximian, Inc.
+ * Copyright (C) 2007 William Jon McCann <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKBstr.h>
+
+#ifdef HAVE_X11_EXTENSIONS_XINPUT_H
+#include <X11/extensions/XInput.h>
+#include <X11/extensions/XIproto.h>
+#endif
+
+#ifdef HAVE_LIBMATENOTIFY
+#include <libmatenotify/notify.h>
+#endif /* HAVE_LIBMATENOTIFY */
+
+#include "mate-settings-profile.h"
+#include "msd-a11y-keyboard-manager.h"
+#include "msd-a11y-preferences-dialog.h"
+
+#define CONFIG_ROOT "/desktop/mate/accessibility/keyboard"
+#define NOTIFICATION_TIMEOUT 30
+
+#define MSD_A11Y_KEYBOARD_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MSD_TYPE_A11Y_KEYBOARD_MANAGER, MsdA11yKeyboardManagerPrivate))
+
+struct MsdA11yKeyboardManagerPrivate
+{
+ int xkbEventBase;
+ gboolean stickykeys_shortcut_val;
+ gboolean slowkeys_shortcut_val;
+ GtkWidget *stickykeys_alert;
+ GtkWidget *slowkeys_alert;
+ GtkWidget *preferences_dialog;
+ GtkStatusIcon *status_icon;
+ XkbDescRec *original_xkb_desc;
+
+ guint mateconf_notify;
+
+#ifdef HAVE_LIBMATENOTIFY
+ NotifyNotification *notification;
+#endif /* HAVE_LIBMATENOTIFY */
+};
+
+static void msd_a11y_keyboard_manager_class_init (MsdA11yKeyboardManagerClass *klass);
+static void msd_a11y_keyboard_manager_init (MsdA11yKeyboardManager *a11y_keyboard_manager);
+static void msd_a11y_keyboard_manager_finalize (GObject *object);
+static void msd_a11y_keyboard_manager_ensure_status_icon (MsdA11yKeyboardManager *manager);
+static void set_server_from_mateconf (MsdA11yKeyboardManager *manager,
+ MateConfClient *client);
+
+G_DEFINE_TYPE (MsdA11yKeyboardManager, msd_a11y_keyboard_manager, G_TYPE_OBJECT)
+
+static gpointer manager_object = NULL;
+
+#undef DEBUG_ACCESSIBILITY
+#ifdef DEBUG_ACCESSIBILITY
+#define d(str) g_debug (str)
+#else
+#define d(str) do { } while (0)
+#endif
+
+#ifdef HAVE_X11_EXTENSIONS_XINPUT_H
+static GdkFilterReturn
+devicepresence_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *xev = (XEvent *) xevent;
+ XEventClass class_presence;
+ int xi_presence;
+
+ DevicePresence (gdk_x11_get_default_xdisplay (), xi_presence, class_presence);
+
+ if (xev->type == xi_presence)
+ {
+ XDevicePresenceNotifyEvent *dpn = (XDevicePresenceNotifyEvent *) xev;
+ if (dpn->devchange == DeviceEnabled) {
+ MateConfClient *client;
+ client = mateconf_client_get_default ();
+ set_server_from_mateconf (data, client);
+ g_object_unref (client);
+ }
+ }
+ return GDK_FILTER_CONTINUE;
+}
+
+static gboolean
+supports_xinput_devices (void)
+{
+ gint op_code, event, error;
+
+ return XQueryExtension (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+ "XInputExtension",
+ &op_code,
+ &event,
+ &error);
+}
+
+static void
+set_devicepresence_handler (MsdA11yKeyboardManager *manager)
+{
+ Display *display;
+ XEventClass class_presence;
+ int xi_presence;
+
+ if (!supports_xinput_devices ())
+ return;
+
+ display = gdk_x11_get_default_xdisplay ();
+
+ gdk_error_trap_push ();
+ DevicePresence (display, xi_presence, class_presence);
+ /* FIXME:
+ * Note that this might overwrite other events, see:
+ * https://bugzilla.gnome.org/show_bug.cgi?id=610245#c2
+ **/
+ XSelectExtensionEvent (display,
+ RootWindow (display, DefaultScreen (display)),
+ &class_presence, 1);
+
+ gdk_flush ();
+ if (!gdk_error_trap_pop ())
+ gdk_window_add_filter (NULL, devicepresence_filter, manager);
+}
+#endif
+
+static gboolean
+xkb_enabled (MsdA11yKeyboardManager *manager)
+{
+ gboolean have_xkb;
+ int opcode, errorBase, major, minor;
+
+ have_xkb = XkbQueryExtension (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+ &opcode,
+ &manager->priv->xkbEventBase,
+ &errorBase,
+ &major,
+ &minor)
+ && XkbUseExtension (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), &major, &minor);
+
+ return have_xkb;
+}
+
+static XkbDescRec *
+get_xkb_desc_rec (MsdA11yKeyboardManager *manager)
+{
+ XkbDescRec *desc;
+ Status status = Success;
+
+ gdk_error_trap_push ();
+ desc = XkbGetMap (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), XkbAllMapComponentsMask, XkbUseCoreKbd);
+ if (desc != NULL) {
+ desc->ctrls = NULL;
+ status = XkbGetControls (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), XkbAllControlsMask, desc);
+ }
+ gdk_error_trap_pop ();
+
+ g_return_val_if_fail (desc != NULL, NULL);
+ g_return_val_if_fail (desc->ctrls != NULL, NULL);
+ g_return_val_if_fail (status == Success, NULL);
+
+ return desc;
+}
+
+static int
+get_int (MateConfClient *client,
+ char const *key)
+{
+ int res = mateconf_client_get_int (client, key, NULL);
+ if (res <= 0) {
+ res = 1;
+ }
+ return res;
+}
+
+static gboolean
+set_int (MateConfClient *client,
+ MateConfChangeSet *cs,
+ char const *key,
+ int val)
+{
+ mateconf_change_set_set_int (cs, key, val);
+#ifdef DEBUG_ACCESSIBILITY
+ if (val != mateconf_client_get_int (client, key, NULL)) {
+ g_warning ("%s changed", key);
+ }
+#endif
+ return val != mateconf_client_get_int (client, key, NULL);
+}
+
+static gboolean
+set_bool (MateConfClient *client,
+ MateConfChangeSet *cs,
+ char const *key,
+ int val)
+{
+ gboolean bval = (val != 0);
+
+ mateconf_change_set_set_bool (cs, key, bval ? TRUE : FALSE);
+#ifdef DEBUG_ACCESSIBILITY
+ if (bval != mateconf_client_get_bool (client, key, NULL)) {
+ d ("%s changed", key);
+ return TRUE;
+ }
+#endif
+ return (bval != mateconf_client_get_bool (client, key, NULL));
+}
+
+static unsigned long
+set_clear (gboolean flag,
+ unsigned long value,
+ unsigned long mask)
+{
+ if (flag) {
+ return value | mask;
+ }
+ return value & ~mask;
+}
+
+static gboolean
+set_ctrl_from_mateconf (XkbDescRec *desc,
+ MateConfClient *client,
+ char const *key,
+ unsigned long mask)
+{
+ gboolean result = mateconf_client_get_bool (client, key, NULL);
+ desc->ctrls->enabled_ctrls = set_clear (result, desc->ctrls->enabled_ctrls, mask);
+ return result;
+}
+
+static void
+set_server_from_mateconf (MsdA11yKeyboardManager *manager,
+ MateConfClient *client)
+{
+ XkbDescRec *desc;
+ gboolean enable_accessX;
+
+ mate_settings_profile_start (NULL);
+
+ desc = get_xkb_desc_rec (manager);
+ if (!desc) {
+ return;
+ }
+
+ /* general */
+ enable_accessX = mateconf_client_get_bool (client, CONFIG_ROOT "/enable", NULL);
+
+ desc->ctrls->enabled_ctrls = set_clear (enable_accessX,
+ desc->ctrls->enabled_ctrls,
+ XkbAccessXKeysMask);
+
+ if (set_ctrl_from_mateconf (desc, client, CONFIG_ROOT "/timeout_enable",
+ XkbAccessXTimeoutMask)) {
+ desc->ctrls->ax_timeout = get_int (client,
+ CONFIG_ROOT "/timeout");
+ /* disable only the master flag via the server we will disable
+ * the rest on the rebound without affecting mateconf state
+ * don't change the option flags at all.
+ */
+ desc->ctrls->axt_ctrls_mask = XkbAccessXKeysMask | XkbAccessXFeedbackMask;
+ desc->ctrls->axt_ctrls_values = 0;
+ desc->ctrls->axt_opts_mask = 0;
+ }
+
+ desc->ctrls->ax_options = set_clear (mateconf_client_get_bool (client, CONFIG_ROOT "/feature_state_change_beep", NULL),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask);
+
+ /* bounce keys */
+ if (set_ctrl_from_mateconf (desc,
+ client,
+ CONFIG_ROOT "/bouncekeys_enable",
+ XkbBounceKeysMask)) {
+ desc->ctrls->debounce_delay = get_int (client,
+ CONFIG_ROOT "/bouncekeys_delay");
+ desc->ctrls->ax_options = set_clear (mateconf_client_get_bool (client, CONFIG_ROOT "/bouncekeys_beep_reject", NULL),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_BKRejectFBMask);
+ }
+
+ /* mouse keys */
+ if (set_ctrl_from_mateconf (desc,
+ client,
+ CONFIG_ROOT "/mousekeys_enable",
+ XkbMouseKeysMask | XkbMouseKeysAccelMask)) {
+ desc->ctrls->mk_interval = 100; /* msec between mousekey events */
+ desc->ctrls->mk_curve = 50;
+
+ /* We store pixels / sec, XKB wants pixels / event */
+ desc->ctrls->mk_max_speed = get_int (client,
+ CONFIG_ROOT "/mousekeys_max_speed") / (1000 / desc->ctrls->mk_interval);
+ if (desc->ctrls->mk_max_speed <= 0)
+ desc->ctrls->mk_max_speed = 1;
+
+ desc->ctrls->mk_time_to_max = get_int (client, /* events before max */
+ CONFIG_ROOT "/mousekeys_accel_time") / desc->ctrls->mk_interval;
+ if (desc->ctrls->mk_time_to_max <= 0)
+ desc->ctrls->mk_time_to_max = 1;
+
+ desc->ctrls->mk_delay = get_int (client, /* ms before 1st event */
+ CONFIG_ROOT "/mousekeys_init_delay");
+ }
+
+ /* slow keys */
+ if (set_ctrl_from_mateconf (desc,
+ client,
+ CONFIG_ROOT "/slowkeys_enable",
+ XkbSlowKeysMask)) {
+ desc->ctrls->ax_options = set_clear (mateconf_client_get_bool (client, CONFIG_ROOT "/slowkeys_beep_press", NULL),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_SKPressFBMask);
+ desc->ctrls->ax_options = set_clear (mateconf_client_get_bool (client, CONFIG_ROOT "/slowkeys_beep_accept", NULL),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_SKAcceptFBMask);
+ desc->ctrls->ax_options = set_clear (mateconf_client_get_bool (client, CONFIG_ROOT "/slowkeys_beep_reject", NULL),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_SKRejectFBMask);
+ desc->ctrls->slow_keys_delay = get_int (client,
+ CONFIG_ROOT "/slowkeys_delay");
+ /* anything larger than 500 seems to loose all keyboard input */
+ if (desc->ctrls->slow_keys_delay > 500)
+ desc->ctrls->slow_keys_delay = 500;
+ }
+
+ /* sticky keys */
+ if (set_ctrl_from_mateconf (desc,
+ client,
+ CONFIG_ROOT "/stickykeys_enable",
+ XkbStickyKeysMask)) {
+ desc->ctrls->ax_options |= XkbAX_LatchToLockMask;
+ desc->ctrls->ax_options = set_clear (mateconf_client_get_bool (client, CONFIG_ROOT "/stickykeys_two_key_off", NULL),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_TwoKeysMask);
+ desc->ctrls->ax_options = set_clear (mateconf_client_get_bool (client, CONFIG_ROOT "/stickykeys_modifier_beep", NULL),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_StickyKeysFBMask);
+ }
+
+ /* toggle keys */
+ desc->ctrls->ax_options = set_clear (mateconf_client_get_bool (client, CONFIG_ROOT "/togglekeys_enable", NULL),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_IndicatorFBMask);
+
+ /*
+ g_debug ("CHANGE to : 0x%x", desc->ctrls->enabled_ctrls);
+ g_debug ("CHANGE to : 0x%x (2)", desc->ctrls->ax_options);
+ */
+
+ gdk_error_trap_push ();
+ XkbSetControls (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+ XkbSlowKeysMask |
+ XkbBounceKeysMask |
+ XkbStickyKeysMask |
+ XkbMouseKeysMask |
+ XkbMouseKeysAccelMask |
+ XkbAccessXKeysMask |
+ XkbAccessXTimeoutMask |
+ XkbAccessXFeedbackMask |
+ XkbControlsEnabledMask,
+ desc);
+
+ XkbFreeKeyboard (desc, XkbAllComponentsMask, True);
+
+ XSync (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), FALSE);
+ gdk_error_trap_pop ();
+
+ mate_settings_profile_end (NULL);
+}
+
+static gboolean
+ax_response_callback (MsdA11yKeyboardManager *manager,
+ GtkWindow *parent,
+ gint response_id,
+ guint revert_controls_mask,
+ gboolean enabled)
+{
+ MateConfClient *client;
+ GdkScreen *screen;
+ GError *err;
+
+ switch (response_id) {
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_REJECT:
+ case GTK_RESPONSE_CANCEL:
+
+ client = mateconf_client_get_default ();
+
+ /* we're reverting, so we invert sense of 'enabled' flag */
+ d ("cancelling AccessX request");
+ if (revert_controls_mask == XkbStickyKeysMask) {
+ mateconf_client_set_bool (client,
+ CONFIG_ROOT "/stickykeys_enable",
+ !enabled,
+ NULL);
+ }
+ else if (revert_controls_mask == XkbSlowKeysMask) {
+ mateconf_client_set_bool (client,
+ CONFIG_ROOT "/slowkeys_enable",
+ !enabled,
+ NULL);
+ }
+ mateconf_client_suggest_sync (client, NULL);
+ set_server_from_mateconf (manager, client);
+
+ g_object_unref (client);
+
+ break;
+
+ case GTK_RESPONSE_HELP:
+ if (!parent)
+ screen = gdk_screen_get_default ();
+ else
+ screen = gtk_widget_get_screen (GTK_WIDGET (parent));
+
+ err = NULL;
+ if (!gtk_show_uri (screen,
+ "ghelp:user-guide#goscustaccess-6",
+ gtk_get_current_event_time(),
+ &err)) {
+ GtkWidget *error_dialog = gtk_message_dialog_new (parent,
+ 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("There was an error displaying help: %s"),
+ err->message);
+ g_signal_connect (error_dialog, "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_window_set_resizable (GTK_WINDOW (error_dialog), FALSE);
+ gtk_widget_show (error_dialog);
+ g_error_free (err);
+ }
+ return FALSE;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+static void
+ax_stickykeys_response (GtkDialog *dialog,
+ gint response_id,
+ MsdA11yKeyboardManager *manager)
+{
+ if (ax_response_callback (manager, GTK_WINDOW (dialog),
+ response_id, XkbStickyKeysMask,
+ manager->priv->stickykeys_shortcut_val)) {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ }
+}
+
+static void
+ax_slowkeys_response (GtkDialog *dialog,
+ gint response_id,
+ MsdA11yKeyboardManager *manager)
+{
+ if (ax_response_callback (manager, GTK_WINDOW (dialog),
+ response_id, XkbSlowKeysMask,
+ manager->priv->slowkeys_shortcut_val)) {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ }
+}
+
+static void
+maybe_show_status_icon (MsdA11yKeyboardManager *manager)
+{
+ gboolean show;
+ MateConfClient *client;
+
+ /* for now, show if accessx is enabled */
+ client = mateconf_client_get_default ();
+ show = mateconf_client_get_bool (client, CONFIG_ROOT "/enable", NULL);
+ g_object_unref (client);
+
+ if (!show && manager->priv->status_icon == NULL)
+ return;
+
+ msd_a11y_keyboard_manager_ensure_status_icon (manager);
+ gtk_status_icon_set_visible (manager->priv->status_icon, show);
+}
+
+#ifdef HAVE_LIBMATENOTIFY
+static void
+on_notification_closed (NotifyNotification *notification,
+ MsdA11yKeyboardManager *manager)
+{
+ g_object_unref (manager->priv->notification);
+ manager->priv->notification = NULL;
+}
+
+static void
+on_slow_keys_action (NotifyNotification *notification,
+ const char *action,
+ MsdA11yKeyboardManager *manager)
+{
+ gboolean res;
+ int response_id;
+
+ g_assert (action != NULL);
+
+ if (strcmp (action, "accept") == 0) {
+ response_id = GTK_RESPONSE_ACCEPT;
+ } else if (strcmp (action, "reject") == 0) {
+ response_id = GTK_RESPONSE_REJECT;
+ } else {
+ return;
+ }
+
+ res = ax_response_callback (manager, NULL,
+ response_id, XkbSlowKeysMask,
+ manager->priv->slowkeys_shortcut_val);
+ if (res) {
+ notify_notification_close (manager->priv->notification, NULL);
+ }
+}
+
+static void
+on_sticky_keys_action (NotifyNotification *notification,
+ const char *action,
+ MsdA11yKeyboardManager *manager)
+{
+ gboolean res;
+ int response_id;
+
+ g_assert (action != NULL);
+
+ if (strcmp (action, "accept") == 0) {
+ response_id = GTK_RESPONSE_ACCEPT;
+ } else if (strcmp (action, "reject") == 0) {
+ response_id = GTK_RESPONSE_REJECT;
+ } else {
+ return;
+ }
+
+ res = ax_response_callback (manager, NULL,
+ response_id, XkbStickyKeysMask,
+ manager->priv->stickykeys_shortcut_val);
+ if (res) {
+ notify_notification_close (manager->priv->notification, NULL);
+ }
+}
+
+#endif /* HAVE_LIBMATENOTIFY */
+
+static gboolean
+ax_slowkeys_warning_post_bubble (MsdA11yKeyboardManager *manager,
+ gboolean enabled)
+{
+#ifdef HAVE_LIBMATENOTIFY
+ gboolean res;
+ const char *title;
+ const char *message;
+ GError *error;
+
+ title = enabled ?
+ _("Do you want to activate Slow Keys?") :
+ _("Do you want to deactivate Slow Keys?");
+ message = _("You just held down the Shift key for 8 seconds. This is the shortcut "
+ "for the Slow Keys feature, which affects the way your keyboard works.");
+
+ if (manager->priv->status_icon == NULL || ! gtk_status_icon_is_embedded (manager->priv->status_icon)) {
+ return FALSE;
+ }
+
+ if (manager->priv->slowkeys_alert != NULL) {
+ gtk_widget_destroy (manager->priv->slowkeys_alert);
+ }
+
+ if (manager->priv->notification != NULL) {
+ notify_notification_close (manager->priv->notification, NULL);
+ }
+
+ msd_a11y_keyboard_manager_ensure_status_icon (manager);
+ manager->priv->notification = notify_notification_new (title,
+ message,
+ "preferences-desktop-accessibility",
+ NULL);
+ notify_notification_attach_to_status_icon (manager->priv->notification, manager->priv->status_icon);
+ notify_notification_set_timeout (manager->priv->notification, NOTIFICATION_TIMEOUT * 1000);
+
+ notify_notification_add_action (manager->priv->notification,
+ "reject",
+ enabled ? _("Don't activate") : _("Don't deactivate"),
+ (NotifyActionCallback) on_slow_keys_action,
+ manager,
+ NULL);
+ notify_notification_add_action (manager->priv->notification,
+ "accept",
+ enabled ? _("Activate") : _("Deactivate"),
+ (NotifyActionCallback) on_slow_keys_action,
+ manager,
+ NULL);
+
+ g_signal_connect (manager->priv->notification,
+ "closed",
+ G_CALLBACK (on_notification_closed),
+ manager);
+
+ error = NULL;
+ res = notify_notification_show (manager->priv->notification, &error);
+ if (! res) {
+ g_warning ("MsdA11yKeyboardManager: unable to show notification: %s", error->message);
+ g_error_free (error);
+ notify_notification_close (manager->priv->notification, NULL);
+ }
+
+ return res;
+#else
+ return FALSE;
+#endif /* HAVE_LIBMATENOTIFY */
+}
+
+
+static void
+ax_slowkeys_warning_post_dialog (MsdA11yKeyboardManager *manager,
+ gboolean enabled)
+{
+ const char *title;
+ const char *message;
+
+ title = enabled ?
+ _("Do you want to activate Slow Keys?") :
+ _("Do you want to deactivate Slow Keys?");
+ message = _("You just held down the Shift key for 8 seconds. This is the shortcut "
+ "for the Slow Keys feature, which affects the way your keyboard works.");
+
+ if (manager->priv->slowkeys_alert != NULL) {
+ gtk_widget_show (manager->priv->slowkeys_alert);
+ return;
+ }
+
+ manager->priv->slowkeys_alert = gtk_message_dialog_new (NULL,
+ 0,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ "%s", title);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (manager->priv->slowkeys_alert),
+ "%s", message);
+
+ gtk_dialog_add_button (GTK_DIALOG (manager->priv->slowkeys_alert),
+ GTK_STOCK_HELP,
+ GTK_RESPONSE_HELP);
+ gtk_dialog_add_button (GTK_DIALOG (manager->priv->slowkeys_alert),
+ enabled ? _("Do_n't activate") : _("Do_n't deactivate"),
+ GTK_RESPONSE_REJECT);
+ gtk_dialog_add_button (GTK_DIALOG (manager->priv->slowkeys_alert),
+ enabled ? _("_Activate") : _("_Deactivate"),
+ GTK_RESPONSE_ACCEPT);
+
+ gtk_window_set_title (GTK_WINDOW (manager->priv->slowkeys_alert),
+ _("Slow Keys Alert"));
+ gtk_window_set_icon_name (GTK_WINDOW (manager->priv->slowkeys_alert),
+ "input-keyboard");
+ gtk_dialog_set_default_response (GTK_DIALOG (manager->priv->slowkeys_alert),
+ GTK_RESPONSE_ACCEPT);
+
+ g_signal_connect (manager->priv->slowkeys_alert,
+ "response",
+ G_CALLBACK (ax_slowkeys_response),
+ manager);
+ gtk_widget_show (manager->priv->slowkeys_alert);
+
+ g_object_add_weak_pointer (G_OBJECT (manager->priv->slowkeys_alert),
+ (gpointer*) &manager->priv->slowkeys_alert);
+}
+
+static void
+ax_slowkeys_warning_post (MsdA11yKeyboardManager *manager,
+ gboolean enabled)
+{
+
+ manager->priv->slowkeys_shortcut_val = enabled;
+
+ /* alway try to show something */
+ if (! ax_slowkeys_warning_post_bubble (manager, enabled)) {
+ ax_slowkeys_warning_post_dialog (manager, enabled);
+ }
+}
+
+static gboolean
+ax_stickykeys_warning_post_bubble (MsdA11yKeyboardManager *manager,
+ gboolean enabled)
+{
+#ifdef HAVE_LIBMATENOTIFY
+ gboolean res;
+ const char *title;
+ const char *message;
+ GError *error;
+
+ title = enabled ?
+ _("Do you want to activate Sticky Keys?") :
+ _("Do you want to deactivate Sticky Keys?");
+ message = enabled ?
+ _("You just pressed the Shift key 5 times in a row. This is the shortcut "
+ "for the Sticky Keys feature, which affects the way your keyboard works.") :
+ _("You just pressed two keys at once, or pressed the Shift key 5 times in a row. "
+ "This turns off the Sticky Keys feature, which affects the way your keyboard works.");
+
+ if (manager->priv->status_icon == NULL || ! gtk_status_icon_is_embedded (manager->priv->status_icon)) {
+ return FALSE;
+ }
+
+ if (manager->priv->slowkeys_alert != NULL) {
+ gtk_widget_destroy (manager->priv->slowkeys_alert);
+ }
+
+ if (manager->priv->notification != NULL) {
+ notify_notification_close (manager->priv->notification, NULL);
+ }
+
+ msd_a11y_keyboard_manager_ensure_status_icon (manager);
+ manager->priv->notification = notify_notification_new (title,
+ message,
+ "preferences-desktop-accessibility",
+ NULL);
+ notify_notification_attach_to_status_icon (manager->priv->notification, manager->priv->status_icon);
+ notify_notification_set_timeout (manager->priv->notification, NOTIFICATION_TIMEOUT * 1000);
+
+ notify_notification_add_action (manager->priv->notification,
+ "reject",
+ enabled ? _("Don't activate") : _("Don't deactivate"),
+ (NotifyActionCallback) on_sticky_keys_action,
+ manager,
+ NULL);
+ notify_notification_add_action (manager->priv->notification,
+ "accept",
+ enabled ? _("Activate") : _("Deactivate"),
+ (NotifyActionCallback) on_sticky_keys_action,
+ manager,
+ NULL);
+
+ g_signal_connect (manager->priv->notification,
+ "closed",
+ G_CALLBACK (on_notification_closed),
+ manager);
+
+ error = NULL;
+ res = notify_notification_show (manager->priv->notification, &error);
+ if (! res) {
+ g_warning ("MsdA11yKeyboardManager: unable to show notification: %s", error->message);
+ g_error_free (error);
+ notify_notification_close (manager->priv->notification, NULL);
+ }
+
+ return res;
+#else
+ return FALSE;
+#endif /* HAVE_LIBMATENOTIFY */
+}
+
+static void
+ax_stickykeys_warning_post_dialog (MsdA11yKeyboardManager *manager,
+ gboolean enabled)
+{
+ const char *title;
+ const char *message;
+
+ title = enabled ?
+ _("Do you want to activate Sticky Keys?") :
+ _("Do you want to deactivate Sticky Keys?");
+ message = enabled ?
+ _("You just pressed the Shift key 5 times in a row. This is the shortcut "
+ "for the Sticky Keys feature, which affects the way your keyboard works.") :
+ _("You just pressed two keys at once, or pressed the Shift key 5 times in a row. "
+ "This turns off the Sticky Keys feature, which affects the way your keyboard works.");
+
+ if (manager->priv->stickykeys_alert != NULL) {
+ gtk_widget_show (manager->priv->stickykeys_alert);
+ return;
+ }
+
+ manager->priv->stickykeys_alert = gtk_message_dialog_new (NULL,
+ 0,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ "%s", title);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (manager->priv->stickykeys_alert),
+ "%s", message);
+
+ gtk_dialog_add_button (GTK_DIALOG (manager->priv->stickykeys_alert),
+ GTK_STOCK_HELP,
+ GTK_RESPONSE_HELP);
+ gtk_dialog_add_button (GTK_DIALOG (manager->priv->stickykeys_alert),
+ enabled ? _("Do_n't activate") : _("Do_n't deactivate"),
+ GTK_RESPONSE_REJECT);
+ gtk_dialog_add_button (GTK_DIALOG (manager->priv->stickykeys_alert),
+ enabled ? _("_Activate") : _("_Deactivate"),
+ GTK_RESPONSE_ACCEPT);
+
+ gtk_window_set_title (GTK_WINDOW (manager->priv->stickykeys_alert),
+ _("Sticky Keys Alert"));
+ gtk_window_set_icon_name (GTK_WINDOW (manager->priv->stickykeys_alert),
+ "input-keyboard");
+ gtk_dialog_set_default_response (GTK_DIALOG (manager->priv->stickykeys_alert),
+ GTK_RESPONSE_ACCEPT);
+
+ g_signal_connect (manager->priv->stickykeys_alert,
+ "response",
+ G_CALLBACK (ax_stickykeys_response),
+ manager);
+ gtk_widget_show (manager->priv->stickykeys_alert);
+
+ g_object_add_weak_pointer (G_OBJECT (manager->priv->stickykeys_alert),
+ (gpointer*) &manager->priv->stickykeys_alert);
+}
+
+static void
+ax_stickykeys_warning_post (MsdA11yKeyboardManager *manager,
+ gboolean enabled)
+{
+
+ manager->priv->stickykeys_shortcut_val = enabled;
+
+ /* alway try to show something */
+ if (! ax_stickykeys_warning_post_bubble (manager, enabled)) {
+ ax_stickykeys_warning_post_dialog (manager, enabled);
+ }
+}
+
+static void
+set_mateconf_from_server (MsdA11yKeyboardManager *manager)
+{
+ MateConfClient *client;
+ MateConfChangeSet *cs;
+ XkbDescRec *desc;
+ gboolean changed = FALSE;
+ gboolean slowkeys_changed;
+ gboolean stickykeys_changed;
+
+ cs = mateconf_change_set_new ();
+ desc = get_xkb_desc_rec (manager);
+ if (! desc) {
+ return;
+ }
+
+ client = mateconf_client_get_default ();
+
+ /*
+ fprintf (stderr, "changed to : 0x%x\n", desc->ctrls->enabled_ctrls);
+ fprintf (stderr, "changed to : 0x%x (2)\n", desc->ctrls->ax_options);
+ */
+
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/enable",
+ desc->ctrls->enabled_ctrls & XkbAccessXKeysMask);
+
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/feature_state_change_beep",
+ desc->ctrls->ax_options & (XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask));
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/timeout_enable",
+ desc->ctrls->enabled_ctrls & XkbAccessXTimeoutMask);
+ changed |= set_int (client,
+ cs,
+ CONFIG_ROOT "/timeout",
+ desc->ctrls->ax_timeout);
+
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/bouncekeys_enable",
+ desc->ctrls->enabled_ctrls & XkbBounceKeysMask);
+ changed |= set_int (client,
+ cs,
+ CONFIG_ROOT "/bouncekeys_delay",
+ desc->ctrls->debounce_delay);
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/bouncekeys_beep_reject",
+ desc->ctrls->ax_options & XkbAX_BKRejectFBMask);
+
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/mousekeys_enable",
+ desc->ctrls->enabled_ctrls & XkbMouseKeysMask);
+ changed |= set_int (client,
+ cs,
+ CONFIG_ROOT "/mousekeys_max_speed",
+ desc->ctrls->mk_max_speed * (1000 / desc->ctrls->mk_interval));
+ /* NOTE : mk_time_to_max is measured in events not time */
+ changed |= set_int (client,
+ cs,
+ CONFIG_ROOT "/mousekeys_accel_time",
+ desc->ctrls->mk_time_to_max * desc->ctrls->mk_interval);
+ changed |= set_int (client,
+ cs,
+ CONFIG_ROOT "/mousekeys_init_delay",
+ desc->ctrls->mk_delay);
+
+ slowkeys_changed = set_bool (client,
+ cs,
+ CONFIG_ROOT "/slowkeys_enable",
+ desc->ctrls->enabled_ctrls & XkbSlowKeysMask);
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/slowkeys_beep_press",
+ desc->ctrls->ax_options & XkbAX_SKPressFBMask);
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/slowkeys_beep_accept",
+ desc->ctrls->ax_options & XkbAX_SKAcceptFBMask);
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/slowkeys_beep_reject",
+ desc->ctrls->ax_options & XkbAX_SKRejectFBMask);
+ changed |= set_int (client,
+ cs,
+ CONFIG_ROOT "/slowkeys_delay",
+ desc->ctrls->slow_keys_delay);
+
+ stickykeys_changed = set_bool (client,
+ cs,
+ CONFIG_ROOT "/stickykeys_enable",
+ desc->ctrls->enabled_ctrls & XkbStickyKeysMask);
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/stickykeys_two_key_off",
+ desc->ctrls->ax_options & XkbAX_TwoKeysMask);
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/stickykeys_modifier_beep",
+ desc->ctrls->ax_options & XkbAX_StickyKeysFBMask);
+
+ changed |= set_bool (client,
+ cs,
+ CONFIG_ROOT "/togglekeys_enable",
+ desc->ctrls->ax_options & XkbAX_IndicatorFBMask);
+
+ if (!changed && stickykeys_changed ^ slowkeys_changed) {
+ /*
+ * sticky or slowkeys has changed, singly, without our intervention.
+ * 99% chance this is due to a keyboard shortcut being used.
+ * we need to detect via this hack until we get
+ * XkbAXN_AXKWarning notifications working (probable XKB bug),
+ * at which time we can directly intercept such shortcuts instead.
+ * See cb_xkb_event_filter () below.
+ */
+
+ /* sanity check: are keyboard shortcuts available? */
+ if (desc->ctrls->enabled_ctrls & XkbAccessXKeysMask) {
+ if (slowkeys_changed) {
+ ax_slowkeys_warning_post (manager,
+ desc->ctrls->enabled_ctrls & XkbSlowKeysMask);
+ } else {
+ ax_stickykeys_warning_post (manager,
+ desc->ctrls->enabled_ctrls & XkbStickyKeysMask);
+ }
+ }
+ }
+
+ XkbFreeKeyboard (desc, XkbAllComponentsMask, True);
+
+ changed |= (stickykeys_changed | slowkeys_changed);
+
+ if (changed) {
+ mateconf_client_commit_change_set (client, cs, FALSE, NULL);
+ mateconf_client_suggest_sync (client, NULL);
+ }
+ mateconf_change_set_unref (cs);
+
+ g_object_unref (client);
+}
+
+static GdkFilterReturn
+cb_xkb_event_filter (GdkXEvent *xevent,
+ GdkEvent *ignored1,
+ MsdA11yKeyboardManager *manager)
+{
+ XEvent *xev = (XEvent *) xevent;
+ XkbEvent *xkbEv = (XkbEvent *) xevent;
+
+ if (xev->xany.type == (manager->priv->xkbEventBase + XkbEventCode) &&
+ xkbEv->any.xkb_type == XkbControlsNotify) {
+ d ("XKB state changed");
+ set_mateconf_from_server (manager);
+ } else if (xev->xany.type == (manager->priv->xkbEventBase + XkbEventCode) &&
+ xkbEv->any.xkb_type == XkbAccessXNotify) {
+ if (xkbEv->accessx.detail == XkbAXN_AXKWarning) {
+ d ("About to turn on an AccessX feature from the keyboard!");
+ /*
+ * TODO: when XkbAXN_AXKWarnings start working, we need to
+ * invoke ax_keys_warning_dialog_run here instead of in
+ * set_mateconf_from_server().
+ */
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+keyboard_callback (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ MsdA11yKeyboardManager *manager)
+{
+ set_server_from_mateconf (manager, client);
+ maybe_show_status_icon (manager);
+}
+
+static void
+register_config_callback (MsdA11yKeyboardManager *manager,
+ MateConfClient *client,
+ const char *path,
+ MateConfClientNotifyFunc func,
+ guint *notify)
+{
+ mateconf_client_add_dir (client, path, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ *notify = mateconf_client_notify_add (client, path, func, manager, NULL, NULL);
+}
+
+static gboolean
+start_a11y_keyboard_idle_cb (MsdA11yKeyboardManager *manager)
+{
+ guint event_mask;
+ MateConfClient *client;
+
+ g_debug ("Starting a11y_keyboard manager");
+ mate_settings_profile_start (NULL);
+
+ if (!xkb_enabled (manager))
+ goto out;
+
+ client = mateconf_client_get_default ();
+
+ register_config_callback (manager,
+ client,
+ CONFIG_ROOT,
+ (MateConfClientNotifyFunc) keyboard_callback,
+ &manager->priv->mateconf_notify);
+
+#ifdef HAVE_X11_EXTENSIONS_XINPUT_H
+ set_devicepresence_handler (manager);
+#endif
+
+ /* Save current xkb state so we can restore it on exit
+ */
+ manager->priv->original_xkb_desc = get_xkb_desc_rec (manager);
+
+ event_mask = XkbControlsNotifyMask;
+#ifdef DEBUG_ACCESSIBILITY
+ event_mask |= XkbAccessXNotifyMask; /* make default when AXN_AXKWarning works */
+#endif
+
+ /* be sure to init before starting to monitor the server */
+ set_server_from_mateconf (manager, client);
+ g_object_unref (client);
+
+ XkbSelectEvents (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+ XkbUseCoreKbd,
+ event_mask,
+ event_mask);
+
+ gdk_window_add_filter (NULL,
+ (GdkFilterFunc) cb_xkb_event_filter,
+ manager);
+
+ maybe_show_status_icon (manager);
+
+ out:
+ mate_settings_profile_end (NULL);
+
+ return FALSE;
+}
+
+
+gboolean
+msd_a11y_keyboard_manager_start (MsdA11yKeyboardManager *manager,
+ GError **error)
+{
+ mate_settings_profile_start (NULL);
+
+ g_idle_add ((GSourceFunc) start_a11y_keyboard_idle_cb, manager);
+
+ mate_settings_profile_end (NULL);
+
+ return TRUE;
+}
+
+static void
+restore_server_xkb_config (MsdA11yKeyboardManager *manager)
+{
+ gdk_error_trap_push ();
+ XkbSetControls (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+ XkbSlowKeysMask |
+ XkbBounceKeysMask |
+ XkbStickyKeysMask |
+ XkbMouseKeysMask |
+ XkbMouseKeysAccelMask |
+ XkbAccessXKeysMask |
+ XkbAccessXTimeoutMask |
+ XkbAccessXFeedbackMask |
+ XkbControlsEnabledMask,
+ manager->priv->original_xkb_desc);
+
+ XkbFreeKeyboard (manager->priv->original_xkb_desc,
+ XkbAllComponentsMask, True);
+
+ XSync (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), FALSE);
+ gdk_error_trap_pop ();
+
+ manager->priv->original_xkb_desc = NULL;
+}
+
+void
+msd_a11y_keyboard_manager_stop (MsdA11yKeyboardManager *manager)
+{
+ MsdA11yKeyboardManagerPrivate *p = manager->priv;
+
+ g_debug ("Stopping a11y_keyboard manager");
+
+#ifdef HAVE_X11_EXTENSIONS_XINPUT_H
+ gdk_window_remove_filter (NULL, devicepresence_filter, manager);
+#endif
+
+ if (p->status_icon)
+ gtk_status_icon_set_visible (p->status_icon, FALSE);
+
+ if (p->mateconf_notify != 0) {
+ MateConfClient *client = mateconf_client_get_default ();
+ mateconf_client_remove_dir (client, CONFIG_ROOT, NULL);
+ mateconf_client_notify_remove (client, p->mateconf_notify);
+ g_object_unref (client);
+ p->mateconf_notify = 0;
+ }
+
+ gdk_window_remove_filter (NULL,
+ (GdkFilterFunc) cb_xkb_event_filter,
+ manager);
+
+ /* Disable all the AccessX bits
+ */
+ restore_server_xkb_config (manager);
+
+ if (p->slowkeys_alert != NULL)
+ gtk_widget_destroy (p->slowkeys_alert);
+
+ if (p->stickykeys_alert != NULL)
+ gtk_widget_destroy (p->stickykeys_alert);
+
+ p->slowkeys_shortcut_val = FALSE;
+ p->stickykeys_shortcut_val = FALSE;
+}
+
+static void
+msd_a11y_keyboard_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MsdA11yKeyboardManager *self;
+
+ self = MSD_A11Y_KEYBOARD_MANAGER (object);
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+msd_a11y_keyboard_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MsdA11yKeyboardManager *self;
+
+ self = MSD_A11Y_KEYBOARD_MANAGER (object);
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GObject *
+msd_a11y_keyboard_manager_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_properties)
+{
+ MsdA11yKeyboardManager *a11y_keyboard_manager;
+ MsdA11yKeyboardManagerClass *klass;
+
+ klass = MSD_A11Y_KEYBOARD_MANAGER_CLASS (g_type_class_peek (MSD_TYPE_A11Y_KEYBOARD_MANAGER));
+
+ a11y_keyboard_manager = MSD_A11Y_KEYBOARD_MANAGER (G_OBJECT_CLASS (msd_a11y_keyboard_manager_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_properties));
+
+ return G_OBJECT (a11y_keyboard_manager);
+}
+
+static void
+msd_a11y_keyboard_manager_dispose (GObject *object)
+{
+ MsdA11yKeyboardManager *a11y_keyboard_manager;
+
+ a11y_keyboard_manager = MSD_A11Y_KEYBOARD_MANAGER (object);
+
+ G_OBJECT_CLASS (msd_a11y_keyboard_manager_parent_class)->dispose (object);
+}
+
+static void
+msd_a11y_keyboard_manager_class_init (MsdA11yKeyboardManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = msd_a11y_keyboard_manager_get_property;
+ object_class->set_property = msd_a11y_keyboard_manager_set_property;
+ object_class->constructor = msd_a11y_keyboard_manager_constructor;
+ object_class->dispose = msd_a11y_keyboard_manager_dispose;
+ object_class->finalize = msd_a11y_keyboard_manager_finalize;
+
+ g_type_class_add_private (klass, sizeof (MsdA11yKeyboardManagerPrivate));
+}
+
+static void
+on_preferences_dialog_response (GtkDialog *dialog,
+ int response,
+ MsdA11yKeyboardManager *manager)
+{
+ g_signal_handlers_disconnect_by_func (dialog,
+ on_preferences_dialog_response,
+ manager);
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ manager->priv->preferences_dialog = NULL;
+}
+
+static void
+on_status_icon_activate (GtkStatusIcon *status_icon,
+ MsdA11yKeyboardManager *manager)
+{
+ if (manager->priv->preferences_dialog == NULL) {
+ manager->priv->preferences_dialog = msd_a11y_preferences_dialog_new ();
+ g_signal_connect (manager->priv->preferences_dialog,
+ "response",
+ G_CALLBACK (on_preferences_dialog_response),
+ manager);
+
+ gtk_window_present (GTK_WINDOW (manager->priv->preferences_dialog));
+ } else {
+ g_signal_handlers_disconnect_by_func (manager->priv->preferences_dialog,
+ on_preferences_dialog_response,
+ manager);
+ gtk_widget_destroy (GTK_WIDGET (manager->priv->preferences_dialog));
+ manager->priv->preferences_dialog = NULL;
+ }
+}
+
+static void
+msd_a11y_keyboard_manager_ensure_status_icon (MsdA11yKeyboardManager *manager)
+{
+ mate_settings_profile_start (NULL);
+
+ if (!manager->priv->status_icon) {
+
+ manager->priv->status_icon = gtk_status_icon_new_from_icon_name ("preferences-desktop-accessibility");
+ g_signal_connect (manager->priv->status_icon,
+ "activate",
+ G_CALLBACK (on_status_icon_activate),
+ manager);
+ }
+
+ mate_settings_profile_end (NULL);
+}
+
+static void
+msd_a11y_keyboard_manager_init (MsdA11yKeyboardManager *manager)
+{
+ manager->priv = MSD_A11Y_KEYBOARD_MANAGER_GET_PRIVATE (manager);
+
+#ifdef HAVE_LIBMATENOTIFY
+ notify_init ("mate-settings-daemon");
+#endif /* HAVE_LIBMATENOTIFY */
+}
+
+static void
+msd_a11y_keyboard_manager_finalize (GObject *object)
+{
+ MsdA11yKeyboardManager *a11y_keyboard_manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (MSD_IS_A11Y_KEYBOARD_MANAGER (object));
+
+ a11y_keyboard_manager = MSD_A11Y_KEYBOARD_MANAGER (object);
+
+ g_return_if_fail (a11y_keyboard_manager->priv != NULL);
+
+ G_OBJECT_CLASS (msd_a11y_keyboard_manager_parent_class)->finalize (object);
+}
+
+MsdA11yKeyboardManager *
+msd_a11y_keyboard_manager_new (void)
+{
+ if (manager_object != NULL) {
+ g_object_ref (manager_object);
+ } else {
+ manager_object = g_object_new (MSD_TYPE_A11Y_KEYBOARD_MANAGER, NULL);
+ g_object_add_weak_pointer (manager_object,
+ (gpointer *) &manager_object);
+ }
+
+ return MSD_A11Y_KEYBOARD_MANAGER (manager_object);
+}