Bug#1111485: Acknowledgement (orca: Incorrectly determines the active keyboard layout)

Samuel Thibault sthibault at debian.org
Sun Aug 24 20:13:32 BST 2025


Hello,

Илья Пащук, le dim. 24 août 2025 13:35:58 +0300, a ecrit:
> +resolve_keysym_with_xkb (Display *dpy, KeyCode keycode, unsigned int state, unsigned int numlock_physical_mask)
> +{
> +  /* Игнорируем Lock/NumLock при выборе уровня */

Could you translate these comments to english please?

Samuel

> +  unsigned int cleaned = state & ~(numlock_physical_mask | LockMask);
> +
> +  XkbStateRec st;
> +  memset (&st, 0, sizeof (st));
> +  XkbGetState (dpy, XkbUseCoreKbd, &st);
> +
> +  /* Вычислим «флаги» модификаторов, а не индекс уровня напрямую */
> +  unsigned int altgr_mask = XkbKeysymToModifiers (dpy, XK_ISO_Level3_Shift);
> +  unsigned int level5_mask = XkbKeysymToModifiers (dpy, XK_ISO_Level5_Shift);
> +
> +  gboolean shift_on = (cleaned & ShiftMask) != 0;
> +  gboolean altgr_on = (altgr_mask && (cleaned & altgr_mask));
> +  gboolean level5_on = (level5_mask && (cleaned & level5_mask));
> +
> +  /* Приоритетная последовательность уровней для распространённых типов (2/4/8 level):
> +   * - без Level5:
> +   *   (none) -> [0,1,2,3]
> +   *   (Shift) -> [1,0,3,2]
> +   *   (AltGr) -> [2,3,0,1]
> +   *   (Shift+AltGr) -> [3,2,1,0]
> +   * - с Level5: аналогично, но сначала проверяем «высшие» уровни (4..7) при активном Level5
> +   */
> +  int candidates[8];
> +  int n = 0;
> +
> +  if (!level5_on)
> +    {
> +      if (!shift_on && !altgr_on) { int pref[] = {0,1,2,3}; memcpy(candidates, pref, sizeof(pref)); n = 4; }
> +      else if (shift_on && !altgr_on) { int pref[] = {1,0,3,2}; memcpy(candidates, pref, sizeof(pref)); n = 4; }
> +      else if (!shift_on && altgr_on) { int pref[] = {2,3,0,1}; memcpy(candidates, pref, sizeof(pref)); n = 4; }
> +      else /* shift_on && altgr_on */ { int pref[] = {3,2,1,0}; memcpy(candidates, pref, sizeof(pref)); n = 4; }
> +
> +      /* затем добиваем уровни 4..7 про запас */
> +      for (int lvl = 4; lvl < 8; lvl++) candidates[n++] = lvl;
> +    }
> +  else
> +    {
> +      /* Level5 активен: сначала 4..7, по аналогичной логике */
> +      if (!shift_on && !altgr_on) { int pref[] = {4,5,6,7}; memcpy(candidates, pref, sizeof(pref)); n = 4; }
> +      else if (shift_on && !altgr_on) { int pref[] = {5,4,7,6}; memcpy(candidates, pref, sizeof(pref)); n = 4; }
> +      else if (!shift_on && altgr_on) { int pref[] = {6,7,4,5}; memcpy(candidates, pref, sizeof(pref)); n = 4; }
> +      else /* shift_on && altgr_on */ { int pref[] = {7,6,5,4}; memcpy(candidates, pref, sizeof(pref)); n = 4; }
> +
> +      /* затем уровни 0..3 как запасной вариант */
> +      for (int lvl = 0; lvl < 4; lvl++) candidates[n++] = lvl;
> +    }
> +
> +  /* 1) Пробуем кандидатов в текущей группе */
> +  for (int i = 0; i < n; i++)
> +    {
> +      KeySym t = XkbKeycodeToKeysym (dpy, keycode, st.group, candidates[i]);
> +      if (t != NoSymbol && t != 0)
> +        return t;
> +    }
> +
> +  /* 2) Полный перебор уровней текущей группы на всякий случай */
> +  for (int lvl = 0; lvl < 8; lvl++)
> +    {
> +      KeySym t = XkbKeycodeToKeysym (dpy, keycode, st.group, lvl);
> +      if (t != NoSymbol && t != 0)
> +        return t;
> +    }
> +
> +  /* 3) Последний шанс: группа 0, сначала по тем же кандидатам */
> +  for (int i = 0; i < n; i++)
> +    {
> +      KeySym t = XkbKeycodeToKeysym (dpy, keycode, 0, candidates[i]);
> +      if (t != NoSymbol && t != 0)
> +        return t;
> +    }
> +
> +  /* 4) И полный перебор группы 0 */
> +  for (int lvl = 0; lvl < 8; lvl++)
> +    {
> +      KeySym t = XkbKeycodeToKeysym (dpy, keycode, 0, lvl);
> +      if (t != NoSymbol && t != 0)
> +        return t;
> +    }
> +
> +  return NoSymbol;
> +}
> +
>  static gboolean
>  do_event_dispatch (gpointer user_data)
>  {
> @@ -352,7 +444,6 @@ do_event_dispatch (gpointer user_data)
>    XEvent xevent;
>    char text[10];
>    KeySym keysym;
> -  XComposeStatus status;
>    guint modifiers;
>  
>    g_object_ref (device);
> @@ -365,7 +456,14 @@ do_event_dispatch (gpointer user_data)
>          {
>          case KeyPress:
>          case KeyRelease:
> -          XLookupString (&xevent.xkey, text, sizeof (text), &keysym, &status);
> +          /* Resolve keysym via XKB using current state; avoid stale text. */
> +          {
> +            keysym = resolve_keysym_with_xkb (priv->display,
> +                                              xevent.xkey.keycode,
> +                                              xevent.xkey.state,
> +                                              priv->numlock_physical_mask);
> +            text[0] = '\0';
> +          }
>            modifiers = xevent.xkey.state | priv->virtual_mods_enabled;
>            if (modifiers & priv->numlock_physical_mask)
>              {
> @@ -388,9 +486,14 @@ do_event_dispatch (gpointer user_data)
>                  case XI_KeyPress:
>                  case XI_KeyRelease:
>                    xi2keyevent (xiDevEv, &keyevent);
> -                  XLookupString ((XKeyEvent *) &keyevent, text, sizeof (text), &keysym, &status);
> -                  if (text[0] < ' ')
> +                  /* Resolve keysym via XKB using current state (XI2 path). */
> +                  {
> +                    keysym = resolve_keysym_with_xkb (priv->display,
> +                                                      xiDevEv->detail,
> +                                                      keyevent.xkey.state,
> +                                                      priv->numlock_physical_mask);
>                      text[0] = '\0';
> +                  }
>                    set_virtual_modifier (device, xiRawEv->detail, xevent.xcookie.evtype == XI_KeyPress);
>                    modifiers = keyevent.xkey.state | priv->virtual_mods_enabled;
>                    if (modifiers & priv->numlock_physical_mask)
> Index: at-spi2-core-2.56.2/registryd/deviceeventcontroller-x11.c
> ===================================================================
> --- at-spi2-core-2.56.2.orig/registryd/deviceeventcontroller-x11.c
> +++ at-spi2-core-2.56.2/registryd/deviceeventcontroller-x11.c
> @@ -720,6 +720,64 @@ spi_controller_register_with_devices (Sp
>    x_default_error_handler = XSetErrorHandler (_spi_controller_device_error_handler);
>  }
>  
> +/* Compute effective XKB level from modifier state:
> + * bit0: Shift, bit1: ISO_Level3_Shift (AltGr), bit2: ISO_Level5_Shift.
> + * Lock and NumLock do not affect level.
> + */
> +static unsigned int
> +get_level_from_state (Display *dpy, unsigned int state)
> +{
> +  unsigned int level = 0;
> +  unsigned int altgr_mask = XkbKeysymToModifiers (dpy, XK_ISO_Level3_Shift);
> +  unsigned int level5_mask = XkbKeysymToModifiers (dpy, XK_ISO_Level5_Shift);
> +
> +  if (state & ShiftMask)
> +    level |= 1;
> +  if (altgr_mask && (state & altgr_mask))
> +    level |= 2;
> +  if (level5_mask && (state & level5_mask))
> +    level |= 4;
> +
> +  return level;
> +}
> +
> +/* Resolve keysym using explicit current group (st.group) and effective level.
> + * Fallback: scan levels in current group, then group 0 for specials.
> + */
> +static KeySym
> +resolve_keysym_with_xkb (Display *dpy, KeyCode keycode, unsigned int state)
> +{
> +  unsigned int cleaned = state & ~(_numlock_physical_mask | LockMask);
> +
> +  XkbStateRec st;
> +  memset (&st, 0, sizeof (st));
> +  XkbGetState (dpy, XkbUseCoreKbd, &st);
> +
> +  unsigned int level = get_level_from_state (dpy, cleaned);
> +
> +  KeySym ks = XkbKeycodeToKeysym (dpy, keycode, st.group, level);
> +  if (ks != NoSymbol && ks != 0)
> +    return ks;
> +
> +  /* Fallback 1: scan levels in current group */
> +  for (int lvl = 0; lvl < 8; lvl++)
> +    {
> +      KeySym t = XkbKeycodeToKeysym (dpy, keycode, st.group, lvl);
> +      if (t != NoSymbol && t != 0)
> +        return t;
> +    }
> +
> +  /* Fallback 2: scan group 0 */
> +  for (int lvl = 0; lvl < 8; lvl++)
> +    {
> +      KeySym t = XkbKeycodeToKeysym (dpy, keycode, 0, lvl);
> +      if (t != NoSymbol && t != 0)
> +        return t;
> +    }
> +
> +  return NoSymbol;
> +}
> +
>  static Accessibility_DeviceEvent
>  spi_keystroke_from_x_key_event (XKeyEvent *x_key_event)
>  {
> @@ -729,7 +787,11 @@ spi_keystroke_from_x_key_event (XKeyEven
>    char cbuf[21];
>    int nbytes;
>  
> -  nbytes = XLookupString (x_key_event, cbuf, cbuf_bytes, &keysym, NULL);
> +  /* Resolve keysym via XKB using current state; honors group and effective modifiers. */
> +  keysym = resolve_keysym_with_xkb (spi_get_display (),
> +                                    x_key_event->keycode,
> +                                    x_key_event->state);
> +
>    key_event.id = (dbus_int32_t) (keysym);
>    key_event.hw_code = (dbus_int16_t) x_key_event->keycode;
>    if (((XEvent *) x_key_event)->type == KeyPress)
> @@ -820,22 +882,21 @@ spi_keystroke_from_x_key_event (XKeyEven
>        key_event.event_string = g_strdup ("Right");
>        break;
>      default:
> -      if (nbytes > 0)
> -        {
> -          gunichar c;
> -          cbuf[nbytes] = '\0'; /* OK since length is cbuf_bytes+1 */
> -          key_event.event_string = g_strdup (cbuf);
> -          c = keysym2ucs (keysym);
> -          if (c > 0 && !g_unichar_iscntrl (c))
> -            {
> -              key_event.is_text = TRUE;
> -              /* incorrect for some composed chars? */
> -            }
> -        }
> -      else
> -        {
> -          key_event.event_string = g_strdup ("");
> -        }
> +      {
> +        gunichar uc = (gunichar) keysym2ucs (keysym);
> +        if (uc > 0 && !g_unichar_iscntrl (uc))
> +          {
> +            char utf8[6];
> +            int len = g_unichar_to_utf8 (uc, utf8);
> +            utf8[len] = '\0';
> +            key_event.event_string = g_strdup (utf8);
> +            key_event.is_text = TRUE;
> +          }
> +        else
> +          {
> +            key_event.event_string = g_strdup ("");
> +          }
> +      }
>      }
>  
>    key_event.timestamp = (dbus_uint32_t) x_key_event->time;

> _______________________________________________
> Pkg-a11y-devel mailing list
> Pkg-a11y-devel at alioth-lists.debian.net
> https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/pkg-a11y-devel


-- 
Samuel
 Créer une hiérarchie supplementaire pour remedier à un problème (?) de
 dispersion est d'une logique digne des Shadocks.
 * BT in: Guide du Cabaliste Usenet - La Cabale vote oui (les Shadocks aussi) *



More information about the Pkg-a11y-devel mailing list