Minor fixes to input method handling

* compositor.h (struct _TextInputFuncs): Make filter_input
return a KeySym.
(ConfigureWidth, ConfigureHeight): Remove macros.
* seat.c (LookupKeysym): New function.
(DispatchKey): Handle keysym return from filter input function.
* text_input.c (CurrentCursorX, CurrentCursorY, CurrentCursorWidth)
(CurrentCursorHeight): New functions.
(DoGeometryAllocation, Commit): Handle scaling correctly.
(ConvertWcharString, PreeditDrawCallback): Handle wide character
strings.
(CreateIC): Do geometry allocation before setting the focus, not
after.
(LookupString): Only prefer keysym if control chars were
returned.
(FilterInputCallback): Return keysym.
(XLTextInputDispatchCoreEvent): Improve debugging code.
* xdg_toplevel.c (HandleConfigureEvent): Fix scaling of event
coordinates during resize.
This commit is contained in:
oldosfan 2022-10-07 02:08:41 +00:00
parent b6dec13e99
commit 694952b7f7
4 changed files with 252 additions and 88 deletions

View file

@ -1275,7 +1275,7 @@ struct _TextInputFuncs
void (*focus_out) (Seat *); void (*focus_out) (Seat *);
/* The last argument is actually an XIDeviceEvent *. */ /* The last argument is actually an XIDeviceEvent *. */
Bool (*filter_input) (Seat *, Surface *, void *); Bool (*filter_input) (Seat *, Surface *, void *, KeySym *);
}; };
extern void XLTextInputDispatchCoreEvent (Surface *, XEvent *); extern void XLTextInputDispatchCoreEvent (Surface *, XEvent *);
@ -1518,6 +1518,3 @@ extern void XLInitSinglePixelBuffer (void);
#define IntSubtractWrapv(a, b, r) __builtin_sub_overflow (a, b, r) #define IntSubtractWrapv(a, b, r) __builtin_sub_overflow (a, b, r)
#define IntMultiplyWrapv(a, b, r) __builtin_mul_overflow (a, b, r) #define IntMultiplyWrapv(a, b, r) __builtin_mul_overflow (a, b, r)
#define ConfigureWidth(event) ((event)->xconfigure.width / global_scale_factor)
#define ConfigureHeight(event) ((event)->xconfigure.height / global_scale_factor)

49
seat.c
View file

@ -4017,10 +4017,31 @@ DispatchButton (Subcompositor *subcompositor, XIDeviceEvent *xev)
xev->event_x, xev->event_y); xev->event_x, xev->event_y);
} }
static KeySym
LookupKeysym (XIDeviceEvent *xev, KeyCode keycode)
{
unsigned int state, mods_return;
KeySym keysym;
/* Convert the state to a core state mask. */
state = ((xev->mods.effective & ~(1 << 13 | 1 << 14))
| (xev->group.effective << 13));
keysym = 0;
/* Look up the keysym. */
XkbLookupKeySym (compositor.display, keycode,
state, &mods_return, &keysym);
/* Return the keysym. */
return keysym;
}
static void static void
DispatchKey (XIDeviceEvent *xev) DispatchKey (XIDeviceEvent *xev)
{ {
Seat *seat; Seat *seat;
KeySym keysym;
KeyCode keycode;
seat = XLLookUpAssoc (seats, xev->deviceid); seat = XLLookUpAssoc (seats, xev->deviceid);
@ -4033,10 +4054,12 @@ DispatchKey (XIDeviceEvent *xev)
if (seat->focus_surface) if (seat->focus_surface)
{ {
keysym = 0;
if (input_funcs if (input_funcs
&& seat->flags & IsTextInputSeat && seat->flags & IsTextInputSeat
&& input_funcs->filter_input (seat, seat->focus_surface, && input_funcs->filter_input (seat, seat->focus_surface,
xev)) xev, &keysym))
/* The input method decided to filter the key. */ /* The input method decided to filter the key. */
return; return;
@ -4044,13 +4067,33 @@ DispatchKey (XIDeviceEvent *xev)
if (xev->flags & XIKeyRepeat) if (xev->flags & XIKeyRepeat)
return; return;
keycode = xev->detail;
/* If the input method specified a keysym to use, use it. */
if (keysym)
{
/* If looking up the event keycode also results in the
keysym, then just use the keycode specified in the
event. */
if (LookupKeysym (xev, keycode) == keysym)
keycode = xev->detail;
else
keycode = XKeysymToKeycode (compositor.display, keysym);
/* But if no corresponding keycode could be found, use the
keycode provided in the event. */
if (!keycode)
keycode = xev->detail;
}
if (xev->evtype == XI_KeyPress) if (xev->evtype == XI_KeyPress)
SendKeyboardKey (seat, seat->focus_surface, SendKeyboardKey (seat, seat->focus_surface,
xev->time, WaylandKeycode (xev->detail), xev->time, WaylandKeycode (keycode),
WL_KEYBOARD_KEY_STATE_PRESSED); WL_KEYBOARD_KEY_STATE_PRESSED);
else else
SendKeyboardKey (seat, seat->focus_surface, SendKeyboardKey (seat, seat->focus_surface,
xev->time, WaylandKeycode (xev->detail), xev->time, WaylandKeycode (keycode),
WL_KEYBOARD_KEY_STATE_RELEASED); WL_KEYBOARD_KEY_STATE_RELEASED);
} }
} }

View file

@ -241,6 +241,70 @@ 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 int
CurrentCursorX (TextInput *input)
{
int x, y;
XLAssert (input->client_info->focus_surface != NULL);
/* Scale these coordinates into window coordinates. */
TruncateSurfaceToWindow (input->client_info->focus_surface,
input->current_state.cursor_x,
input->current_state.cursor_y,
&x, &y);
return x;
}
static int
CurrentCursorY (TextInput *input)
{
int x, y;
XLAssert (input->client_info->focus_surface != NULL);
/* Scale these coordinates into window coordinates. */
TruncateSurfaceToWindow (input->client_info->focus_surface,
input->current_state.cursor_x,
input->current_state.cursor_y,
&x, &y);
return y;
}
static int
CurrentCursorWidth (TextInput *input)
{
int width, height;
XLAssert (input->client_info->focus_surface != NULL);
/* Scale these coordinates into window coordinates. */
TruncateScaleToWindow (input->client_info->focus_surface,
input->current_state.cursor_width,
input->current_state.cursor_height,
&width, &height);
return width;
}
static int
CurrentCursorHeight (TextInput *input)
{
int width, height;
XLAssert (input->client_info->focus_surface != NULL);
/* Scale these coordinates into window coordinates. */
TruncateScaleToWindow (input->client_info->focus_surface,
input->current_state.cursor_width,
input->current_state.cursor_height,
&width, &height);
return height;
}
/* Byte-text position conversion. */ /* Byte-text position conversion. */
@ -558,7 +622,7 @@ static void
DoGeometryAllocation (TextInput *input) DoGeometryAllocation (TextInput *input)
{ {
XPoint spot; XPoint spot;
XRectangle area, needed; XRectangle area, *needed;
XVaNestedList attr; XVaNestedList attr;
View *view; View *view;
char *rc; char *rc;
@ -577,9 +641,9 @@ DoGeometryAllocation (TextInput *input)
if (input->current_state.pending & PendingCursorRectangle) if (input->current_state.pending & PendingCursorRectangle)
{ {
spot.x = input->current_state.cursor_x; spot.x = CurrentCursorX (input);
spot.y = (input->current_state.cursor_y spot.y = (CurrentCursorY (input)
+ input->current_state.cursor_height - 1); + CurrentCursorHeight (input));
} }
else else
{ {
@ -611,44 +675,46 @@ DoGeometryAllocation (TextInput *input)
if (!rc) if (!rc)
{ {
DebugPrint ("IM suggested the given size: %d %d", DebugPrint ("IM suggested the given size: %d %d",
needed.width, needed.height); needed->width, needed->height);
/* Place the rectangle below and to the right of the /* Place the rectangle below and to the right of the
caret. */ caret. */
if (input->current_state.pending & PendingCursorRectangle) if (input->current_state.pending & PendingCursorRectangle)
{ {
needed.x = (input->current_state.cursor_x needed->x = (CurrentCursorX (input)
+ input->current_state.cursor_width); + CurrentCursorWidth (input));
needed.y = (input->current_state.cursor_y needed->y = (CurrentCursorY (input)
+ input->current_state.cursor_height); + CurrentCursorHeight (input));
FitRect (&needed, ViewWidth (view), ViewHeight (view), FitRect (needed, ViewWidth (view), ViewHeight (view),
input->current_state.cursor_x, CurrentCursorX (input), CurrentCursorY (input),
input->current_state.cursor_y, CurrentCursorWidth (input),
input->current_state.cursor_width, CurrentCursorHeight (input));
input->current_state.cursor_height);
DebugPrint ("filled rectangle: %d %d %d %d", DebugPrint ("filled rectangle: %d %d %d %d",
needed.x, needed.y, needed.width, needed->x, needed->y, needed->width,
needed.height); needed->height);
} }
else else
{ {
/* No caret was specified... Place the preedit window on /* No caret was specified... Place the preedit window on
the bottom right corner of the view. */ the bottom left corner of the view. */
needed.x = ViewWidth (view); needed->x = 0;
needed.y = ViewHeight (view); needed->y = ViewHeight (view) - needed->height;
DebugPrint ("placed rectangle: %d %d %d %d", DebugPrint ("placed rectangle: %d %d %d %d",
needed.x, needed.y, needed.width, needed->x, needed->y, needed->width,
needed.height); needed->height);
} }
/* Set the geometry. */ /* Set the geometry. */
attr = XVaCreateNestedList (0, XNArea, &needed, NULL); attr = XVaCreateNestedList (0, XNArea, needed, NULL);
XSetICValues (input->xic, XNPreeditAttributes, attr, NULL); XSetICValues (input->xic, XNPreeditAttributes, attr, NULL);
XFree (attr); XFree (attr);
/* Free the rectangle returned. */
XFree (needed);
} }
} }
@ -671,44 +737,23 @@ DoGeometryAllocation (TextInput *input)
if (!rc) if (!rc)
{ {
DebugPrint ("IM suggested the given size: %d %d", DebugPrint ("IM suggested the given size: %d %d",
needed.width, needed.height); needed->width, needed->height);
/* Place the rectangle below and to the right of the /* Place the rectangle at the bottom of the window. */
caret. */ needed->x = ViewWidth (view) - needed->width;
needed->y = ViewHeight (view) - needed->height;
if (input->current_state.pending & PendingCursorRectangle) DebugPrint ("placed rectangle at bottom right: %d %d %d %d",
{ needed->x, needed->y, needed->width,
needed.x = (input->current_state.cursor_x needed->height);
+ input->current_state.cursor_width);
needed.y = (input->current_state.cursor_y
+ input->current_state.cursor_height);
FitRect (&needed, ViewWidth (view), ViewHeight (view),
input->current_state.cursor_x,
input->current_state.cursor_y,
input->current_state.cursor_width,
input->current_state.cursor_height);
DebugPrint ("filled rectangle: %d %d %d %d",
needed.x, needed.y, needed.width,
needed.height);
}
else
{
/* No caret was specified... Place the preedit window on
the bottom right corner of the view. */
needed.x = ViewWidth (view);
needed.y = ViewHeight (view);
DebugPrint ("placed rectangle: %d %d %d %d",
needed.x, needed.y, needed.width,
needed.height);
}
/* Set the geometry. */ /* Set the geometry. */
attr = XVaCreateNestedList (0, XNArea, &needed, NULL); attr = XVaCreateNestedList (0, XNArea, needed, NULL);
XSetICValues (input->xic, XNStatusAttributes, attr, NULL); XSetICValues (input->xic, XNStatusAttributes, attr, NULL);
XFree (attr); XFree (attr);
/* Free the needed rectangle. */
XFree (needed);
} }
} }
} }
@ -770,11 +815,11 @@ Commit (struct wl_client *client, struct wl_resource *resource)
else else
XFree (XmbResetIC (input->xic)); XFree (XmbResetIC (input->xic));
if (input->xic)
XSetICFocus (input->xic);
/* Perform geometry/position allocation on the IC. */ /* Perform geometry/position allocation on the IC. */
DoGeometryAllocation (input); DoGeometryAllocation (input);
if (input->xic)
XSetICFocus (input->xic);
} }
else else
{ {
@ -1512,12 +1557,62 @@ PreeditDoneCallback (XIC ic, XPointer client_data, XPointer call_data)
UpdatePreedit (input); UpdatePreedit (input);
} }
static char *
ConvertWcharString (PreeditBuffer *buffer, const wchar_t *input,
size_t input_size, size_t *string_size)
{
char *output, *oldlocale;
int rc;
size_t bytes;
/* Since the text is intended for BUFFER, switch to BUFFER's
locale. */
oldlocale = XLStrdup (setlocale (LC_CTYPE, NULL));
/* Switch to the new locale. */
if (!setlocale (LC_CTYPE, buffer->locale))
{
/* Setting the locale failed. Return an empty string. */
XLFree (oldlocale);
*string_size = 0;
return NULL;
}
output = XLCalloc (input_size + 1, MB_CUR_MAX);
bytes = 0;
while (input_size)
{
input_size--;
rc = wctomb (output + bytes, *input++);
if (rc == -1)
/* Invalid wide character code. */
continue;
/* Otherwise, move the string forward this much. */
bytes += rc;
}
/* Return the string and the number of bytes put in it. */
*string_size = bytes;
/* Clear shift state. */
wctomb (NULL, L'\0');
/* Restore the old locale. */
setlocale (LC_CTYPE, oldlocale);
XLFree (oldlocale);
return output;
}
static void static void
PreeditDrawCallback (XIC ic, XPointer client_data, PreeditDrawCallback (XIC ic, XPointer client_data,
XIMPreeditDrawCallbackStruct *call_data) XIMPreeditDrawCallbackStruct *call_data)
{ {
TextInput *input; TextInput *input;
size_t string_size; size_t string_size;
char *multi_byte_string;
input = (TextInput *) client_data; input = (TextInput *) client_data;
DebugPrint ("text input: %p", input); DebugPrint ("text input: %p", input);
@ -1529,13 +1624,6 @@ PreeditDrawCallback (XIC ic, XPointer client_data,
call_data->chg_first, call_data->chg_first,
call_data->chg_length); call_data->chg_length);
if (call_data->text
&& call_data->text->encoding_is_wchar)
{
DebugPrint ("wchar encoding not yet implemented");
return;
}
/* Delete text between chg_first and chg_first + chg_length. */ /* Delete text between chg_first and chg_first + chg_length. */
if (call_data->chg_length if (call_data->chg_length
&& !PreeditDeleteChars (input->buffer, call_data->chg_first, && !PreeditDeleteChars (input->buffer, call_data->chg_first,
@ -1547,17 +1635,35 @@ PreeditDrawCallback (XIC ic, XPointer client_data,
if (call_data->text) if (call_data->text)
{ {
/* The multibyte string should be NULL terminated. */ if (call_data->text->encoding_is_wchar)
string_size = strlen (call_data->text->string.multi_byte); {
DebugPrint ("converting wide character string");
multi_byte_string
= ConvertWcharString (input->buffer,
call_data->text->string.wide_char,
call_data->text->length,
&string_size);
}
else
{
/* The multibyte string should be NULL terminated. */
string_size = strlen (call_data->text->string.multi_byte);
multi_byte_string = call_data->text->string.multi_byte;
}
DebugPrint ("inserting text of size %d, %zu", DebugPrint ("inserting text of size %d, %zu",
call_data->text->length, string_size); call_data->text->length, string_size);
/* Now, insert whatever text was specified at chg_first. */ /* Now, insert whatever text was specified at chg_first. */
if (!PreeditInsertChars (input->buffer, call_data->chg_first, if (!PreeditInsertChars (input->buffer, call_data->chg_first,
call_data->text->string.multi_byte, multi_byte_string, string_size,
string_size, call_data->text->length)) call_data->text->length))
DebugPrint ("insertion failed"); DebugPrint ("insertion failed");
if (call_data->text->encoding_is_wchar)
/* We must free the conversion results. */
XLFree (multi_byte_string);
} }
/* Now set the caret position. */ /* Now set the caret position. */
@ -2279,9 +2385,9 @@ CreateIC (TextInput *input)
if (input->current_state.pending & PendingCursorRectangle) if (input->current_state.pending & PendingCursorRectangle)
{ {
spot.x = input->current_state.cursor_x; spot.x = CurrentCursorX (input);
spot.y = (input->current_state.cursor_y spot.y = (CurrentCursorY (input)
+ input->current_state.cursor_height - 1); + CurrentCursorHeight (input));
} }
else else
{ {
@ -3021,6 +3127,12 @@ LookupString (TextInput *input, XEvent *event, KeySym *keysym_return)
Status status; Status status;
KeySym keysym; KeySym keysym;
if (event->xkey.type != KeyPress)
{
DebugPrint ("ignoring key release event");
return False;
}
/* First, do XmbLookupString with the default buffer size. */ /* First, do XmbLookupString with the default buffer size. */
buffer = alloca (256); buffer = alloca (256);
nbytes = XmbLookupString (input->xic, &event->xkey, nbytes = XmbLookupString (input->xic, &event->xkey,
@ -3059,11 +3171,12 @@ 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 character and a keysym /* If the string happens to consist of only 1 control character and
was also found, give preference to the keysym. */ a keysym was also found, give preference to the keysym. */
if (buffer_size == 1 && status == XLookupBoth) if (buffer_size == 1 && status == XLookupBoth
&& buffer[0] > 0 && buffer[0] < 32)
{ {
DebugPrint ("using keysym in preference to single char"); DebugPrint ("using keysym in preference to single control char");
XFree (buffer); XFree (buffer);
@ -3081,7 +3194,8 @@ LookupString (TextInput *input, XEvent *event, KeySym *keysym_return)
} }
static Bool static Bool
FilterInputCallback (Seat *seat, Surface *surface, void *event) FilterInputCallback (Seat *seat, Surface *surface, void *event,
KeySym *keysym)
{ {
XIDeviceEvent *xev; XIDeviceEvent *xev;
XEvent xkey; XEvent xkey;
@ -3118,7 +3232,7 @@ FilterInputCallback (Seat *seat, Surface *surface, void *event)
/* Otherwise, call XmbLookupString. If a keysym is returned, /* Otherwise, call XmbLookupString. If a keysym is returned,
return False. Otherwise, commit the string looked up and return False. Otherwise, commit the string looked up and
return True. */ return True. */
return LookupString (input, &xkey, NULL); return LookupString (input, &xkey, keysym);
} }
/* Otherwise, do nothing. */ /* Otherwise, do nothing. */
@ -3204,7 +3318,8 @@ XLTextInputDispatchCoreEvent (Surface *surface, XEvent *event)
else else
{ {
/* Since that failed, dispatch the event to the seat. */ /* Since that failed, dispatch the event to the seat. */
DebugPrint ("lookup failed; dispatching event to seat"); DebugPrint ("lookup failed; dispatching event to seat; "
"keysym is: %lu", keysym);
XLSeatDispatchCoreKeyEvent (im_seat, surface, event, keysym); XLSeatDispatchCoreKeyEvent (im_seat, surface, event, keysym);
} }

View file

@ -1316,7 +1316,7 @@ RestoreStateTo (XdgToplevel *toplevel, int width, int height)
static Bool static Bool
HandleConfigureEvent (XdgToplevel *toplevel, XEvent *event) HandleConfigureEvent (XdgToplevel *toplevel, XEvent *event)
{ {
int width, height; int width, height, configure_width, configure_height;
if (event->xconfigure.send_event) if (event->xconfigure.send_event)
/* Handle only synthetic events, since that's what the /* Handle only synthetic events, since that's what the
@ -1357,9 +1357,18 @@ HandleConfigureEvent (XdgToplevel *toplevel, XEvent *event)
if (!MaybePostDelayedConfigure (toplevel, StatePendingConfigureSize)) if (!MaybePostDelayedConfigure (toplevel, StatePendingConfigureSize))
{ {
/* Scale the configure event width and height to the
surface. */
TruncateScaleToSurface (toplevel->role->surface,
event->xconfigure.width,
event->xconfigure.height,
&configure_width,
&configure_height);
/* Calculate the new window size. */
XLXdgRoleCalcNewWindowSize (toplevel->role, XLXdgRoleCalcNewWindowSize (toplevel->role,
ConfigureWidth (event), configure_width,
ConfigureHeight (event), configure_height,
&width, &height); &width, &height);
SendConfigure (toplevel, width, height); SendConfigure (toplevel, width, height);