From 996f6785a885349855a1233113ba092816175843 Mon Sep 17 00:00:00 2001 From: Aleksey Fedotov Date: Mon, 23 Feb 2015 19:08:24 +0300 Subject: [PATCH] xkb: implementation of keyboard layout switched This patch provides functions to get/set current keyboard layout. Current implementation doesn't support any configuration of layout, it's a merely a layout indicator and switcher, however layout configuration can be set by tools like setxkbmap or by any third-party tools. --- CMakeLists.txt | 1 + awesome.c | 4 + awesomeConfig.cmake | 1 + docs/capi/awesome.lua.in | 13 +++ event.c | 14 +++ luaa.c | 6 ++ xkb.c | 185 +++++++++++++++++++++++++++++++++++++++ xkb.h | 36 ++++++++ 8 files changed, 260 insertions(+) create mode 100644 xkb.c create mode 100644 xkb.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3609687af..5aac6c4f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ set(AWE_SRCS ${SOURCE_DIR}/strut.c ${SOURCE_DIR}/systray.c ${SOURCE_DIR}/xwindow.c + ${SOURCE_DIR}/xkb.c ${SOURCE_DIR}/common/atoms.c ${SOURCE_DIR}/common/backtrace.c ${SOURCE_DIR}/common/buffer.c diff --git a/awesome.c b/awesome.c index 0d8cb6a53..e07a79e05 100644 --- a/awesome.c +++ b/awesome.c @@ -26,6 +26,7 @@ #include "common/backtrace.h" #include "common/version.h" #include "common/xutil.h" +#include "xkb.h" #include "dbus.h" #include "event.h" #include "ewmh.h" @@ -499,6 +500,9 @@ main(int argc, char **argv) /* init spawn (sn) */ spawn_init(); + /* init xkb */ + xkb_init(); + /* The default GC is just a newly created associated with a window with * depth globalconf.default_depth */ xcb_window_t tmp_win = xcb_generate_id(globalconf.connection); diff --git a/awesomeConfig.cmake b/awesomeConfig.cmake index e2bb98c49..c2b889dfe 100644 --- a/awesomeConfig.cmake +++ b/awesomeConfig.cmake @@ -140,6 +140,7 @@ pkg_check_modules(AWESOME_REQUIRED REQUIRED xcb-util>=0.3.8 xcb-keysyms>=0.3.4 xcb-icccm>=0.3.8 + xcb-xkb>=1.11 cairo-xcb libstartup-notification-1.0>=0.10 xproto>=7.0.15 diff --git a/docs/capi/awesome.lua.in b/docs/capi/awesome.lua.in index 2b27b2b46..5afbcad75 100644 --- a/docs/capi/awesome.lua.in +++ b/docs/capi/awesome.lua.in @@ -75,3 +75,16 @@ -- @param name A string with the event name. -- @param ... Signal arguments. -- @function emit_signal + +--- Switch keyboard layout group. +-- @param num keyboard layout number, integer from 0 to 3 +-- @function xkb_set_layout_group + +--- Get current keyboard layout group. +-- @return current keyboard layout number +-- @function xkb_get_layout_group + + +--- Get description of configured layouts +-- @return String with description of configured layouts +-- @function xkb_get_group_names diff --git a/event.c b/event.c index c0a8bfae0..4461ea00d 100644 --- a/event.c +++ b/event.c @@ -32,6 +32,7 @@ #include "mousegrabber.h" #include "luaa.h" #include "systray.h" +#include "xkb.h" #include "objects/screen.h" #include "common/atoms.h" #include "common/xutil.h" @@ -42,6 +43,7 @@ #include #include #include +#include #define DO_EVENT_HOOK_CALLBACK(type, xcbtype, xcbeventprefix, arraytype, match) \ static void \ @@ -880,6 +882,7 @@ void event_handle(xcb_generic_event_t *event) static uint8_t randr_screen_change_notify = 0; static uint8_t shape_notify = 0; + static uint8_t xkb_notify = 0; if(randr_screen_change_notify == 0) { @@ -898,10 +901,21 @@ void event_handle(xcb_generic_event_t *event) shape_notify = shape_query->first_event + XCB_SHAPE_NOTIFY; } + if(xkb_notify == 0) + { + /* check for xkb extension */ + const xcb_query_extension_reply_t *xkb_query; + xkb_query = xcb_get_extension_data(globalconf.connection, &xcb_xkb_id); + if(xkb_query->present) + xkb_notify = xkb_query->first_event; + } + if (response_type == randr_screen_change_notify) event_handle_randr_screen_change_notify((void *) event); if (response_type == shape_notify) event_handle_shape_notify((void *) event); + if (response_type == xkb_notify) + event_handle_xkb_notify((void *) event); } // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/luaa.c b/luaa.c index bfd1b6b3b..2c16152c4 100644 --- a/luaa.c +++ b/luaa.c @@ -37,6 +37,7 @@ #include "selection.h" #include "spawn.h" #include "systray.h" +#include "xkb.h" #include #include @@ -359,6 +360,9 @@ luaA_init(xdgHandle* xdg) { "get_xproperty", luaA_get_xproperty }, { "__index", luaA_awesome_index }, { "__newindex", luaA_default_newindex }, + { "xkb_set_layout_group", luaA_xkb_set_layout_group}, + { "xkb_get_layout_group", luaA_xkb_get_layout_group}, + { "xkb_get_group_names", luaA_xkb_get_group_names}, { NULL, NULL } }; @@ -473,6 +477,8 @@ luaA_init(xdgHandle* xdg) signal_add(&global_signals, "debug::newindex::miss"); signal_add(&global_signals, "systray::update"); signal_add(&global_signals, "wallpaper_changed"); + signal_add(&global_signals, "xkb::map_changed"); + signal_add(&global_signals, "xkb::group_changed"); signal_add(&global_signals, "refresh"); signal_add(&global_signals, "startup"); signal_add(&global_signals, "exit"); diff --git a/xkb.c b/xkb.c new file mode 100644 index 000000000..ae5f872fe --- /dev/null +++ b/xkb.c @@ -0,0 +1,185 @@ +/* + * xkb.c - keyboard layout control functions + * + * Copyright © 2015 Aleksey Fedotov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "xkb.h" +#include "globalconf.h" +#include + + +/* \brief switch keyboard layout + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + * \luastack + * \lparam layout number, integer from 0 to 3 + */ +int +luaA_xkb_set_layout_group(lua_State *L) +{ + unsigned group = luaL_checkinteger(L, 1); + xcb_xkb_latch_lock_state (globalconf.connection, XCB_XKB_ID_USE_CORE_KBD, + 0, 0, true, group, 0, 0, 0); + return 0; +} + +/* \brief get current layout number + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + * \luastack + * \lreturn current layout number, integer from 0 to 3 + */ +int +luaA_xkb_get_layout_group(lua_State *L) +{ + xcb_xkb_get_state_cookie_t state_c; + state_c = xcb_xkb_get_state_unchecked (globalconf.connection, + XCB_XKB_ID_USE_CORE_KBD); + xcb_xkb_get_state_reply_t* state_r; + state_r = xcb_xkb_get_state_reply (globalconf.connection, + state_c, NULL); + if (!state_r) + { + free(state_r); + return 0; + } + lua_pushinteger(L, state_r->group); + free(state_r); + return 1; +} + +/* \brief get layout short names + * \param L The Lua VM state. + * \return The number of elements pushed on stack. + * \luastack + * \lreturn string describing current layout settings, \ + * example: 'pc+us+de:2+inet(evdev)+group(alt_shift_toggle)+ctrl(nocaps)' + */ +int +luaA_xkb_get_group_names(lua_State *L) +{ + xcb_xkb_get_names_cookie_t name_c; + name_c = xcb_xkb_get_names_unchecked (globalconf.connection, + XCB_XKB_ID_USE_CORE_KBD, + XCB_XKB_NAME_DETAIL_SYMBOLS); + xcb_xkb_get_names_reply_t* name_r; + name_r = xcb_xkb_get_names_reply (globalconf.connection, name_c, NULL); + + if (!name_r) + { + luaA_warn(L, "Failed to get xkb symbols name"); + return 0; + } + + xcb_xkb_get_names_value_list_t name_list; + void *buffer = xcb_xkb_get_names_value_list(name_r); + xcb_xkb_get_names_value_list_unpack ( + buffer, name_r->nTypes, name_r->indicators, + name_r->virtualMods, name_r->groupNames, name_r->nKeys, + name_r->nKeyAliases, name_r->nRadioGroups, name_r->which, + &name_list); + + xcb_get_atom_name_cookie_t atom_name_c; + atom_name_c = xcb_get_atom_name_unchecked(globalconf.connection, name_list.symbolsName); + xcb_get_atom_name_reply_t *atom_name_r; + atom_name_r = xcb_get_atom_name_reply(globalconf.connection, atom_name_c, NULL); + if (!atom_name_r) { + luaA_warn(L, "Failed to get atom symbols name"); + free(name_r); + return 0; + } + + const char *name = xcb_get_atom_name_name(atom_name_r); + size_t name_len = xcb_get_atom_name_name_length(atom_name_r); + lua_pushlstring(L, name, name_len); + + free(atom_name_r); + free(name_r); + return 1; +} + +/** 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(); + /* The pad0 field of xcb_generic_event_t contains the event sub-type, + * unfortunately xkb doesn't provide a usable struct for getting this in a + * nicer way*/ + switch (event->pad0) + { + case XCB_XKB_NEW_KEYBOARD_NOTIFY: + { + xcb_xkb_new_keyboard_notify_event_t *new_keyboard_event = (void*)event; + if (new_keyboard_event->changed & XCB_XKB_NKN_DETAIL_KEYCODES) + { + signal_object_emit(L, &global_signals, "xkb::map_changed", 0); + } + break; + } + case XCB_XKB_NAMES_NOTIFY: + { + signal_object_emit(L, &global_signals, "xkb::map_changed", 0); + break; + } + case XCB_XKB_STATE_NOTIFY: + { + xcb_xkb_state_notify_event_t *state_notify_event = (void*)event; + if (state_notify_event->changed & XCB_XKB_STATE_PART_GROUP_STATE) + { + lua_pushnumber(L, state_notify_event->group); + signal_object_emit(L, &global_signals, "xkb::group_changed", 1); + } + break; + } + } +} + +/** Initialize XKB support + */ + +void xkb_init(void) +{ + /* check that XKB extension present in this X server */ + const xcb_query_extension_reply_t *xkb_r; + xkb_r = xcb_get_extension_data(globalconf.connection, &xcb_xkb_id); + if (!xkb_r || !xkb_r->present) + { + fatal("Xkb extension not present"); + } + + /* check xkb version */ + xcb_xkb_use_extension_cookie_t ext_cookie = xcb_xkb_use_extension(globalconf.connection, 1, 0); + xcb_xkb_use_extension_reply_t *ext_reply = xcb_xkb_use_extension_reply (globalconf.connection, ext_cookie, NULL); + if (!ext_reply || !ext_reply->supported) + { + fatal("Required xkb extension is not supported"); + } + unsigned int map = XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY; + xcb_xkb_select_events_checked(globalconf.connection, + XCB_XKB_ID_USE_CORE_KBD, + map, + 0, + map, + 0, + 0, + 0); + +} diff --git a/xkb.h b/xkb.h new file mode 100644 index 000000000..1a401ce2d --- /dev/null +++ b/xkb.h @@ -0,0 +1,36 @@ +/* + * xkb.h - Keyboard layout manager header + * + * Copyright © 2015 Aleksey Fedotov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef AWESOME_XKB_H +#define AWESOME_XKB_H + +#include +#include + +void event_handle_xkb_notify(xcb_generic_event_t* event); +void xkb_init(void); + +int luaA_xkb_set_layout_group(lua_State *L); +int luaA_xkb_get_layout_group(lua_State *L); +int luaA_xkb_get_group_names(lua_State *L); + + +#endif