Game Development Community

Use WM_CHAR to support other natural language(Chinese) input

by Yanzheng · in Technical Issues · 09/09/2007 (4:20 am) · 13 replies

TGEA support unicode,but inputting other natural languages(except english) is difficult.Now i will show you a easy way to use the WM_CHAR message to support language input,for example chinese.

first,we should understand one thing,that when macro UNICODE is difined,the WM_CHAR will return the combination char of the Languages, decided by Locales, and Keyboard Layouts,not the keycode user pressed.

The WM_CHAR message uses Unicode transformation format (UTF)-16.

The TranslateMessage function generates a WM_CHAR message when the user presses any of the following keys:

Any character key
BACKSPACE
ENTER (carriage return)
ESC
SHIFT+ENTER (linefeed)
TAB


about WM_CHAR,it is enough.So let us see the code can support input.

first i add a new event type ,which is used to point when the message to be processed,in event.h,i add
/// Input device types
enum InputDeviceTypes
{
   UnknownDeviceType,
   MouseDeviceType,
   KeyboardDeviceType,
   JoystickDeviceType,
#ifdef TORQUE_OS_WIN32          // XInput support for Windows (Xbox 360 Controllers) -- jason_cahill
   XInputDeviceType,
#endif
   ImmCharInput,				//imm char input
};
the enum ImmCharInput is used to point a wm_char message is to be dealed with.

second:
i change some code in the winwindows.cpp to support transfer the WM_CHAR

1,Add a variable to store the language input state
bool  chineseInput = false;  //global variable when chinese is inputing

#1
09/09/2007 (4:23 am)
2,Add a function to transfer WM_CHAR :
//--------------------------------------
static void processWmCharMessage(UINT message, WPARAM wParam, LPARAM lParam)
{
		switch(wParam)
		{
		case 0x08:			//backspace
		case 0x0A:			//linefeed
		case 0x09:			//escape
		case 0x1B:			//table
		case 0x0D:			//enter
		  			return;    //because the torque support the keydown process,so we don't transfer it to the wm_char process function
		break;
		}

		InputEvent event;
		
		event.deviceInst = 0;
		event.deviceType = ImmCharInput;
		event.objType = 0;
		event.objInst = 0;
		event.action =  0;
		event.modifier = 0;
		event.ascii = wParam;
		event.fValue = 0.0;

		Game->postEvent(event);
}

now ,everything is ok,what we will do is changing code in the GuiTextEditCtrl.cpp to support chinese input:

1,add code :
extern  bool  chineseInput;    //support the state of the input
2,add wm_char process function
bool GuiTextEditCtrl::onWmChar(const GuiEvent &event)
{
   if(! isActive())
      return false;

	S32 stringLen = mTextBuffer.length();
 
 
   if ( mFont->isValidChar( event.ascii ) )
   {
      // Get the character ready to add to a UTF8 string.
      UTF16 conv[2] = { event.ascii, 0 };
      StringBuffer convertedChar(conv);

      //see if it's a number field
      if ( mProfile->mNumbersOnly )
      {
         if (event.ascii == '-')
         {
            //a minus sign only exists at the beginning, and only a single minus sign
            if ( mCursorPos != 0 )
            {
               playDeniedSound();
               return true;
            }

            if ( mInsertOn && ( mTextBuffer.getChar(0) == '-' ) ) 
            {
               playDeniedSound();
               return true;
            }
         }
         // BJTODO: This is probably not unicode safe.
         else if ( event.ascii < '0' || event.ascii > '9' )
         {
            playDeniedSound();
            return true;
         }
      }

      //save the current state
      saveUndoState();

      //delete anything highlighted
      if ( mBlockEnd > 0 )
      {
         mTextBuffer.cut(mBlockStart, mBlockEnd-mBlockStart);
         mCursorPos  = mBlockStart;
         mBlockStart = 0;
         mBlockEnd   = 0;
      }

      if ( ( mInsertOn && ( stringLen < mMaxStrLen ) ) ||
          ( !mInsertOn && ( mCursorPos < mMaxStrLen ) ) )
      {
         if ( mCursorPos == stringLen )
         {
            mTextBuffer.append(convertedChar);
            mCursorPos++;
         }
         else
         {
            if ( mInsertOn )
            {
               mTextBuffer.insert(mCursorPos, convertedChar);
               mCursorPos++;
            }
            else
            {
               mTextBuffer.cut(mCursorPos, 1);
               mTextBuffer.insert(mCursorPos, convertedChar);
               mCursorPos++;
            }
         }
      }
      else
         playDeniedSound();

      //reset the history index
      mHistoryDirty = true;

      //execute the console command if it exists
      execConsoleCallback();

      return true;
   }

   //not handled - pass the event to it's parent
 
   return Parent::onWmChar( event );
}
#2
09/09/2007 (4:23 am)
3,change code in the onkeydown function to avoid deal with some unwanted keydown message

in GuiTextEditCtrl::onKeyDown(const GuiEvent &event) ,we add some code :

//-------------new code----------------//
	if(chineseInput)
		return true;
//--------------end---------------------//
   if ( mFont->isValidChar( event.ascii ) )

oh,sorry i forget a very import message to control the language input state .

back to the winwindows.h,add some code in void OurDispatchMessages() function:
in the last of the function,i add some code:
switch(message)
	  {
		case  WM_INPUTLANGCHANGE:
			{
				switch( LOWORD(lParam))
				{
					case LID_TRADITIONAL_CHINESE:	
					case LID_JAPANESE:				
					case LID_KOREAN:				
					case LID_SIMPLIFIED_CHINESE:	
						chineseInput = true;
						break;
					default:
						chineseInput = false;
						break;
				};
			}
			break;

		case WM_CHAR:
			{
				if(chineseInput)
				{
					processWmCharMessage(message, wParam, lParam);
				}
			}
			break;
	  }
now,every thing is ok,input your chinese now,of course,In theory korean,japanese can work too.but i didn't test them.
#3
09/09/2007 (4:24 am)
I lost language id,which is defined by windows:
// Define language ID
#define LID_TRADITIONAL_CHINESE	0x0404
#define LID_JAPANESE			0x0411
#define LID_KOREAN				0x0412
#define LID_SIMPLIFIED_CHINESE	0x0804
#4
09/10/2007 (1:12 am)
Of course,you should also define onWmChar(const GuiEvent &event) in GuiControl your self
#5
09/10/2007 (6:10 am)
In GuiCanvas.cpp,we add
else if(event->deviceType == ImmCharInput)
   {
         if (mFirstResponder)
         {
			mLastEvent.ascii = event->ascii;
			mLastEvent.modifier = event->modifier;
			mLastEvent.keyCode = event->objInst;
            if(mFirstResponder->onWmChar(mLastEvent))
               return true;
         }
   }
in the end of the function processInputEvent function
#6
11/09/2008 (11:02 pm)
作者辛苦了
#7
11/10/2008 (12:36 am)
What's that
#8
11/15/2008 (5:36 am)
It's Chinese, thank you for your contribution. I thought your post should be in Chinese instead of English, because it is really for Chinese developers.
#9
02/16/2009 (5:57 pm)
Hi!Yanzheng, I'm a Chinese developer,so i want to input in chinese and have something to resoved.Thank for your contribution,But i don't understand about "Game->postEvent(event)".
#10
02/16/2009 (6:03 pm)
Game->postEvent(event)?
you should read more code of the engine.it is the basic Mechanism of torque.if you don't post the event,the loop will not process the event,and you will not get the result.
#11
02/16/2009 (11:12 pm)
I'm very glad to see your reply. And thank you for replying my question.
I know how to input chinese in TGE. I'm having some difficulty understanding how to input chinese in TGEA.I expect you give me suggestion. Very ths
#12
02/18/2009 (1:14 am)
HI Yanzheng,I have knew how to input chinese in TGEA, and resoved it.
It is the basic of IME Windows
#13
02/18/2009 (1:18 am)
congratulation!