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 _TextInputState TextInputState;
|
||||||
typedef struct _TextPosition TextPosition;
|
typedef struct _TextPosition TextPosition;
|
||||||
typedef struct _PreeditBuffer PreeditBuffer;
|
typedef struct _PreeditBuffer PreeditBuffer;
|
||||||
|
typedef struct _KeysymMap KeysymMap;
|
||||||
|
|
||||||
typedef enum _XimStyleKind XimStyleKind;
|
typedef enum _XimStyleKind XimStyleKind;
|
||||||
|
|
||||||
|
@ -113,6 +114,18 @@ enum
|
||||||
PendingSurroundingText = (1 << 2),
|
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
|
struct _PreeditBuffer
|
||||||
{
|
{
|
||||||
/* Buffer data. */
|
/* Buffer data. */
|
||||||
|
@ -188,6 +201,10 @@ struct _TextInput
|
||||||
|
|
||||||
/* Number of commit requests performed. */
|
/* Number of commit requests performed. */
|
||||||
uint32_t serial;
|
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
|
/* 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. */
|
/* The order in which XIM input styles will be searched for. */
|
||||||
static XimStyleKind xim_style_order[5];
|
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
|
static int
|
||||||
CurrentCursorX (TextInput *input)
|
CurrentCursorX (TextInput *input)
|
||||||
{
|
{
|
||||||
|
@ -929,6 +1033,11 @@ 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
|
||||||
|
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);
|
memset (&input->current_state, 0, sizeof input->current_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -986,6 +1095,9 @@ 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. */
|
||||||
|
ClearKeysymMap (&input->keysym_map);
|
||||||
|
|
||||||
/* Free the text input itself. */
|
/* Free the text input itself. */
|
||||||
XLFree (input);
|
XLFree (input);
|
||||||
}
|
}
|
||||||
|
@ -1081,21 +1193,21 @@ NoticeLeave (TextInputClientInfo *info)
|
||||||
|
|
||||||
DebugPrint ("client info: %p", info);
|
DebugPrint ("client info: %p", info);
|
||||||
|
|
||||||
input = info->inputs.next;
|
input = info->inputs.next;
|
||||||
while (input != &info->inputs)
|
while (input != &info->inputs)
|
||||||
{
|
{
|
||||||
DebugPrint ("sending leave to text input %p", input);
|
DebugPrint ("sending leave to text input %p", input);
|
||||||
|
|
||||||
/* Otherwise, if info->focus_surface->resource is still
|
/* Otherwise, if info->focus_surface->resource is still
|
||||||
there, send the leave event to each text input. */
|
there, send the leave event to each text input. */
|
||||||
if (info->focus_surface->resource)
|
if (info->focus_surface->resource)
|
||||||
/* Send the enter event to each text input. */
|
/* Send the enter event to each text input. */
|
||||||
zwp_text_input_v3_send_leave (input->resource,
|
zwp_text_input_v3_send_leave (input->resource,
|
||||||
info->focus_surface->resource);
|
info->focus_surface->resource);
|
||||||
InputDoLeave (input, info->focus_surface);
|
InputDoLeave (input, info->focus_surface);
|
||||||
|
|
||||||
input = input->next;
|
input = input->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* And clear the focus surface. */
|
/* And clear the focus surface. */
|
||||||
info->focus_surface = NULL;
|
info->focus_surface = NULL;
|
||||||
|
@ -3157,10 +3269,14 @@ LookupString (TextInput *input, XEvent *event, KeySym *keysym_return)
|
||||||
DebugPrint ("status is: %d", (int) status);
|
DebugPrint ("status is: %d", (int) status);
|
||||||
|
|
||||||
/* If no string was returned, return False. Otherwise, convert the
|
/* If no string was returned, return False. Otherwise, convert the
|
||||||
string to UTF-8 and commit it. */
|
string to UTF-8 and commit it. However, use the keysym if both a
|
||||||
if (status != XLookupChars && status != XLookupBoth)
|
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. */
|
/* Return the keysym if it was looked up. */
|
||||||
*keysym_return = keysym;
|
*keysym_return = keysym;
|
||||||
|
|
||||||
|
@ -3175,21 +3291,6 @@ LookupString (TextInput *input, XEvent *event, KeySym *keysym_return)
|
||||||
/* Convert the string. */
|
/* Convert the string. */
|
||||||
buffer = ConvertString (buffer, nbytes, &buffer_size);
|
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)
|
if (buffer)
|
||||||
CommitString (input, buffer, buffer_size);
|
CommitString (input, buffer, buffer_size);
|
||||||
XFree (buffer);
|
XFree (buffer);
|
||||||
|
@ -3328,6 +3429,47 @@ 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);
|
||||||
|
|
||||||
|
/* 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);
|
XLSeatDispatchCoreKeyEvent (im_seat, surface, event, keysym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue