liballeg / allegro5

The official Allegro 5 git repository. Pull requests welcome!
https://liballeg.org
Other
1.88k stars 283 forks source link

Text input / IME support #1191

Open tehsausage opened 3 years ago

tehsausage commented 3 years ago

Basing this idea off of SDL's implementation: https://wiki.libsdl.org/Tutorials/TextInput

Problem

There is no way to input "international" characters in to Allegro programs using an IME.

This would be a desirable feature to have for games with any chat or text input features, or when developing tools for games where text may need to be entered.

Proposed Feature

Add an API to enable "text input mode" for a display.

In this mode, raw keyboard input no longer necessarily maps directly to an Allegro keyboard event, and the function of some keys may be hijacked temporarily by the IME for inputting "compositions".

For example: Typing most letters while using fcitx will begin a composition. During composition, the directional arrow keys, home/end, space, enter, tab and escape keys, as well as other keys such as F7, are all hijacked for composition editing purposes, and not delivered to the program when pressed.

The intended use would be to enable "text input mode" while keyboard focus in an Allegro program is logically inside of a text input field. The program should respond to the receipt of ALLEGRO_EVENT_TEXT_COMPOSITION events by updating the display of their text input field as appropriate to show the composition-in-progress (conventionally: by displaying the composition's characters with an underline in the location where text is to be inserted), before finally receiving an ALLEGRO_EVENT_TEXT_INPUT event for the final output.

A program should consider that a composition can occur while the cursor is positioned in the middle of existing text. The composition could be displayed simply overlapping existing text (though care should be taken to wrap around at the edge of the window), or inserted in such a way that it affects word wrapping and layout as it changes. Clicking to move the cursor may either cancel the composition (call al_stop_text_input followed by al_start_text_input), or re-position the composition within the text. Care should be taken to not count the composition's characters towards the character count of the input field, and it should behave consistently when using the mouse to re-position the cursor.

If a particular IME or platform cannot provide composition events, it may operate silently without sending ALLEGRO_EVENT_TEXT_COMPOSITION events, and only emitting the final ALLEGRO_EVENT_TEXT_INPUT event.

Note that in practice it is not uncommon to see games which do not display compositions, thus handling of ALLEGRO_EVENT_TEXT_COMPOSITION events to display compositions is optional, even if the feature is used.

Proposed API

al_start_text_input(ALLEGRO_DISPLAY* dpy)

Enables "text input mode" on the display. Some keyboard input events may now be captured by the the IME, and ALLEGRO_EVENT_TEXT_COMPOSITION and ALLEGRO_EVENT_TEXT_INPUT events may be generated.

al_stop_text_input(ALLEGRO_DISPLAY* dpy)

Disables "text input mode" on the display.

Unclear what calling this should do if there is a composition in progress. It needs to be determined if all IME implementations can be instructed to "cancel" the current composition.

_(It might be more conventional to name this pair of functions al_set_text_input_mode(ALLEGRO_DISPLAY*, bool))_

Event: ALLEGRO_EVENT_TEXT_COMPOSITION

Parameters:

This is received for a display when text input composition begins or is updated.

⚠️ Big Issue: Its not clear how text should be allocated and cleaned up. There's currently no built-in Allegro events which require explicit clean-up, but text input handling is happening asynchronously with event handling, so the text must necessarily be dynamically allocated.

A transparent solution could be to have al_get_event() call a destructor for the previous event, but that could lead to surprising lifetime errors.

Alternatively ALLEGRO_EVENT could be made just a little bigger to support an embedded string of up to around 64 bytes, but that is far from an in-feasibly large and could easily be reached.

Event: ALLEGRO_EVENT_TEXT_INPUT

Parameters:

This is received for a display when text input composition completes, and the final characters are committed.

This event could be omitted, and instead a sequence of ALLEGRO_KEY_CHAR events could be used in its place. This would eliminate the concern about the lifetime of the string for this event, at least, but risks overflowing the event queue, and causing partial strings to be written.

al_set_text_input_area(int x, int y, int w, int h) (maybe)

Defines the size and location of the text input field, so that the platform knows where it can draw IME UI pop-ups, such as is supported on Windows:

image

Will only take effect while in "text input mode". Setting a width and height of 0 would disable the effects of the function (I would expect there be a way to instruct platforms to not display UI elements at all).

A multi-line text input field should use the height of 1 line, or perhaps use the bounding box of the composition text display. Its not clear if updating this necessarily causes any currently displayed UI element to move.

Platforms:


Example

Example of the events a program might receive while in text input mode, and how it might choose to display the composition in progress:

kanji

Step 1:

Step 2:

Step 3:

Step 4:

Step 5:

Step 6:

Step 7:

Step 8:

Step 9:

beoran commented 3 years ago

Yes, this is a great idea, and I looked into it before, but I found this is not very easy to implement, at least not on Linux, especially because XIM is very old and gnarly. As for the memory freeing problem, an al_acknowledge_text_event() function could be used to free the buffer and send acknowledgement to the IME. There is a precedent in Allegro for acknowledging events.