Fix key release tracking for input methods

* compositor.h: Update prototypes.
* seat.c (XLSeatDispatchCoreKeyEvent): Remove argument `keysym'.
Stop handling keysyms here.
(XLKeysymToKeycode): New function.
* text_input.c (struct _KeysymMap): Rename to KeycodeMap.
(struct _TextInput, ClearKeysymMap, InsertKeysym, RemoveKeysym)
(GetKeysym, GetKeycode, InputDoLeave, HandleResourceDestroy)
(ScanForwardWord, ScanBackwardWord, CalculateKeycodeForEvent)
(XLTextInputDispatchCoreEvent, InitInputStyles): Calculate
keycodes instead of passing the keysym to DispatchCoreKeyEvent
here, and record the keycodes used instead.
This commit is contained in:
hujianwei 2022-11-11 02:12:14 +00:00
parent 18d18aabb5
commit c90af69ecd
3 changed files with 123 additions and 78 deletions

View file

@ -1595,8 +1595,7 @@ extern void XLSeatSetTextInputFuncs (TextInputFuncs *);
extern int XLSeatGetKeyboardDevice (Seat *);
extern int XLSeatGetPointerDevice (Seat *);
extern Seat *XLSeatGetInputMethodSeat (void);
extern void XLSeatDispatchCoreKeyEvent (Seat *, Surface *, XEvent *,
KeySym);
extern void XLSeatDispatchCoreKeyEvent (Seat *, Surface *, XEvent *);
extern Seat *XLPointerGetSeat (Pointer *);
extern void XLSeatGetMouseData (Seat *, Surface **, double *, double *,
double *, double *);
@ -1610,6 +1609,7 @@ extern SwipeGesture *XLSeatGetSwipeGesture (Seat *, struct wl_resource *);
extern PinchGesture *XLSeatGetPinchGesture (Seat *, struct wl_resource *);
extern void XLSeatDestroySwipeGesture (SwipeGesture *);
extern void XLSeatDestroyPinchGesture (PinchGesture *);
extern KeyCode XLKeysymToKeycode (KeySym, XEvent *);
extern Cursor InitDefaultCursor (void);

60
seat.c
View file

@ -6154,14 +6154,10 @@ XLSeatGetInputMethodSeat (void)
}
void
XLSeatDispatchCoreKeyEvent (Seat *seat, Surface *surface, XEvent *event,
KeySym keysym)
XLSeatDispatchCoreKeyEvent (Seat *seat, Surface *surface, XEvent *event)
{
unsigned int effective;
unsigned int state, group;
unsigned int mods_return;
KeyCode keycode;
KeySym sym_return;
/* Dispatch a core event generated by an input method to SEAT. If
SURFACE is no longer the focus surface, refrain from doing
@ -6178,33 +6174,6 @@ XLSeatDispatchCoreKeyEvent (Seat *seat, Surface *surface, XEvent *event,
/* Get the effective state of the seat. */
effective = seat->base | seat->latched | seat->locked;
/* Determine what keycode to use. If a keysym was provided, try to
find a corresponding keycode. */
if (keysym)
{
/* If looking up the event keycode also results in the keysym,
then just use the keycode specified in the event. This is
because French keyboard layouts have multiple keycodes that
decode to the same keysym, which causes problems later on
when Wayland clients keep repeating the "a" key, as a keysym
was looked up for the key press but not for the corresponding
key release. */
if (XkbLookupKeySym (compositor.display, event->xkey.keycode,
event->xkey.state, &mods_return, &sym_return)
&& keysym == sym_return)
keycode = event->xkey.keycode;
else
keycode = XKeysymToKeycode (compositor.display, keysym);
/* But if no corresponding keycode could be found, use the
keycode provided in the event. */
if (!keycode)
keycode = event->xkey.keycode;
}
else
keycode = event->xkey.keycode;
if (group != seat->effective_group || state != effective)
/* The modifiers in the provided core event are different from
what the focus surface was previously sent. Send a new
@ -6215,11 +6184,11 @@ XLSeatDispatchCoreKeyEvent (Seat *seat, Surface *surface, XEvent *event,
/* Then send the event. */
if (event->xkey.type == KeyPress)
SendKeyboardKey (seat, seat->focus_surface, event->xkey.time,
WaylandKeycode (keycode),
WaylandKeycode (event->xkey.keycode),
WL_KEYBOARD_KEY_STATE_PRESSED);
else
SendKeyboardKey (seat, seat->focus_surface, event->xkey.time,
WaylandKeycode (keycode),
WaylandKeycode (event->xkey.keycode),
WL_KEYBOARD_KEY_STATE_RELEASED);
/* Restore the modifiers. */
@ -6432,6 +6401,29 @@ XLSeatCancelExternalGrab (Seat *seat)
seat->external_grab_time);
}
KeyCode
XLKeysymToKeycode (KeySym keysym, XEvent *event)
{
unsigned int i, mods_return;
KeySym keysym_return;
if (!xkb_desc)
return 0;
/* Look up the keycode correspnding to the given keysym. Return 0
if there is none. */
for (i = xkb_desc->min_key_code; i <= xkb_desc->max_key_code; ++i)
{
if (XkbTranslateKeyCode (xkb_desc, i, event->xkey.state,
&mods_return, &keysym_return)
&& keysym_return == keysym)
return i;
}
return 0;
}
/* This is a particularly ugly hack, but there is no other way to
expose all the internals needed by test_seat.c. */

View file

@ -32,6 +32,7 @@ along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
#include "text-input-unstable-v3.h"
#include <X11/extensions/XInput2.h>
#include <X11/XKBlib.h>
/* X Input Method (XIM) support.
@ -94,7 +95,7 @@ typedef struct _TextInput TextInput;
typedef struct _TextInputState TextInputState;
typedef struct _TextPosition TextPosition;
typedef struct _PreeditBuffer PreeditBuffer;
typedef struct _KeysymMap KeysymMap;
typedef struct _KeycodeMap KeycodeMap;
typedef enum _XimStyleKind XimStyleKind;
@ -114,15 +115,17 @@ enum
PendingSurroundingText = (1 << 2),
};
struct _KeysymMap
struct _KeycodeMap
{
/* Packed map between keycodes and keysyms. */
/* Packed map between keycodes specified in KeyPress events and
keycodes that were actually sent to applications. */
KeyCode *keycodes;
/* The keysyms. */
KeySym *keysyms;
/* The keycodes that were computed from keysyms and actually sent to
applications. */
KeyCode *keysyms;
/* The number of keycodes and keysyms in this map. */
/* The number of keycodes and used keycodes in this map. */
int key_count;
};
@ -204,7 +207,7 @@ struct _TextInput
/* Map between keys currently held down and keysyms they looked up
to. */
KeysymMap keysym_map;
KeycodeMap keysym_map;
};
/* Structure describing a list of TextInput resources associated with
@ -261,7 +264,7 @@ static XimStyleKind xim_style_order[5];
static void
ClearKeysymMap (KeysymMap *map)
ClearKeycodeMap (KeycodeMap *map)
{
XLFree (map->keycodes);
XLFree (map->keysyms);
@ -271,18 +274,18 @@ ClearKeysymMap (KeysymMap *map)
}
static void
InsertKeysym (KeysymMap *map, KeyCode keycode, KeySym keysym)
InsertKeycode (KeycodeMap *map, KeyCode keycode, KeyCode keycode_used)
{
int i;
/* Insert keysym into map, under keycode. See if map already
/* Insert keycode_used into map, under keycode. See if map already
contains the given keycode. */
for (i = 0; i < map->key_count; ++i)
{
if (map->keycodes[i] == keycode)
{
map->keysyms[i] = keysym;
map->keysyms[i] = keycode_used;
return;
}
}
@ -296,11 +299,11 @@ InsertKeysym (KeysymMap *map, KeyCode keycode, KeySym keysym)
= XLRealloc (map->keysyms,
map->key_count * sizeof *map->keysyms);
map->keycodes[map->key_count - 1] = keycode;
map->keysyms[map->key_count - 1] = keysym;
map->keysyms[map->key_count - 1] = keycode_used;
}
static void
RemoveKeysym (KeysymMap *map, KeyCode keycode)
RemoveKeysym (KeycodeMap *map, KeyCode keycode)
{
int i;
@ -329,8 +332,8 @@ RemoveKeysym (KeysymMap *map, KeyCode keycode)
}
}
static KeySym
GetKeysym (KeysymMap *map, KeyCode keycode)
static KeyCode
GetKeycode (KeycodeMap *map, KeyCode keycode)
{
int i;
@ -1033,10 +1036,10 @@ InputDoLeave (TextInput *input, Surface *old_surface)
if (input->current_state.surrounding_text)
XLFree (input->current_state.surrounding_text);
/* Clear the keysym-keycode table. Correlating key release with key
press events is no longer important, as a leave event has been
sent to the seat. */
ClearKeysymMap (&input->keysym_map);
/* Clear the keycode-keycode table. Correlating key release with
key press events is no longer important, as a leave event has
been sent to the seat. */
ClearKeycodeMap (&input->keysym_map);
memset (&input->current_state, 0, sizeof input->current_state);
}
@ -1095,8 +1098,8 @@ HandleResourceDestroy (struct wl_resource *resource)
if (input->buffer)
FreePreeditBuffer (input->buffer);
/* Destroy the map of pressed keycodes to keysyms. */
ClearKeysymMap (&input->keysym_map);
/* Destroy the map of pressed keycodes to keycodes. */
ClearKeycodeMap (&input->keysym_map);
/* Free the text input itself. */
XLFree (input);
@ -3357,6 +3360,35 @@ static TextInputFuncs input_funcs =
.filter_input = FilterInputCallback,
};
static KeyCode
CalculateKeycodeForEvent (XEvent *event, KeySym keysym)
{
KeySym sym_return;
unsigned int mods_return;
/* Calculate the keycode to actually send clients along with an
event, given the keysym specified by the input method. Return 0
if no special treatment is required. */
if (!keysym)
return 0;
/* If looking up the event keycode also results in the keysym,
then just use the keycode specified in the event. This is
because French keyboard layouts have multiple keycodes that
decode to the same keysym, which causes problems later on
when Wayland clients keep repeating the "a" key, as a keysym
was looked up for the key press but not for the corresponding
key release. */
if (XkbLookupKeySym (compositor.display, event->xkey.keycode,
event->xkey.state, &mods_return, &sym_return)
&& keysym == sym_return)
return 0;
/* Otherwise, convert the keysym to a keycode and use that. */
return XLKeysymToKeycode (keysym, event);
}
void
XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
{
@ -3364,6 +3396,7 @@ XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
TextInputClientInfo *info;
TextInput *input;
KeySym keysym;
KeyCode effective_keycode;
DebugPrint ("dispatching core event to surface %p:\n"
"\ttype: %d\n"
@ -3429,23 +3462,31 @@ XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
DebugPrint ("lookup failed; dispatching event to seat; "
"keysym is: %lu", keysym);
/* First, clear effective_keycode. */
effective_keycode = 0;
/* If the event is a KeyPress event and a keysym was
looked up, record the keysym in the text input's
keycode-keysym table, so the correct keycode can be
looked up upon the next KeyRelease event. X input
methods tend not to filter the KeyRelease events, so
the KeyRelease event for an event that changed the
keysym will be sent with the wrong keycode, which
does not matter much with X programs, but leads to
Wayland programs constantly autorepeating the keycode
for which a KeyPress event was sent. */
looked up, calculate a keycode for the keysym, and
record in the text input's keycode-keycode table, so
the correct keycode can be looked up upon the next
KeyRelease event. X input methods tend not to filter
the KeyRelease events, so the KeyRelease event for an
event that changed the keysym will be sent with the
wrong keycode, which does not matter much with X
programs, but leads to Wayland programs constantly
autorepeating the keycode for which a KeyPress event
was sent. */
if (event->xkey.type == KeyPress && keysym)
{
DebugPrint ("inserting keysym %lu into map under %u",
keysym, event->xkey.keycode);
InsertKeysym (&input->keysym_map, event->xkey.keycode,
keysym);
/* Compute the keycode. */
effective_keycode
= CalculateKeycodeForEvent (event, keysym);
DebugPrint ("inserting keycode %lu into map under %u",
effective_keycode, event->xkey.keycode);
InsertKeycode (&input->keysym_map, event->xkey.keycode,
effective_keycode);
}
else if (event->xkey.type == KeyRelease)
{
@ -3454,23 +3495,35 @@ XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
event for the keycode. Otherwise, the input
method probably knows better than us, so use the
keysym provided by the input method. */
if (!keysym)
{
keysym = GetKeysym (&input->keysym_map,
effective_keycode
= GetKeycode (&input->keysym_map,
event->xkey.keycode);
DebugPrint ("obtained keysym %lu for keycode %u"
DebugPrint ("obtained keycode %lu for keycode %u"
" while processing KeyRelease event",
keysym, event->xkey.keycode);
}
else
effective_keycode
= CalculateKeycodeForEvent (event, keysym);
DebugPrint ("removing keycode %u from map",
event->xkey.keycode);
/* Remove the keycode from the keysym map. */
/* Remove the keycode from the keycode-keysym
map. */
RemoveKeysym (&input->keysym_map, event->xkey.keycode);
}
XLSeatDispatchCoreKeyEvent (im_seat, surface, event, keysym);
/* Finally, if an effective keycode was calculated,
replace the keycode in the event with it. */
if (effective_keycode)
event->xkey.keycode = effective_keycode;
XLSeatDispatchCoreKeyEvent (im_seat, surface, event);
}
}
}