FreeRDP/libfreerdp/locale/keyboard_x11.c
Martin Fleisz 9cff6e9366 locale: Fix X11 keyboard layout detection
This fixes an issue with keyboard layout detection reintroduced in #8960

The problem has already been fixed in #6688 but got lost after
refactoring.

The issue is that the layout specifier might be a comma separated list
with country specifiers i.e. `"at,us"` which was not correctly handled.
2023-09-28 21:16:17 +02:00

141 lines
3.7 KiB
C

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 Bernhard Miklautz <bernhard.miklautz@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include "liblocale.h"
#include "keyboard_x11.h"
#include "xkb_layout_ids.h"
static BOOL parse_xkb_rule_names(char* xkb_rule, unsigned long num_bytes, char** layout,
char** variant)
{
size_t i, index;
/* Sample output for "Canadian Multilingual Standard"
*
* _XKB_RULES_NAMES_BACKUP(STRING) = "xorg", "pc105", "ca", "multi", "magic"
*
* Format: "rules", "model", "layout", "variant", "options"
*
* Where "xorg" is the set of rules
* "pc105" the keyboard model
* "ca" the keyboard layout(s) (can also be something like 'us,uk')
* "multi" the keyboard layout variant(s) (in the examples, “,winkeys” - which means first
* layout uses some “default” variant and second uses “winkeys” variant)
* "magic" - configuration option (in the examples,
* “eurosign:e,lv3:ralt_switch,grp:rctrl_toggle”
* - three options)
*/
for (i = 0, index = 0; i < num_bytes; i++, index++)
{
char* ptr = xkb_rule + i;
switch (index)
{
case 0: // rules
break;
case 1: // model
break;
case 2: // layout
{
/* If multiple languages are present we just take the first one */
char* delimiter = strchr(ptr, ',');
if (delimiter)
*delimiter = '\0';
*layout = ptr;
break;
}
case 3: // variant
*variant = ptr;
break;
case 4: // option
break;
default:
break;
}
i += strlen(ptr);
}
return TRUE;
}
static DWORD kbd_layout_id_from_x_property(Display* display, Window root, char* property_name)
{
char* layout = NULL;
char* variant = NULL;
char* rule;
Atom property = None;
Atom type = None;
int item_size = 0;
unsigned long items = 0;
unsigned long unread_items = 0;
DWORD layout_id = 0;
property = XInternAtom(display, property_name, False);
if (XGetWindowProperty(display, root, property, 0, 1024, False, XA_STRING, &type, &item_size,
&items, &unread_items, (unsigned char**)&rule) != Success)
{
return 0;
}
if (type != XA_STRING || item_size != 8 || unread_items != 0)
{
XFree(rule);
return 0;
}
parse_xkb_rule_names(rule, items, &layout, &variant);
DEBUG_KBD("%s layout: %s, variant: %s", property_name, layout, variant);
layout_id = find_keyboard_layout_in_xorg_rules(layout, variant);
XFree(rule);
return layout_id;
}
int freerdp_detect_keyboard_layout_from_xkb(DWORD* keyboardLayoutId)
{
Display* display = XOpenDisplay(NULL);
if (!display)
return 0;
Window root = DefaultRootWindow(display);
if (!root)
return 0;
/* We start by looking for _XKB_RULES_NAMES_BACKUP which appears to be used by libxklavier */
DWORD id = kbd_layout_id_from_x_property(display, root, "_XKB_RULES_NAMES_BACKUP");
if (0 == id)
id = kbd_layout_id_from_x_property(display, root, "_XKB_RULES_NAMES");
if (0 != id)
*keyboardLayoutId = id;
XCloseDisplay(display);
return (int)id;
}