From e1f87a67b4352ad60b4fc0291a121515fe7f15d3 Mon Sep 17 00:00:00 2001 From: Quentin Glidic Date: Sun, 21 Feb 2016 13:10:32 +0100 Subject: rofi: Migrate to libxkbcommon Signed-off-by: Quentin Glidic --- configure.ac | 2 +- include/keyb.h | 2 +- include/mode-private.h | 2 +- include/rofi.h | 3 +- include/textbox.h | 3 +- include/view-internal.h | 2 +- include/view.h | 4 +- include/x11-helper.h | 2 +- include/xkb-internal.h | 15 +++++++ include/xkb.h | 6 +++ source/keyb.c | 4 +- source/rofi.c | 82 +++++++++++++++++++++++++++++++++---- source/scrollbar.c | 1 + source/textbox.c | 6 +-- source/view.c | 106 ++++++++++++++++++++++++++++-------------------- source/x11-helper.c | 6 +-- 16 files changed, 178 insertions(+), 68 deletions(-) create mode 100644 include/xkb-internal.h create mode 100644 include/xkb.h diff --git a/configure.ac b/configure.ac index d7f711fc..63de6ae2 100644 --- a/configure.ac +++ b/configure.ac @@ -83,7 +83,7 @@ dnl --------------------------------------------------------------------- dnl X11, Glib, Xinerama, Pango, Cairo, libstartup notification dnl --------------------------------------------------------------------- PKG_CHECK_MODULES([glib], [glib-2.0 >= 2.40]) -GW_CHECK_XCB([xcb-aux xkbcommon xkbcommon-x11]) +GW_CHECK_XCB([xcb-aux xcb-xkb xkbcommon xkbcommon-x11]) PKG_CHECK_MODULES([x11], [x11 x11-xcb]) PKG_CHECK_MODULES([xinerama], [xinerama]) PKG_CHECK_MODULES([pango], [pango pangocairo]) diff --git a/include/keyb.h b/include/keyb.h index bd195017..37d67884 100644 --- a/include/keyb.h +++ b/include/keyb.h @@ -99,7 +99,7 @@ void cleanup_abe ( void ); * Check if this key has been triggered. * @returns TRUE if key combo matches, FALSE otherwise. */ -int abe_test_action ( KeyBindingAction action, unsigned int mask, KeySym key ); +int abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t key ); /*@}*/ #endif // ROFI_KEYB_H diff --git a/include/mode-private.h b/include/mode-private.h index 511f4e61..e85ac96b 100644 --- a/include/mode-private.h +++ b/include/mode-private.h @@ -43,7 +43,7 @@ struct _Mode /** Keybindings (keysym and modmask) */ char * keycfg; char * keystr; - KeySym keysym; + xkb_keysym_t keysym; unsigned int modmask; /** diff --git a/include/rofi.h b/include/rofi.h index 411acb37..252850d5 100644 --- a/include/rofi.h +++ b/include/rofi.h @@ -1,6 +1,7 @@ #ifndef ROFI_MAIN_H #define ROFI_MAIN_H #include +#include #include #include #include @@ -45,7 +46,7 @@ const Mode * rofi_get_mode ( unsigned int index ); * @return the index of the switcher that matches the key combination * specified by key and modstate. Returns -1 if none was found */ -int locate_switcher ( KeySym key, unsigned int modstate ); +int locate_switcher ( xkb_keysym_t key, unsigned int modstate ); void rofi_set_return_code ( int code ); /** Reset terminal */ diff --git a/include/textbox.h b/include/textbox.h index c4caa7ca..b1a40670 100644 --- a/include/textbox.h +++ b/include/textbox.h @@ -1,6 +1,7 @@ #ifndef ROFI_TEXTBOX_H #define ROFI_TEXTBOX_H +#include #include #include #include @@ -113,7 +114,7 @@ void textbox_draw ( textbox *tb, cairo_t *draw ); * * @returns if the key was handled (1), unhandled(0) or handled and return was pressed (-1) */ -int textbox_keypress ( textbox *tb, xcb_key_press_event_t *ev, char *pad, int pad_len, KeySym key, Status stat ); +int textbox_keypress ( textbox *tb, xcb_key_press_event_t *ev, char *pad, int pad_len, xkb_keysym_t key ); /** * @param tb Handle to the textbox diff --git a/include/view-internal.h b/include/view-internal.h index 6dd61ba2..b4bbd8c6 100644 --- a/include/view-internal.h +++ b/include/view-internal.h @@ -69,7 +69,7 @@ struct RofiViewState MenuFlags menu_flags; // Handlers. - void ( *x11_event_loop )( struct RofiViewState *state, xcb_generic_event_t *ev ); + void ( *x11_event_loop )( struct RofiViewState *state, xcb_generic_event_t *ev, xkb_stuff *xkb ); void ( *finalize )( struct RofiViewState *state ); }; /** @} */ diff --git a/include/view.h b/include/view.h index d6a3aa19..79a2997b 100644 --- a/include/view.h +++ b/include/view.h @@ -1,6 +1,8 @@ #ifndef ROFI_VIEW_H #define ROFI_VIEW_H +#include "xkb.h" + /** * @defgroup View View * @@ -47,7 +49,7 @@ void rofi_view_finalize ( RofiViewState *state ); MenuReturn rofi_view_get_return_value ( const RofiViewState *state ); unsigned int rofi_view_get_next_position ( const RofiViewState *state ); -void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *event ); +void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *event, xkb_stuff *xkb ); unsigned int rofi_view_get_completed ( const RofiViewState *state ); const char * rofi_view_get_user_input ( const RofiViewState *state ); diff --git a/include/x11-helper.h b/include/x11-helper.h index a3647b71..1f60b0c7 100644 --- a/include/x11-helper.h +++ b/include/x11-helper.h @@ -124,7 +124,7 @@ void x11_ungrab_key ( Display *display, unsigned int modmask, KeySym key ); * * Parse key from user input string. */ -void x11_parse_key ( char *combo, unsigned int *mod, KeySym *key ); +void x11_parse_key ( char *combo, unsigned int *mod, xkb_keysym_t *key ); /** * @param display The connection to the X server. diff --git a/include/xkb-internal.h b/include/xkb-internal.h new file mode 100644 index 00000000..76606460 --- /dev/null +++ b/include/xkb-internal.h @@ -0,0 +1,15 @@ +#ifndef ROFI_XKB_INTERNAL_H +#define ROFI_XKB_INTERNAL_H + +#include + +struct xkb_stuff { + xcb_connection_t *xcb_connection; + struct xkb_context *context; + uint8_t first_event; + int32_t device_id; + struct xkb_keymap *keymap; + struct xkb_state *state; +}; + +#endif diff --git a/include/xkb.h b/include/xkb.h new file mode 100644 index 00000000..2af1ccb8 --- /dev/null +++ b/include/xkb.h @@ -0,0 +1,6 @@ +#ifndef ROFI_XKB_H +#define ROFI_XKB_H + +typedef struct xkb_stuff xkb_stuff; + +#endif diff --git a/source/keyb.c b/source/keyb.c index 7c6e7046..4ed04a67 100644 --- a/source/keyb.c +++ b/source/keyb.c @@ -8,7 +8,7 @@ typedef struct _KeyBinding { unsigned int modmask; - KeySym keysym; + xkb_keysym_t keysym; } KeyBinding; typedef struct _ActionBindingEntry @@ -133,7 +133,7 @@ void cleanup_abe ( void ) } } -int abe_test_action ( KeyBindingAction action, unsigned int mask, KeySym key ) +int abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t key ) { ActionBindingEntry *akb = &( abe[action] ); diff --git a/source/rofi.c b/source/rofi.c index f3e2557e..18b9c76f 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -36,7 +36,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -66,6 +68,7 @@ #include "view.h" #include "view-internal.h" +#include "xkb-internal.h" gboolean daemon_mode = FALSE; // Pidfile. @@ -75,9 +78,10 @@ SnDisplay *sndisplay = NULL; SnLauncheeContext *sncontext = NULL; xcb_connection_t *xcb_connection = NULL; xcb_screen_t *xcb_screen = NULL; -Display *display = NULL; -char *display_str = NULL; -char *config_path = NULL; +struct xkb_stuff xkb = { NULL }; +Display *display = NULL; +char *display_str = NULL; +char *config_path = NULL; // Array of modi. Mode **modi = NULL; unsigned int num_modi = 0; @@ -128,7 +132,7 @@ static int switcher_get ( const char *name ) } extern unsigned int NumlockMask; -int locate_switcher ( KeySym key, unsigned int modstate ) +int locate_switcher ( xkb_keysym_t key, unsigned int modstate ) { // ignore annoying modifiers unsigned int modstate_filtered = modstate & ~( LockMask | NumlockMask ); @@ -285,10 +289,10 @@ int show_error_message ( const char *msg, int markup ) * Function that listens for global key-presses. * This is only used when in daemon mode. */ -static void handle_keypress ( xcb_key_press_event_t *ev ) +static void handle_keypress ( xcb_key_press_event_t *ev, struct xkb_stuff *xkb ) { - int index; - KeySym key = XkbKeycodeToKeysym ( display, ev->detail, 0, 0 ); + xkb_keysym_t key = xkb_state_key_get_one_sym ( xkb->state, ev->detail ); + int index; index = locate_switcher ( key, ev->state ); if ( index >= 0 ) { run_switcher ( index ); @@ -557,7 +561,7 @@ static gboolean main_loop_x11_event_handler ( xcb_generic_event_t *ev, G_GNUC_UN sn_xcb_display_process_event ( sndisplay, ev ); } if ( state != NULL ) { - rofi_view_itterrate ( state, ev ); + rofi_view_itterrate ( state, ev, &xkb ); if ( rofi_view_get_completed ( state ) ) { // This menu is done. rofi_view_finalize ( state ); @@ -576,7 +580,7 @@ static gboolean main_loop_x11_event_handler ( xcb_generic_event_t *ev, G_GNUC_UN // Ignore it. // If keypress, handle it. if ( ( ev->response_type & ~0x80 ) == XCB_KEY_PRESS ) { - handle_keypress ( (xcb_key_press_event_t *) ev ); + handle_keypress ( (xcb_key_press_event_t *) ev, &xkb ); } } return G_SOURCE_CONTINUE; @@ -600,6 +604,7 @@ static gboolean main_loop_signal_handler_hup ( G_GNUC_UNUSED gpointer data ) grab_global_keybindings (); // We need to flush, otherwise the first key presses are not caught. xcb_flush ( xcb_connection ); + XFlush ( display ); return G_SOURCE_CONTINUE; } @@ -804,6 +809,65 @@ int main ( int argc, char *argv[] ) xcb_connection = XGetXCBConnection ( display ); xcb_screen = xcb_aux_get_screen ( xcb_connection, DefaultScreen ( display ) ); + if ( xkb_x11_setup_xkb_extension ( xcb_connection, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, NULL, NULL, &xkb.first_event, NULL ) < 0 ) { + fprintf ( stderr, "cannot setup XKB extension!\n" ); + return EXIT_FAILURE; + } + + xkb.context = xkb_context_new ( XKB_CONTEXT_NO_FLAGS ); + if ( xkb.context == NULL ) { + fprintf ( stderr, "cannot create XKB context!\n" ); + return EXIT_FAILURE; + } + xkb.xcb_connection = xcb_connection; + + xkb.device_id = xkb_x11_get_core_keyboard_device_id ( xcb_connection ); + + enum + { + required_events = + ( XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | + XCB_XKB_EVENT_TYPE_MAP_NOTIFY | + XCB_XKB_EVENT_TYPE_STATE_NOTIFY ), + + required_nkn_details = + ( XCB_XKB_NKN_DETAIL_KEYCODES ), + + required_map_parts = + ( XCB_XKB_MAP_PART_KEY_TYPES | + XCB_XKB_MAP_PART_KEY_SYMS | + XCB_XKB_MAP_PART_MODIFIER_MAP | + XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | + XCB_XKB_MAP_PART_KEY_ACTIONS | + XCB_XKB_MAP_PART_VIRTUAL_MODS | + XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP ), + + required_state_details = + ( XCB_XKB_STATE_PART_MODIFIER_BASE | + XCB_XKB_STATE_PART_MODIFIER_LATCH | + XCB_XKB_STATE_PART_MODIFIER_LOCK | + XCB_XKB_STATE_PART_GROUP_BASE | + XCB_XKB_STATE_PART_GROUP_LATCH | + XCB_XKB_STATE_PART_GROUP_LOCK ), + }; + + static const xcb_xkb_select_events_details_t details = { + .affectNewKeyboard = required_nkn_details, + .newKeyboardDetails = required_nkn_details, + .affectState = required_state_details, + .stateDetails = required_state_details, + }; + xcb_xkb_select_events ( xcb_connection, xkb.device_id, required_events, /* affectWhich */ + 0, /* clear */ + 0, /* selectAll */ + required_map_parts, /* affectMap */ + required_map_parts, /* map */ + &details ); + + xkb.keymap = xkb_x11_keymap_new_from_device ( xkb.context, xcb_connection, xkb.device_id, XKB_KEYMAP_COMPILE_NO_FLAGS ); + xkb.state = xkb_x11_state_new_from_device ( xkb.keymap, xcb_connection, xkb.device_id ); + main_loop = g_main_loop_new ( NULL, FALSE ); TICK_N ( "Setup mainloop" ); diff --git a/source/scrollbar.c b/source/scrollbar.c index 35b3d3d3..2f3eee63 100644 --- a/source/scrollbar.c +++ b/source/scrollbar.c @@ -23,6 +23,7 @@ */ #include +#include #include #include #include diff --git a/source/textbox.c b/source/textbox.c index bb784eb8..d9225fb3 100644 --- a/source/textbox.c +++ b/source/textbox.c @@ -533,14 +533,14 @@ static void textbox_cursor_del_word ( textbox *tb ) // 0 = unhandled // 1 = handled // -1 = handled and return pressed (finished) -int textbox_keypress ( textbox *tb, xcb_key_press_event_t *ev, char *pad, int pad_len, KeySym key, Status stat ) +int textbox_keypress ( textbox *tb, xcb_key_press_event_t *ev, char *pad, int pad_len, xkb_keysym_t key ) { if ( !( tb->flags & TB_EDITABLE ) ) { return 0; } int old_blink = tb->blink; tb->blink = 2; - if ( stat == XLookupKeySym || stat == XLookupBoth ) { + if ( key != XKB_KEY_NoSymbol ) { // Left or Ctrl-b if ( abe_test_action ( MOVE_CHAR_BACK, ev->state, key ) ) { textbox_cursor_dec ( tb ); @@ -606,7 +606,7 @@ int textbox_keypress ( textbox *tb, xcb_key_press_event_t *ev, char *pad, int pa return -1; } } - if ( pad_len > 0 && ( stat == XLookupBoth || stat == XLookupChars ) ) { + if ( pad_len > 0 ) { // Filter When alt/ctrl is pressed do not accept the character. if ( !g_ascii_iscntrl ( *pad ) ) { textbox_insert ( tb, tb->cursor, pad, pad_len ); diff --git a/source/view.c b/source/view.c index c5ef9b84..ffde0a32 100644 --- a/source/view.c +++ b/source/view.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include #include @@ -54,7 +56,7 @@ #include "rofi.h" #include "mode.h" -#include "rofi.h" +#include "xkb-internal.h" #include "helper.h" #include "textbox.h" #include "scrollbar.h" @@ -367,34 +369,58 @@ static void rofi_view_resize ( RofiViewState *state ) state->update = TRUE; } -void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *event ) +void rofi_view_itterrate ( RofiViewState *state, xcb_generic_event_t *event, xkb_stuff *xkb ) { uint8_t type = event->response_type & ~0x80; - switch ( type ) - { - case XCB_EXPOSE: - state->update = TRUE; - break; - case XCB_CONFIGURE_NOTIFY: - { - xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *) event; - if ( xce->window == main_window ) { - if ( state->x != xce->x || state->y != xce->y ) { - state->x = xce->x; - state->y = xce->y; - state->update = TRUE; - } - if ( state->w != xce->width || state->h != xce->height ) { - state->w = xce->width; - state->h = xce->height; - cairo_xlib_surface_set_size ( surface, state->w, state->h ); - rofi_view_resize ( state ); - } + if ( type == xkb->first_event ) { + switch ( event->pad0 ) + { + case XCB_XKB_MAP_NOTIFY: + xkb_state_unref ( xkb->state ); + xkb_keymap_unref ( xkb->keymap ); + xkb->keymap = xkb_x11_keymap_new_from_device ( xkb->context, xkb->xcb_connection, xkb->device_id, 0 ); + xkb->state = xkb_x11_state_new_from_device ( xkb->keymap, xkb->xcb_connection, xkb->device_id ); + break; + case XCB_XKB_STATE_NOTIFY: + { + xcb_xkb_state_notify_event_t *ksne = (xcb_xkb_state_notify_event_t *) event; + xkb_state_update_mask ( xkb->state, + ksne->baseMods, + ksne->latchedMods, + ksne->lockedMods, + ksne->baseGroup, + ksne->latchedGroup, + ksne->lockedGroup ); + break; + } } } - break; - default: - state->x11_event_loop ( state, event, xkb ); + else{ switch ( type ) + { + case XCB_EXPOSE: + state->update = TRUE; + break; + case XCB_CONFIGURE_NOTIFY: + { + xcb_configure_notify_event_t *xce = (xcb_configure_notify_event_t *) event; + if ( xce->window == main_window ) { + if ( state->x != xce->x || state->y != xce->y ) { + state->x = xce->x; + state->y = xce->y; + state->update = TRUE; + } + if ( state->w != xce->width || state->h != xce->height ) { + state->w = xce->width; + state->h = xce->height; + cairo_xlib_surface_set_size ( surface, state->w, state->h ); + rofi_view_resize ( state ); + } + } + break; + } + default: + state->x11_event_loop ( state, event, xkb ); + } } rofi_view_update ( state ); } @@ -996,7 +1022,7 @@ static void rofi_view_paste ( RofiViewState *state, xcb_selection_notify_event_t * * Keyboard navigation through the elements. */ -static int rofi_view_keyboard_navigation ( RofiViewState *state, KeySym key, unsigned int modstate ) +static int rofi_view_keyboard_navigation ( RofiViewState *state, xkb_keysym_t key, unsigned int modstate ) { // pressing one of the global key bindings closes the switcher. This allows fast closing of the // menu if an item is not selected @@ -1246,7 +1272,7 @@ void rofi_view_setup_fake_transparency ( Display *display, RofiViewState *state } } -static void rofi_view_mainloop_iter ( RofiViewState *state, xcb_generic_event_t *ev ) +static void rofi_view_mainloop_iter ( RofiViewState *state, xcb_generic_event_t *ev, xkb_stuff *xkb ) { switch ( ev->response_type & ~0x80 ) { @@ -1279,20 +1305,14 @@ static void rofi_view_mainloop_iter ( RofiViewState *state, xcb_generic_event_t case XCB_KEY_PRESS: { xcb_key_press_event_t *xkpe = (xcb_key_press_event_t *) ev; - XEvent fake_event; - fake_event.type = KeyPress; - fake_event.xany.display = display; - fake_event.xany.window = xkpe->event; - fake_event.xkey.state = xkpe->state; - fake_event.xkey.keycode = xkpe->detail; - // This is needed for letting the Input Method handle combined keys. - // E.g. `e into รจ - Status stat; - char pad[32]; - KeySym key; // = XkbKeycodeToKeysym ( display, ev->xkey.keycode, 0, 0 ); - int len = Xutf8LookupString ( xic, &( fake_event.xkey ), pad, sizeof ( pad ), &key, &stat ); - pad[len] = 0; - if ( stat == XLookupKeySym || stat == XLookupBoth ) { + xcb_keysym_t key; + char pad[32]; + int len = 0; + + key = xkb_state_key_get_one_sym ( xkb->state, xkpe->detail ); + len = xkb_state_key_get_utf8 ( xkb->state, xkpe->detail, pad, sizeof ( pad ) ); + + if ( key != XKB_KEY_NoSymbol ) { // Handling of paste if ( abe_test_action ( PASTE_PRIMARY, xkpe->state, key ) ) { XConvertSelection ( display, XA_PRIMARY, netatoms[UTF8_STRING], netatoms[UTF8_STRING], main_window, CurrentTime ); @@ -1364,7 +1384,7 @@ static void rofi_view_mainloop_iter ( RofiViewState *state, xcb_generic_event_t break; } - int rc = textbox_keypress ( state->text, xkpe, pad, len, key, stat ); + int rc = textbox_keypress ( state->text, xkpe, pad, len, key ); // Row is accepted. if ( rc < 0 ) { int shift = ( ( xkpe->state & ShiftMask ) == ShiftMask ); @@ -1625,7 +1645,7 @@ RofiViewState *rofi_view_create ( Mode *sw, } return state; } -static void __error_dialog_event_loop ( RofiViewState *state, xcb_generic_event_t *ev ) +static void __error_dialog_event_loop ( RofiViewState *state, xcb_generic_event_t *ev, G_GNUC_UNUSED xkb_stuff *xkb ) { // Handle event. switch ( ev->response_type & ~0x80 ) diff --git a/source/x11-helper.c b/source/x11-helper.c index 6e18c802..576ec72c 100644 --- a/source/x11-helper.c +++ b/source/x11-helper.c @@ -433,7 +433,7 @@ static void x11_figure_out_numlock_mask ( Display *display ) } // convert a Mod+key arg to mod mask and keysym -void x11_parse_key ( char *combo, unsigned int *mod, KeySym *key ) +void x11_parse_key ( char *combo, unsigned int *mod, xkb_keysym_t *key ) { GString *str = g_string_new ( "" ); unsigned int modmask = 0; @@ -506,9 +506,9 @@ void x11_parse_key ( char *combo, unsigned int *mod, KeySym *key ) i--; } - KeySym sym = XStringToKeysym ( combo + i ); + xkb_keysym_t sym = xkb_keysym_from_name ( combo + i, XKB_KEYSYM_CASE_INSENSITIVE ); - if ( sym == NoSymbol || ( !modmask && ( strchr ( combo, '-' ) || strchr ( combo, '+' ) ) ) ) { + if ( sym == XKB_KEY_NoSymbol || ( !modmask && ( strchr ( combo, '-' ) || strchr ( combo, '+' ) ) ) ) { g_string_append_printf ( str, "Sorry, rofi cannot understand the key combination: %s\n", combo ); g_string_append ( str, "\nRofi supports the following modifiers:\n\t" ); g_string_append ( str, "Shift,Control,Alt,AltGR,SuperL,SuperR," ); -- cgit v1.2.3