Open tehsausage opened 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.
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 anALLEGRO_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 byal_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 finalALLEGRO_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
andALLEGRO_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:
display
: The display that text is being input ontext
: AnALLEGRO_USTR
of the current text composition. If length is 0, then composition is effectively cancelled.cursor
: Position of the cursor within the composition.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:
display
: The display that text is being input ontext
: AnALLEGRO_USTR
of the text that was finally enteredThis 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:
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:
Windows: Win32 has some support for transparently handling its IME, so a partial implementation could be to disable DirectInput keyboard handling in favour of WM_CHAR window events for as long as "text input mode" is active. Need to investigate how full IME support could be integrated. Windows 10 also has a win+period shortcut to open an emoji selector that should work with the same interface.
Linux: As X11 is the only back-end, XIM would probably need to be used? I'm not certain how well XIM works, or if most common IME setups even configure XIM support. Ideally there would be toolkit-specific backend support added to Allegro so that GTK and Qt accessibility APIs could be used, as they seem to be much better supported.
Android and iOS: This API could possibly be used to trigger the touch-screen keyboard to show up, as there doesn't seem to be a way to do this in Allegro at the moment?
ALLEGRO_EVENT_TEXT_INPUT
would also be convenient when entering complete words at a time. I'm not interested in working on mobile platforms, however.Others: I haven't considered any other platforms. Platforms that use the SDL2 backend should easily be supported through the SDL TextInput API.
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:
Step 1:
ALLEGRO_EVENT_TEXT_COMPOSITION(text="k", cursor=1)
Step 2:
ALLEGRO_EVENT_TEXT_COMPOSITION(text="か", cursor=1)
Step 3:
ALLEGRO_EVENT_TEXT_COMPOSITION(text="かn", cursor=2)
Step 4:
ALLEGRO_EVENT_TEXT_COMPOSITION(text="かんj", cursor=3)
Step 5:
ALLEGRO_EVENT_TEXT_COMPOSITION(text="かんじ", cursor=3)
Step 6:
ALLEGRO_EVENT_TEXT_COMPOSITION(text="かんじ", cursor=2)
Step 7:
ALLEGRO_EVENT_TEXT_COMPOSITION(text="感じ", cursor=0)
Step 8:
ALLEGRO_EVENT_TEXT_COMPOSITION(text="漢字", cursor=0)
Step 9:
ALLEGRO_EVENT_TEXT_INPUT(text="漢字")