Fix misuse of UCSConvertCase in XConvertCase

There are two issues with the use of `UCSConvertCase` in `XConvertCase`:

- Some Latin-1 keysyms do not map within Latin-1, e.g. `ssharp`.
  Only Latin-1 keysyms have the same value as the Unicode code point of
  their corresponding character. So `UCSConvertCase` does not work for
  some Latin-1 keysyms as it returns Unicode code points outside Latin-1
  that do not match their corresponding keysyms values.
- Some Unicode keysyms should map to Latin-1 keysyms (<0x100). But the
  Unicode keysym mask 0x01000000 is applied blindly to the result of
  `UCSConvertCase`, resulting in invalid Unicode keysyms (0x010000nn)
  while they should be Latin-1 keysyms.

Example with ß/ẞ:

```c
KeySym lower, upper;

XConvertCase(XK_ssharp, &lower, &upper);
// Expected: lower == XK_ssharp, upper == U1E9E
// Got:      lower == XK_ssharp, upper == 0x1E9E

XConvertCase(U1E9E, &lower, &upper);
// Expected: lower == XK_ssharp, upper == U1E9E
// Got:      lower == 0x10000df, upper == U1E9E
```

Part-of: <https://gitlab.freedesktop.org/xorg/lib/libx11/-/merge_requests/274>
This commit is contained in:
Pierre Le Marre 2024-12-09 14:51:17 +01:00
parent 5a7d94e07f
commit 7c75a06645

View file

@ -462,6 +462,9 @@ UCSConvertCase( register unsigned code,
else if ( (code >= 0x00e0 && code <= 0x00f6) ||
(code >= 0x00f8 && code <= 0x00fe) )
*upper -= 0x20;
/* The following code points do not map within Latin-1 and
* require special handling in XConvertCase
*/
else if (code == 0x00ff) /* y with diaeresis */
*upper = 0x0178;
else if (code == 0x00b5) /* micro sign */
@ -655,15 +658,34 @@ XConvertCase(
{
/* Latin 1 keysym */
if (sym < 0x100) {
UCSConvertCase(sym, lower, upper);
return;
/* Special cases that do not map within Latin-1 */
switch (sym) {
case XK_ydiaeresis:
*lower = sym;
*upper = XK_Ydiaeresis;
return;
case XK_mu:
*lower = sym;
*upper = XK_Greek_MU;
return;
case XK_ssharp:
*lower = sym;
*upper = 0x1001e9e;
return;
default:
UCSConvertCase(sym, lower, upper);
return;
}
}
/* Unicode keysym */
if ((sym & 0xff000000) == 0x01000000) {
UCSConvertCase((sym & 0x00ffffff), lower, upper);
*upper |= 0x01000000;
*lower |= 0x01000000;
/* Use the Unicode keysym mask only for non Latin-1 */
if (*upper >= 0x100)
*upper |= 0x01000000;
if (*lower >= 0x100)
*lower |= 0x01000000;
return;
}