From 69a67a7f04fc5c0e36a290ad7a3e7afb4e801083 Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Thu, 11 Jun 2020 11:56:29 +0200 Subject: a11y-keyboard: Manually beep for togglekeys This allows a customizable and possibly different beep sequence when a togglekey is enabled or disabled. This is very useful for the user to know for sure whether the feature got enabled or disabled. Back in the days of buzzers, XKB was supposed to use different sounds for each of those, but this is no longer the case now in the vast majority of setups the beeps are intercepted and use a single recorded sound. XKB beeps are also unfortunately not configurable, although they possibly should be on paper: in theory, one could alter the bell used by listening to `XkbBellNotify` events, which provides a way to discriminate bells through a name. Unfortunately XKB's togglekeys seems to suffer from a bug (?) for a long time, in that it will always ring the `AX_IndicatorOn` bell if there is *at least one* indicator on at the moment the bell is rung, and `AX_IndicatorOff` if and only if *all* indicators are off. This makes it virtually useless as it is not possible to discriminate between an indicator getting turned on or off in most cases, especially with NumLock which often stays always on. Given this behavior dates at the very least as far back as X.org 1.16 which is from 2014, it probably is not very realistic to rely on a fix. So instead implement togglekeys on our end by listening to the `XkbIndicatorStateNotify` events. --- plugins/a11y-keyboard/msd-a11y-keyboard-manager.c | 63 +++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'plugins') diff --git a/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c b/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c index e448afc..06e8586 100644 --- a/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c +++ b/plugins/a11y-keyboard/msd-a11y-keyboard-manager.c @@ -957,6 +957,53 @@ set_settings_from_server (MsdA11yKeyboardManager *manager) g_object_unref (settings); } +typedef struct { + GdkDisplay *display; + gint count; +} BeepSequenceData; + +static gboolean +on_beep_dequence_timeout (gpointer user_data) +{ + BeepSequenceData *data = user_data; + + gdk_display_beep (data->display); + data->count--; + + return data->count > 0; +} + +static void +beep_sequence (MsdA11yKeyboardManager *manager, + GdkDisplay *display, + gint count, + gint delay) +{ + g_return_if_fail (MSD_IS_A11Y_KEYBOARD_MANAGER (manager)); + + if (count < 1) + return; + + if (! display) + display = gdk_display_get_default (); + + gdk_display_beep (display); + if (count > 1) { + BeepSequenceData *data = g_malloc (sizeof *data); + + data->display = display; + data->count = count - 1; + + /* don't allow a delay below 50ms, it doesn't make much sense + * anyway and is more likely to be a broken setting */ + g_warn_if_fail (delay >= 50); + delay = MAX (delay, 50); + + g_timeout_add_full (G_PRIORITY_DEFAULT, (guint) delay, + on_beep_dequence_timeout, data, g_free); + } +} + static GdkFilterReturn cb_xkb_event_filter (GdkXEvent *xevent, GdkEvent *ignored1, @@ -979,6 +1026,21 @@ cb_xkb_event_filter (GdkXEvent *xevent, * set_settings_from_server(). */ } + } else if (xev->xany.type == (manager->priv->xkbEventBase + XkbEventCode) && + xkbEv->any.xkb_type == XkbIndicatorStateNotify && + g_settings_get_boolean (manager->priv->settings, "togglekeys-enable")) { + GdkDisplay *display = gdk_x11_lookup_xdisplay (xkbEv->any.display); + gint beep_count; + gint beep_delay; + + if (xkbEv->indicators.state & xkbEv->indicators.changed) { + beep_count = g_settings_get_int (manager->priv->settings, "togglekeys-enable-beep-count"); + beep_delay = g_settings_get_int (manager->priv->settings, "togglekeys-enable-beep-delay"); + } else { + beep_count = g_settings_get_int (manager->priv->settings, "togglekeys-disable-beep-count"); + beep_delay = g_settings_get_int (manager->priv->settings, "togglekeys-disable-beep-delay"); + } + beep_sequence (manager, display, beep_count, beep_delay); } return GDK_FILTER_CONTINUE; @@ -1036,6 +1098,7 @@ start_a11y_keyboard_idle_cb (MsdA11yKeyboardManager *manager) manager->priv->original_xkb_desc = get_xkb_desc_rec (manager); event_mask = XkbControlsNotifyMask; + event_mask |= XkbIndicatorStateNotifyMask; #ifdef MATE_ENABLE_DEBUG event_mask |= XkbAccessXNotifyMask; /* make default when AXN_AXKWarning works */ #endif /* MATE_ENABLE_DEBUG */ -- cgit v1.2.1