1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-25 23:21:33 +03:00

localed: match converted keymaps before legacy

Before, X11 keymap fr-pc105-oss would be converted to fr,
even though fr-oss exists. Now, if
/usr/lib/kbd/keymaps/xkb/<layout>[-<variant>].map[.gz] exists,
<layout>[-<variant>] will be used as the console keymap,
falling back to the legacy mappings otherwise.

% sudo localectl set-x11-keymap pl pc105
% localectl
   System Locale: LANG=en_US.UTF-8
       VC Keymap: pl                      (was pl2 before)
      X11 Layout: pl
       X11 Model: pc105
% sudo localectl set-x11-keymap fr pc105 oss
% localectl
   System Locale: LANG=en_US.UTF-8
       VC Keymap: fr-oss                  (was fr before)
      X11 Layout: fr
       X11 Model: pc105
     X11 Variant: oss
% sudo localectl set-x11-keymap fr pc105
% localectl
   System Locale: LANG=en_US.UTF-8
       VC Keymap: fr
      X11 Layout: fr
       X11 Model: pc105
% sudo localectl set-x11-keymap gb
% localectl
   System Locale: LANG=en_US.UTF-8
       VC Keymap: gb                     (was uk before)
      X11 Layout: gb
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2013-11-18 13:42:57 -05:00
parent 98fce79dea
commit 0732ef7acf
3 changed files with 128 additions and 75 deletions

View File

@ -42,6 +42,7 @@
#include "set.h"
#include "path-util.h"
#include "utf8.h"
#include "def.h"
static bool arg_no_pager = false;
static bool arg_ask_password = true;
@ -437,15 +438,14 @@ static int nftw_cb(
static int list_vconsole_keymaps(sd_bus *bus, char **args, unsigned n) {
_cleanup_strv_free_ char **l = NULL;
const char *dir;
keymaps = set_new(string_hash_func, string_compare_func);
if (!keymaps)
return log_oom();
nftw("/usr/share/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
nftw("/usr/share/kbd/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
nftw("/usr/lib/kbd/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
nftw("/lib/kbd/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS)
nftw(dir, nftw_cb, 20, FTW_MOUNT|FTW_PHYS);
l = set_get_strv(keymaps);
if (!l) {

View File

@ -633,8 +633,114 @@ static int vconsole_convert_to_x11(Context *c, sd_bus *bus) {
return 0;
}
static int find_converted_keymap(Context *c, char **new_keymap) {
const char *dir;
_cleanup_free_ char *n;
if (c->x11_variant)
n = strjoin(c->x11_layout, "-", c->x11_variant, NULL);
else
n = strdup(c->x11_layout);
if (!n)
return -ENOMEM;
NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) {
_cleanup_free_ char *p = NULL, *pz = NULL;
p = strjoin(dir, "xkb/", n, ".map", NULL);
pz = strjoin(dir, "xkb/", n, ".map.gz", NULL);
if (!p || !pz)
return -ENOMEM;
if (access(p, F_OK) == 0 || access(pz, F_OK) == 0) {
*new_keymap = n;
n = NULL;
return 1;
}
}
return 0;
}
static int find_legacy_keymap(Context *c, char **new_keymap) {
_cleanup_fclose_ FILE *f;
unsigned n = 0;
unsigned best_matching = 0;
f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
if (!f)
return -errno;
for (;;) {
_cleanup_strv_free_ char **a = NULL;
unsigned matching = 0;
int r;
r = read_next_mapping(f, &n, &a);
if (r < 0)
return r;
if (r == 0)
break;
/* Determine how well matching this entry is */
if (streq_ptr(c->x11_layout, a[1]))
/* If we got an exact match, this is best */
matching = 10;
else {
size_t x;
x = strcspn(c->x11_layout, ",");
/* We have multiple X layouts, look for an
* entry that matches our key with everything
* but the first layout stripped off. */
if (x > 0 &&
strlen(a[1]) == x &&
strneq(c->x11_layout, a[1], x))
matching = 5;
else {
size_t w;
/* If that didn't work, strip off the
* other layouts from the entry, too */
w = strcspn(a[1], ",");
if (x > 0 && x == w &&
memcmp(c->x11_layout, a[1], x) == 0)
matching = 1;
}
}
if (matching > 0 &&
streq_ptr(c->x11_model, a[2])) {
matching++;
if (streq_ptr(c->x11_variant, a[3])) {
matching++;
if (streq_ptr(c->x11_options, a[4]))
matching++;
}
}
/* The best matching entry so far, then let's save that */
if (matching > best_matching) {
best_matching = matching;
free(*new_keymap);
*new_keymap = strdup(a[0]);
if (!*new_keymap)
return -ENOMEM;
}
}
return 0;
}
static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
bool modified = false;
int r;
assert(bus);
@ -646,79 +752,15 @@ static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
context_free_x11(c);
} else {
_cleanup_fclose_ FILE *f;
unsigned n = 0;
unsigned best_matching = 0;
char *new_keymap = NULL;
f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
if (!f)
return -errno;
for (;;) {
_cleanup_strv_free_ char **a = NULL;
unsigned matching = 0;
int r;
r = read_next_mapping(f, &n, &a);
r = find_converted_keymap(c, &new_keymap);
if (r < 0)
return r;
else if (r == 0) {
r = find_legacy_keymap(c, &new_keymap);
if (r < 0)
return r;
if (r == 0)
break;
/* Determine how well matching this entry is */
if (streq_ptr(c->x11_layout, a[1]))
/* If we got an exact match, this is best */
matching = 10;
else {
size_t x;
x = strcspn(c->x11_layout, ",");
/* We have multiple X layouts, look
* for an entry that matches our key
* with the everything but the first
* layout stripped off. */
if (x > 0 &&
strlen(a[1]) == x &&
strneq(c->x11_layout, a[1], x))
matching = 5;
else {
size_t w;
/* If that didn't work, strip
* off the other layouts from
* the entry, too */
w = strcspn(a[1], ",");
if (x > 0 && x == w &&
memcmp(c->x11_layout, a[1], x) == 0)
matching = 1;
}
}
if (matching > 0 &&
streq_ptr(c->x11_model, a[2])) {
matching++;
if (streq_ptr(c->x11_variant, a[3])) {
matching++;
if (streq_ptr(c->x11_options, a[4]))
matching++;
}
}
/* The best matching entry so far, then let's
* save that */
if (matching > best_matching) {
best_matching = matching;
free(new_keymap);
new_keymap = strdup(a[0]);
if (!new_keymap)
return -ENOMEM;
}
}
if (!streq_ptr(c->vc_keymap, new_keymap)) {
@ -730,8 +772,6 @@ static int x11_convert_to_vconsole(Context *c, sd_bus *bus) {
}
if (modified) {
int r;
r = vconsole_write_data(c);
if (r < 0)
log_error("Failed to set virtual console keymap: %s", strerror(-r));

View File

@ -43,3 +43,16 @@
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
#define REBOOT_PARAM_FILE "/run/systemd/reboot-param"
#ifdef HAVE_SPLIT_USR
#define KBD_KEYMAP_DIRS \
"/usr/share/keymaps/\0" \
"/usr/share/kbd/keymaps/\0" \
"/usr/lib/kbd/keymaps/\0" \
"/lib/kbd/keymaps/\0"
#else
#define KBD_KEYMAP_DIRS \
"/usr/share/keymaps/\0" \
"/usr/share/kbd/keymaps/\0" \
"/usr/lib/kbd/keymaps/\0"
#endif