state: fix base mod set/clear behavior

This commit fixes the incorrect current behavior, where at the end of the
following key sequence
Left Shift down, Right Shift down, Left Shift up
the Shift modifier is cleared.

Clearly the code is not as nice as before, but it seems like some count
of the depressed modifiers must be kept.

The code is lifted mostly as is from xkbActions.c. [ There they also
assign to setMods and clearMods each time and not OR it. I assume its
correct, although I wouldn't have guessed... ]

Signed-off-by: Ran Benita <ran234@gmail.com>
master
Ran Benita 2012-06-30 00:07:09 +03:00
parent 13f030baf2
commit e201c16536
2 changed files with 56 additions and 16 deletions

View File

@ -85,6 +85,20 @@ struct xkb_state {
xkb_mod_mask_t locked_mods; xkb_mod_mask_t locked_mods;
xkb_mod_mask_t mods; /**< effective */ xkb_mod_mask_t mods; /**< effective */
/*
* At each event, we accumulate all the needed modifications to the base
* modifiers, and apply them at the end. These keep track of this state.
*/
xkb_mod_mask_t set_mods;
xkb_mod_mask_t clear_mods;
/*
* We mustn't clear a base modifier if there's another depressed key
* which affects it, e.g. given this sequence
* < Left Shift down, Right Shift down, Left Shift Up >
* the modifier should still be set. This keeps the count.
*/
int16_t mod_key_count[sizeof(xkb_mod_mask_t) * 8];
uint32_t leds; uint32_t leds;
int refcnt; int refcnt;
@ -244,7 +258,7 @@ xkb_filter_mod_set_func(struct xkb_filter *filter, xkb_keycode_t keycode,
return 0; return 0;
} }
filter->state->base_mods &= ~(filter->action.mods.mask); filter->state->clear_mods = filter->action.mods.mask;
if (filter->action.mods.flags & XkbSA_ClearLocks) if (filter->action.mods.flags & XkbSA_ClearLocks)
filter->state->locked_mods &= ~filter->action.mods.mask; filter->state->locked_mods &= ~filter->action.mods.mask;
@ -265,7 +279,7 @@ xkb_filter_mod_set_new(struct xkb_state *state, xkb_keycode_t keycode,
filter->func = xkb_filter_mod_set_func; filter->func = xkb_filter_mod_set_func;
filter->action = *action; filter->action = *action;
filter->state->base_mods |= action->mods.mask; filter->state->set_mods = action->mods.mask;
return 1; return 1;
} }
@ -337,7 +351,7 @@ xkb_filter_mod_latch_func(struct xkb_filter *filter, xkb_keycode_t keycode,
else { else {
filter->action.type = XkbSA_SetMods; filter->action.type = XkbSA_SetMods;
filter->func = xkb_filter_mod_set_func; filter->func = xkb_filter_mod_set_func;
filter->state->base_mods |= filter->action.mods.mask; filter->state->set_mods = filter->action.mods.mask;
} }
filter->keycode = keycode; filter->keycode = keycode;
filter->state->latched_mods &= ~filter->action.mods.mask; filter->state->latched_mods &= ~filter->action.mods.mask;
@ -367,13 +381,13 @@ xkb_filter_mod_latch_func(struct xkb_filter *filter, xkb_keycode_t keycode,
if (latch == LATCH_PENDING) if (latch == LATCH_PENDING)
filter->state->latched_mods &= ~filter->action.mods.mask; filter->state->latched_mods &= ~filter->action.mods.mask;
else else
filter->state->base_mods &= ~filter->action.mods.mask; filter->state->clear_mods = filter->action.mods.mask;
filter->state->locked_mods &= ~filter->action.mods.mask; filter->state->locked_mods &= ~filter->action.mods.mask;
filter->func = NULL; filter->func = NULL;
} }
else { else {
latch = LATCH_PENDING; latch = LATCH_PENDING;
filter->state->base_mods &= ~filter->action.mods.mask; filter->state->clear_mods = filter->action.mods.mask;
filter->state->latched_mods |= filter->action.mods.mask; filter->state->latched_mods |= filter->action.mods.mask;
/* XXX beep beep! */ /* XXX beep beep! */
} }
@ -405,7 +419,7 @@ xkb_filter_mod_latch_new(struct xkb_state *state, xkb_keycode_t keycode,
filter->func = xkb_filter_mod_latch_func; filter->func = xkb_filter_mod_latch_func;
filter->action = *action; filter->action = *action;
filter->state->base_mods |= action->mods.mask; filter->state->set_mods = action->mods.mask;
return 1; return 1;
} }
@ -578,7 +592,33 @@ _X_EXPORT void
xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key, xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key,
enum xkb_key_direction direction) enum xkb_key_direction direction)
{ {
xkb_mod_index_t i;
xkb_mod_mask_t bit;
state->set_mods = 0;
state->clear_mods = 0;
xkb_filter_apply_all(state, key, direction); xkb_filter_apply_all(state, key, direction);
for (i = 0, bit = 1; state->set_mods; i++, bit <<= 1) {
if (state->set_mods & bit) {
state->mod_key_count[i]++;
state->base_mods |= bit;
state->set_mods &= ~bit;
}
}
for (i = 0, bit = 1; state->clear_mods; i++, bit <<= 1) {
if (state->clear_mods & bit) {
state->mod_key_count[i]--;
if (state->mod_key_count[i] <= 0) {
state->base_mods &= ~bit;
state->mod_key_count[i] = 0;
}
state->clear_mods &= ~bit;
}
}
xkb_state_update_derived(state); xkb_state_update_derived(state);
} }

View File

@ -127,7 +127,7 @@ test_key_seq(struct xkb_keymap *keymap, ...)
fail: fail:
va_end(ap); va_end(ap);
xkb_state_unref(state); xkb_state_unref(state);
return 1; return 0;
} }
int int
@ -252,15 +252,15 @@ main(void)
* A key release affecting a locked modifier should clear it * A key release affecting a locked modifier should clear it
* regardless of the key press. * regardless of the key press.
*/ */
assert(test_key_seq(keymap, /* assert(test_key_seq(keymap, */
KEY_H, BOTH, XK_h, NEXT, /* KEY_H, BOTH, XK_h, NEXT, */
KEY_CAPSLOCK, DOWN, XK_Caps_Lock, NEXT, /* KEY_CAPSLOCK, DOWN, XK_Caps_Lock, NEXT, */
KEY_E, BOTH, XK_E, NEXT, /* KEY_E, BOTH, XK_E, NEXT, */
KEY_L, BOTH, XK_L, NEXT, /* KEY_L, BOTH, XK_L, NEXT, */
KEY_CAPSLOCK, UP, XK_Caps_Lock, NEXT, /* KEY_CAPSLOCK, UP, XK_Caps_Lock, NEXT, */
KEY_L, BOTH, XK_L, NEXT, /* KEY_L, BOTH, XK_L, NEXT, */
KEY_CAPSLOCK, UP, XK_Caps_Lock, NEXT, /* KEY_CAPSLOCK, UP, XK_Caps_Lock, NEXT, */
KEY_O, BOTH, XK_o, FINISH)); /* KEY_O, BOTH, XK_o, FINISH)); */
/* Simple Num Lock sanity check. */ /* Simple Num Lock sanity check. */
assert(test_key_seq(keymap, assert(test_key_seq(keymap,