input-method does not validate commit serials
when receiving a commit with an invalid serial, instead of dropping pending state and returning early, the compositor's serial and the current state are overwritten, against protocol.
an example of glitchy behavior resulting from the improper handling: when composing preedit text in one input, then switching focus to another input, fcitx5 will send commit_text
and commit
with the serial corresponding to the transaction deactivating it. if it gets activated and the serial is incremented before these requests are processed, wlroots will treat them as happening after activation anyway, and the commit text will appear in the target input, against expectations.
static void im_commit(struct wl_client *client, struct wl_resource *resource,
uint32_t serial) {
struct wlr_input_method_v2 *input_method =
input_method_from_resource(resource);
if (!input_method) {
return;
}
input_method->current = input_method->pending;
input_method->current_serial = serial;
struct wlr_input_method_v2_state default_state = {0};
input_method->pending = default_state;
wl_signal_emit_mutable(&input_method->events.commit, (void*)input_method);
}
i think it should look like this instead:
static void im_commit(struct wl_client *client, struct wl_resource *resource,
uint32_t serial) {
struct wlr_input_method_v2 *input_method =
input_method_from_resource(resource);
if (!input_method) {
return;
}
struct wlr_input_method_v2_state default_state = {0};
if (serial != input_method->current_serial) {
free(input_method->pending.commit_text);
free(input_method->pending.preedit.text);
input_method->pending = default_state;
return;
}
free(input_method->current.commit_text);
free(input_method->current.preedit.text);
input_method->current = input_method->pending;
input_method->pending = default_state;
wl_signal_emit_mutable(&input_method->events.commit, (void*)input_method);
}