DirectInput to CEGUI utf32

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Revision as of 15:06, 26 February 2011 by Capek (Talk | contribs) (Robot: Cosmetic changes)

Jump to: navigation, search

This function converts DirectInput's scan codes into a utf32 character value compatible with the injectChar() function. It converts both simple characters as well as characters composed of a dead key as well as a single character, simplifying multi-lingual support. The value of zero is returned if a character could not be converted.

Some characters are composed by two keystrokes; a dead key followed by a simple key. An example with the French (Canada) keyboard is the letter è, which is composed of the dead key (diacritic) ALT-[ followed by the letter e. In order to correctly generate the letter è the dead key must be converted from a standalone diacritic to a combining diacritic. The unicode character set specifies the combining diacritics in the range of 0x300 to 0x36F. This current implementation only supports 5 diacritic, making the following code compatible with both English and French languages. The list of combining diacritics can be obtained from http://www.fileformat.info/info/unicode/block/combining_diacritical_marks/images.htm

By default CEGUI only loads a portion of the character set specified in a font. The following code loads the entire Latin character set:

CEGUI::Font* guiFont = CEGUI::FontManager::getSingletonPtr()->createFont("Arial", "c:/windows/fonts/arial.ttf", 16, CEGUI::Default);
guiFont->defineFontGlyphs(0x0020, 0x0323); // Latin character set
mGUISystem->setDefaultFont(guiFont);

The Character Map (charmap.exe) tool is very useful when trying to figure out the range of characters needed.


CEGUI::utf32 keycodeToUTF32( unsigned int scanCode)
{
	CEGUI::utf32 utf = 0;

	BYTE keyboardState[256];
	unsigned char ucBuffer[3];
	static WCHAR deadKey = '\0';

	// Retrieve the keyboard layout in order to perform the necessary convertions
	HKL hklKeyboardLayout = GetKeyboardLayout(0); // 0 means current thread 
	// This seemingly cannot fail 
	// If this value is cached then the application must respond to WM_INPUTLANGCHANGE 

	// Retrieve the keyboard state
	// Handles CAPS-lock and SHIFT states
	if (GetKeyboardState(keyboardState) == FALSE)
		return utf;

	/* 0. Convert virtual-key code into a scan code
       1. Convert scan code into a virtual-key code
	      Does not distinguish between left- and right-hand keys.
       2. Convert virtual-key code into an unshifted character value
	      in the low order word of the return value. Dead keys (diacritics)
		  are indicated by setting the top bit of the return value.
       3. Windows NT/2000/XP: Convert scan code into a virtual-key
	      Distinguishes between left- and right-hand keys.*/
	UINT virtualKey = MapVirtualKeyEx(scanCode, 3, hklKeyboardLayout);
	if (virtualKey == 0) // No translation possible
		return utf;

    /* Parameter 5:
		0. No menu is active
		1. A menu is active
       Return values:
		Negative. Returned a dead key
		0. No translation available
		1. A translation exists 
		2. Dead-key could not be combined with character 	*/
	int ascii = ToAsciiEx(virtualKey, scanCode, keyboardState, (LPWORD) ucBuffer, 0, hklKeyboardLayout);
	if(deadKey != '\0' && ascii == 1)
	{
		// A dead key is stored and we have just converted a character key
		// Combine the two into a single character
		WCHAR wcBuffer[3];
		WCHAR out[3];
		wcBuffer[0] = ucBuffer[0];
		wcBuffer[1] = deadKey;
		wcBuffer[2] = '\0';
		if( FoldStringW(MAP_PRECOMPOSED, (LPWSTR) wcBuffer, 3, (LPWSTR) out, 3) )
			utf = out[0];
		else
		{
			// FoldStringW failed
			DWORD dw = GetLastError();
			switch(dw)
			{
			case ERROR_INSUFFICIENT_BUFFER:
			case ERROR_INVALID_FLAGS:
			case ERROR_INVALID_PARAMETER:
				break;
			}
		}
		deadKey = '\0';
	}
	else if (ascii == 1)
	{
		// We have a single character
		utf = ucBuffer[0];
		deadKey = '\0';
	}
	else
	{
		// Convert a non-combining diacritical mark into a combining diacritical mark
		switch(ucBuffer[0])
		{
		case 0x5E: // Circumflex accent: â
			deadKey = 0x302;
			break;
		case 0x60: // Grave accent: à
			deadKey = 0x300;
			break;
		case 0xA8: // Diaeresis: ü
			deadKey = 0x308;
			break;
		case 0xB4: // Acute accent: é
			deadKey = 0x301;
			break;
		case 0xB8: // Cedilla: ç
			deadKey = 0x327;
			break;
		default:
			deadKey = ucBuffer[0];
		}
	}

	return utf;
}