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 XLSeatGetKeyboardDevice (Seat *);
extern int XLSeatGetPointerDevice (Seat *); extern int XLSeatGetPointerDevice (Seat *);
extern Seat *XLSeatGetInputMethodSeat (void); extern Seat *XLSeatGetInputMethodSeat (void);
extern void XLSeatDispatchCoreKeyEvent (Seat *, Surface *, XEvent *, extern void XLSeatDispatchCoreKeyEvent (Seat *, Surface *, XEvent *);
KeySym);
extern Seat *XLPointerGetSeat (Pointer *); extern Seat *XLPointerGetSeat (Pointer *);
extern void XLSeatGetMouseData (Seat *, Surface **, double *, double *, extern void XLSeatGetMouseData (Seat *, Surface **, double *, double *,
double *, double *); double *, double *);
@ -1610,6 +1609,7 @@ extern SwipeGesture *XLSeatGetSwipeGesture (Seat *, struct wl_resource *);
extern PinchGesture *XLSeatGetPinchGesture (Seat *, struct wl_resource *); extern PinchGesture *XLSeatGetPinchGesture (Seat *, struct wl_resource *);
extern void XLSeatDestroySwipeGesture (SwipeGesture *); extern void XLSeatDestroySwipeGesture (SwipeGesture *);
extern void XLSeatDestroyPinchGesture (PinchGesture *); extern void XLSeatDestroyPinchGesture (PinchGesture *);
extern KeyCode XLKeysymToKeycode (KeySym, XEvent *);
extern Cursor InitDefaultCursor (void); extern Cursor InitDefaultCursor (void);

60
seat.c
View file

@ -6154,14 +6154,10 @@ XLSeatGetInputMethodSeat (void)
} }
void void
XLSeatDispatchCoreKeyEvent (Seat *seat, Surface *surface, XEvent *event, XLSeatDispatchCoreKeyEvent (Seat *seat, Surface *surface, XEvent *event)
KeySym keysym)
{ {
unsigned int effective; unsigned int effective;
unsigned int state, group; 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 /* Dispatch a core event generated by an input method to SEAT. If
SURFACE is no longer the focus surface, refrain from doing 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. */ /* Get the effective state of the seat. */
effective = seat->base | seat->latched | seat->locked; 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) if (group != seat->effective_group || state != effective)
/* The modifiers in the provided core event are different from /* The modifiers in the provided core event are different from
what the focus surface was previously sent. Send a new 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. */ /* Then send the event. */
if (event->xkey.type == KeyPress) if (event->xkey.type == KeyPress)
SendKeyboardKey (seat, seat->focus_surface, event->xkey.time, SendKeyboardKey (seat, seat->focus_surface, event->xkey.time,
WaylandKeycode (keycode), WaylandKeycode (event->xkey.keycode),
WL_KEYBOARD_KEY_STATE_PRESSED); WL_KEYBOARD_KEY_STATE_PRESSED);
else else
SendKeyboardKey (seat, seat->focus_surface, event->xkey.time, SendKeyboardKey (seat, seat->focus_surface, event->xkey.time,
WaylandKeycode (keycode), WaylandKeycode (event->xkey.keycode),
WL_KEYBOARD_KEY_STATE_RELEASED); WL_KEYBOARD_KEY_STATE_RELEASED);
/* Restore the modifiers. */ /* Restore the modifiers. */
@ -6432,6 +6401,29 @@ XLSeatCancelExternalGrab (Seat *seat)
seat->external_grab_time); 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 /* This is a particularly ugly hack, but there is no other way to
expose all the internals needed by test_seat.c. */ 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 "text-input-unstable-v3.h"
#include <X11/extensions/XInput2.h> #include <X11/extensions/XInput2.h>
#include <X11/XKBlib.h>
/* X Input Method (XIM) support. /* X Input Method (XIM) support.
@ -94,7 +95,7 @@ typedef struct _TextInput TextInput;
typedef struct _TextInputState TextInputState; typedef struct _TextInputState TextInputState;
typedef struct _TextPosition TextPosition; typedef struct _TextPosition TextPosition;
typedef struct _PreeditBuffer PreeditBuffer; typedef struct _PreeditBuffer PreeditBuffer;
typedef struct _KeysymMap KeysymMap; typedef struct _KeycodeMap KeycodeMap;
typedef enum _XimStyleKind XimStyleKind; typedef enum _XimStyleKind XimStyleKind;
@ -114,15 +115,17 @@ enum
PendingSurroundingText = (1 << 2), 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; KeyCode *keycodes;
/* The keysyms. */ /* The keycodes that were computed from keysyms and actually sent to
KeySym *keysyms; 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; int key_count;
}; };
@ -204,7 +207,7 @@ struct _TextInput
/* Map between keys currently held down and keysyms they looked up /* Map between keys currently held down and keysyms they looked up
to. */ to. */
KeysymMap keysym_map; KeycodeMap keysym_map;
}; };
/* Structure describing a list of TextInput resources associated with /* Structure describing a list of TextInput resources associated with
@ -261,7 +264,7 @@ static XimStyleKind xim_style_order[5];
static void static void
ClearKeysymMap (KeysymMap *map) ClearKeycodeMap (KeycodeMap *map)
{ {
XLFree (map->keycodes); XLFree (map->keycodes);
XLFree (map->keysyms); XLFree (map->keysyms);
@ -271,18 +274,18 @@ ClearKeysymMap (KeysymMap *map)
} }
static void static void
InsertKeysym (KeysymMap *map, KeyCode keycode, KeySym keysym) InsertKeycode (KeycodeMap *map, KeyCode keycode, KeyCode keycode_used)
{ {
int i; 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. */ contains the given keycode. */
for (i = 0; i < map->key_count; ++i) for (i = 0; i < map->key_count; ++i)
{ {
if (map->keycodes[i] == keycode) if (map->keycodes[i] == keycode)
{ {
map->keysyms[i] = keysym; map->keysyms[i] = keycode_used;
return; return;
} }
} }
@ -296,11 +299,11 @@ InsertKeysym (KeysymMap *map, KeyCode keycode, KeySym keysym)
= XLRealloc (map->keysyms, = XLRealloc (map->keysyms,
map->key_count * sizeof *map->keysyms); map->key_count * sizeof *map->keysyms);
map->keycodes[map->key_count - 1] = keycode; map->keycodes[map->key_count - 1] = keycode;
map->keysyms[map->key_count - 1] = keysym; map->keysyms[map->key_count - 1] = keycode_used;
} }
static void static void
RemoveKeysym (KeysymMap *map, KeyCode keycode) RemoveKeysym (KeycodeMap *map, KeyCode keycode)
{ {
int i; int i;
@ -329,8 +332,8 @@ RemoveKeysym (KeysymMap *map, KeyCode keycode)
} }
} }
static KeySym static KeyCode
GetKeysym (KeysymMap *map, KeyCode keycode) GetKeycode (KeycodeMap *map, KeyCode keycode)
{ {
int i; int i;
@ -1033,10 +1036,10 @@ InputDoLeave (TextInput *input, Surface *old_surface)
if (input->current_state.surrounding_text) if (input->current_state.surrounding_text)
XLFree (input->current_state.surrounding_text); XLFree (input->current_state.surrounding_text);
/* Clear the keysym-keycode table. Correlating key release with key /* Clear the keycode-keycode table. Correlating key release with
press events is no longer important, as a leave event has been key press events is no longer important, as a leave event has
sent to the seat. */ been sent to the seat. */
ClearKeysymMap (&input->keysym_map); ClearKeycodeMap (&input->keysym_map);
memset (&input->current_state, 0, sizeof input->current_state); memset (&input->current_state, 0, sizeof input->current_state);
} }
@ -1095,8 +1098,8 @@ HandleResourceDestroy (struct wl_resource *resource)
if (input->buffer) if (input->buffer)
FreePreeditBuffer (input->buffer); FreePreeditBuffer (input->buffer);
/* Destroy the map of pressed keycodes to keysyms. */ /* Destroy the map of pressed keycodes to keycodes. */
ClearKeysymMap (&input->keysym_map); ClearKeycodeMap (&input->keysym_map);
/* Free the text input itself. */ /* Free the text input itself. */
XLFree (input); XLFree (input);
@ -1854,7 +1857,7 @@ ScanForwardWord (const char *string, size_t string_size,
caret.charpos++; caret.charpos++;
caret.bytepos++; caret.bytepos++;
} }
while (start < string + string_size) while (start < string + string_size)
{ {
punct_found = False; punct_found = False;
@ -2021,7 +2024,7 @@ ScanBackwardWord (const char *string, size_t string_size,
return caret_before; return caret_before;
} }
} }
return caret; return caret;
} }
@ -3357,6 +3360,35 @@ static TextInputFuncs input_funcs =
.filter_input = FilterInputCallback, .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 void
XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event) XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
{ {
@ -3364,6 +3396,7 @@ XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
TextInputClientInfo *info; TextInputClientInfo *info;
TextInput *input; TextInput *input;
KeySym keysym; KeySym keysym;
KeyCode effective_keycode;
DebugPrint ("dispatching core event to surface %p:\n" DebugPrint ("dispatching core event to surface %p:\n"
"\ttype: %d\n" "\ttype: %d\n"
@ -3429,23 +3462,31 @@ XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
DebugPrint ("lookup failed; dispatching event to seat; " DebugPrint ("lookup failed; dispatching event to seat; "
"keysym is: %lu", keysym); "keysym is: %lu", keysym);
/* First, clear effective_keycode. */
effective_keycode = 0;
/* If the event is a KeyPress event and a keysym was /* If the event is a KeyPress event and a keysym was
looked up, record the keysym in the text input's looked up, calculate a keycode for the keysym, and
keycode-keysym table, so the correct keycode can be record in the text input's keycode-keycode table, so
looked up upon the next KeyRelease event. X input the correct keycode can be looked up upon the next
methods tend not to filter the KeyRelease events, so KeyRelease event. X input methods tend not to filter
the KeyRelease event for an event that changed the the KeyRelease events, so the KeyRelease event for an
keysym will be sent with the wrong keycode, which event that changed the keysym will be sent with the
does not matter much with X programs, but leads to wrong keycode, which does not matter much with X
Wayland programs constantly autorepeating the keycode programs, but leads to Wayland programs constantly
for which a KeyPress event was sent. */ autorepeating the keycode for which a KeyPress event
was sent. */
if (event->xkey.type == KeyPress && keysym) if (event->xkey.type == KeyPress && keysym)
{ {
DebugPrint ("inserting keysym %lu into map under %u", /* Compute the keycode. */
keysym, event->xkey.keycode); effective_keycode
InsertKeysym (&input->keysym_map, event->xkey.keycode, = CalculateKeycodeForEvent (event, keysym);
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) else if (event->xkey.type == KeyRelease)
{ {
@ -3454,23 +3495,35 @@ XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
event for the keycode. Otherwise, the input event for the keycode. Otherwise, the input
method probably knows better than us, so use the method probably knows better than us, so use the
keysym provided by the input method. */ keysym provided by the input method. */
if (!keysym) if (!keysym)
{ {
keysym = GetKeysym (&input->keysym_map, effective_keycode
event->xkey.keycode); = GetKeycode (&input->keysym_map,
DebugPrint ("obtained keysym %lu for keycode %u" event->xkey.keycode);
DebugPrint ("obtained keycode %lu for keycode %u"
" while processing KeyRelease event", " while processing KeyRelease event",
keysym, event->xkey.keycode); keysym, event->xkey.keycode);
} }
else
effective_keycode
= CalculateKeycodeForEvent (event, keysym);
DebugPrint ("removing keycode %u from map", DebugPrint ("removing keycode %u from map",
event->xkey.keycode); 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); 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);
} }
} }
} }
@ -3556,7 +3609,7 @@ InitInputStyles (void)
string = value.addr; string = value.addr;
end = string + strlen (string); end = string + strlen (string);
i = 0; i = 0;
while (string < end) while (string < end)
{ {
/* Find the next comma. */ /* Find the next comma. */