ime: Avoid keymap reset on each injection.
This commit is contained in:
parent
541ea8fa53
commit
27ab72ec72
1 changed files with 72 additions and 29 deletions
101
src/ime.cpp
101
src/ime.cpp
|
@ -6,6 +6,7 @@
|
|||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
|
@ -104,7 +105,10 @@ struct wlserver_input_method {
|
|||
// Used to send emulated input events
|
||||
struct wlr_keyboard keyboard;
|
||||
struct wlr_input_device keyboard_device;
|
||||
std::unordered_map<uint32_t, struct wlserver_input_method_key> keys;
|
||||
std::deque<struct wlserver_input_method_key> keys;
|
||||
uint32_t next_keycode_index;
|
||||
|
||||
struct wl_event_source *ime_reset_ime_keyboard_event_source;
|
||||
};
|
||||
|
||||
struct wlserver_input_method_manager {
|
||||
|
@ -129,26 +133,42 @@ static xkb_keysym_t keysym_from_ch(uint32_t ch)
|
|||
|
||||
static uint32_t keycode_from_ch(struct wlserver_input_method *ime, uint32_t ch)
|
||||
{
|
||||
if (ime->keys.count(ch) > 0) {
|
||||
return ime->keys[ch].keycode;
|
||||
}
|
||||
|
||||
xkb_keysym_t keysym = keysym_from_ch(ch);
|
||||
if (keysym == XKB_KEY_NoSymbol) {
|
||||
return XKB_KEYCODE_INVALID;
|
||||
}
|
||||
|
||||
if (ime->keys.size() >= allow_keycodes_len) {
|
||||
// TODO: maybe use keycodes above KEY_MAX?
|
||||
ime_log.errorf("Key codes exhausted!");
|
||||
return XKB_KEYCODE_INVALID;
|
||||
// Repeated chars can re-use keycode
|
||||
if (!ime->keys.empty() && ime->keys.back().keysym == keysym)
|
||||
{
|
||||
return ime->keys.back().keycode;
|
||||
}
|
||||
|
||||
uint32_t keycode = allow_keycodes[ime->keys.size()];
|
||||
ime->keys[ch] = (struct wlserver_input_method_key){ keycode, keysym };
|
||||
if (ime->keys.size() >= allow_keycodes_len) {
|
||||
// TODO: maybe use keycodes above KEY_MAX?
|
||||
ime_log.errorf("Key codes wrapped within 100ms!");
|
||||
ime->keys.pop_front();
|
||||
// FALLTHROUGH and allow re-use (oldest key probably fine anyway)
|
||||
}
|
||||
|
||||
uint32_t keycode = allow_keycodes[ime->next_keycode_index++ % allow_keycodes_len];
|
||||
ime->keys.push_back((struct wlserver_input_method_key){ keycode, keysym });
|
||||
return keycode;
|
||||
}
|
||||
|
||||
static bool generate_keymap_key(FILE *f, uint32_t keycode, xkb_keysym_t keysym)
|
||||
{
|
||||
char keysym_name[256];
|
||||
int ret = xkb_keysym_get_name(keysym, keysym_name, sizeof(keysym_name));
|
||||
if (ret <= 0) {
|
||||
ime_log.errorf("xkb_keysym_get_name failed for keysym %u", keysym);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(f, " key <K%u> {[ %s ]};\n", keycode, keysym_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct xkb_keymap *generate_keymap(struct wlserver_input_method *ime)
|
||||
{
|
||||
uint32_t keycode_offset = 8;
|
||||
|
@ -157,8 +177,9 @@ static struct xkb_keymap *generate_keymap(struct wlserver_input_method *ime)
|
|||
size_t str_size = 0;
|
||||
FILE *f = open_memstream(&str, &str_size);
|
||||
|
||||
uint32_t min_keycode = allow_keycodes[0];
|
||||
uint32_t max_keycode = allow_keycodes[ime->keys.size()];
|
||||
// min/max from the set of all allow_keycodes and actions
|
||||
uint32_t min_keycode = KEY_1;
|
||||
uint32_t max_keycode = KEY_DELETE;
|
||||
fprintf(f,
|
||||
"xkb_keymap {\n"
|
||||
"\n"
|
||||
|
@ -169,7 +190,11 @@ static struct xkb_keymap *generate_keymap(struct wlserver_input_method *ime)
|
|||
keycode_offset + max_keycode
|
||||
);
|
||||
|
||||
for (const auto kv : ime->keys) {
|
||||
for (const auto kk : ime->keys) {
|
||||
uint32_t keycode = kk.keycode;
|
||||
fprintf(f, " <K%u> = %u;\n", keycode, keycode + keycode_offset);
|
||||
}
|
||||
for (const auto kv : actions) {
|
||||
uint32_t keycode = kv.second.keycode;
|
||||
fprintf(f, " <K%u> = %u;\n", keycode, keycode + keycode_offset);
|
||||
}
|
||||
|
@ -187,18 +212,22 @@ static struct xkb_keymap *generate_keymap(struct wlserver_input_method *ime)
|
|||
"xkb_symbols \"(unnamed)\" {\n"
|
||||
);
|
||||
|
||||
for (const auto kv : ime->keys) {
|
||||
uint32_t keycode = kv.second.keycode;
|
||||
xkb_keysym_t keysym = kv.second.keysym;
|
||||
|
||||
char keysym_name[256];
|
||||
int ret = xkb_keysym_get_name(keysym, keysym_name, sizeof(keysym_name));
|
||||
if (ret <= 0) {
|
||||
ime_log.errorf("xkb_keysym_get_name failed for keysym %u", keysym);
|
||||
for (const auto kk : ime->keys) {
|
||||
if (!generate_keymap_key(f, kk.keycode, kk.keysym))
|
||||
{
|
||||
fclose(f);
|
||||
free(str);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
for (const auto kv : actions) {
|
||||
const auto kk = kv.second;
|
||||
if (!generate_keymap_key(f, kk.keycode, kk.keysym))
|
||||
{
|
||||
fclose(f);
|
||||
free(str);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fprintf(f, " key <K%u> {[ %s ]};\n", keycode, keysym_name);
|
||||
}
|
||||
|
||||
fprintf(f,
|
||||
|
@ -294,15 +323,14 @@ static bool try_type_keysym(struct wlserver_input_method *ime, xkb_keysym_t keys
|
|||
static void type_text(struct wlserver_input_method *ime, const char *text)
|
||||
{
|
||||
// If possible, try to type the character without switching the keymap
|
||||
if (utf8_size(text) == 1 && text[1] == '\0') {
|
||||
// ...unless we're already using a fancy keymap
|
||||
if (utf8_size(text) == 1 && text[1] == '\0' && ime->keys.empty()) {
|
||||
xkb_keysym_t keysym = keysym_from_ch(text[0]);
|
||||
if (keysym != XKB_KEY_NoSymbol && try_type_keysym(ime, keysym)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ime->keys.clear();
|
||||
|
||||
std::vector<xkb_keycode_t> keycodes;
|
||||
while (text[0] != '\0') {
|
||||
uint32_t ch = utf8_decode(&text);
|
||||
|
@ -332,6 +360,9 @@ static void type_text(struct wlserver_input_method *ime, const char *text)
|
|||
wlr_seat_keyboard_notify_key(seat, 0, keycodes[i], WL_KEYBOARD_KEY_STATE_PRESSED);
|
||||
wlr_seat_keyboard_notify_key(seat, 0, keycodes[i], WL_KEYBOARD_KEY_STATE_RELEASED);
|
||||
}
|
||||
|
||||
// Reset keymap when we're idle for a while
|
||||
wl_event_source_timer_update(ime->ime_reset_ime_keyboard_event_source, 100 /* ms */);
|
||||
}
|
||||
|
||||
static void perform_action(struct wlserver_input_method *ime, enum gamescope_input_method_action action)
|
||||
|
@ -346,8 +377,7 @@ static void perform_action(struct wlserver_input_method *ime, enum gamescope_inp
|
|||
return;
|
||||
}
|
||||
|
||||
ime->keys.clear();
|
||||
ime->keys[0] = key;
|
||||
// Keymap always contains all actions[]
|
||||
|
||||
struct xkb_keymap *keymap = generate_keymap(ime);
|
||||
if (keymap == nullptr) {
|
||||
|
@ -441,6 +471,16 @@ static const struct wlr_input_device_impl keyboard_device_impl = {
|
|||
.destroy = keyboard_device_destroy,
|
||||
};
|
||||
|
||||
static int reset_ime_keyboard(void *data)
|
||||
{
|
||||
struct wlserver_input_method *ime = (struct wlserver_input_method *)data;
|
||||
|
||||
ime->keys.clear();
|
||||
ime->next_keycode_index = 0; // preserve old behavior; could just let this keep going
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void manager_handle_create_input_method(struct wl_client *client, struct wl_resource *manager_resource, struct wl_resource *seat_resource, uint32_t id)
|
||||
{
|
||||
struct wlserver_input_method_manager *manager = (struct wlserver_input_method_manager *)wl_resource_get_user_data(manager_resource);
|
||||
|
@ -458,6 +498,7 @@ static void manager_handle_create_input_method(struct wl_client *client, struct
|
|||
ime->resource = ime_resource;
|
||||
ime->manager = manager;
|
||||
ime->serial = 1;
|
||||
ime->next_keycode_index = 0;
|
||||
|
||||
wlr_keyboard_init(&ime->keyboard, &keyboard_impl);
|
||||
wlr_input_device_init(&ime->keyboard_device, WLR_INPUT_DEVICE_KEYBOARD, &keyboard_device_impl, "ime", 0, 0);
|
||||
|
@ -468,6 +509,8 @@ static void manager_handle_create_input_method(struct wl_client *client, struct
|
|||
wl_resource_set_user_data(ime->resource, ime);
|
||||
gamescope_input_method_send_done(ime->resource, ime->serial);
|
||||
|
||||
ime->ime_reset_ime_keyboard_event_source = wl_event_loop_add_timer(manager->server->event_loop, reset_ime_keyboard, ime);
|
||||
|
||||
active_input_method = ime;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue