forked from 12to11/12to11
Improve handling of keysyms returned by input methods
* text_input.c (struct _KeysymMap): New struct. (struct _TextInput): New field `keysym_map'. (ClearKeysymMap, InsertKeysym, RemoveKeysym, GetKeysym): New function. (InputDoLeave, HandleResourceDestroy): Clear the keysym map. (NoticeLeave): Fix coding style. (LookupString): Always prefer keysyms looked up. (XLTextInputDispatchCoreEvent): Store keysym into map upon KeyPress under the keycode, and use that keysym with the corresponding KeyRelease.
This commit is contained in:
parent
163dca96bd
commit
bf568311a6
1 changed files with 173 additions and 31 deletions
204
text_input.c
204
text_input.c
|
@ -94,6 +94,7 @@ typedef struct _TextInput TextInput;
|
|||
typedef struct _TextInputState TextInputState;
|
||||
typedef struct _TextPosition TextPosition;
|
||||
typedef struct _PreeditBuffer PreeditBuffer;
|
||||
typedef struct _KeysymMap KeysymMap;
|
||||
|
||||
typedef enum _XimStyleKind XimStyleKind;
|
||||
|
||||
|
@ -113,6 +114,18 @@ enum
|
|||
PendingSurroundingText = (1 << 2),
|
||||
};
|
||||
|
||||
struct _KeysymMap
|
||||
{
|
||||
/* Packed map between keycodes and keysyms. */
|
||||
KeyCode *keycodes;
|
||||
|
||||
/* The keysyms. */
|
||||
KeySym *keysyms;
|
||||
|
||||
/* The number of keycodes and keysyms in this map. */
|
||||
int key_count;
|
||||
};
|
||||
|
||||
struct _PreeditBuffer
|
||||
{
|
||||
/* Buffer data. */
|
||||
|
@ -188,6 +201,10 @@ struct _TextInput
|
|||
|
||||
/* Number of commit requests performed. */
|
||||
uint32_t serial;
|
||||
|
||||
/* Map between keys currently held down and keysyms they looked up
|
||||
to. */
|
||||
KeysymMap keysym_map;
|
||||
};
|
||||
|
||||
/* Structure describing a list of TextInput resources associated with
|
||||
|
@ -241,6 +258,93 @@ static XIMStyle xim_style;
|
|||
/* The order in which XIM input styles will be searched for. */
|
||||
static XimStyleKind xim_style_order[5];
|
||||
|
||||
|
||||
|
||||
static void
|
||||
ClearKeysymMap (KeysymMap *map)
|
||||
{
|
||||
XLFree (map->keycodes);
|
||||
XLFree (map->keysyms);
|
||||
map->keycodes = NULL;
|
||||
map->keysyms = NULL;
|
||||
map->key_count = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
InsertKeysym (KeysymMap *map, KeyCode keycode, KeySym keysym)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Insert keysym 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;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, add it to the end. */
|
||||
map->key_count++;
|
||||
map->keycodes
|
||||
= XLRealloc (map->keycodes,
|
||||
map->key_count * sizeof *map->keycodes);
|
||||
map->keysyms
|
||||
= XLRealloc (map->keysyms,
|
||||
map->key_count * sizeof *map->keysyms);
|
||||
map->keycodes[map->key_count - 1] = keycode;
|
||||
map->keysyms[map->key_count - 1] = keysym;
|
||||
}
|
||||
|
||||
static void
|
||||
RemoveKeysym (KeysymMap *map, KeyCode keycode)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Find where the keycode is located in the map. */
|
||||
|
||||
for (i = 0; i < map->key_count; ++i)
|
||||
{
|
||||
if (map->keycodes[i] == keycode)
|
||||
{
|
||||
/* Remove the keycode from the list and shrink it. */
|
||||
memcpy (&map->keycodes[i], &map->keycodes[i + 1],
|
||||
(map->key_count - (i + 1)) * sizeof *map->keycodes);
|
||||
memcpy (&map->keysyms[i], &map->keysyms[i + 1],
|
||||
(map->key_count - (i + 1)) * sizeof *map->keysyms);
|
||||
|
||||
map->key_count -= 1;
|
||||
map->keycodes
|
||||
= XLRealloc (map->keycodes,
|
||||
map->key_count * sizeof *map->keycodes);
|
||||
map->keysyms
|
||||
= XLRealloc (map->keysyms,
|
||||
map->key_count * sizeof *map->keysyms);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static KeySym
|
||||
GetKeysym (KeysymMap *map, KeyCode keycode)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < map->key_count; ++i)
|
||||
{
|
||||
if (map->keycodes[i] == keycode)
|
||||
return map->keysyms[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
CurrentCursorX (TextInput *input)
|
||||
{
|
||||
|
@ -929,6 +1033,11 @@ 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);
|
||||
|
||||
memset (&input->current_state, 0, sizeof input->current_state);
|
||||
}
|
||||
|
||||
|
@ -986,6 +1095,9 @@ HandleResourceDestroy (struct wl_resource *resource)
|
|||
if (input->buffer)
|
||||
FreePreeditBuffer (input->buffer);
|
||||
|
||||
/* Destroy the map of pressed keycodes to keysyms. */
|
||||
ClearKeysymMap (&input->keysym_map);
|
||||
|
||||
/* Free the text input itself. */
|
||||
XLFree (input);
|
||||
}
|
||||
|
@ -1081,21 +1193,21 @@ NoticeLeave (TextInputClientInfo *info)
|
|||
|
||||
DebugPrint ("client info: %p", info);
|
||||
|
||||
input = info->inputs.next;
|
||||
while (input != &info->inputs)
|
||||
{
|
||||
DebugPrint ("sending leave to text input %p", input);
|
||||
input = info->inputs.next;
|
||||
while (input != &info->inputs)
|
||||
{
|
||||
DebugPrint ("sending leave to text input %p", input);
|
||||
|
||||
/* Otherwise, if info->focus_surface->resource is still
|
||||
there, send the leave event to each text input. */
|
||||
if (info->focus_surface->resource)
|
||||
/* Send the enter event to each text input. */
|
||||
zwp_text_input_v3_send_leave (input->resource,
|
||||
info->focus_surface->resource);
|
||||
InputDoLeave (input, info->focus_surface);
|
||||
/* Otherwise, if info->focus_surface->resource is still
|
||||
there, send the leave event to each text input. */
|
||||
if (info->focus_surface->resource)
|
||||
/* Send the enter event to each text input. */
|
||||
zwp_text_input_v3_send_leave (input->resource,
|
||||
info->focus_surface->resource);
|
||||
InputDoLeave (input, info->focus_surface);
|
||||
|
||||
input = input->next;
|
||||
}
|
||||
input = input->next;
|
||||
}
|
||||
|
||||
/* And clear the focus surface. */
|
||||
info->focus_surface = NULL;
|
||||
|
@ -3157,10 +3269,14 @@ LookupString (TextInput *input, XEvent *event, KeySym *keysym_return)
|
|||
DebugPrint ("status is: %d", (int) status);
|
||||
|
||||
/* If no string was returned, return False. Otherwise, convert the
|
||||
string to UTF-8 and commit it. */
|
||||
if (status != XLookupChars && status != XLookupBoth)
|
||||
string to UTF-8 and commit it. However, use the keysym if both a
|
||||
keysym and string were looked up, as a keysym allows modifiers to
|
||||
be correctly dispatched to the seat and avoids doing potentially
|
||||
expensive character conversion. */
|
||||
if (status != XLookupChars)
|
||||
{
|
||||
if (status == XLookupKeySym && keysym_return)
|
||||
if ((status == XLookupKeySym
|
||||
|| status == XLookupBoth) && keysym_return)
|
||||
/* Return the keysym if it was looked up. */
|
||||
*keysym_return = keysym;
|
||||
|
||||
|
@ -3175,21 +3291,6 @@ LookupString (TextInput *input, XEvent *event, KeySym *keysym_return)
|
|||
/* Convert the string. */
|
||||
buffer = ConvertString (buffer, nbytes, &buffer_size);
|
||||
|
||||
/* If the string happens to consist of only 1 control character and
|
||||
a keysym was also found, give preference to the keysym. */
|
||||
if (buffer_size == 1 && status == XLookupBoth
|
||||
&& buffer[0] > 0 && buffer[0] < 32)
|
||||
{
|
||||
DebugPrint ("using keysym in preference to single control char");
|
||||
|
||||
XFree (buffer);
|
||||
|
||||
if (keysym_return)
|
||||
*keysym_return = keysym;
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
CommitString (input, buffer, buffer_size);
|
||||
XFree (buffer);
|
||||
|
@ -3328,6 +3429,47 @@ XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
|
|||
DebugPrint ("lookup failed; dispatching event to seat; "
|
||||
"keysym is: %lu", keysym);
|
||||
|
||||
/* 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. */
|
||||
|
||||
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);
|
||||
}
|
||||
else if (event->xkey.type == KeyRelease)
|
||||
{
|
||||
/* If no keysym was committed by the input method,
|
||||
use the keysym provided in the last KeyPress
|
||||
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,
|
||||
event->xkey.keycode);
|
||||
DebugPrint ("obtained keysym %lu for keycode %u"
|
||||
" while processing KeyRelease event",
|
||||
keysym, event->xkey.keycode);
|
||||
}
|
||||
|
||||
DebugPrint ("removing keycode %u from map",
|
||||
event->xkey.keycode);
|
||||
|
||||
/* Remove the keycode from the keysym map. */
|
||||
RemoveKeysym (&input->keysym_map, event->xkey.keycode);
|
||||
}
|
||||
|
||||
XLSeatDispatchCoreKeyEvent (im_seat, surface, event, keysym);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue