forked from 12to11/12to11
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:
parent
18d18aabb5
commit
c90af69ecd
3 changed files with 123 additions and 78 deletions
|
@ -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
60
seat.c
|
@ -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. */
|
||||||
|
|
||||||
|
|
131
text_input.c
131
text_input.c
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue