SDL and IME

Hi all,
Some time ago, Daniel Vogel posted a code snipplet to the GD-Windows mailing
list showing how the unreal engine handled IME messages to support character
composition in Asian langauges. I’m not an expert on this topic, and thought
that SDL handled that internally when the unicode conversion was enabled.
However, this is not the case, because the key presses in composition mode
change the value of previously tiped characters. You could support this on
top of SDL by handling the composition messages, but I think it would be
desiderable to support this inside SDL, at least on the OSes where IME is
supported. What do you think? Does somebody know how does this work on other
OSes?

Ignacio Casta?o
@Ignacio_Castano_______________________________________________________________
Yahoo! Messenger
Nueva versi?n: Webcam, voz, y mucho m?s ?Gratis!
Desc?rgalo ya desde http://messenger.yahoo.es

The IME issue was raised before in this list, and Sam said if
there is enough interest, it will be put into TODO list. But
I guess he didn’t receive enough reply …

And yes, I’ve some dirty tricks to get SDL window accepting
Win32 IME input. It was only tested with SDL 1.2.4:

/*
The hack on Win32 is to use another “IME” window for all related
messaging, and replace SDL’s message proc function (GWL_WNDPROC) by our
own function. But SDL somehow screws up with system messaging on
Windows XP causing IME unable to be activated with SDL_app, so we need
to patch SDL source src/video/windx5/SDL_dx5events.c:

    45a46,48
    > #undef WM_APP
    > #define WM_APP 0x1
    > 
    545a549
    >                       TranslateMessage(&msg);
    603a608
    >                               TranslateMessage(&msg);

*/

And then you use something like:

int r;
WNDPROC sdl_proc = NULL;
HWND hWndEdit = NULL, hWnd = NULL;
SDL_SysWMinfo info;

SDL_VERSION(&(info.version));
r = SDL_GetWMInfo(&info);
if (r < 0) {
    return -1;
}
hWnd = info.window;
hWndEdit = CreateWindowEx(
            0,
            "IME", // IME class
            NULL, // no window title
            WS_DISABLED | WS_POPUP, // disabled window
            0, 0, 0, 0, // no need to set size
            hWnd, // parent window
            (int)NULL, 
            (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
            NULL);

sdl_proc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)proc_message);

You need to define your own proc_message(…) function to process
windows messages, in which you also need to pass any message not
processed back to “sdl_proc”.

Assuming you know how to fiddle with Win32 IME messages, you
can do all the work in your own proc_message(…). Personally, I
never got the time to dive into IME APIs deeply, I’ll be glad
if you can paste the Daniel Vogel’s code snipplet here.

Of course, the best thing would be SDL to support native IMEs
on multiple platforms. I’ve code that dealt with X11 too, but
again, it only works partially…

Regards,
.paul.On Sat, Oct 26, 2002 at 05:12:21PM +0200, Ignacio Casta?o wrote:

Hi all,
Some time ago, Daniel Vogel posted a code snipplet to the GD-Windows mailing
list showing how the unreal engine handled IME messages to support character
composition in Asian langauges. I’m not an expert on this topic, and thought
that SDL handled that internally when the unicode conversion was enabled.
However, this is not the case, because the key presses in composition mode
change the value of previously tiped characters. You could support this on
top of SDL by handling the composition messages, but I think it would be
desiderable to support this inside SDL, at least on the OSes where IME is
supported. What do you think? Does somebody know how does this work on other
OSes?

Ignacio Casta?o
castanyo at yahoo.es


Yahoo! Messenger
Nueva versi?n: Webcam, voz, y mucho m?s ?Gratis!
Desc?rgalo ya desde http://messenger.yahoo.es


SDL mailing list
SDL at libsdl.org
http://www.libsdl.org/mailman/listinfo/sdl

I may be completely wrong, but I think that you don’t need an IME window
class to get IME messages, you can use an ‘input context’ context instead
(See Daniel’s code below).

Handling and sending to the user all the IME messages would be quite
complex, specially because they are probably very different on Linux or OSX.

The way Daniel handles them is nice because he just removes the characters
used during composition and sends the new characters. That could be done
inside SDL transparently, the only addition would be a flag to distinguish
between real keystrokes and faked or emulated ones.

Ignacio Casta??o
@Ignacio_Castano

Daniel Vogel wrote:

As I couldn’t find much useful documentation/ examples when I was looking
for a way to add Asian language support to our input code I thought I
should
post what works for us now so other people might have it easier than I did
:slight_smile:

The below snippet should work fine with Japanese, Chinese and Korean
Windows
(even Win98) though YMMV.

BTW, as a little hint - make sure you have your Korean keyboard plugged in
before installing Korean Windows if you don’t speak the language as
otherwise you won’t be able to use the IME key… had to learn that the
hard
way :wink:

Some globals.

INT SupportsIME,
CurrentIMESize;

After creating the window.

HIMC hImc = ImmGetContext( Window->hWnd );
if( !hImc )
{
debugf(TEXT(“Creating IME context.”));
hImc = ImmCreateContext();
if( hImc )
ImmAssociateContext( Window->hWnd, hImc );
else
debugf(TEXT(“OS doesn’t support IME.”));
}
else
ImmReleaseContext( Window->hWnd, hImc );

SupportsIME = hImc != NULL;

Once per frame (could possibly be done by handling some message but I
didn’t> have time to look into this closer and hey, it works ;)).

// IME stuff.
if( SupportsIME )
{
HIMC hImc = ImmGetContext( Window->hWnd );
if( !hImc )
{
debugf(TEXT(“Creating IME context.”));
hImc = ImmCreateContext();
if( hImc )
ImmAssociateContext( Window->hWnd, hImc );
else
SupportsIME = 0;

CurrentIMESize = 0;
}
else
ImmReleaseContext( Window->hWnd, hImc );
}

Below is some code that handles the IME windows message.

case WM_IME_COMPOSITION:
{
// Final composition string.
if( lParam & GCS_RESULTSTR )
{
HIMC hImc = ImmGetContext(Window->hWnd);

if( !hImc )
appErrorf( TEXT(“No IME context”) );

// Get the size of the result string.
INT Size = ImmGetCompositionString( hImc, GCS_RESULTSTR, NULL, 0 );

TCHAR* String = new TCHAR[Size+1];
appMemzero( String, sizeof(TCHAR) * (Size+1) );

// Get the result strings that is generated by IME.
Size = ImmGetCompositionString( hImc, GCS_RESULTSTR, String, Size );
Size /= sizeof( TCHAR );

// Send backspaces.
for( INT i=0; i<CurrentIMESize; i++ )
{
CauseInputEvent( IK_Backspace, IST_Press );
CauseInputEvent( IK_Backspace, IST_Release );
}

// Send key to input system.
for( INT i=0; i<Size; i++ )
{
INT Key = String[i];
if( Key )
Client->Engine->Key( this, IK_Unicode, String[i] );
}

delete [] String;

ImmReleaseContext(Window->hWnd, hImc);

CurrentIMESize = 0;
}
// Composition in progress.
else if( lParam & GCS_COMPSTR )
{
HIMC hImc = ImmGetContext(Window->hWnd);

if( !hImc )
appErrorf( TEXT(“No IME context”) );

// Get the size of the result string.
INT Size = ImmGetCompositionString( hImc, GCS_COMPSTR, NULL, 0 );

TCHAR* String = new TCHAR[Size+1];
appMemzero( String, sizeof(TCHAR) * (Size+1) );

// Get the result strings that is generated by IME.
Size = ImmGetCompositionString( hImc, GCS_COMPSTR, String, Size );
Size /= sizeof( TCHAR );

// Send backspaces.
for( INT i=0; i<CurrentIMESize; i++ )
{
CauseInputEvent( IK_Backspace, IST_Press );
CauseInputEvent( IK_Backspace, IST_Release );
}

// Send key to input system
for( INT i=0; i<Size; i++ )
{
INT Key = String[i];
if( Key )
Client->Engine->Key( this, IK_Unicode, String[i] );
}

delete [] String;

ImmReleaseContext(Window->hWnd, hImc);

CurrentIMESize = Size;
}
else
return DefWindowProcX( Window->hWnd, iMessage, wParam, lParam );

return 0;
}

You only want IME stuff for chatting and not for controlling the player so
here’s some code that toggles it on the fly.

InParamter: bool Enable

if( SupportsIME )
{
if( !Enable )
{
ImmAssociateContext( Window->hWnd, NULL );
CurrentIMESize = 0;
}
else
{
HIMC hImc = ImmGetContext( Window->hWnd );
if( !hImc )
{
debugf(TEXT(“Creating IME context.”));
hImc = ImmCreateContext();
if( hImc )
ImmAssociateContext( Window->hWnd, hImc );
else
SupportsIME = 0;

CurrentIMESize = 0;
}
else
{
ImmAssociateContext( Window->hWnd, hImc );
ImmReleaseContext( Window->hWnd, hImc );
}
}
}

Hope this of help to someone out there :slight_smile:

– Daniel, Epic Games Inc.


Yahoo! Messenger
Nueva versi?n: Webcam, voz, y mucho m?s ?Gratis!
Desc?rgalo ya desde http://messenger.yahoo.es