This now uses a similar mechanism as is used by native MSCTF, and exposed in https://gitlab.winehq.org/wine/wine/-/merge_requests/2749, with WM_IME_NOTIFY private messages.
For the asynchronous mode, used in winex11, WM_IME_NOTIFY / IMN_WINE_SET_COMP_STRING messages are sent, with a lparam cookie. To keep the implementations close between synchronous and asynchronous mode, this triggers calls to ImeToAsciiEx, in the same way ImmTranslateMessage does in the synchronous mode, and it then sends the generated IME messages to the window instead of posting them. In this asynchronous mode, the cookie is passed in the composition string private area where the user driver will find it.
For winex11, the cookie is a pointer to an ime_update structure, containing pointers to the last composition string and result strings. As we're sending messages, it works synchronously and it can live on the kernel stack for now, but I think we might want to make this more asynchronous in the future and dynamically allocate the updates. This would cause leaks if the messages aren't processed though.
I'm adding winemac changes as well to show that it works there as well in a synchronous mode.