Merge branch 'wayland-hyper' of https://github.com/orki/kitty

This commit is contained in:
Kovid Goyal 2021-08-18 08:29:51 +05:30
commit bdebb19648
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 156 additions and 11 deletions

View File

@ -67,11 +67,16 @@ Variables that influence kitty behavior
Same as :envvar:`VISUAL`. Used if :envvar:`VISUAL` is not set. Same as :envvar:`VISUAL`. Used if :envvar:`VISUAL` is not set.
.. envvar:: GLFW_IM_MODULE .. envvar:: GLFW_IM_MODULE
Set this to ``ibus`` to enable support for IME under X11. Set this to ``ibus`` to enable support for IME under X11.
.. envvar:: KITTY_WAYLAND_DETECT_MODIFIERS
When set to a non-empty value, kitty attempts to autodiscover XKB
modifiers under Wayland. It is possible for the autodiscovery to
fail; the default Wayland XKB mappings are used in this case.
Variables that kitty sets when running child programs Variables that kitty sets when running child programs

View File

@ -145,8 +145,8 @@ Modifiers
This protocol supports six modifier keys, :kbd:`shift, alt, ctrl, super, hyper This protocol supports six modifier keys, :kbd:`shift, alt, ctrl, super, hyper
and meta` as well as :kbd:`num_lock and caps_lock`. Here :kbd:`super` is either and meta` as well as :kbd:`num_lock and caps_lock`. Here :kbd:`super` is either
the *Windows/Linux* key or the *Cmd* key on mac keyboards. :kbd:`hyper` and the *Windows/Linux* key or the *Cmd* key on mac keyboards. :kbd:`hyper` and
:kbd:`meta` are typically present only on X11 based systems with special XKB :kbd:`meta` are typically present only on X11/Wayland based systems with
rules. Modifiers are encoded as a bit field with:: special XKB rules. Modifiers are encoded as a bit field with::
shift 0b1 (1) shift 0b1 (1)
alt 0b10 (2) alt 0b10 (2)

156
glfw/xkb_glfw.c vendored
View File

@ -383,22 +383,162 @@ glfw_xkb_update_masks(_GLFWXKBData *xkb) {
#define xkb_glfw_load_keymap(keymap, map_str) keymap = xkb_keymap_new_from_string(xkb->context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0); #define xkb_glfw_load_keymap(keymap, map_str) keymap = xkb_keymap_new_from_string(xkb->context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
#define xkb_glfw_load_state(keymap, state) state = xkb_state_new(keymap); #define xkb_glfw_load_state(keymap, state) state = xkb_state_new(keymap);
typedef struct {
struct xkb_state *state;
int failed;
xkb_mod_mask_t used_mods;
xkb_mod_mask_t shift, control, capsLock, numLock, alt, super, meta, hyper;
/* Combination modifiers to try */
int try_shift;
xkb_keycode_t shift_keycode;
} modifier_mapping_algorithm_t;
/* Algorithm for mapping virtual modifiers to real modifiers:
* 1. create new state
* 2. for each key in keymap
* a. send key down to state
* b. if it affected exactly one bit in modifier map
* i) get keysym
* ii) if keysym matches one of the known modifiers, save it for that modifier
* iii) if modifier is latched, send key up and key down to toggle again
* c. send key up to reset the state
* 3. if shift key found in step 2, run step 2 with all shift+key for each key
* 4. if shift, control, alt and super are not all found, declare failure
* 5. if failure, use static mapping from xkbcommon-names.h
*
* Step 3 is needed because many popular keymaps map meta to alt+shift.
*
* We could do better by constructing a system of linear equations, but it should not be
* needed in any sane system. We could also use this algorithm with X11, but X11
* provides XkbVirtualModsToReal which is guaranteed to be accurate, while this
* algorithm is only a heuristic.
*
* We don't touch level3 or level5 modifiers.
*/
static void modifier_mapping_algorithm( struct xkb_keymap *keymap UNUSED, xkb_keycode_t key, void *data ) {
modifier_mapping_algorithm_t *algorithm = ( modifier_mapping_algorithm_t * )data;
if ( algorithm->failed )
return;
if ( algorithm->try_shift ) {
if ( key == algorithm->shift_keycode ) return;
xkb_state_update_key( algorithm->state, algorithm->shift_keycode, XKB_KEY_DOWN );
}
enum xkb_state_component changed_type = xkb_state_update_key( algorithm->state, key, XKB_KEY_DOWN );
if ( changed_type & ( XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED ) ) {
xkb_mod_mask_t mods = xkb_state_serialize_mods( algorithm->state,
algorithm->try_shift ? XKB_STATE_MODS_EFFECTIVE : ( XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED ) );
const xkb_keysym_t *keysyms;
int num_keysyms = xkb_state_key_get_syms( algorithm->state, key, &keysyms );
/* We can handle exactly one keysym with exactly one bit set in the implementation
* below; with a lot more gymnastics, we could set up an 8x8 linear system and solve
* for each modifier in case there are some modifiers that are only present in
* combination with others, but it is not worth the effort. */
if ( num_keysyms == 1 && mods && ( mods & ( mods-1 ) ) == 0 ) {
#define S2( k, a ) \
do { \
if ( keysyms[0] == XKB_KEY_##k##_L || keysyms[0] == XKB_KEY_##k##_R ) { \
if ( !algorithm->a ) \
algorithm->a = mods; \
else if ( algorithm->a != mods ) \
algorithm->failed = 1; \
} \
} while ( 0 )
#define S1( k, a ) if ( ( keysyms[0] == XKB_KEY_##k ) && !algorithm->a ) algorithm->a = mods
S2( Shift, shift );
S2( Control, control );
S1( Caps_Lock, capsLock );
S1( Shift_Lock, numLock );
S2( Alt, alt );
S2( Super, super );
S2( Meta, meta );
S2( Hyper, hyper );
#undef S1
#undef S2
}
if ( !algorithm->shift_keycode && ( keysyms[0] == XKB_KEY_Shift_L || keysyms[0] == XKB_KEY_Shift_R ) )
algorithm->shift_keycode = key;
/* If this is a lock, then up and down to remove lock state*/
if ( changed_type & XKB_STATE_MODS_LOCKED ) { /* What should we do for LATCHED here? */
xkb_state_update_key( algorithm->state, key, XKB_KEY_UP );
xkb_state_update_key( algorithm->state, key, XKB_KEY_DOWN );
}
}
xkb_state_update_key( algorithm->state, key, XKB_KEY_UP );
if ( algorithm->try_shift ) {
xkb_state_update_key( algorithm->state, algorithm->shift_keycode, XKB_KEY_UP );
}
}
static int local_modifier_mapping(_GLFWXKBData *xkb) {
modifier_mapping_algorithm_t algorithm;
algorithm.failed = 0;
algorithm.used_mods = 0;
algorithm.shift = algorithm.control = algorithm.capsLock = algorithm.numLock = algorithm.alt = algorithm.super = algorithm.meta = algorithm.hyper = 0;
algorithm.try_shift = 0;
algorithm.shift_keycode = 0;
algorithm.state = xkb_state_new( xkb->keymap );
if ( algorithm.state != NULL )
{
xkb_keymap_key_for_each( xkb->keymap, &modifier_mapping_algorithm, &algorithm );
if ( !algorithm.shift_keycode )
algorithm.failed = 1;
if ( !( algorithm.shift && algorithm.control && algorithm.alt && algorithm.super && algorithm.meta && algorithm.hyper )
&& !algorithm.failed ) {
algorithm.try_shift = 1;
xkb_keymap_key_for_each( xkb->keymap, &modifier_mapping_algorithm, &algorithm );
}
xkb_state_unref( algorithm.state );
if ( !algorithm.failed && !( algorithm.shift && algorithm.control && algorithm.alt && algorithm.super ) )
algorithm.failed = 1; /* must have found at least those 4 modifiers */
}
if ( !algorithm.failed ) {
#define S( a ) xkb->a##Idx = XKB_MOD_INVALID; xkb->a##Mask = 0
S(control); S(shift); S(capsLock); S(alt); S(super); S(hyper); S(meta); S(numLock);
#undef S
unsigned indx, shifted, used_bits = 0;
for (indx = 0, shifted = 1; indx < 32; ++indx, shifted <<= 1) {
#define S( a ) if ( (xkb->a##Idx == XKB_MOD_INVALID) && !( used_bits & shifted ) && algorithm.a == shifted ) xkb->a##Idx = indx, xkb->a##Mask = shifted, used_bits |= shifted
S(control); S(shift); S(capsLock); S(alt); S(super); S(hyper); S(meta); S(numLock);
#undef S
}
}
if ( algorithm.failed )
debug( "Wayland modifier autodetection algorithm failed; using defaults\n" );
return !algorithm.failed;
}
static void static void
glfw_xkb_update_masks(_GLFWXKBData *xkb) { glfw_xkb_update_masks(_GLFWXKBData *xkb) {
// Should find better solution under Wayland // Should find better solution under Wayland
// See https://github.com/kovidgoyal/kitty/pull/3430 for discussion // See https://github.com/kovidgoyal/kitty/pull/3943 for discussion
if ( getenv( "KITTY_WAYLAND_DETECT_MODIFIERS" ) == NULL || !local_modifier_mapping( xkb ) ) {
#define S( a ) xkb->a##Idx = XKB_MOD_INVALID; xkb->a##Mask = 0 #define S( a ) xkb->a##Idx = XKB_MOD_INVALID; xkb->a##Mask = 0
S(hyper); S(meta); S(hyper); S(meta);
#undef S #undef S
#define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx; #define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx;
S(control, XKB_MOD_NAME_CTRL); S(control, XKB_MOD_NAME_CTRL);
S(shift, XKB_MOD_NAME_SHIFT); S(shift, XKB_MOD_NAME_SHIFT);
S(capsLock, XKB_MOD_NAME_CAPS); S(capsLock, XKB_MOD_NAME_CAPS);
S(numLock, XKB_MOD_NAME_NUM); S(numLock, XKB_MOD_NAME_NUM);
S(alt, XKB_MOD_NAME_ALT); S(alt, XKB_MOD_NAME_ALT);
S(super, XKB_MOD_NAME_LOGO); S(super, XKB_MOD_NAME_LOGO);
#undef S #undef S
}
debug("Modifier indices alt: 0x%x super: 0x%x hyper: 0x%x meta: 0x%x numlock: 0x%x shift: 0x%x capslock: 0x%x control: 0x%x\n",
xkb->altIdx, xkb->superIdx, xkb->hyperIdx, xkb->metaIdx, xkb->numLockIdx, xkb->shiftIdx, xkb->capsLockIdx, xkb->controlIdx);
} }
#endif #endif