[gnome-flashback] a11y-keyboard: initial version
- From: Alberts Muktupāvels <muktupavels src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-flashback] a11y-keyboard: initial version
- Date: Wed, 12 Sep 2018 11:55:18 +0000 (UTC)
commit 15a4900dfb17860b7c60476a6e6bb3eddd622eaf
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date: Fri Sep 7 15:15:11 2018 +0300
a11y-keyboard: initial version
GNOME Settings Daemon removed a11y-keyboard plugin in 9553071d0c26
commit because related functionality has been moved to mutter.
Fix this by adding new a11y-keyboard module in gnome-flashback. This
module is heavily based on original plugin from gnome-settings-daemon
and should provide same functionality.
https://gitlab.gnome.org/GNOME/gnome-flashback/issues/6
configure.ac | 10 +-
data/schemas/org.gnome.gnome-flashback.gschema.xml | 5 +
gnome-flashback/Makefile.am | 2 +
gnome-flashback/gf-application.c | 4 +
gnome-flashback/liba11y-keyboard/Makefile.am | 50 ++
.../liba11y-keyboard/gf-a11y-keyboard.c | 941 +++++++++++++++++++++
.../liba11y-keyboard/gf-a11y-keyboard.h | 33 +
.../org.freedesktop.Notifications.xml | 45 +
po/POTFILES.in | 1 +
9 files changed, 1090 insertions(+), 1 deletion(-)
---
diff --git a/configure.ac b/configure.ac
index 0d22748..f18a39d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -92,7 +92,7 @@ GTK_REQUIRED=3.22.0
LIBGNOME_DESKTOP_REQUIRED=3.12.0
CANBERRA_REQUIRED=0.13
GLIB_REQUIRED=2.44.0
-GSETTINGS_DESKTOP_SCHEMAS_REQUIRED=3.21.4
+GSETTINGS_DESKTOP_SCHEMAS_REQUIRED=3.24.0
POLKIT_AGENT_REQUIRED=0.97
POLKIT_GOBJECT_REQUIRED=0.97
IBUS_REQUIRED=1.5.2
@@ -115,6 +115,13 @@ PKG_CHECK_MODULES([GNOME_FLASHBACK], [
gtk+-3.0 >= $GTK_REQUIRED
])
+PKG_CHECK_MODULES([A11Y_KEYBOARD], [
+ gio-unix-2.0 >= $GLIB_REQUIRED
+ gtk+-3.0
+ gsettings-desktop-schemas >= $GSETTINGS_DESKTOP_SCHEMAS_REQUIRED
+ x11
+])
+
PKG_CHECK_MODULES([AUDIO_DEVICE_SELECTION], [
gio-unix-2.0 >= $GLIB_REQUIRED
gtk+-3.0 >= $GTK_REQUIRED
@@ -290,6 +297,7 @@ AC_CONFIG_FILES([
data/xsessions/Makefile
gnome-flashback/Makefile
+ gnome-flashback/liba11y-keyboard/Makefile
gnome-flashback/libaudio-device-selection/Makefile
gnome-flashback/libautomount-manager/Makefile
gnome-flashback/libbluetooth-applet/Makefile
diff --git a/data/schemas/org.gnome.gnome-flashback.gschema.xml
b/data/schemas/org.gnome.gnome-flashback.gschema.xml
index 79d115b..529ae34 100644
--- a/data/schemas/org.gnome.gnome-flashback.gschema.xml
+++ b/data/schemas/org.gnome.gnome-flashback.gschema.xml
@@ -1,5 +1,10 @@
<schemalist gettext-domain="gnome-flashback">
<schema id="org.gnome.gnome-flashback" path="/org/gnome/gnome-flashback/">
+ <key name="a11y-keyboard" type="b">
+ <default>true</default>
+ <summary>a11y-keyboard</summary>
+ </key>
+
<key name="audio-device-selection" type="b">
<default>true</default>
<summary>Audio device selection</summary>
diff --git a/gnome-flashback/Makefile.am b/gnome-flashback/Makefile.am
index 580c131..7fc52aa 100644
--- a/gnome-flashback/Makefile.am
+++ b/gnome-flashback/Makefile.am
@@ -1,6 +1,7 @@
NULL =
SUBDIRS = \
+ liba11y-keyboard \
libaudio-device-selection \
libautomount-manager \
libbluetooth-applet \
@@ -60,6 +61,7 @@ gnome_flashback_LDFLAGS = \
gnome_flashback_LDADD = \
$(GNOME_FLASHBACK_LIBS) \
$(top_builddir)/backends/libbackends.la \
+ $(top_builddir)/gnome-flashback/liba11y-keyboard/liba11y-keyboard.la \
$(top_builddir)/gnome-flashback/libaudio-device-selection/libaudio-device-selection.la \
$(top_builddir)/gnome-flashback/libautomount-manager/libautomount-manager.la \
$(top_builddir)/gnome-flashback/libbluetooth-applet/libbluetooth-applet.la \
diff --git a/gnome-flashback/gf-application.c b/gnome-flashback/gf-application.c
index 51d841b..ab9dd5e 100644
--- a/gnome-flashback/gf-application.c
+++ b/gnome-flashback/gf-application.c
@@ -23,6 +23,7 @@
#include "gf-application.h"
#include "gf-confirm-display-change-dialog.h"
#include "backends/gf-backend.h"
+#include "liba11y-keyboard/gf-a11y-keyboard.h"
#include "libaudio-device-selection/gf-audio-device-selection.h"
#include "libautomount-manager/gsd-automount-manager.h"
#include "libbluetooth-applet/gf-bluetooth-applet.h"
@@ -58,6 +59,7 @@ struct _GfApplication
FlashbackIdleMonitor *idle_monitor;
FlashbackPolkit *polkit;
FlashbackShell *shell;
+ GfA11yKeyboard *a11y_keyboard;
GfAudioDeviceSelection *audio_device_selection;
GfBluetoothApplet *bluetooth;
GfDesktopBackground *background;
@@ -157,6 +159,7 @@ settings_changed (GSettings *settings,
SETTING_CHANGED (idle_monitor, "idle-monitor", flashback_idle_monitor_new)
SETTING_CHANGED (polkit, "polkit", flashback_polkit_new)
SETTING_CHANGED (shell, "shell", flashback_shell_new)
+ SETTING_CHANGED (a11y_keyboard, "a11y-keyboard", gf_a11y_keyboard_new)
SETTING_CHANGED (audio_device_selection, "audio-device-selection", gf_audio_device_selection_new)
SETTING_CHANGED (bluetooth, "bluetooth-applet", gf_bluetooth_applet_new)
SETTING_CHANGED (background, "desktop-background", gf_desktop_background_new)
@@ -203,6 +206,7 @@ gf_application_dispose (GObject *object)
g_clear_object (&application->idle_monitor);
g_clear_object (&application->polkit);
g_clear_object (&application->shell);
+ g_clear_object (&application->a11y_keyboard);
g_clear_object (&application->audio_device_selection);
g_clear_object (&application->bluetooth);
g_clear_object (&application->background);
diff --git a/gnome-flashback/liba11y-keyboard/Makefile.am b/gnome-flashback/liba11y-keyboard/Makefile.am
new file mode 100644
index 0000000..ff3385d
--- /dev/null
+++ b/gnome-flashback/liba11y-keyboard/Makefile.am
@@ -0,0 +1,50 @@
+NULL =
+
+noinst_LTLIBRARIES = \
+ liba11y-keyboard.la \
+ $(NULL)
+
+liba11y_keyboard_la_CPPFLAGS = \
+ $(NULL)
+
+liba11y_keyboard_la_CFLAGS = \
+ $(A11Y_KEYBOARD_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(AM_CFLAGS) \
+ $(NULL)
+
+liba11y_keyboard_la_SOURCES = \
+ gf-a11y-keyboard.c \
+ gf-a11y-keyboard.h \
+ $(BUILT_SOURCES) \
+ $(NULL)
+
+liba11y_keyboard_la_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(AM_LDFLAGS) \
+ $(NULL)
+
+liba11y_keyboard_la_LIBADD = \
+ $(A11Y_KEYBOARD_LIBS) \
+ $(NULL)
+
+gf-notifications-gen.h:
+gf-notifications-gen.c: org.freedesktop.Notifications.xml
+ $(AM_V_GEN) $(GDBUS_CODEGEN) --c-namespace Gf \
+ --generate-c-code gf-notifications-gen \
+ $(srcdir)/org.freedesktop.Notifications.xml
+
+BUILT_SOURCES = \
+ gf-notifications-gen.c \
+ gf-notifications-gen.h \
+ $(NULL)
+
+EXTRA_DIST = \
+ org.freedesktop.Notifications.xml \
+ $(NULL)
+
+CLEANFILES = \
+ $(BUILT_SOURCES) \
+ $(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/gnome-flashback/liba11y-keyboard/gf-a11y-keyboard.c
b/gnome-flashback/liba11y-keyboard/gf-a11y-keyboard.c
new file mode 100644
index 0000000..36a4f47
--- /dev/null
+++ b/gnome-flashback/liba11y-keyboard/gf-a11y-keyboard.c
@@ -0,0 +1,941 @@
+/*
+ * Copyright (C) 2001 Ximian, Inc.
+ * Copyright (C) 2007 William Jon McCann <mccann jhu edu>
+ * Copyright (C) 2018 Alberts Muktupāvels
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "gf-a11y-keyboard.h"
+
+#include "gf-notifications-gen.h"
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <glib/gi18n.h>
+
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKBstr.h>
+
+#define KEYBOARD_A11Y_SCHEMA "org.gnome.desktop.a11y.keyboard"
+
+#define DEFAULT_XKB_SET_CONTROLS_MASK XkbSlowKeysMask | \
+ XkbBounceKeysMask | \
+ XkbStickyKeysMask | \
+ XkbMouseKeysMask | \
+ XkbMouseKeysAccelMask | \
+ XkbAccessXKeysMask | \
+ XkbAccessXTimeoutMask | \
+ XkbAccessXFeedbackMask | \
+ XkbControlsEnabledMask
+
+struct _GfA11yKeyboard
+{
+ GObject parent;
+
+ guint start_idle_id;
+ int xkbEventBase;
+ guint device_added_id;
+ gboolean stickykeys_shortcut_val;
+ gboolean slowkeys_shortcut_val;
+
+ XkbDescRec *desc;
+
+ GSettings *settings;
+
+ GfNotificationsGen *notifications;
+ guint slowkeys_id;
+ guint stickykeys_id;
+};
+
+G_DEFINE_TYPE (GfA11yKeyboard, gf_a11y_keyboard, G_TYPE_OBJECT)
+
+static int
+get_int (GSettings *settings,
+ const char *key)
+{
+ int res;
+
+ res = g_settings_get_int (settings, key);
+ if (res <= 0)
+ res = 1;
+
+ return res;
+}
+
+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_gsettings (XkbDescRec *desc,
+ GSettings *settings,
+ char const *key,
+ unsigned long mask)
+{
+ gboolean result;
+
+ result = g_settings_get_boolean (settings, key);
+ desc->ctrls->enabled_ctrls = set_clear (result, desc->ctrls->enabled_ctrls, mask);
+
+ return result;
+}
+
+static XkbDescRec *
+get_xkb_desc_rec (void)
+{
+ GdkDisplay *display;
+ Display *xdisplay;
+ XkbDescRec *desc;
+ Status status;
+
+ display = gdk_display_get_default ();
+ xdisplay = gdk_x11_display_get_xdisplay (display);
+
+ gdk_x11_display_error_trap_push (display);
+
+ desc = XkbGetMap (xdisplay, XkbAllMapComponentsMask, XkbUseCoreKbd);
+ status = Success;
+
+ if (desc != NULL)
+ {
+ desc->ctrls = NULL;
+ status = XkbGetControls (xdisplay, XkbAllControlsMask, desc);
+ }
+
+ gdk_x11_display_error_trap_pop_ignored (display);
+
+ 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 void
+set_server_from_gsettings (GfA11yKeyboard *a11y_keyboard)
+{
+ XkbDescRec *desc;
+ GSettings *settings;
+ GdkDisplay *display;
+ Display *xdisplay;
+
+ desc = get_xkb_desc_rec ();
+ if (!desc)
+ return;
+
+ settings = a11y_keyboard->settings;
+
+ /* general */
+ desc->ctrls->enabled_ctrls = set_clear (g_settings_get_boolean (settings, "enable"),
+ desc->ctrls->enabled_ctrls,
+ XkbAccessXKeysMask);
+
+ if (set_ctrl_from_gsettings (desc, settings, "timeout-enable",
+ XkbAccessXTimeoutMask))
+ {
+ desc->ctrls->ax_timeout = get_int (settings, "disable-timeout");
+ /* disable only the master flag via the server we will disable
+ * the rest on the rebound without affecting GSettings 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 (g_settings_get_boolean (settings, "feature-state-change-beep"),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask);
+
+ /* bounce keys */
+ if (set_ctrl_from_gsettings (desc, settings, "bouncekeys-enable", XkbBounceKeysMask))
+ {
+ desc->ctrls->debounce_delay = get_int (settings, "bouncekeys-delay");
+ desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "bouncekeys-beep-reject"),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_BKRejectFBMask);
+ }
+
+ /* mouse keys */
+ if (set_ctrl_from_gsettings (desc, settings, "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 (settings, "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 (settings, /* events before max */
+ "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 (settings, /* ms before 1st event */
+ "mousekeys-init-delay");
+ }
+
+
+ /* slow keys */
+ if (set_ctrl_from_gsettings (desc, settings, "slowkeys-enable", XkbSlowKeysMask))
+ {
+ desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "slowkeys-beep-press"),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_SKPressFBMask);
+ desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "slowkeys-beep-accept"),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_SKAcceptFBMask);
+ desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "slowkeys-beep-reject"),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_SKRejectFBMask);
+ desc->ctrls->slow_keys_delay = get_int (settings, "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_gsettings (desc, settings, "stickykeys-enable", XkbStickyKeysMask))
+ {
+ desc->ctrls->ax_options |= XkbAX_LatchToLockMask;
+ desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "stickykeys-two-key-off"),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_TwoKeysMask);
+ desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "stickykeys-modifier-beep"),
+ desc->ctrls->ax_options,
+ XkbAccessXFeedbackMask | XkbAX_StickyKeysFBMask);
+ }
+
+ /* toggle keys */
+ desc->ctrls->ax_options = set_clear (g_settings_get_boolean (settings, "togglekeys-enable"),
+ 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);
+ */
+
+ display = gdk_display_get_default ();
+ xdisplay = gdk_x11_display_get_xdisplay (display);
+
+ gdk_x11_display_error_trap_push (display);
+
+ XkbSetControls (xdisplay, DEFAULT_XKB_SET_CONTROLS_MASK, desc);
+ XkbFreeKeyboard (desc, XkbAllComponentsMask, True);
+ XSync (xdisplay, False);
+
+ gdk_x11_display_error_trap_pop_ignored (display);
+}
+
+static void
+slowkeys_notify_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ guint id;
+ GfA11yKeyboard *a11y_keyboard;
+
+ error = NULL;
+ gf_notifications_gen_call_notify_finish (GF_NOTIFICATIONS_GEN (source_object),
+ &id, res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+ else if (error != NULL)
+ {
+ g_warning ("a11y-keyboard: unable to show notification: %s", error->message);
+ g_error_free (error);
+
+ return;
+ }
+
+ a11y_keyboard = GF_A11Y_KEYBOARD (user_data);
+ a11y_keyboard->slowkeys_id = id;
+}
+
+static void
+ax_slowkeys_warning_post_bubble (GfA11yKeyboard *a11y_keyboard,
+ gboolean enabled)
+{
+ const char *icon;
+ const char *title;
+ const char *message;
+ const char **actions;
+ GVariantBuilder hints;
+
+ icon = "preferences-desktop-accessibility-symbolic";
+
+ title = enabled ?
+ _("Slow Keys Turned On") :
+ _("Slow Keys Turned Off");
+
+ 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.");
+
+ actions = g_new0 (const char *, 5);
+ actions[0] = "reject";
+ actions[1] = enabled ? _("Turn Off") : _("Turn On");
+ actions[2] = "accept";
+ actions[3] = enabled ? _("Leave On") : _("Leave Off");
+ actions[4] = NULL;
+
+ g_variant_builder_init (&hints, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&hints, "{sv}", "urgency",
+ g_variant_new_byte (0x02 /* critical */));
+
+ gf_notifications_gen_call_notify (a11y_keyboard->notifications,
+ _("Universal Access"),
+ 0,
+ icon,
+ title,
+ message,
+ actions,
+ g_variant_builder_end (&hints),
+ 0,
+ NULL,
+ slowkeys_notify_cb,
+ a11y_keyboard);
+
+ g_free (actions);
+}
+
+static void
+ax_slowkeys_warning_post (GfA11yKeyboard *a11y_keyboard,
+ gboolean enabled)
+{
+ a11y_keyboard->slowkeys_shortcut_val = enabled;
+ ax_slowkeys_warning_post_bubble (a11y_keyboard, enabled);
+}
+
+static void
+stickykeys_notify_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ guint id;
+ GfA11yKeyboard *a11y_keyboard;
+
+ error = NULL;
+ gf_notifications_gen_call_notify_finish (GF_NOTIFICATIONS_GEN (source_object),
+ &id, res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+ else if (error != NULL)
+ {
+ g_warning ("a11y-keyboard: unable to show notification: %s", error->message);
+ g_error_free (error);
+
+ return;
+ }
+
+ a11y_keyboard = GF_A11Y_KEYBOARD (user_data);
+ a11y_keyboard->stickykeys_id = id;
+}
+
+static void
+ax_stickykeys_warning_post_bubble (GfA11yKeyboard *a11y_keyboard,
+ gboolean enabled)
+{
+ const char *icon;
+ const char *title;
+ const char *message;
+ const char **actions;
+ GVariantBuilder hints;
+
+ icon = "preferences-desktop-accessibility-symbolic";
+
+ title = enabled ?
+ _("Sticky Keys Turned On") :
+ _("Sticky Keys Turned Off");
+
+ 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.");
+
+ actions = g_new0 (const char *, 5);
+ actions[0] = "reject";
+ actions[1] = enabled ? _("Turn Off") : _("Turn On");
+ actions[2] = "accept";
+ actions[3] = enabled ? _("Leave On") : _("Leave Off");
+ actions[4] = NULL;
+
+ g_variant_builder_init (&hints, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&hints, "{sv}", "urgency",
+ g_variant_new_byte (0x02 /* critical */));
+
+ gf_notifications_gen_call_notify (a11y_keyboard->notifications,
+ _("Universal Access"),
+ 0,
+ icon,
+ title,
+ message,
+ actions,
+ g_variant_builder_end (&hints),
+ 0,
+ NULL,
+ stickykeys_notify_cb,
+ a11y_keyboard);
+
+ g_free (actions);
+}
+
+static void
+ax_stickykeys_warning_post (GfA11yKeyboard *a11y_keyboard,
+ gboolean enabled)
+{
+
+ a11y_keyboard->stickykeys_shortcut_val = enabled;
+ ax_stickykeys_warning_post_bubble (a11y_keyboard, enabled);
+}
+
+static gboolean
+set_bool (GSettings *settings,
+ const char *key,
+ int val)
+{
+ gboolean bval;
+ gboolean prev_val;
+
+ bval = (val != 0);
+ prev_val = g_settings_get_boolean (settings, key);
+
+ g_settings_set_boolean (settings, key, bval ? TRUE : FALSE);
+
+ if (bval != prev_val)
+ {
+ g_debug ("%s changed", key);
+ return TRUE;
+ }
+
+ return bval != prev_val;
+}
+
+static gboolean
+set_int (GSettings *settings,
+ const char *key,
+ int val)
+{
+ int prev_val;
+
+ prev_val = g_settings_get_int (settings, key);
+
+ g_settings_set_int (settings, key, val);
+
+ if (val != prev_val)
+ g_debug ("%s changed", key);
+
+ return val != prev_val;
+}
+
+static void
+set_gsettings_from_server (GfA11yKeyboard *a11y_keyboard)
+{
+ XkbDescRec *desc;
+ GSettings *settings;
+ gboolean changed;
+ gboolean slowkeys_changed;
+ gboolean stickykeys_changed;
+
+ desc = get_xkb_desc_rec ();
+ if (!desc)
+ return;
+
+ /* Create a new one, so that only those settings are delayed */
+ settings = g_settings_new (KEYBOARD_A11Y_SCHEMA);
+ g_settings_delay (settings);
+
+ /*
+ fprintf (stderr, "changed to : 0x%x\n", desc->ctrls->enabled_ctrls);
+ fprintf (stderr, "changed to : 0x%x (2)\n", desc->ctrls->ax_options);
+ */
+
+ changed = FALSE;
+
+ changed |= set_bool (settings, "enable",
+ desc->ctrls->enabled_ctrls & XkbAccessXKeysMask);
+
+ changed |= set_bool (settings, "feature-state-change-beep",
+ desc->ctrls->ax_options & (XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask));
+
+ changed |= set_bool (settings, "timeout-enable",
+ desc->ctrls->enabled_ctrls & XkbAccessXTimeoutMask);
+
+ changed |= set_int (settings, "disable-timeout",
+ desc->ctrls->ax_timeout);
+
+ changed |= set_bool (settings, "bouncekeys-enable",
+ desc->ctrls->enabled_ctrls & XkbBounceKeysMask);
+
+ changed |= set_int (settings, "bouncekeys-delay",
+ desc->ctrls->debounce_delay);
+
+ changed |= set_bool (settings, "bouncekeys-beep-reject",
+ desc->ctrls->ax_options & XkbAX_BKRejectFBMask);
+
+ changed |= set_bool (settings, "mousekeys-enable",
+ desc->ctrls->enabled_ctrls & XkbMouseKeysMask);
+
+ changed |= set_int (settings, "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 (settings, "mousekeys-accel-time",
+ desc->ctrls->mk_time_to_max * desc->ctrls->mk_interval);
+
+ changed |= set_int (settings, "mousekeys-init-delay",
+ desc->ctrls->mk_delay);
+
+ slowkeys_changed = set_bool (settings, "slowkeys-enable",
+ desc->ctrls->enabled_ctrls & XkbSlowKeysMask);
+
+ changed |= set_bool (settings, "slowkeys-beep-press",
+ desc->ctrls->ax_options & XkbAX_SKPressFBMask);
+
+ changed |= set_bool (settings, "slowkeys-beep-accept",
+ desc->ctrls->ax_options & XkbAX_SKAcceptFBMask);
+
+ changed |= set_bool (settings, "slowkeys-beep-reject",
+ desc->ctrls->ax_options & XkbAX_SKRejectFBMask);
+
+ changed |= set_int (settings, "slowkeys-delay",
+ desc->ctrls->slow_keys_delay);
+
+ stickykeys_changed = set_bool (settings, "stickykeys-enable",
+ desc->ctrls->enabled_ctrls & XkbStickyKeysMask);
+
+ changed |= set_bool (settings, "stickykeys-two-key-off",
+ desc->ctrls->ax_options & XkbAX_TwoKeysMask);
+
+ changed |= set_bool (settings, "stickykeys-modifier-beep",
+ desc->ctrls->ax_options & XkbAX_StickyKeysFBMask);
+
+ changed |= set_bool (settings, "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 xkb_event_filter_cb () below.
+ */
+
+ /* sanity check: are keyboard shortcuts available? */
+ if (desc->ctrls->enabled_ctrls & XkbAccessXKeysMask)
+ {
+ gboolean enabled;
+
+ if (slowkeys_changed)
+ {
+ enabled = desc->ctrls->enabled_ctrls & XkbSlowKeysMask;
+ ax_slowkeys_warning_post (a11y_keyboard, enabled);
+ }
+ else
+ {
+ enabled = desc->ctrls->enabled_ctrls & XkbStickyKeysMask;
+ ax_stickykeys_warning_post (a11y_keyboard, enabled);
+ }
+ }
+ }
+
+ XkbFreeKeyboard (desc, XkbAllComponentsMask, True);
+
+ g_settings_apply (settings);
+ g_object_unref (settings);
+}
+
+static GdkFilterReturn
+xkb_event_filter_cb (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ GfA11yKeyboard *a11y_keyboard;
+ XEvent *xev;
+ XkbEvent *xkbEv;
+
+ a11y_keyboard = GF_A11Y_KEYBOARD (user_data);
+ xev = (XEvent *) xevent;
+ xkbEv = (XkbEvent *) xevent;
+
+ /* 'event_type' is set to zero on notifying us of updates in
+ * response to client requests (including our own) and non-zero
+ * to notify us of key/mouse events causing changes (like
+ * pressing shift 5 times to enable sticky keys).
+ *
+ * We only want to update GSettings when it's in response to an
+ * explicit user input event, so require a non-zero event_type.
+ */
+ if (xev->xany.type == (a11y_keyboard->xkbEventBase + XkbEventCode) &&
+ xkbEv->any.xkb_type == XkbControlsNotify &&
+ xkbEv->ctrls.event_type != 0)
+ {
+ g_debug ("XKB state changed");
+ set_gsettings_from_server (a11y_keyboard);
+ }
+ else if (xev->xany.type == (a11y_keyboard->xkbEventBase + XkbEventCode) &&
+ xkbEv->any.xkb_type == XkbAccessXNotify)
+ {
+ if (xkbEv->accessx.detail == XkbAXN_AXKWarning)
+ {
+ g_debug ("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_gsettings_from_server().
+ */
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static gboolean
+xkb_enabled (GfA11yKeyboard *a11y_keyboard)
+{
+ GdkDisplay *display;
+ Display *xdisplay;
+ int opcode, errorBase, major, minor;
+
+ display = gdk_display_get_default ();
+ xdisplay = gdk_x11_display_get_xdisplay (display);
+
+ if (!XkbQueryExtension (xdisplay,
+ &opcode,
+ &a11y_keyboard->xkbEventBase,
+ &errorBase,
+ &major,
+ &minor))
+ return FALSE;
+
+ if (!XkbUseExtension (xdisplay, &major, &minor))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+keyboard_settings_changed_cb (GSettings *settings,
+ const char *key,
+ GfA11yKeyboard *a11y_keyboard)
+{
+ set_server_from_gsettings (a11y_keyboard);
+}
+
+static void
+device_added_cb (GdkSeat *seat,
+ GdkDevice *device,
+ GfA11yKeyboard *a11y_keyboard)
+{
+ if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
+ set_server_from_gsettings (a11y_keyboard);
+}
+
+static void
+set_devicepresence_handler (GfA11yKeyboard *a11y_keyboard)
+{
+ GdkDisplay *display;
+ GdkSeat *seat;
+
+ display = gdk_display_get_default ();
+ seat = gdk_display_get_default_seat (display);
+
+ a11y_keyboard->device_added_id = g_signal_connect (seat, "device-added",
+ G_CALLBACK (device_added_cb),
+ a11y_keyboard);
+}
+
+static gboolean
+start_a11y_keyboard_idle_cb (gpointer user_data)
+{
+ GfA11yKeyboard *a11y_keyboard;
+ GdkDisplay *display;
+ Display *xdisplay;
+ guint event_mask;
+
+ a11y_keyboard = GF_A11Y_KEYBOARD (user_data);
+
+ if (!xkb_enabled (a11y_keyboard))
+ {
+ a11y_keyboard->start_idle_id = 0;
+ return G_SOURCE_REMOVE;
+ }
+
+ a11y_keyboard->settings = g_settings_new (KEYBOARD_A11Y_SCHEMA);
+ g_signal_connect (a11y_keyboard->settings, "changed",
+ G_CALLBACK (keyboard_settings_changed_cb),
+ a11y_keyboard);
+
+ set_devicepresence_handler (a11y_keyboard);
+
+ /* Get the original configuration from the server */
+ a11y_keyboard->desc = get_xkb_desc_rec ();
+
+ display = gdk_display_get_default ();
+ xdisplay = gdk_x11_display_get_xdisplay (display);
+
+ event_mask = XkbControlsNotifyMask;
+ event_mask |= XkbAccessXNotifyMask; /* make default when AXN_AXKWarning works */
+
+ /* be sure to init before starting to monitor the server */
+ set_server_from_gsettings (a11y_keyboard);
+
+ XkbSelectEvents (xdisplay, XkbUseCoreKbd, event_mask, event_mask);
+
+ gdk_window_add_filter (NULL, xkb_event_filter_cb, a11y_keyboard);
+
+ a11y_keyboard->start_idle_id = 0;
+ return G_SOURCE_REMOVE;
+}
+
+static void
+ax_response_callback (GfA11yKeyboard *a11y_keyboard,
+ const char *action,
+ guint revert_controls_mask,
+ gboolean enabled)
+{
+ if (g_strcmp0 (action, "reject") != 0)
+ return;
+
+ /* we're reverting, so we invert sense of 'enabled' flag */
+ g_debug ("cancelling AccessX request");
+
+ if (revert_controls_mask == XkbStickyKeysMask)
+ {
+ g_settings_set_boolean (a11y_keyboard->settings,
+ "stickykeys-enable",
+ !enabled);
+ }
+ else if (revert_controls_mask == XkbSlowKeysMask)
+ {
+ g_settings_set_boolean (a11y_keyboard->settings,
+ "slowkeys-enable",
+ !enabled);
+ }
+
+ set_server_from_gsettings (a11y_keyboard);
+}
+
+static void
+action_invoked_cb (GfNotificationsGen *notifications,
+ guint id,
+ const gchar *action_key,
+ GfA11yKeyboard *a11y_keyboard)
+{
+ if (id == a11y_keyboard->slowkeys_id)
+ {
+ g_assert (action_key != NULL);
+
+ ax_response_callback (a11y_keyboard, action_key, XkbSlowKeysMask,
+ a11y_keyboard->slowkeys_shortcut_val);
+ }
+ else if (id == a11y_keyboard->stickykeys_id)
+ {
+ g_assert (action_key != NULL);
+
+ ax_response_callback (a11y_keyboard, action_key, XkbStickyKeysMask,
+ a11y_keyboard->stickykeys_shortcut_val);
+ }
+}
+
+static void
+notification_closed_cb (GfNotificationsGen *notifications,
+ guint id,
+ guint reason,
+ GfA11yKeyboard *a11y_keyboard)
+{
+ if (id == a11y_keyboard->slowkeys_id)
+ a11y_keyboard->slowkeys_id = 0;
+ else if (id == a11y_keyboard->stickykeys_id)
+ a11y_keyboard->stickykeys_id = 0;
+}
+
+static void
+notifications_proxy_ready_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GfNotificationsGen *notifications;
+ GfA11yKeyboard *a11y_keyboard;
+
+ error = NULL;
+ notifications = gf_notifications_gen_proxy_new_for_bus_finish (res, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ a11y_keyboard = GF_A11Y_KEYBOARD (user_data);
+ a11y_keyboard->notifications = notifications;
+
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+
+ return;
+ }
+
+ g_signal_connect (a11y_keyboard->notifications, "action-invoked",
+ G_CALLBACK (action_invoked_cb), a11y_keyboard);
+
+ g_signal_connect (a11y_keyboard->notifications, "notification-closed",
+ G_CALLBACK (notification_closed_cb), a11y_keyboard);
+}
+
+static void
+gf_a11y_keyboard_finalize (GObject *object)
+{
+ GfA11yKeyboard *a11y_keyboard;
+
+ a11y_keyboard = GF_A11Y_KEYBOARD (object);
+
+ if (a11y_keyboard->desc != NULL)
+ {
+ XkbDescRec *desc;
+
+ desc = get_xkb_desc_rec ();
+
+ if (desc != NULL)
+ {
+ if (a11y_keyboard->desc->ctrls->enabled_ctrls != desc->ctrls->enabled_ctrls)
+ {
+ GdkDisplay *display;
+ Display *xdisplay;
+
+ display = gdk_display_get_default ();
+ xdisplay = gdk_x11_display_get_xdisplay (display);
+
+ gdk_x11_display_error_trap_push (display);
+
+ XkbSetControls (xdisplay,
+ DEFAULT_XKB_SET_CONTROLS_MASK,
+ a11y_keyboard->desc);
+
+ XSync (xdisplay, False);
+
+ gdk_x11_display_error_trap_pop_ignored (display);
+ }
+
+ XkbFreeKeyboard (desc, XkbAllComponentsMask, True);
+ }
+
+ XkbFreeKeyboard (a11y_keyboard->desc, XkbAllComponentsMask, True);
+ a11y_keyboard->desc = NULL;
+ }
+
+ if (a11y_keyboard->start_idle_id != 0)
+ {
+ g_source_remove (a11y_keyboard->start_idle_id);
+ a11y_keyboard->start_idle_id = 0;
+ }
+
+ if (a11y_keyboard->device_added_id != 0)
+ {
+ GdkDisplay *display;
+ GdkSeat *seat;
+
+ display = gdk_display_get_default ();
+ seat = gdk_display_get_default_seat (display);
+
+ g_signal_handler_disconnect (seat, a11y_keyboard->device_added_id);
+ a11y_keyboard->device_added_id = 0;
+ }
+
+ g_clear_object (&a11y_keyboard->settings);
+
+ gdk_window_remove_filter (NULL, xkb_event_filter_cb, a11y_keyboard);
+
+ a11y_keyboard->slowkeys_shortcut_val = FALSE;
+ a11y_keyboard->stickykeys_shortcut_val = FALSE;
+
+ if (a11y_keyboard->slowkeys_id != 0)
+ {
+ gf_notifications_gen_call_close_notification (a11y_keyboard->notifications,
+ a11y_keyboard->slowkeys_id,
+ NULL, NULL, NULL);
+ a11y_keyboard->slowkeys_id = 0;
+ }
+
+ if (a11y_keyboard->stickykeys_id != 0)
+ {
+ gf_notifications_gen_call_close_notification (a11y_keyboard->notifications,
+ a11y_keyboard->stickykeys_id,
+ NULL, NULL, NULL);
+ a11y_keyboard->stickykeys_id = 0;
+ }
+
+ g_clear_object (&a11y_keyboard->notifications);
+
+ G_OBJECT_CLASS (gf_a11y_keyboard_parent_class)->finalize (object);
+}
+
+static void
+gf_a11y_keyboard_class_init (GfA11yKeyboardClass *a11y_keyboard_class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (a11y_keyboard_class);
+
+ object_class->finalize = gf_a11y_keyboard_finalize;
+}
+
+static void
+gf_a11y_keyboard_init (GfA11yKeyboard *a11y_keyboard)
+{
+ a11y_keyboard->start_idle_id = g_idle_add (start_a11y_keyboard_idle_cb, a11y_keyboard);
+ g_source_set_name_by_id (a11y_keyboard->start_idle_id, "[gnome-flashback] start_a11y_keyboard_idle_cb");
+
+ gf_notifications_gen_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.freedesktop.Notifications",
+ "/org/freedesktop/Notifications",
+ NULL,
+ notifications_proxy_ready_cb,
+ a11y_keyboard);
+}
+
+GfA11yKeyboard *
+gf_a11y_keyboard_new (void)
+{
+ return g_object_new (GF_TYPE_A11Y_KEYBOARD, NULL);
+}
diff --git a/gnome-flashback/liba11y-keyboard/gf-a11y-keyboard.h
b/gnome-flashback/liba11y-keyboard/gf-a11y-keyboard.h
new file mode 100644
index 0000000..a8f1276
--- /dev/null
+++ b/gnome-flashback/liba11y-keyboard/gf-a11y-keyboard.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 Alberts Muktupāvels
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GF_A11Y_KEYBOARD_H
+#define GF_A11Y_KEYBOARD_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GF_TYPE_A11Y_KEYBOARD (gf_a11y_keyboard_get_type ())
+G_DECLARE_FINAL_TYPE (GfA11yKeyboard, gf_a11y_keyboard,
+ GF, A11Y_KEYBOARD, GObject)
+
+GfA11yKeyboard *gf_a11y_keyboard_new (void);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/liba11y-keyboard/org.freedesktop.Notifications.xml
b/gnome-flashback/liba11y-keyboard/org.freedesktop.Notifications.xml
new file mode 100644
index 0000000..dc82267
--- /dev/null
+++ b/gnome-flashback/liba11y-keyboard/org.freedesktop.Notifications.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.freedesktop.Notifications">
+ <annotation name="org.gtk.GDBus.C.Name" value="NotificationsGen" />
+
+ <method name="CloseNotification">
+ <arg type="u" name="id" direction="in" />
+ </method>
+
+ <method name="GetCapabilities">
+ <arg type="as" name="capabilities" direction="out" />
+ </method>
+
+ <method name="GetServerInformation">
+ <arg type="s" name="name" direction="out" />
+ <arg type="s" name="vendor" direction="out" />
+ <arg type="s" name="version" direction="out" />
+ <arg type="s" name="spec_version" direction="out" />
+ </method>
+
+ <method name="Notify">
+ <arg type="s" name="app_name" direction="in" />
+ <arg type="u" name="replaces_id" direction="in" />
+ <arg type="s" name="app_icon" direction="in" />
+ <arg type="s" name="summary" direction="in" />
+ <arg type="s" name="body" direction="in" />
+ <arg type="as" name="actions" direction="in" />
+ <arg type="a{sv}" name="hints" direction="in" />
+ <arg type="i" name="expire_timeout" direction="in" />
+ <arg type="u" name="id" direction="out" />
+ </method>
+
+ <signal name="ActionInvoked">
+ <arg type="u" name="id" />
+ <arg type="s" name="action_key" />
+ </signal>
+
+ <signal name="NotificationClosed">
+ <arg type="u" name="id" />
+ <arg type="u" name="reason" />
+ </signal>
+ </interface>
+</node>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6bd42c3..3367e17 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -16,6 +16,7 @@ data/xsessions/gnome-flashback-compiz.desktop.in.in
data/xsessions/gnome-flashback-metacity.desktop.in.in
gnome-flashback/gf-confirm-display-change-dialog.c
gnome-flashback/gf-main.c
+gnome-flashback/liba11y-keyboard/gf-a11y-keyboard.c
gnome-flashback/libaudio-device-selection/gf-audio-device-selection-dialog.c
gnome-flashback/libautomount-manager/gsd-automount-manager.c
gnome-flashback/libautomount-manager/gsd-autorun.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]