// // Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. // // This file is part of the VNC system. // // The VNC system 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. // // If the source code for the VNC system is not available from the place // whence you received this file, check http://www.uk.research.att.com/vnc or // contact the authors on vnc@uk.research.att.com for information on obtaining // it. // #include "stdhdrs.h" #include "vncviewer.h" #include "KeyMap.h" // ModifierKeyReleaser is a class which helps simplify generating a "fake" release // of modifier key (shift, ctrl, alt, etc.) to server. An instance of the class is // created for every modifier key which may need to be released. // Then either release() may be called to send key release event for KeySym that is // associated with that modifier key. // The destructor of the class automatically send the key press event for the released // KeySym if release() was called during the existance of the instance. class ModifierKeyReleaser { public: ModifierKeyReleaser(ClientConnection* clientCon_, BYTE virtKey, bool extended) : clientCon(clientCon_), extVkey(virtKey + (extended ? 256 : 0)), keysym(0), modKeyName(_T('\0')) { switch (extVkey) { case VK_CONTROL : case VK_LCONTROL : modKeyName = _T("Left Ctrl "); break; case (VK_CONTROL + 256) : case VK_RCONTROL : modKeyName = _T("Right Ctrl "); break; case VK_MENU : case VK_LMENU : modKeyName = _T("Left Alt "); break; case (VK_MENU + 256) : case VK_RMENU : modKeyName = _T("Right Alt "); break; } } void release(CARD32* downKeysym) { if (downKeysym[extVkey] != XK_VoidSymbol) { keysym = downKeysym[extVkey]; vnclog.Print(5, _T(" Fake release %s(%sVirtKey 0x%02x): "), modKeyName, ((extVkey & 256) ? _T("extended ") : _T("")), (extVkey & 255)); clientCon->SendKeyEvent(keysym, false); } } ~ModifierKeyReleaser() { if (keysym) { vnclog.Print(5, _T("Fake press %s(%sVirtKey 0x%02x)\n"), modKeyName, ((extVkey & 256) ? _T("extended ") : _T("")), (extVkey & 255)); clientCon->SendKeyEvent(keysym, true); } } ClientConnection *clientCon; UINT extVkey; CARD32 keysym; TCHAR *modKeyName; }; KeyMap::KeyMap() : storedDeadChar(0), sendDeadKey(true), useUnicodeKeysym(true) { BuildUCS2KS_Map(); } void KeyMap::SetKeyMapOption1(bool sendDeadKeyOpt) { sendDeadKey = sendDeadKeyOpt; if (sendDeadKeyOpt) storedDeadChar = 0; } void KeyMap::SetKeyMapOption2(bool useUnicodeKeysymOpt) { useUnicodeKeysym = useUnicodeKeysymOpt; } void KeyMap::Reset() { vnclog.Print(8, _T("Reset keyboard for first use\n")); storedDeadChar = 0; GetKeyboardState(KBKeysState); FlushDeadKey(KBKeysState); for (int i = 0; i < (sizeof(downKeysym) / sizeof(CARD32)); i++) { downKeysym[i] = XK_VoidSymbol; } for (int ii = 0; ii < (sizeof(downUnicode) / sizeof(WCHAR)); ii++) { downUnicode[ii] = NULL; } } // When losing focus the client should send key release to server for all sent key presses void KeyMap::ReleaseAllKeys(ClientConnection* clientCon) { for (int extVkey = 0; extVkey < (sizeof(downKeysym) / sizeof(CARD32)); extVkey++) { if (downKeysym[extVkey] != XK_VoidSymbol) { vnclog.Print(7, _T("\nReleasing %sVirtKey 0x%02x ->") _T(" Send KeySym 0x%08x (release)\n"), ((extVkey & 256) ? _T("extended ") : _T("")), (extVkey & 255), downKeysym[extVkey]); clientCon->SendKeyEvent(downKeysym[extVkey], false); downKeysym[extVkey] = XK_VoidSymbol; } } } void KeyMap::FlushDeadKey(BYTE* keyState) { // Flush out the stored dead key 'state' for next usage // To flush out the stored dead key 'state' we can not have any Alt key pressed keyState[VK_MENU] = keyState[VK_LMENU] = keyState[VK_RMENU] = 0; int ret = ToUnicode(VK_SPACE, 0, keyState, ucsChar, (sizeof(ucsChar) / sizeof(WCHAR)), 0); vnclog.Print(8, _T("Flush dead key gives: ")); if (ret < 0) { vnclog.Print(8, _T("dead key: 0x%04x (%c)\n"), *ucsChar, *ucsChar); FlushDeadKey(keyState); } else { for (int i=0; i < ret; i++) { vnclog.Print(8, _T("%d character(s): 0x%04x (%c)\n"), ret, *(ucsChar+i), *(ucsChar+i)); } } } void KeyMap::StoreModifier(BYTE* modifierState, BYTE* keyState) { if (keyState[VK_SHIFT] & 0x80) *modifierState |= 1; if (keyState[VK_CONTROL] & 0x80) *modifierState |= 2; if (keyState[VK_MENU] & 0x80) *modifierState |= 4; } void KeyMap::SetModifier(BYTE modifierState, BYTE* keyState) { if (modifierState & 1) { if ((keyState[VK_SHIFT] & 0x80) == 0) { keyState[VK_SHIFT] = keyState[VK_LSHIFT] = 0x80; keyState[VK_RSHIFT] = 0; vnclog.Print(8, _T("[Emulating Shift press] ")); } } else { if ((keyState[VK_SHIFT] & 0x80) != 0) { keyState[VK_SHIFT] = keyState[VK_LSHIFT] = keyState[VK_RSHIFT] = 0; vnclog.Print(8, _T("[Emulating Shift release] ")); } } if (modifierState & 2) { if ((keyState[VK_CONTROL] & 0x80) == 0) { keyState[VK_CONTROL] = keyState[VK_LCONTROL] = 0x80; keyState[VK_RCONTROL] = 0; vnclog.Print(8, _T("[Emulating Control press] ")); } } else { if ((keyState[VK_CONTROL] & 0x80) != 0) { keyState[VK_CONTROL] = keyState[VK_LCONTROL] = keyState[VK_RCONTROL] = 0; vnclog.Print(8, _T("[Emulating Control release] ")); } } if (modifierState & 4) { if ((keyState[VK_MENU] & 0x80) == 0) { keyState[VK_MENU] = keyState[VK_LMENU] = 0x80; keyState[VK_RMENU] = 0; vnclog.Print(8, _T("[Emulating Alt press] ")); } } else { if ((keyState[VK_MENU] & 0x80) != 0) { keyState[VK_MENU] = keyState[VK_LMENU] = keyState[VK_RMENU] = 0; vnclog.Print(8, _T("[Emulating Alt release] ")); } } } void KeyMap::BuildUCS2KS_Map(void) { int i; CARD32 j; WCHAR ucs; // Building map for KeySym Latin-2, 3, 4, 8 & 9 and UCS Latin Extended-A & B for (i = 0, j = 0x01a1; i < (sizeof(keysym_to_unicode_1a1_1ff) / sizeof(short)); i++, j++) { ksLatinExtMap[j] = ucs = (WCHAR) keysym_to_unicode_1a1_1ff[i]; if (IsUCSLatinExtAB(ucs)) ucsLatinExtABMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } for (i = 0, j = 0x02a1; i < (sizeof(keysym_to_unicode_2a1_2fe) / sizeof(short)); i++, j++) { ksLatinExtMap[j] = ucs = (WCHAR) keysym_to_unicode_1a1_1ff[i]; if (IsUCSLatinExtAB(ucs)) ucsLatinExtABMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } for (i = 0, j = 0x03a2; i < (sizeof(keysym_to_unicode_3a2_3fe) / sizeof(short)); i++, j++) { ksLatinExtMap[j] = ucs = (WCHAR) keysym_to_unicode_1a1_1ff[i]; if (IsUCSLatinExtAB(ucs)) ucsLatinExtABMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } for (i = 0, j = 0x12a1; i < (sizeof(keysym_to_unicode_12a1_12fe) / sizeof(short)); i++, j++) { ksLatinExtMap[j] = ucs = (WCHAR) keysym_to_unicode_1a1_1ff[i]; if (IsUCSLatinExtAB(ucs)) ucsLatinExtABMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } for (i = 0, j = 0x13bc; i < (sizeof(keysym_to_unicode_13bc_13be) / sizeof(short)); i++, j++) { ksLatinExtMap[j] = ucs = (WCHAR) keysym_to_unicode_1a1_1ff[i]; if (IsUCSLatinExtAB(ucs)) ucsLatinExtABMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Katakana and UCS Katakana, CJK Symbols and Punctuation for (i = 0, j = 0x04a1; i < (sizeof(keysym_to_unicode_4a1_4df) / sizeof(short)); i++, j++) { ksKatakanaMap[j] = ucs = (WCHAR) keysym_to_unicode_4a1_4df[i]; if (IsUCSKatakana(ucs)) ucsKatakanaMap[ucs] = j; else if (IsUCSCJKSym_Punc(ucs)) ucsCJKSym_PuncMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Arabic and UCS Arabic for (i = 0, j = 0x0590; i < (sizeof(keysym_to_unicode_590_5fe) / sizeof(short)); i++, j++) { ksArabicMap[j] = ucs = (WCHAR) keysym_to_unicode_590_5fe[i]; if (IsUCSArabic(ucs)) ucsArabicMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Cyrillic and UCS Cyrillic for (i = 0, j = 0x0680; i < (sizeof(keysym_to_unicode_680_6ff) / sizeof(short)); i++, j++) { ksCyrillicMap[j] = ucs = (WCHAR) keysym_to_unicode_680_6ff[i]; if (IsUCSCyrillic(ucs)) ucsCyrillicMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Greek and UCS Greek and Coptic for (i = 0, j = 0x07a1; i < (sizeof(keysym_to_unicode_7a1_7f9) / sizeof(short)); i++, j++) { ksGreekMap[j] = ucs = (WCHAR) keysym_to_unicode_7a1_7f9[i]; if (IsUCSGreek_Coptic(ucs)) ucsGreek_CopticMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Technical and UCS Others for (i = 0, j = 0x08a4; i < (sizeof(keysym_to_unicode_8a4_8fe) / sizeof(short)); i++, j++) { ksTechnicalMap[j] = ucs = (WCHAR) keysym_to_unicode_8a4_8fe[i]; if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Special and UCS Others for (i = 0, j = 0x09df; i < (sizeof(keysym_to_unicode_9df_9f8) / sizeof(short)); i++, j++) { ksSpecialMap[j] = ucs = (WCHAR) keysym_to_unicode_aa1_afe[i]; if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Publishing and UCS Others for (i = 0, j = 0x0aa1; i < (sizeof(keysym_to_unicode_aa1_afe) / sizeof(short)); i++, j++) { ksPublishingMap[j] = ucs = (WCHAR) keysym_to_unicode_aa1_afe[i]; if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Hebrew and UCS Hebrew, General Punctuation for (i = 0, j = 0x0cdf; i < (sizeof(keysym_to_unicode_cdf_cfa) / sizeof(short)); i++, j++) { ksHebrewMap[j] = ucs = (WCHAR) keysym_to_unicode_cdf_cfa[i]; if (IsUCSHebrew(ucs)) ucsHebrewMap[ucs] = j; else if (IsUCSGenPunc(ucs)) ucsGenPuncMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Thai and UCS Thai for (i = 0, j = 0x0da1; i < (sizeof(keysym_to_unicode_da1_df9) / sizeof(short)); i++, j++) { ksThaiMap[j] = ucs = (WCHAR) keysym_to_unicode_da1_df9[i]; if (IsUCSThai(ucs)) ucsThaiMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Korean and UCS Hangul Jamo for (i = 0, j = 0x0ea0; i < (sizeof(keysym_to_unicode_ea0_eff) / sizeof(short)); i++, j++) { ksKoreanMap[j] = ucs = (WCHAR) keysym_to_unicode_ea0_eff[i]; if (IsUCSHangulJamo(ucs)) ucsHangulJamoMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Armenian and UCS Armenian, General Punctuation for (i = 0, j = 0x14a1; i < (sizeof(keysym_to_unicode_14a1_14ff) / sizeof(short)); i++, j++) { ksArmenianMap[j] = ucs = (WCHAR) keysym_to_unicode_14a1_14ff[i]; if (IsUCSArmenian(ucs)) ucsArmenianMap[ucs] = j; else if (IsUCSGenPunc(ucs)) ucsGenPuncMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Georgian and UCS Georgian for (i = 0, j = 0x15d0; i < (sizeof(keysym_to_unicode_15d0_15f6) / sizeof(short)); i++, j++) { ksGeorgianMap[j] = ucs = (WCHAR) keysym_to_unicode_15d0_15f6[i]; if (IsUCSGeorgian(ucs)) ucsGeorgianMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Azeri and UCS Others for (i = 0, j = 0x16a0; i < (sizeof(keysym_to_unicode_16a0_16f6) / sizeof(short)); i++, j++) { ksAzeriMap[j] = ucs = (WCHAR) keysym_to_unicode_16a0_16f6[i]; if (IsUCSLatinExtAdd(ucs)) ucsLatinExtAddMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Vietnamese and UCS Latin Extended Additional, Latin Extended-A & B for (i = 0, j = 0x1e9f; i < (sizeof(keysym_to_unicode_1e9f_1eff) / sizeof(short)); i++, j++) { ksVietnameseMap[j] = ucs = (WCHAR) keysym_to_unicode_1e9f_1eff[i]; if (IsUCSLatinExtAdd(ucs)) ucsLatinExtAddMap[ucs] = j; else if (IsUCSLatinExtAB(ucs)) ucsLatinExtABMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } // Building map for KeySym Currency and UCS Currency Symbols for (i = 0, j = 0x20a0; i < (sizeof(keysym_to_unicode_20a0_20ac) / sizeof(short)); i++, j++) { ksCurrencyMap[j] = ucs = (WCHAR) keysym_to_unicode_20a0_20ac[i]; if (IsUCSCurrSym(ucs)) ucsCurrSymMap[ucs] = j; else if (ucs && (ucsOthersMap.find(ucs) != ucsOthersMap.end())) ucsOthersMap[ucs] = j; } } CARD32 KeyMap::UCS2X(WCHAR UnicodeChar) { CARD32 XChar = XK_VoidSymbol; bool isUnicodeKeysym = false; if (IsUCSPrintableLatin1(UnicodeChar)) { // If the key is printable Latin-1 character // Latin-1 in UCS and X KeySym are exactly the same code vnclog.Print(8, _T(" 0x%04x (Latin-1 '%c'): "), UnicodeChar, UnicodeChar); XChar = (CARD32) (UnicodeChar & 0xffff); } else if (UnicodeChar < 255) { // Do not send non printable Latin-1 character vnclog.Print(8, _T(" 0x%04x (Non printable Latin-1): "), UnicodeChar); } else if (IsUCSLatinExtAB(UnicodeChar)) { // Latin Extended-A or B vnclog.Print(8, _T(" 0x%04x (Latin Extended-A or B '%c'): "), UnicodeChar, UnicodeChar); if (ucsLatinExtABMap.find(UnicodeChar) != ucsLatinExtABMap.end()) { XChar = ucsLatinExtABMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsLatinExtABMap\n")); } } else if (IsUCSGreek_Coptic(UnicodeChar)) { // Greek and Coptic vnclog.Print(8, _T(" 0x%04x (Greek and Coptic '%c'): "), UnicodeChar, UnicodeChar); if (ucsGreek_CopticMap.find(UnicodeChar) != ucsGreek_CopticMap.end()) { XChar = ucsGreek_CopticMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsGreek_CopticMap\n")); } } else if (IsUCSCyrillic(UnicodeChar)) { // Cyrillic vnclog.Print(8, _T(" 0x%04x (Cyrillic '%c'): "), UnicodeChar, UnicodeChar); if (ucsCyrillicMap.find(UnicodeChar) != ucsCyrillicMap.end()) { XChar = ucsCyrillicMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsCyrillicMap\n")); } } else if (IsUCSArmenian(UnicodeChar)) { // Armenian vnclog.Print(8, _T(" 0x%04x (Armenian '%c'): "), UnicodeChar, UnicodeChar); if (ucsArmenianMap.find(UnicodeChar) != ucsArmenianMap.end()) { XChar = ucsArmenianMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsCyrillicMap\n")); } } else if (IsUCSHebrew(UnicodeChar)) { // Hebrew vnclog.Print(8, _T(" 0x%04x (Hebrew '%c'): "), UnicodeChar, UnicodeChar); if (ucsHebrewMap.find(UnicodeChar) != ucsHebrewMap.end()) { XChar = ucsHebrewMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsHebrewMap\n")); } } else if (IsUCSArabic(UnicodeChar)) { // Arabic vnclog.Print(8, _T(" 0x%04x (Arabic '%c'): "), UnicodeChar, UnicodeChar); if (ucsArabicMap.find(UnicodeChar) != ucsArabicMap.end()) { XChar = ucsArabicMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsArabicMap\n")); } } else if (IsUCSThai(UnicodeChar)) { // Thai vnclog.Print(8, _T(" 0x%04x (Thai '%c'): "), UnicodeChar, UnicodeChar); if (ucsThaiMap.find(UnicodeChar) != ucsThaiMap.end()) { XChar = ucsThaiMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsThaiMap\n")); } } else if (IsUCSGeorgian(UnicodeChar)) { // Georgian vnclog.Print(8, _T(" 0x%04x (Georgian '%c'): "), UnicodeChar, UnicodeChar); if (ucsGeorgianMap.find(UnicodeChar) != ucsGeorgianMap.end()) { XChar = ucsGeorgianMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsGeorgianMap\n")); } } else if (IsUCSHangulJamo(UnicodeChar)) { // Hangul Jamo vnclog.Print(8, _T(" 0x%04x (Hangul Jamo '%c'): "), UnicodeChar, UnicodeChar); if (ucsHangulJamoMap.find(UnicodeChar) != ucsHangulJamoMap.end()) { XChar = ucsHangulJamoMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsHangulJamoMap\n")); } } else if (IsUCSLatinExtAdd(UnicodeChar)) { // Latin Extended Additional vnclog.Print(8, _T(" 0x%04x (Latin Extended Additional '%c'): "), UnicodeChar, UnicodeChar); if (ucsLatinExtAddMap.find(UnicodeChar) != ucsLatinExtAddMap.end()) { XChar = ucsLatinExtAddMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsLatinExtAddMap\n")); } } else if (IsUCSGenPunc(UnicodeChar)) { // General Punctuation vnclog.Print(8, _T(" 0x%04x (General Punctuation '%c'): "), UnicodeChar, UnicodeChar); if (ucsGenPuncMap.find(UnicodeChar) != ucsGenPuncMap.end()) { XChar = ucsGenPuncMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsGenPuncMap\n")); } } else if (IsUCSCurrSym(UnicodeChar)) { // Currency Symbols vnclog.Print(8, _T(" 0x%04x (Currency Symbols '%c'): "), UnicodeChar); if (ucsCurrSymMap.find(UnicodeChar) != ucsCurrSymMap.end()) { XChar = ucsCurrSymMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsCurrSymMap\n")); } } else if (IsUCSCJKSym_Punc(UnicodeChar)) { // CJK Symbols and Punctuation vnclog.Print(8, _T(" 0x%04x (CJK Symbols and Punctuation '%c'): "), UnicodeChar, UnicodeChar); if (ucsCJKSym_PuncMap.find(UnicodeChar) != ucsCJKSym_PuncMap.end()) { XChar = ucsCJKSym_PuncMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsCJKSym_PuncMap\n")); } } else if (IsUCSKatakana(UnicodeChar)) { // Katakana vnclog.Print(8, _T(" 0x%04x (Katakana '%c'): "), UnicodeChar, UnicodeChar); if (ucsKatakanaMap.find(UnicodeChar) != ucsKatakanaMap.end()) { XChar = ucsKatakanaMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" The character is missing in ucsKatakanaMap\n")); } } else if (ucsOthersMap.find(UnicodeChar) != ucsOthersMap.end()) { // Other Unicode Characters vnclog.Print(8, _T(" 0x%04x (Other Unicode Characters '%c'): "), UnicodeChar, UnicodeChar); XChar = ucsOthersMap[UnicodeChar]; } else if (useUnicodeKeysym) { isUnicodeKeysym = true; } else { vnclog.Print(8, _T(" 0x%04x (Not supported yet)\n"), UnicodeChar); } if (isUnicodeKeysym) { vnclog.Print(8, _T(" 0x%04x (Other Unicode Characters '%c'): "), UnicodeChar, UnicodeChar); // Send the undefined X KeySym by using X protocol convention for // defining new KeySym (Unicode KeySym): // KeySym = ((UCS & 0xffffff) | 0x01000000) // Since Windows only support UCS-16 so we can have this formula: XChar = (((CARD32) UnicodeChar) & 0xffff) | 0x01000000; } return XChar; } bool reset=false; void KeyMap::PCtoX(BYTE virtKey, DWORD keyData, ClientConnection* clientCon) { bool down = ((keyData & 0x80000000) == 0); bool extended = ((keyData & 0x1000000) != 0); bool repeated = ((keyData & 0xc0000000) == 0x40000000); UINT extVkey = virtKey + (extended ? 256 : 0); vnclog.Print(8, _T("\nPCtoX: %svirtKey 0x%02x%s%s, keyData 0x%08x\n"), (extended ? _T("extended ") : _T("")), virtKey, (repeated ? _T(" repeated") : _T("")), (down ? _T(" down") : _T(" up")), keyData); // If this is a key release then just send the associated sent KeySym when // this key was pressed if (!down) { vnclog.Print(8, _T("Release the associated KeySym when this VirtKey was pressed\n")); if (downUnicode[extVkey]) { vnclog.Print(8, _T(" 0x%04x (%c): "), downUnicode[extVkey], downUnicode[extVkey]); downUnicode[extVkey] = NULL; } else { vnclog.Print(8, _T(" Control character: ")); } releaseKey(clientCon, extVkey); vnclog.Print(8, _T("\n")); GetKeyboardState(KBKeysState); if (!((KBKeysState[VK_MENU] & 0x80) && (KBKeysState[VK_CONTROL] & 0x80))) { if (storedDeadChar && reset) { reset=false; keybd_event(VK_SPACE, 0, 0, 0); keybd_event(VK_SPACE, 0, KEYEVENTF_KEYUP, 0); } } return; } // We try to look it up in our key table // Look up the desired code in the keyMap table try to find the exact match according to // the extended flag, then try the opposite of the extended flag CARD32 foundXCode = XK_VoidSymbol; bool exactMatched = false; vnclog.Print(8, _T("Looking in key table ")); for (UINT i = 0; i < (sizeof(keyMap) / sizeof(vncKeyMapping_t)); i++) { if (keyMap[i].WinCode == virtKey) { foundXCode = keyMap[i].XCode; if (extended == keyMap[i].extVK) { exactMatched = true; break; } } } if (foundXCode != XK_VoidSymbol) { vnclog.Print(8, _T("-> keyMap gives (from %s extended flag) KeySym %u (0x%08x)\n"), (exactMatched ? _T("matched") : _T("opposite")), foundXCode, foundXCode); pressKey(clientCon, extVkey, foundXCode); vnclog.Print(8, _T("\n")); return; } else { vnclog.Print(8, _T("-> not in special keyMap\n")); } // Under CE, we're not so concerned about this bit because we handle a WM_CHAR message later #ifndef UNDER_CE GetKeyboardState(KBKeysState); ModifierKeyReleaser lctrl(clientCon, VK_CONTROL, 0); ModifierKeyReleaser lalt(clientCon, VK_MENU, 0); ModifierKeyReleaser ralt(clientCon, VK_MENU, 1); if ((KBKeysState[VK_MENU] & 0x80) && (KBKeysState[VK_CONTROL] & 0x80)) { // This is a Ctrl-Alt (AltGr) key on international keyboards (= LCtrl-RAlt) // Ex. Ctrl-Alt-Q gives '@' on German keyboards vnclog.Print(8, _T("Ctrl-Alt pressed:\n")); // We must release Control and Alt (AltGr) if they were both pressed, so the character // is seen without them by the VNC server // We don't release the Right Control; this allows German users // to use it for doing Ctrl-AltGr-x, e.g. Ctl-@, etc lctrl.release(downKeysym); lalt.release(downKeysym); ralt.release(downKeysym); } else { // This is not a Ctrl-Alt (AltGr) key vnclog.Print(8, _T("Ctrl-Alt not pressed, fake release any Ctrl key\n")); // There are no KeySym corresponding to control characters, e.g. Ctrl-F // The server has already known whether the Ctrl key is pressed from the previouse key event // So we are interested in the key that would be there if the Ctrl key were not pressed KBKeysState[VK_CONTROL] = KBKeysState[VK_LCONTROL] = KBKeysState[VK_RCONTROL] = 0; } int ret; if (storedDeadChar) { SHORT virtDeadKey; BYTE prevModifierState = 0; vnclog.Print(8, _T("[Storing base character modifier(s)]\n")); StoreModifier(&prevModifierState, KBKeysState); virtDeadKey = VkKeyScanW(storedDeadChar); vnclog.Print(8, _T("[A dead key was stored, restoring the dead key state:") _T(" 0x%02x (%c) using virtDeadKey 0x%02x] "), storedDeadChar, storedDeadChar, virtDeadKey); SetModifier(HIBYTE(virtDeadKey), KBKeysState); vnclog.Print(8, _T("\n")); ToUnicode((virtDeadKey & 0xff), 0, KBKeysState, ucsChar, (sizeof(ucsChar) / sizeof(WCHAR)), 0); vnclog.Print(8, _T("[Restoring base character modifier(s)] ")); SetModifier(prevModifierState, KBKeysState); vnclog.Print(8, _T("\n")); storedDeadChar = 0; ret = ToUnicode(virtKey, 0, KBKeysState, ucsChar, (sizeof(ucsChar) / sizeof(WCHAR)), 0); } else ret = ToUnicode(virtKey, 0, KBKeysState, ucsChar, (sizeof(ucsChar) / sizeof(WCHAR)), 0); if (ret < 0 || ret==2) { // It is a dead key vnclog.Print(8, _T("ToUnicode returns dead key: 0x%02x (%c) "), *ucsChar, *ucsChar); if (sendDeadKey) { // We try to look it up in our dead key table // Look up the desired code in the deadKeyMap table foundXCode = XK_VoidSymbol; for (UINT i = 0; i < (sizeof(deadKeyMap) / sizeof(vncDeadKeyMapping_t)); i++) { if (deadKeyMap[i].deadKeyChar == *ucsChar) { foundXCode = deadKeyMap[i].XCode; break; } } if (foundXCode != XK_VoidSymbol) { vnclog.Print(8, _T("-> deadKeyMap gives KeySym %u (0x%08x)\n"), foundXCode, foundXCode); pressKey(clientCon, extVkey, foundXCode); } else { vnclog.Print(8, _T("-> not in deadKeyMap\n")); } } else { storedDeadChar = *ucsChar; reset=true; vnclog.Print(8, _T("-> Store the dead key state, wait for next key-stroke\n")); } FlushDeadKey(KBKeysState); } else if (ret > 0) { vnclog.Print(8, _T("ToUnicode returns %d character(s):\n"), ret); for (int i = 0; i < ret; i++) { CARD32 xChar = UCS2X(*(ucsChar+i)); if (xChar != XK_VoidSymbol) { downUnicode[extVkey] = *(ucsChar+i); pressKey(clientCon, extVkey, xChar); } } } else { vnclog.Print(8, _T("No character is generated by this key event\n")); } #endif vnclog.Print(8, _T("\n")); }; // This will send key release event to server if the VK key was previously pressed. // The same KeySym that was sent when this VK key was pressed will be sent. void KeyMap::releaseKey(ClientConnection* clientCon, UINT extVkey) { if (downKeysym[extVkey] != XK_VoidSymbol) { vnclog.Print(4, _T("Send KeySym 0x%08x (release)\n"), downKeysym[extVkey]); clientCon->SendKeyEvent(downKeysym[extVkey], false); downKeysym[extVkey] = XK_VoidSymbol; } else { vnclog.Print(4, _T("No associated KeySym\n")); } } // This will send key press event to server if the VK key was not previously pressed. // The sent KeySym will be recorded so in can be retrieved later when this VK key is // released. // However it is possible that the VK key has been pressed by other KeySym, then // we need to release the old KeySym first. // One example is in a repeated key press event (auto repeat), the modifiers state // may differ from the previous key press event. If this is the case then it may // generate different character(s). Since the server may have different keyboard // layout, this character(s) may be mapped to different KeyCap (ScanCode). // Other example is if a single key press generates several characterss void KeyMap::pressKey(ClientConnection* clientCon, UINT extVkey, CARD32 keysym) { if (downKeysym[extVkey] != XK_VoidSymbol) { vnclog.Print(4, _T("The %sVirtKey 0x%02x has been pressed -> Release previous") _T(" associated KeySym 0x%08x\n"), ((extVkey & 256) ? _T("extended ") : _T("")), (extVkey & 255), downKeysym[extVkey]); clientCon->SendKeyEvent(downKeysym[extVkey], false); downKeysym[extVkey] = XK_VoidSymbol; vnclog.Print(4, _T(" New KeySym: ")); } vnclog.Print(4, _T("Send KeySym 0x%08x (press)\n"), keysym); clientCon->SendKeyEvent(keysym, true); downKeysym[extVkey] = keysym; }