From 9f3a6757e9d429462416f9502d2f67c4843d8e2c Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Tue, 28 Mar 2017 10:23:09 +0200 Subject: [PATCH] Asynchronously update the keyboard state When the keyboard layout is modified via xmodmap, each single "change" (line of input to xmodmap) causes an "the keyboard configuration changed"-event to be sent. Awesome reacted to each of these events by reloading the keyboard layout. Thus, awesome reloaded the keyboard layout a lot and appeared to freeze. Fix this by asynchronously update the keyboard state: When such an event comes in, instead of reloading things immediately, we set a flag which makes us update the state at the end of the main loop iteration. This means that many events still cause only a single (or at least few) re-quering of the layout. Thus, a lot of time is saved. This commit removes the argument to the (undocumented!) signal xkb::group_changed. Previously, the argument was the active group number. Since this argument was unused and I'm lazy, I just removed it. The alternative would be that it might be visible to Lua that some "the active group changed"-events are dropped. Fixes: https://github.com/awesomeWM/awesome/issues/1494 Signed-off-by: Uli Schlachter --- event.h | 4 +++ globalconf.h | 6 +++++ tests/test-keyboard-layout-changes.lua | 29 +++++++++++++++++++++ xkb.c | 36 ++++++++++++++++++-------- xkb.h | 1 + 5 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 tests/test-keyboard-layout-changes.lua diff --git a/event.h b/event.h index 52cce408..ad270bcf 100644 --- a/event.h +++ b/event.h @@ -42,9 +42,13 @@ void client_destroy_later(void); /* objects/screen.c */ void screen_refresh(void); +/* xkb.c */ +void xkb_refresh(void); + static inline int awesome_refresh(void) { + xkb_refresh(); screen_refresh(); luaA_emit_refresh(); drawin_refresh(); diff --git a/globalconf.h b/globalconf.h index 77e3a76b..46d42fd4 100644 --- a/globalconf.h +++ b/globalconf.h @@ -186,6 +186,12 @@ typedef struct struct xkb_context *xkb_ctx; /* xkb state of dead keys on keyboard */ struct xkb_state *xkb_state; + /* Do we have a pending reload? */ + bool xkb_reload_keymap; + /* Do we have a pending map change? */ + bool xkb_map_changed; + /* Do we have a pending group change? */ + bool xkb_group_changed; /** The preferred size of client icons for this screen */ uint32_t preferred_icon_size; /** Cached wallpaper information */ diff --git a/tests/test-keyboard-layout-changes.lua b/tests/test-keyboard-layout-changes.lua new file mode 100644 index 00000000..2693a877 --- /dev/null +++ b/tests/test-keyboard-layout-changes.lua @@ -0,0 +1,29 @@ +-- Test for bug #1494: Using xmodmap freezes awesome since it re-queries the +-- keyboard layout many, many times. (xmodmap applies each change on its own) + +local runner = require("_runner") +local spawn = require("awful.spawn") +local GLib = require("lgi").GLib + +local done +local timer = GLib.Timer() + +local steps = { + function(count) + if count == 1 then + -- POSIX allows us to use awk + local cmd = "awk 'BEGIN { for(i=1; i<=1000;i++) print \"keycode 107 = parenleft\" }' | xmodmap -" + spawn.easy_async({"sh", "-c", cmd}, function() + awesome.sync() + done = true + end) + end + if done then + -- Apply some limit on how long awesome may need to process 'things' + return timer:elapsed() < 5 + end + end +} +runner.run_steps(steps) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/xkb.c b/xkb.c index cd994072..b92b2f38 100644 --- a/xkb.c +++ b/xkb.c @@ -297,13 +297,29 @@ xkb_reload_keymap(void) } } +void +xkb_refresh(void) +{ + lua_State *L = globalconf_get_lua_State(); + + if (globalconf.xkb_reload_keymap) + xkb_reload_keymap(); + if (globalconf.xkb_map_changed) + signal_object_emit(L, &global_signals, "xkb::map_changed", 0); + if (globalconf.xkb_group_changed) + signal_object_emit(L, &global_signals, "xkb::group_changed", 0); + + globalconf.xkb_reload_keymap = false; + globalconf.xkb_map_changed = false; + globalconf.xkb_group_changed = false; +} + /** The xkb notify event handler. * \param event The event. */ void event_handle_xkb_notify(xcb_generic_event_t* event) { - lua_State *L = globalconf_get_lua_State(); assert(globalconf.have_xkb); /* The pad0 field of xcb_generic_event_t contains the event sub-type, @@ -315,18 +331,16 @@ event_handle_xkb_notify(xcb_generic_event_t* event) { xcb_xkb_new_keyboard_notify_event_t *new_keyboard_event = (void*)event; - xkb_reload_keymap(); + globalconf.xkb_reload_keymap = true; if (new_keyboard_event->changed & XCB_XKB_NKN_DETAIL_KEYCODES) - { - signal_object_emit(L, &global_signals, "xkb::map_changed", 0); - } + globalconf.xkb_map_changed = true; break; } case XCB_XKB_MAP_NOTIFY: { - xkb_reload_keymap(); - signal_object_emit(L, &global_signals, "xkb::map_changed", 0); + globalconf.xkb_reload_keymap = true; + globalconf.xkb_map_changed = true; break; } case XCB_XKB_STATE_NOTIFY: @@ -342,10 +356,7 @@ event_handle_xkb_notify(xcb_generic_event_t* event) state_notify_event->lockedGroup); if (state_notify_event->changed & XCB_XKB_STATE_PART_GROUP_STATE) - { - lua_pushinteger(L, state_notify_event->group); - signal_object_emit(L, &global_signals, "xkb::group_changed", 1); - } + globalconf.xkb_group_changed = true; break; } @@ -358,6 +369,9 @@ event_handle_xkb_notify(xcb_generic_event_t* event) void xkb_init(void) { + globalconf.xkb_reload_keymap = false; + globalconf.xkb_map_changed = false; + globalconf.xkb_group_changed = false; int success_xkb = xkb_x11_setup_xkb_extension(globalconf.connection, XKB_X11_MIN_MAJOR_XKB_VERSION, diff --git a/xkb.h b/xkb.h index fc91e1f9..e854046e 100644 --- a/xkb.h +++ b/xkb.h @@ -26,6 +26,7 @@ #include void event_handle_xkb_notify(xcb_generic_event_t* event); +void xkb_refresh(void); void xkb_init(void); void xkb_free(void);