Fix #117896: Key-modifiers on window activation fails under Wayland
Register the last held key when activating a window (not only modifiers keys) under Wayland. This resolves a bug where holding the D-key and clicking on a window wouldn't activate grease pencil drawing.
This commit is contained in:
parent
857341a7a7
commit
479b46ca93
@ -135,6 +135,12 @@ static int gwl_registry_handler_interface_slot_max();
|
|||||||
static int gwl_registry_handler_interface_slot_from_string(const char *interface);
|
static int gwl_registry_handler_interface_slot_from_string(const char *interface);
|
||||||
static const GWL_RegistryHandler *gwl_registry_handler_from_interface_slot(int interface_slot);
|
static const GWL_RegistryHandler *gwl_registry_handler_from_interface_slot(int interface_slot);
|
||||||
|
|
||||||
|
static bool xkb_compose_state_feed_and_get_utf8(
|
||||||
|
xkb_compose_state *compose_state,
|
||||||
|
xkb_state *state,
|
||||||
|
const xkb_keycode_t key,
|
||||||
|
char r_utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)]);
|
||||||
|
|
||||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||||
static void gwl_display_event_thread_destroy(GWL_Display *display);
|
static void gwl_display_event_thread_destroy(GWL_Display *display);
|
||||||
|
|
||||||
@ -1103,6 +1109,37 @@ static void gwl_seat_key_layout_active_state_update_mask(GWL_Seat *seat)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Callback that runs from GHOST's timer. */
|
||||||
|
static void gwl_seat_key_repeat_timer_fn(GHOST_ITimerTask *task, uint64_t time_ms)
|
||||||
|
{
|
||||||
|
GWL_KeyRepeatPlayload *payload = static_cast<GWL_KeyRepeatPlayload *>(task->getUserData());
|
||||||
|
|
||||||
|
GWL_Seat *seat = payload->seat;
|
||||||
|
wl_surface *wl_surface_focus = seat->keyboard.wl.surface_window;
|
||||||
|
if (UNLIKELY(wl_surface_focus == nullptr)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus);
|
||||||
|
GHOST_SystemWayland *system = seat->system;
|
||||||
|
const uint64_t event_ms = payload->time_ms_init + time_ms;
|
||||||
|
/* Calculate this value every time in case modifier keys are pressed. */
|
||||||
|
|
||||||
|
char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
|
||||||
|
if (seat->xkb.compose_state &&
|
||||||
|
xkb_compose_state_feed_and_get_utf8(
|
||||||
|
seat->xkb.compose_state, seat->xkb.state, payload->key_code, utf8_buf))
|
||||||
|
{
|
||||||
|
/* `utf8_buf` has been filled by a compose action. */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
xkb_state_key_get_utf8(seat->xkb.state, payload->key_code, utf8_buf, sizeof(utf8_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
system->pushEvent_maybe_pending(new GHOST_EventKey(
|
||||||
|
event_ms, GHOST_kEventKeyDown, win, payload->key_data.gkey, true, utf8_buf));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \note Caller must lock `timer_mutex`.
|
* \note Caller must lock `timer_mutex`.
|
||||||
*/
|
*/
|
||||||
@ -2761,13 +2798,11 @@ static void keyboard_depressed_state_key_event(GWL_Seat *seat,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_depressed_state_push_events_from_change(
|
static void keyboard_depressed_state_push_events_from_change(
|
||||||
GWL_Seat *seat, const GWL_KeyboardDepressedState &key_depressed_prev)
|
GWL_Seat *seat,
|
||||||
|
GHOST_IWindow *win,
|
||||||
|
const uint64_t event_ms,
|
||||||
|
const GWL_KeyboardDepressedState &key_depressed_prev)
|
||||||
{
|
{
|
||||||
GHOST_IWindow *win = ghost_wl_surface_user_data(seat->keyboard.wl.surface_window);
|
|
||||||
const GHOST_SystemWayland *system = seat->system;
|
|
||||||
/* Caller has no time-stamp, set from system. */
|
|
||||||
const uint64_t event_ms = system->getMilliSeconds();
|
|
||||||
|
|
||||||
/* Separate key up and down into separate passes so key down events always come after key up.
|
/* Separate key up and down into separate passes so key down events always come after key up.
|
||||||
* Do this so users of GHOST can use the last pressed or released modifier to check
|
* Do this so users of GHOST can use the last pressed or released modifier to check
|
||||||
* if the modifier is held instead of counting modifiers pressed as is done here,
|
* if the modifier is held instead of counting modifiers pressed as is done here,
|
||||||
@ -4688,6 +4723,8 @@ static void keyboard_handle_enter(void *data,
|
|||||||
CLOG_INFO(LOG, 2, "enter");
|
CLOG_INFO(LOG, 2, "enter");
|
||||||
|
|
||||||
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
|
GWL_Seat *seat = static_cast<GWL_Seat *>(data);
|
||||||
|
GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface);
|
||||||
|
|
||||||
seat->keyboard.serial = serial;
|
seat->keyboard.serial = serial;
|
||||||
seat->keyboard.wl.surface_window = wl_surface;
|
seat->keyboard.wl.surface_window = wl_surface;
|
||||||
|
|
||||||
@ -4699,6 +4736,12 @@ static void keyboard_handle_enter(void *data,
|
|||||||
GWL_KeyboardDepressedState key_depressed_prev = seat->key_depressed;
|
GWL_KeyboardDepressedState key_depressed_prev = seat->key_depressed;
|
||||||
keyboard_depressed_state_reset(seat);
|
keyboard_depressed_state_reset(seat);
|
||||||
|
|
||||||
|
/* Keep track of the last held repeating key, start the repeat timer if one exists. */
|
||||||
|
struct {
|
||||||
|
uint32_t key = std::numeric_limits<uint32_t>::max();
|
||||||
|
xkb_keysym_t sym = 0;
|
||||||
|
} repeat;
|
||||||
|
|
||||||
uint32_t *key;
|
uint32_t *key;
|
||||||
WL_ARRAY_FOR_EACH (key, keys) {
|
WL_ARRAY_FOR_EACH (key, keys) {
|
||||||
const xkb_keycode_t key_code = *key + EVDEV_OFFSET;
|
const xkb_keycode_t key_code = *key + EVDEV_OFFSET;
|
||||||
@ -4708,9 +4751,41 @@ static void keyboard_handle_enter(void *data,
|
|||||||
if (gkey != GHOST_kKeyUnknown) {
|
if (gkey != GHOST_kKeyUnknown) {
|
||||||
keyboard_depressed_state_key_event(seat, gkey, GHOST_kEventKeyDown);
|
keyboard_depressed_state_key_event(seat, gkey, GHOST_kEventKeyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (xkb_keymap_key_repeats(xkb_state_get_keymap(seat->xkb.state), key_code)) {
|
||||||
|
repeat.key = *key;
|
||||||
|
repeat.sym = sym;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keyboard_depressed_state_push_events_from_change(seat, key_depressed_prev);
|
/* Caller has no time-stamp, set from system. */
|
||||||
|
const uint64_t event_ms = seat->system->getMilliSeconds();
|
||||||
|
keyboard_depressed_state_push_events_from_change(seat, win, event_ms, key_depressed_prev);
|
||||||
|
|
||||||
|
if ((repeat.key != std::numeric_limits<uint32_t>::max()) && (seat->key_repeat.rate > 0)) {
|
||||||
|
/* Since the key has been held, immediately send a press event.
|
||||||
|
* This also ensures the key will be registered as pressed, see #117896. */
|
||||||
|
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||||
|
std::lock_guard lock_timer_guard{*seat->system->timer_mutex};
|
||||||
|
#endif
|
||||||
|
/* Should have been cleared on leave, set here just in case. */
|
||||||
|
if (UNLIKELY(seat->key_repeat.timer)) {
|
||||||
|
keyboard_handle_key_repeat_cancel(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
const xkb_keycode_t key_code = repeat.key + EVDEV_OFFSET;
|
||||||
|
const GHOST_TKey gkey = xkb_map_gkey_or_scan_code(repeat.sym, repeat.key);
|
||||||
|
|
||||||
|
GWL_KeyRepeatPlayload *key_repeat_payload = new GWL_KeyRepeatPlayload();
|
||||||
|
key_repeat_payload->seat = seat;
|
||||||
|
key_repeat_payload->key_code = key_code;
|
||||||
|
key_repeat_payload->key_data.gkey = gkey;
|
||||||
|
|
||||||
|
gwl_seat_key_repeat_timer_add(seat, gwl_seat_key_repeat_timer_fn, key_repeat_payload, false);
|
||||||
|
/* Ensure there is a press event on enter so this is known to be held before any mouse
|
||||||
|
* button events which may use a key-binding that depends on this key being held. */
|
||||||
|
gwl_seat_key_repeat_timer_fn(seat->key_repeat.timer, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -5001,33 +5076,7 @@ static void keyboard_handle_key(void *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (key_repeat_payload) {
|
if (key_repeat_payload) {
|
||||||
auto key_repeat_fn = [](GHOST_ITimerTask *task, uint64_t time_ms) {
|
gwl_seat_key_repeat_timer_add(seat, gwl_seat_key_repeat_timer_fn, key_repeat_payload, true);
|
||||||
GWL_KeyRepeatPlayload *payload = static_cast<GWL_KeyRepeatPlayload *>(task->getUserData());
|
|
||||||
|
|
||||||
GWL_Seat *seat = payload->seat;
|
|
||||||
if (wl_surface *wl_surface_focus = seat->keyboard.wl.surface_window) {
|
|
||||||
GHOST_IWindow *win = ghost_wl_surface_user_data(wl_surface_focus);
|
|
||||||
GHOST_SystemWayland *system = seat->system;
|
|
||||||
const uint64_t event_ms = payload->time_ms_init + time_ms;
|
|
||||||
/* Calculate this value every time in case modifier keys are pressed. */
|
|
||||||
|
|
||||||
char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
|
|
||||||
if (seat->xkb.compose_state &&
|
|
||||||
xkb_compose_state_feed_and_get_utf8(
|
|
||||||
seat->xkb.compose_state, seat->xkb.state, payload->key_code, utf8_buf))
|
|
||||||
{
|
|
||||||
/* `utf8_buf` has been filled by a compose action. */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
xkb_state_key_get_utf8(seat->xkb.state, payload->key_code, utf8_buf, sizeof(utf8_buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
system->pushEvent_maybe_pending(new GHOST_EventKey(
|
|
||||||
event_ms, GHOST_kEventKeyDown, win, payload->key_data.gkey, true, utf8_buf));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
gwl_seat_key_repeat_timer_add(seat, key_repeat_fn, key_repeat_payload, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user