/* AbiSource Program Utilities * Copyright (c) 2002 Jordi Mas i Hernāndez - jmas@softcatala.org * (c) 1998-2000 AbiSource, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include "ut_assert.h" #include "ut_types.h" #include "ut_debugmsg.h" #include "ut_Win32OS.h" #include "ev_Keyboard.h" #include "ev_NamedVirtualKey.h" #include "ev_Win32Keyboard.h" #include "ev_EditMethod.h" #include "ev_EditBinding.h" #include "ev_EditEventMapper.h" #include "xap_EncodingManager.h" /* What are we doing here Originally Abiword folks tried to handle the keyboard their "own way", that included not calling TranslateMessage() and trying to interpret the keyboard thru WM_KEYDOWN ignoring most WM_CHAR. This caused serval problems, including ALT+XXX not working and other keyboard related issues. What we do now is: - Keep this code as simple as possible - We always call TranslateMessage() and we let Windows do that job, as all the applications do. - We process all the WM_CHAR messages except the ones that were Abiword command , and we pass those to Windows - We process only the special keys thru WM_KEYDOWN message. Jordi 10/11/2002 */ #define _WIN32KEY_DEBUG 1 // I really hate doing this, but this constant is only defined with _WIN32_WINNT >= // 0x0501, i.e., winXP, and I do want to enable the lot #ifndef WM_UNICHAR #define WM_UNICHAR 0x109 #endif static EV_EditBits s_mapVirtualKeyCodeToNVK(WPARAM nVirtKey); /*****************************************************************/ /*****************************************************************/ ev_Win32Keyboard::ev_Win32Keyboard(EV_EditEventMapper * pEEM) : EV_Keyboard(pEEM), m_hKeyboardLayout(0), m_iconv(UT_ICONV_INVALID), m_bIsUnicodeInput(false), m_bWasAnAbiCommand(false) { HINSTANCE hInstUser = LoadLibrary("USER32.DLL"); if (hInstUser) { m_pToUnicodeEx = reinterpret_cast (GetProcAddress(hInstUser, "ToUnicodeEx")); FreeLibrary(hInstUser); } remapKeyboard(GetKeyboardLayout(0)); } ev_Win32Keyboard::~ev_Win32Keyboard() { if( m_iconv != UT_ICONV_INVALID ) UT_iconv_close( m_iconv ); } void ev_Win32Keyboard::remapKeyboard(HKL hKeyboardLayout) { char szCodePage[16]; if( m_iconv != UT_ICONV_INVALID ) { UT_iconv_close( m_iconv ); m_iconv = UT_ICONV_INVALID; } if( hKeyboardLayout != 0 ) { strcpy( szCodePage, "CP" ); if( GetLocaleInfo( LOWORD( hKeyboardLayout ), LOCALE_IDEFAULTANSICODEPAGE, &szCodePage[2], sizeof( szCodePage ) / sizeof( szCodePage[0] ) - 2 )) { // Unicode locale? // NT-based systems (at least XP) always produce unicode input irrespective of // the ANSI locale -- see WM_CHAR on MSDN and bug 9374 // // (It would be more efficient to do the NT test before calling // GetLocaleInfo(), but for maintanence reasons it is better here.) if( UT_IsWinNT() || !strcmp( szCodePage, "CP0" ) ) { const char *szUCS2Name = XAP_EncodingManager::get_instance()->getNativeUnicodeEncodingName(); UT_ASSERT(szUCS2Name); m_bIsUnicodeInput = true; strcpy( szCodePage, szUCS2Name ); } else m_bIsUnicodeInput = false; UT_DEBUGMSG(("New keyboard codepage: %s\n",szCodePage)); m_iconv = UT_iconv_open( "UCS-4-INTERNAL", szCodePage ); } m_hKeyboardLayout = hKeyboardLayout; } } /* Processes WM_KEYDOWN messages related to special keys */ bool ev_Win32Keyboard::onKeyDown(AV_View * pView, HWND /*hWnd*/, UINT /*iMsg*/, WPARAM nVirtKey, LPARAM keyData) { m_bWasAnAbiCommand = false; EV_EditMethod * pEM; EV_EditModifierState ems = _getModifierState(); EV_EditBits nvk; int charLen; UT_UCSChar charData[2]; UT_DEBUGMSG(("WIN32KEY_DEBUG->onKeyDown %x, %x\n", nVirtKey, keyData)); // ALT key for windows {menus, ... }, ALT+XXX for special chars, etc if (((ems & EV_EMS_ALT) != 0) && ((ems & EV_EMS_CONTROL) == 0)) { #ifdef _WIN32KEY_DEBUG UT_DEBUGMSG(("WIN32KEY_DEBUG->onKeyDown return true (EV_EMS_CONTROL)\n")); #endif return true; } /* This is a Alt+gr combination in an international keyboard */ if (GetKeyState(VK_RMENU) & 0x8000) { #ifdef _WIN32KEY_DEBUG UT_DEBUGMSG(("WIN32KEY_DEBUG->Alt+gr (EV_EMS_CONTROL)\n")); #endif return true; } // Get abiword keyid nvk = s_mapVirtualKeyCodeToNVK(nVirtKey); // If it is not a special key or a CTRL combination, there is nothing to do if (nvk == EV_NVK__IGNORE__ || (nvk == 0 && ((ems & EV_EMS_CONTROL) == 0))) { #ifdef _WIN32KEY_DEBUG UT_DEBUGMSG(("WIN32KEY_DEBUG->onKeyDown return true (IGNORE)\n")); #endif return true; } if (nvk != 0) { // Special key charLen = 0; charData[0] = nvk; } else { // Non-special key with CTRL #if 0 // this causes bug 9618 WCHAR char_value[2]; BYTE keyboardState[256]; ::GetKeyboardState(keyboardState); // Here we pretend the CTRL key is not pressed, otherwise windows will try and convert it // into a control code, this is not what we want keyboardState[VK_CONTROL] &= 0x7F; // mask off high bit if (ToAsciiEx(nVirtKey, keyData & 0x00FF0000, keyboardState, (unsigned short*) &char_value[0], 0, m_hKeyboardLayout)==0) return true; charLen = 1; charData[0] = UT_UCSChar(char_value [0] & 0x000000FF); charData[1] = 0; #else charLen = 1; charData[0] = (UT_UCS4Char)MapVirtualKeyEx(nVirtKey, 2, m_hKeyboardLayout); if(!charData[0]) // no mapping return true; charData[1] = 0; if((ems & EV_EMS_SHIFT) == 0) { // shift not pressed; MapVirtualKeyEx() always returns capital letter, so we // have to lowercase it charData[0] = UT_UCS4_tolower(charData[0]); } #endif } switch (m_pEEM->Keystroke(EV_EKP_PRESS | ems | charData[0], &pEM)) //#define EV_EKP_PRESS ((EV_EditKeyPress) 0x00800000) { case EV_EEMR_BOGUS_START: case EV_EEMR_BOGUS_CONT: case EV_EEMR_INCOMPLETE: // a non-terminal node in state machine return false; case EV_EEMR_COMPLETE: // a terminal node in state machine UT_ASSERT(pEM); invokeKeyboardMethod(pView, pEM, charData, charLen); m_bWasAnAbiCommand = true; return true; default: UT_ASSERT(0); return false; } return true; } bool ev_Win32Keyboard::onIMEChar(AV_View * pView, HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { WCHAR b = wParam; // 2nd byte of MBCS is in high byte of word if (!m_bIsUnicodeInput && (wParam & 0xff00)) b = ((BYTE)(wParam >> 8)) | ((BYTE)wParam << 8); _emitChar(pView,hWnd,iMsg,wParam,lParam,b,0); return true; } void ev_Win32Keyboard::_emitChar(AV_View * pView, HWND /*hWnd*/, UINT iMsg, WPARAM nVirtKey, LPARAM /*keyData*/, UT_uint32 b, EV_EditModifierState ems) { // do the dirty work of pumping this character thru the state machine. UT_UCSChar charData[2]; size_t ret; if( m_iconv != UT_ICONV_INVALID ) { // convert to 8bit string and null terminate size_t len_in, len_out; const char *In = (const char *)&b; char *Out = (char *)&charData; // 2 bytes for Unicode and MBCS // 4 bytes for UNICHAR msg if(iMsg == WM_UNICHAR) len_in = 4; else if(m_bIsUnicodeInput || (nVirtKey & 0xff00)) len_in = 2; else len_in = 1; len_out = sizeof(charData); if ((ret = UT_iconv( m_iconv, &In, &len_in, &Out, &len_out )) == -1) { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } if (Out == (char *)charData) { // m_iconv is waiting for a combination keystroke. Flush the buffer if ((ret = UT_iconv( m_iconv, NULL, &len_in, &Out, &len_out )) == -1) { UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } } } else { charData[0] = b; charData[1] = 0; } EV_EditMethod * pEM; EV_EditEventMapperResult result = m_pEEM->Keystroke(EV_EKP_PRESS|ems|charData[0],&pEM); switch (result) { case EV_EEMR_BOGUS_START: //UT_DEBUGMSG((" Unbound StartChar: %c\n",b)); break; case EV_EEMR_BOGUS_CONT: //UT_DEBUGMSG((" Unbound ContChar: %c\n",b)); break; case EV_EEMR_COMPLETE: UT_ASSERT(pEM); invokeKeyboardMethod(pView,pEM,charData,1); break; case EV_EEMR_INCOMPLETE: //MSG(keyData,((" Non-Terminal-Char: %c\n",b))); break; default: UT_ASSERT(UT_SHOULD_NOT_HAPPEN); break; } return; } /* Processes WM_CHAR messages */ bool ev_Win32Keyboard::onChar(AV_View * pView, HWND hWnd, UINT iMsg, WPARAM nVirtKey, LPARAM keyData) { /* If the key is NOT processed as an Abiword command we follow their path and need to emit the char */ if (m_bWasAnAbiCommand) { #ifdef _WIN32KEY_DEBUG UT_DEBUGMSG(("WIN32KEY_DEBUG->onChar return\n")); #endif // reset the command flag; see bug 8928 m_bWasAnAbiCommand = false; return true; } // Process the key _emitChar(pView,hWnd,iMsg,nVirtKey,keyData,nVirtKey,0); return true; } /* Processes WM_UNICHAR messages The nVirtKey already contains utf-32 char, so we do not need to do any iconv translation */ bool ev_Win32Keyboard::onUniChar(AV_View * pView, HWND /*hWnd*/, UINT /*iMsg*/, WPARAM nVirtKey, LPARAM /*keyData*/) { // as WM_UNICHAR is not proceeded by WM_KEYDOWN message, we need to reset this flag here. m_bWasAnAbiCommand = false; EV_EditModifierState ems = _getModifierState(); EV_EditMethod * pEM; EV_EditEventMapperResult result = m_pEEM->Keystroke(EV_EKP_PRESS|ems|nVirtKey,&pEM); switch (result) { case EV_EEMR_BOGUS_START: //UT_DEBUGMSG((" Unbound StartChar: %c\n",b)); break; case EV_EEMR_BOGUS_CONT: //UT_DEBUGMSG((" Unbound ContChar: %c\n",b)); break; case EV_EEMR_COMPLETE: UT_ASSERT(pEM); invokeKeyboardMethod(pView,pEM,(UT_UCS4Char*)&nVirtKey,1); break; case EV_EEMR_INCOMPLETE: //MSG(keyData,((" Non-Terminal-Char: %c\n",b))); break; default: UT_ASSERT(UT_SHOULD_NOT_HAPPEN); break; } return true; } /*****************************************************************/ /*****************************************************************/ EV_EditBits ev_Win32Keyboard::_getModifierState(void) { EV_EditBits eb = 0; if (GetKeyState(VK_SHIFT) & 0x8000) eb |= EV_EMS_SHIFT; if (GetKeyState(VK_CONTROL) & 0x8000) eb |= EV_EMS_CONTROL; if (GetKeyState(VK_MENU) & 0x8000) eb |= EV_EMS_ALT; return eb; } int ev_Win32Keyboard::_scanCodeToChars(UINT nVirtKey, UINT wScanCode, CONST PBYTE lpKeyState, LPWSTR pwszBuff, int cchBuff) { UT_ASSERT(m_pToUnicodeEx); return (*m_pToUnicodeEx)(nVirtKey,wScanCode,lpKeyState,pwszBuff,cchBuff,0,m_hKeyboardLayout); } /*****************************************************************/ /*****************************************************************/ const static EV_EditBits s_Table_NVK[] = { 0, /* 0x00 */ EV_NVK__IGNORE__, /* VK_LBUTTON 0x01 */ EV_NVK__IGNORE__, /* VK_RBUTTON 0x02 */ EV_NVK__IGNORE__, /* VK_CANCEL 0x03 */ EV_NVK__IGNORE__, /* VK_MBUTTON 0x04 */ 0, /* 0x05 */ 0, /* 0x06 */ 0, /* 0x07 */ EV_NVK_BACKSPACE, /* VK_BACK 0x08 */ EV_NVK_TAB, /* VK_TAB 0x09 */ 0, /* 0x0A */ 0, /* 0x0B */ EV_NVK__IGNORE__, /* VK_CLEAR 0x0C */ EV_NVK_RETURN, /* VK_RETURN 0x0D */ 0, /* 0x0E */ 0, /* 0x0F */ EV_NVK__IGNORE__, /* VK_SHIFT 0x10 */ EV_NVK__IGNORE__, /* VK_CONTROL 0x11 */ EV_NVK__IGNORE__, /* VK_MENU 0x12 */ EV_NVK__IGNORE__, /* VK_PAUSE 0x13 */ EV_NVK__IGNORE__, /* VK_CAPITAL 0x14 */ 0, /* 0x15 */ 0, /* 0x16 */ 0, /* 0x17 */ 0, /* 0x18 */ 0, /* 0x19 */ 0, /* 0x1A */ EV_NVK_ESCAPE, /* VK_ESCAPE 0x1B */ 0, /* 0x1C */ 0, /* 0x1D */ 0, /* 0x1E */ 0, /* 0x1F */ EV_NVK_SPACE, /* VK_SPACE 0x20 */ EV_NVK_PAGEUP, /* VK_PRIOR 0x21 */ EV_NVK_PAGEDOWN, /* VK_NEXT 0x22 */ EV_NVK_END, /* VK_END 0x23 */ EV_NVK_HOME, /* VK_HOME 0x24 */ EV_NVK_LEFT, /* VK_LEFT 0x25 */ EV_NVK_UP, /* VK_UP 0x26 */ EV_NVK_RIGHT, /* VK_RIGHT 0x27 */ EV_NVK_DOWN, /* VK_DOWN 0x28 */ EV_NVK__IGNORE__, /* VK_SELECT 0x29 */ EV_NVK__IGNORE__, /* VK_PRINT 0x2A */ EV_NVK__IGNORE__, /* VK_EXECUTE 0x2B */ EV_NVK__IGNORE__, /* VK_SNAPSHOT 0x2C */ EV_NVK_INSERT, /* VK_INSERT 0x2D */ EV_NVK_DELETE, /* VK_DELETE 0x2E */ EV_NVK_HELP, /* VK_HELP 0x2F */ 0,0,0,0,0,0,0,0,0,0, /* VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */ 0,0,0,0,0,0, /* 0x3a -- 0x3f */ 0, /* 0x40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* VK_A -- */ 0,0,0,0,0,0,0,0,0,0,0, /* -- VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */ EV_NVK__IGNORE__, /* VK_LWIN 0x5B (the left flag button on Win95 keyboards) */ EV_NVK__IGNORE__, /* VK_RWIN 0x5C (the right flag button on Win95 keyboards) */ EV_NVK_MENU_SHORTCUT, /* VK_APPS 0x5D (button with a drop-down-menu on newer Win95 keyboards) */ 0, /* 0x5E */ 0, /* 0x5F */ 0, /* VK_NUMPAD0 0x60 */ 0, /* VK_NUMPAD1 0x61 */ 0, /* VK_NUMPAD2 0x62 */ 0, /* VK_NUMPAD3 0x63 */ 0, /* VK_NUMPAD4 0x64 */ 0, /* VK_NUMPAD5 0x65 */ 0, /* VK_NUMPAD6 0x66 */ 0, /* VK_NUMPAD7 0x67 */ 0, /* VK_NUMPAD8 0x68 */ 0, /* VK_NUMPAD9 0x69 */ 0, /* VK_MULTIPLY 0x6A */ 0, /* VK_ADD 0x6B */ 0, /* VK_SEPARATOR 0x6C */ 0, /* VK_SUBTRACT 0x6D */ 0, /* VK_DECIMAL 0x6E */ 0, /* VK_DIVIDE 0x6F */ EV_NVK_F1, /* VK_F1 0x70 */ EV_NVK_F2, /* VK_F2 0x71 */ EV_NVK_F3, /* VK_F3 0x72 */ EV_NVK_F4, /* VK_F4 0x73 */ EV_NVK_F5, /* VK_F5 0x74 */ EV_NVK_F6, /* VK_F6 0x75 */ EV_NVK_F7, /* VK_F7 0x76 */ EV_NVK_F8, /* VK_F8 0x77 */ EV_NVK_F9, /* VK_F9 0x78 */ EV_NVK_F10, /* VK_F10 0x79 */ EV_NVK_F11, /* VK_F11 0x7A */ EV_NVK_F12, /* VK_F12 0x7B */ EV_NVK_F13, /* VK_F13 0x7C */ EV_NVK_F14, /* VK_F14 0x7D */ EV_NVK_F15, /* VK_F15 0x7E */ EV_NVK_F16, /* VK_F16 0x7F */ EV_NVK_F17, /* VK_F17 0x80 */ EV_NVK_F18, /* VK_F18 0x81 */ EV_NVK_F19, /* VK_F19 0x82 */ EV_NVK_F20, /* VK_F20 0x83 */ EV_NVK_F21, /* VK_F21 0x84 */ EV_NVK_F22, /* VK_F22 0x85 */ EV_NVK_F23, /* VK_F23 0x86 */ EV_NVK_F24, /* VK_F24 0x87 */ 0, /* 0x88 */ 0, /* 0x89 */ 0, /* 0x8A */ 0, /* 0x8B */ 0, /* 0x8C */ 0, /* 0x8D */ 0, /* 0x8E */ 0, /* 0x8F */ EV_NVK__IGNORE__, /* VK_NUMLOCK 0x90 */ EV_NVK__IGNORE__, /* VK_SCROLL 0x91 */ 0, /* 0x92 */ 0, /* 0x93 */ 0, /* 0x94 */ 0, /* 0x95 */ 0, /* 0x96 */ 0, /* 0x97 */ 0, /* 0x98 */ 0, /* 0x99 */ 0, /* 0x9A */ 0, /* 0x9B */ 0, /* 0x9C */ 0, /* 0x9D */ 0, /* 0x9E */ 0, /* 0x9F */ EV_NVK__IGNORE__, /* VK_LSHIFT 0xA0 (not sent) */ EV_NVK__IGNORE__, /* VK_RSHIFT 0xA1 (not sent) */ EV_NVK__IGNORE__, /* VK_LCONTROL 0xA2 (not sent) */ EV_NVK__IGNORE__, /* VK_RCONTROL 0xA3 (not sent) */ EV_NVK__IGNORE__, /* VK_LMENU 0xA4 (not sent) */ EV_NVK__IGNORE__, /* VK_RMENU 0xA5 (not sent) */ 0,0,0,0,0,0,0,0,0,0, /* 0xA6 - 0xAF */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xB0 - 0xBF */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xC0 - 0xCF */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xD0 - 0xDF */ 0,0,0,0,0, /* 0xE0 - 0xE4 */ EV_NVK__IGNORE__, /* VK_PROCESSKEY 0xE5 */ 0,0,0,0,0,0,0,0,0,0, /* 0xE6 - 0xEF */ 0,0,0,0,0,0, /* 0xF0 - 0xF5 */ EV_NVK__IGNORE__, /* VK_ATTN 0xF6 */ EV_NVK__IGNORE__, /* VK_CRSEL 0xF7 */ EV_NVK__IGNORE__, /* VK_EXSEL 0xF8 */ EV_NVK__IGNORE__, /* VK_EREOF 0xF9 */ EV_NVK__IGNORE__, /* VK_PLAY 0xFA */ EV_NVK__IGNORE__, /* VK_ZOOM 0xFB */ EV_NVK__IGNORE__, /* VK_NONAME 0xFC */ EV_NVK__IGNORE__, /* VK_PA1 0xFD */ EV_NVK__IGNORE__, /* VK_OEM_CLEAR 0xFE */ 0 /* 0xFF */ }; static EV_EditBits s_mapVirtualKeyCodeToNVK(WPARAM nVirtKey) { // map the given virtual key into a "named virtual key". // these are referenced by NVK_ symbol so that the cross // platform code can properly refer to them. UT_ASSERT(nVirtKey <= G_N_ELEMENTS(s_Table_NVK)); return s_Table_NVK[nVirtKey]; }