Попробовал немного модифицировать keymap.c. Внёс изменения в keymap_get_ascii и keymap_convert_text_to_ascii, удалил char_to_keycode. Что получилось:
* Преобразование текстового символа в кейкод выполняется полностью в keymap_get_ascii, char_to_keycode удалена как избыточная. (Она, конечно, для частного случая работает быстрее keymap_get_ascii, но кэш должен будет эту проблему решить.)
* Из keymap_convert_text_to_ascii убрал избыточный вложенный цикл.
* keymap_get_ascii проводит сначала поиск по "предпочтительной" раскладке, а затем по всем остальным. А также возвращает код раскладки, в которой найден символ. В свою очередь, keymap_convert_text_to_ascii передаёт в keymap_get_ascii "предпочтительную" раскладку, равную раскладке от предыдущего символа. Это позволяет не путать символы, находящиеся в нескольких раскладках на различных клавишах. Например, "qwerty.,:;йцукен.,:;" конвертируется в "йцукенюбЖжqwerty/?^$" и обратно без смешивания кейкодов знаков препинания в одну кучу.
Данная фича может вызывать неожиданное поведение в случаях вида "qб" -> "й," -> "q?" -> "й,", но тут уже ничего не поможет, кроме телепатии.
Наверное, тут надо предусмотреть опцию в настройках для переключения старого/нового поведения.
Если сегодня-завтра время найдётся, прикручу кэш вокруг keymap_get_ascii.
Index: lib/main/keymap.c
===================================================================
--- lib/main/keymap.c (revision 886)
+++ lib/main/keymap.c (working copy)
@@ -273,16 +273,18 @@
return TRUE;
}
-static void keymap_char_to_keycode(struct _keymap *p, char ch, KeyCode *kc, int *modifier)
+static char keymap_get_ascii(struct _keymap *p, const char *sym, int* preferred_lang, KeyCode *kc, int *modifier, size_t* symbol_len)
{
Display *display = XOpenDisplay(NULL);
-
- if (ch == 10 || ch == 13)
+
+ if (*sym == 10 || *sym == 13)
{
*kc = XKeysymToKeycode(display, XK_Return);
*modifier = 0;
+ if (symbol_len)
+ *symbol_len = 1;
XCloseDisplay(display);
- return;
+ return *sym;
}
XEvent event;
@@ -295,53 +297,21 @@
event.xkey.state = 0;
event.xkey.keycode = XKeysymToKeycode(display, XK_space);
event.xkey.time = CurrentTime;
-
- char *symbol = (char *) malloc((256 + 1) * sizeof(char));
-
- for (int i = p->min_keycode + 1; i <= p->max_keycode; i++)
- {
- event.xkey.keycode = i;
- event.xkey.state = 0;
-
- int nbytes = XLookupString((XKeyEvent *) &event, symbol, 256, NULL, NULL);
- if ((nbytes > 0) && (symbol[0] == ch))
- break;
-
- event.xkey.state = ShiftMask;
-
- nbytes = XLookupString((XKeyEvent *) &event, symbol, 256, NULL, NULL);
- if ((nbytes > 0) && (symbol[0] == ch))
- break;
- }
-
- *kc = event.xkey.keycode;
- *modifier = (event.xkey.state == ShiftMask) ? 1 : 0;
- free(symbol);
- XCloseDisplay(display);
-}
-
-static char keymap_get_ascii(struct _keymap *p, const char *sym, KeyCode *kc, int *modifier)
-{
- Display *display = XOpenDisplay(NULL);
- XEvent event;
- event.type = KeyPress;
- event.xkey.type = KeyPress;
- event.xkey.root = RootWindow(display, DefaultScreen(display));
- event.xkey.subwindow = None;
- event.xkey.same_screen = True;
- event.xkey.display = display;
- event.xkey.state = 0;
- event.xkey.keycode = XKeysymToKeycode(display, XK_space);
- event.xkey.time = CurrentTime;
-
char *symbol = (char *) malloc((256 + 1) * sizeof(char));
char *prev_symbols = (char *) malloc((256 + 1) * sizeof(char));
- for (int lang = 0; lang < p->handle->total_languages; lang++)
+ int _preferred_lang = 0;
+ if (preferred_lang)
+ _preferred_lang = *preferred_lang;
+
+ for (int _lang = 0; _lang < p->handle->total_languages; _lang++)
{
- if (lang == p->latin_group)
- continue;
+ int lang = _lang;
+ if (lang == 0)
+ lang = _preferred_lang;
+ else if (lang <= _preferred_lang)
+ lang--;
KeySym *keymap = p->keymap;
for (int i = p->min_keycode; i <= p->max_keycode; i++)
@@ -379,6 +349,8 @@
if (strncmp(sym, symbol, strlen(symbol)) != 0)
continue;
+ size_t _symbol_len = strlen(symbol);
+
event.xkey.state = 0;
event.xkey.state |= state_masks[m];
event.xkey.state |= state_masks[n];
@@ -393,6 +365,10 @@
free(symbol);
*kc = event.xkey.keycode;
*modifier = get_keycode_mod(lang) | event.xkey.state;
+ if (symbol_len)
+ *symbol_len = _symbol_len;
+ if (preferred_lang)
+ *preferred_lang = lang;
XCloseDisplay(display);
return sym;
}
@@ -441,27 +417,16 @@
int text_len = strlen(text);
int j = 0;
- for (int i = 0; i < text_len; i++)
+ size_t symbol_len = 0;
+ int preferred_lang = 0;
+ for (int i = 0; i < text_len; i += symbol_len)
{
- if (isascii(text[i]) || isspace(text[i]))
- {
- p->char_to_keycode(p, text[i], &kc[j], &kc_mod[j]);
- text[j++] = text[i];
- continue;
- }
+ char new_symbol = p->get_ascii(p, &text[i], &preferred_lang, &kc[j], &kc_mod[j], &symbol_len);
- char new_symbol = p->get_ascii(p, &text[i], &kc[j], &kc_mod[j]);
-
- for(; i < text_len - 1; i++)
- {
- if (isascii(text[i + 1]) || isspace(text[i + 1]))
- break;
-
- if (p->get_ascii(p, &text[i + 1], &kc[i + 1], &kc_mod[i + 1]) != NULLSYM)
- break;
- }
-
- text[j++] = new_symbol;
+ if (new_symbol != NULLSYM && symbol_len > 0)
+ text[j++] = new_symbol;
+ else
+ symbol_len = 1;
}
text[j] = NULLSYM;
@@ -623,7 +588,6 @@
p->get_ascii = keymap_get_ascii;
p->get_cur_ascii_char = keymap_get_cur_ascii_char;
p->convert_text_to_ascii = keymap_convert_text_to_ascii;
- p->char_to_keycode = keymap_char_to_keycode;
p->lower_by_keymaps = keymap_lower_by_keymaps;
p->uninit = keymap_uninit;
Index: lib/main/keymap.h
===================================================================
--- lib/main/keymap.h (revision 886)
+++ lib/main/keymap.h (working copy)
@@ -46,10 +46,9 @@
unsigned int scrolllock_mask;
unsigned int capslock_mask;
- char (*get_ascii)(struct _keymap *p, const char *sym, KeyCode *kc, int *modifier);
+ char (*get_ascii)(struct _keymap *p, const char *sym, int* preferred_lang, KeyCode *kc, int *modifier, size_t* symbol_len);
char (*get_cur_ascii_char) (struct _keymap *p, XEvent e);
void (*convert_text_to_ascii)(struct _keymap *p, char *text, KeyCode *kc, int *kc_mod);
- void (*char_to_keycode)(struct _keymap *p, char ch, KeyCode *kc, int *modifier);
void (*print_keymaps)(struct _keymap *p);
char* (*lower_by_keymaps)(struct _keymap *p, int gr, char *text);
void (*uninit) (struct _keymap *p);