| 1 | /*
|
|---|
| 2 | * synergy -- mouse and keyboard sharing utility
|
|---|
| 3 | * Copyright (C) 2003 Chris Schoeneman
|
|---|
| 4 | * Copyright (C) 2006 Knut St. Osmundsen
|
|---|
| 5 | *
|
|---|
| 6 | * This package is free software; you can redistribute it and/or
|
|---|
| 7 | * modify it under the terms of the GNU General Public License
|
|---|
| 8 | * found in the file COPYING that should have accompanied this file.
|
|---|
| 9 | *
|
|---|
| 10 | * This package is distributed in the hope that it will be useful,
|
|---|
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 13 | * GNU General Public License for more details.
|
|---|
| 14 | */
|
|---|
| 15 |
|
|---|
| 16 | #include "CPMKeyState.h"
|
|---|
| 17 | #include "CThread.h"
|
|---|
| 18 | #include "CFunctionJob.h"
|
|---|
| 19 | #include "CLog.h"
|
|---|
| 20 | #include "CStringUtil.h"
|
|---|
| 21 | #include "CPMUtil.h"
|
|---|
| 22 | #include "IEventQueue.h"
|
|---|
| 23 | #include "TMethodEventJob.h"
|
|---|
| 24 |
|
|---|
| 25 | //
|
|---|
| 26 | // CPMKeyState
|
|---|
| 27 | //
|
|---|
| 28 |
|
|---|
| 29 | // map virtual keys to synergy key enumeration
|
|---|
| 30 | const CPMKeyState::VirtualKey CPMKeyState::s_virtualKey[0x42] =
|
|---|
| 31 | {
|
|---|
| 32 | { /* 0x000 */ kKeyNone, 0, 0, 0 }, // reserved
|
|---|
| 33 | { /* 0x001 */ kKeyNone, 0, 0, 0 }, // VK_BUTTON1
|
|---|
| 34 | { /* 0x002 */ kKeyNone, 0, 0, 0 }, // VK_BUTTON2
|
|---|
| 35 | { /* 0x003 */ kKeyNone, 0, 0, 0 }, // VK_BUTTON3
|
|---|
| 36 | { /* 0x004 */ kKeyBreak, 0, 0, 0 }, // VK_BREAK
|
|---|
| 37 | { /* 0x005 */ kKeyBackSpace, 8, 0, 0 }, // VK_BACKSPACE
|
|---|
| 38 | { /* 0x006 */ kKeyTab, 9, 0, 0 }, // VK_TAB
|
|---|
| 39 | { /* 0x007 */ kKeyLeftTab, 9, 0, 1 }, // VK_BACKTAB
|
|---|
| 40 | { /* 0x008 */ kKeyReturn, 0x0d, 0, 0 }, // VK_NEWLINE
|
|---|
| 41 | { /* 0x009 */ kKeyShift_L, 0, 0, 0 }, // VK_SHIFT
|
|---|
| 42 | { /* 0x00a */ kKeyControl_L, 0, 0, 0 }, // VK_CTRL
|
|---|
| 43 | { /* 0x00b */ kKeyAlt_L, 0, 0, 0 }, // VK_ALT
|
|---|
| 44 | { /* 0x00c */ kKeyAltGr, 0, 0, 0 }, // VK_ALTGRAF
|
|---|
| 45 | { /* 0x00d */ kKeyPause, 0, 0, 0 }, // VK_PAUSE
|
|---|
| 46 | { /* 0x00e */ kKeyCapsLock, 0, 0, 0 }, // VK_CAPSLOCK
|
|---|
| 47 | { /* 0x00f */ kKeyEscape, 0x1b, 0, 0 }, // VK_ESC
|
|---|
| 48 | { /* 0x010 */ ' ', 0x20, 0, 0 }, // VK_SPACE
|
|---|
| 49 | { /* 0x011 */ kKeyPageUp, 0xe0, 1, -1 }, // VK_PAGEUP
|
|---|
| 50 | { /* 0x012 */ kKeyPageDown, 0xe0, 1, -1 }, // VK_PAGEDOWN
|
|---|
| 51 | { /* 0x013 */ kKeyEnd, 0xe0, 1, -1 }, // VK_END
|
|---|
| 52 | { /* 0x014 */ kKeyHome, 0xe0, 1, -1 }, // VK_HOME
|
|---|
| 53 | { /* 0x015 */ kKeyLeft, 0xe0, 1, -1 }, // VK_LEFT
|
|---|
| 54 | { /* 0x016 */ kKeyUp, 0xe0, 1, -1 }, // VK_UP
|
|---|
| 55 | { /* 0x017 */ kKeyRight, 0xe0, 1, -1 }, // VK_RIGHT
|
|---|
| 56 | { /* 0x018 */ kKeyDown, 0xe0, 1, -1 }, // VK_DOWN
|
|---|
| 57 | { /* 0x019 */ kKeyNone, 0xe0, 0, 0 }, // VK_PRINTSCRN
|
|---|
| 58 | { /* 0x01a */ kKeyInsert, 0xe0, 1, 1 }, // VK_INSERT
|
|---|
| 59 | { /* 0x01b */ kKeyDelete, 0xe0, 1, 1 }, // VK_DELETE
|
|---|
| 60 | { /* 0x01c */ kKeyScrollLock, 0, 0, 0 }, // VK_SCRLLOCK
|
|---|
| 61 | { /* 0x01d */ kKeyNumLock, 0, 0, 0 }, // VK_NUMLOCK
|
|---|
| 62 | { /* 0x01e */ kKeyKP_Enter, 0x0d, 1, 1 }, // VK_ENTER
|
|---|
| 63 | { /* 0x01f */ kKeySysReq, 0, 0, 0 }, // VK_SYSRQ
|
|---|
| 64 | { /* 0x020 */ kKeyF1, 0, 0, 1 }, // VK_F1
|
|---|
| 65 | { /* 0x021 */ kKeyF2, 0, 0, 1 }, // VK_F2
|
|---|
| 66 | { /* 0x022 */ kKeyF3, 0, 0, 1 }, // VK_F3
|
|---|
| 67 | { /* 0x023 */ kKeyF4, 0, 0, 1 }, // VK_F4
|
|---|
| 68 | { /* 0x024 */ kKeyF5, 0, 0, 1 }, // VK_F5
|
|---|
| 69 | { /* 0x025 */ kKeyF6, 0, 0, 1 }, // VK_F6
|
|---|
| 70 | { /* 0x026 */ kKeyF7, 0, 0, 1 }, // VK_F7
|
|---|
| 71 | { /* 0x027 */ kKeyF8, 0, 0, 1 }, // VK_F8
|
|---|
| 72 | { /* 0x028 */ kKeyF9, 0, 0, 1 }, // VK_F9
|
|---|
| 73 | { /* 0x029 */ kKeyF10, 0, 0, 1 }, // VK_F10
|
|---|
| 74 | { /* 0x02a */ kKeyF11, 0, 0, 1 }, // VK_F11
|
|---|
| 75 | { /* 0x02b */ kKeyF12, 0, 0, 1 }, // VK_F12
|
|---|
| 76 | { /* 0x02c */ kKeyF13, 0, 0, 1 }, // VK_F13
|
|---|
| 77 | { /* 0x02d */ kKeyF14, 0, 0, 1 }, // VK_F14
|
|---|
| 78 | { /* 0x02e */ kKeyF15, 0, 0, 1 }, // VK_F15
|
|---|
| 79 | { /* 0x02f */ kKeyF16, 0, 0, 1 }, // VK_F16
|
|---|
| 80 | { /* 0x030 */ kKeyF17, 0, 0, 1 }, // VK_F17
|
|---|
| 81 | { /* 0x031 */ kKeyF18, 0, 0, 1 }, // VK_F18
|
|---|
| 82 | { /* 0x032 */ kKeyF19, 0, 0, 1 }, // VK_F19
|
|---|
| 83 | { /* 0x033 */ kKeyF20, 0, 0, 1 }, // VK_F20
|
|---|
| 84 | { /* 0x034 */ kKeyF21, 0, 0, 1 }, // VK_F21
|
|---|
| 85 | { /* 0x035 */ kKeyF22, 0, 0, 1 }, // VK_F22
|
|---|
| 86 | { /* 0x036 */ kKeyF23, 0, 0, 1 }, // VK_F23
|
|---|
| 87 | { /* 0x037 */ kKeyF24, 0, 0, 1 }, // VK_F24
|
|---|
| 88 | { /* 0x038 */ kKeyNone, 0, 0, 0 }, // VK_ENDDRAG
|
|---|
| 89 | { /* 0x039 */ kKeyNone, 0, 0, 0 }, // VK_CLEAR
|
|---|
| 90 | { /* 0x03a */ kKeyNone, 0, 0, 0 }, // VK_EREOF
|
|---|
| 91 | { /* 0x03b */ kKeyNone, 0, 0, 0 }, // VK_PA1
|
|---|
| 92 | { /* 0x03c */ kKeyNone, 0, 0, 0 }, // VK_ATTN
|
|---|
| 93 | { /* 0x03d */ kKeyNone, 0, 0, 0 }, // VK_CRSEL
|
|---|
| 94 | { /* 0x03e */ kKeyNone, 0, 0, 0 }, // VK_EXSEL
|
|---|
| 95 | { /* 0x03f */ kKeyNone, 0, 0, 0 }, // VK_COPY
|
|---|
| 96 | { /* 0x040 */ kKeyNone, 0, 0, 0 }, // VK_BLK1
|
|---|
| 97 | { /* 0x041 */ kKeyNone, 0, 0, 0 }, // VK_BLK2
|
|---|
| 98 | };
|
|---|
| 99 |
|
|---|
| 100 | struct CWin32Modifiers {
|
|---|
| 101 | public:
|
|---|
| 102 | ULONG m_vk;
|
|---|
| 103 | KeyModifierMask m_mask;
|
|---|
| 104 | };
|
|---|
| 105 |
|
|---|
| 106 | static const CWin32Modifiers s_modifiers[] =
|
|---|
| 107 | {
|
|---|
| 108 | { VK_SHIFT, KeyModifierShift },
|
|---|
| 109 | { VK_CTRL, KeyModifierControl },
|
|---|
| 110 | { VK_ALT, KeyModifierAlt },
|
|---|
| 111 | { VK_ALTGRAF, KeyModifierAltGr },
|
|---|
| 112 | };
|
|---|
| 113 |
|
|---|
| 114 | CPMKeyState::CPMKeyState(void* eventTarget, FakeMsgFunc fakeMsg) :
|
|---|
| 115 | m_eventTarget(eventTarget),
|
|---|
| 116 | m_fakeMsg(fakeMsg),
|
|---|
| 117 | m_lastButton(0),
|
|---|
| 118 | m_fixTimer(NULL),
|
|---|
| 119 | m_lastDown(kKeyNone),
|
|---|
| 120 | m_useSavedModifiers(false),
|
|---|
| 121 | m_savedModifiers(0),
|
|---|
| 122 | m_originalSavedModifiers(0)
|
|---|
| 123 | {
|
|---|
| 124 | LOG((CLOG_DEBUG "CPMKeyState:"));
|
|---|
| 125 | }
|
|---|
| 126 |
|
|---|
| 127 | CPMKeyState::~CPMKeyState()
|
|---|
| 128 | {
|
|---|
| 129 | LOG((CLOG_DEBUG "~CPMKeyState:"));
|
|---|
| 130 | disable();
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | void
|
|---|
| 134 | CPMKeyState::disable()
|
|---|
| 135 | {
|
|---|
| 136 | LOG((CLOG_DEBUG "disable:"));
|
|---|
| 137 | if (m_fixTimer != NULL) {
|
|---|
| 138 | EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer);
|
|---|
| 139 | EVENTQUEUE->deleteTimer(m_fixTimer);
|
|---|
| 140 | m_fixTimer = NULL;
|
|---|
| 141 | }
|
|---|
| 142 | m_lastDown = kKeyNone;
|
|---|
| 143 | }
|
|---|
| 144 |
|
|---|
| 145 | KeyButton
|
|---|
| 146 | CPMKeyState::virtualKeyToButton(ULONG virtualKey) const
|
|---|
| 147 | {
|
|---|
| 148 | LOG((CLOG_DEBUG "virtualKeyToButton:"));
|
|---|
| 149 | return m_virtualKeyToButton[virtualKey & 0xffu];
|
|---|
| 150 | }
|
|---|
| 151 |
|
|---|
| 152 | bool
|
|---|
| 153 | CPMKeyState::testAutoRepeat(bool press, bool isRepeat, KeyButton button)
|
|---|
| 154 | {
|
|---|
| 155 | LOG((CLOG_DEBUG "testAutoRepeat:"));
|
|---|
| 156 | if (!isRepeat)
|
|---|
| 157 | isRepeat = press && button == m_lastDown && button != kKeyNone;
|
|---|
| 158 | m_lastDown = press ? button : kKeyNone;
|
|---|
| 159 | return isRepeat;
|
|---|
| 160 | }
|
|---|
| 161 |
|
|---|
| 162 | void
|
|---|
| 163 | CPMKeyState::saveModifiers()
|
|---|
| 164 | {
|
|---|
| 165 | LOG((CLOG_DEBUG "saveModifiers:"));
|
|---|
| 166 | m_savedModifiers = getActiveModifiers();
|
|---|
| 167 | m_originalSavedModifiers = m_savedModifiers;
|
|---|
| 168 | }
|
|---|
| 169 |
|
|---|
| 170 | void
|
|---|
| 171 | CPMKeyState::useSavedModifiers(bool enable)
|
|---|
| 172 | {
|
|---|
| 173 | LOG((CLOG_DEBUG "useSavedModifiers:"));
|
|---|
| 174 | if (enable != m_useSavedModifiers) {
|
|---|
| 175 | m_useSavedModifiers = enable;
|
|---|
| 176 | if (!m_useSavedModifiers) {
|
|---|
| 177 | // transfer any modifier state changes to CKeyState's state
|
|---|
| 178 | KeyModifierMask mask = m_originalSavedModifiers ^ m_savedModifiers;
|
|---|
| 179 | getActiveModifiersRValue() = (getActiveModifiers() & ~mask) | (m_savedModifiers & mask);
|
|---|
| 180 | }
|
|---|
| 181 | }
|
|---|
| 182 | }
|
|---|
| 183 |
|
|---|
| 184 | KeyID
|
|---|
| 185 | CPMKeyState::mapKeyFromEvent(USHORT fsFlags, UCHAR ucRepeat, UCHAR ucScanCode, USHORT usch, USHORT usvk, KeyModifierMask* maskOut) const
|
|---|
| 186 | {
|
|---|
| 187 | LOG((CLOG_DEBUG "mapKeyFromEvent:"));
|
|---|
| 188 | #if 0 /** @todo */
|
|---|
| 189 | static const KeyModifierMask s_controlAlt = KeyModifierControl | KeyModifierAlt;
|
|---|
| 190 |
|
|---|
| 191 | // extract character, virtual key, and if we didn't use AltGr
|
|---|
| 192 | char c = (char)((charAndVirtKey & 0xff00u) >> 8);
|
|---|
| 193 | ULONG vkCode = (charAndVirtKey & 0xffu);
|
|---|
| 194 | bool noAltGr = ((charAndVirtKey & 0xff0000u) != 0);
|
|---|
| 195 |
|
|---|
| 196 | // handle some keys via table lookup
|
|---|
| 197 | KeyID id = getKeyID(vkCode, (KeyButton)((info >> 16) & 0x1ffu));
|
|---|
| 198 |
|
|---|
| 199 | // check if not in table; map character to key id
|
|---|
| 200 | if (id == kKeyNone && c != 0) {
|
|---|
| 201 | if ((c & 0x80u) == 0) {
|
|---|
| 202 | // ASCII
|
|---|
| 203 | id = static_cast<KeyID>(c) & 0xffu;
|
|---|
| 204 | }
|
|---|
| 205 | else {
|
|---|
| 206 | // character is not really ASCII. instead it's some
|
|---|
| 207 | // character in the current ANSI code page. try to
|
|---|
| 208 | // convert that to a Unicode character. if we fail
|
|---|
| 209 | // then use the single byte character as is.
|
|---|
| 210 | char src = c;
|
|---|
| 211 | wchar_t unicode;
|
|---|
| 212 | if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED,
|
|---|
| 213 | &src, 1, &unicode, 1) > 0) {
|
|---|
| 214 | id = static_cast<KeyID>(unicode);
|
|---|
| 215 | }
|
|---|
| 216 | else {
|
|---|
| 217 | id = static_cast<KeyID>(c) & 0xffu;
|
|---|
| 218 | }
|
|---|
| 219 | }
|
|---|
| 220 | }
|
|---|
| 221 |
|
|---|
| 222 | // set modifier mask
|
|---|
| 223 | if (maskOut != NULL) {
|
|---|
| 224 | KeyModifierMask active = getActiveModifiers();
|
|---|
| 225 | if (!noAltGr && (active & s_controlAlt) == s_controlAlt) {
|
|---|
| 226 | // if !noAltGr then we're only interested in matching the
|
|---|
| 227 | // key, not the AltGr. AltGr is down (i.e. control and alt
|
|---|
| 228 | // are down) but we don't want the client to have to match
|
|---|
| 229 | // that so we clear it.
|
|---|
| 230 | active &= ~s_controlAlt;
|
|---|
| 231 | }
|
|---|
| 232 | *maskOut = active;
|
|---|
| 233 | }
|
|---|
| 234 | return id;
|
|---|
| 235 | #else
|
|---|
| 236 | return kKeyNone;
|
|---|
| 237 | #endif
|
|---|
| 238 | }
|
|---|
| 239 |
|
|---|
| 240 | ULONG
|
|---|
| 241 | CPMKeyState::mapKeyToVirtualKey(KeyID key) const
|
|---|
| 242 | {
|
|---|
| 243 | LOG((CLOG_DEBUG "mapKeyToVirtualKey:"));
|
|---|
| 244 | if (key == kKeyNone)
|
|---|
| 245 | return 0;
|
|---|
| 246 | KeyToVKMap::const_iterator i = m_keyToVKMap.find(key);
|
|---|
| 247 | if (i == m_keyToVKMap.end())
|
|---|
| 248 | return 0;
|
|---|
| 249 | return i->second;
|
|---|
| 250 | }
|
|---|
| 251 |
|
|---|
| 252 | void
|
|---|
| 253 | CPMKeyState::sendKeyEvent(void* target,
|
|---|
| 254 | bool press, bool isAutoRepeat,
|
|---|
| 255 | KeyID key, KeyModifierMask mask,
|
|---|
| 256 | SInt32 count, KeyButton button)
|
|---|
| 257 | {
|
|---|
| 258 | LOG((CLOG_DEBUG "sendKeyEvent:"));
|
|---|
| 259 | if (press || isAutoRepeat) {
|
|---|
| 260 | // send key
|
|---|
| 261 | if (press && !isAutoRepeat) {
|
|---|
| 262 | CKeyState::sendKeyEvent(target, true, false, key, mask, 1, button);
|
|---|
| 263 | if (count > 0) {
|
|---|
| 264 | --count;
|
|---|
| 265 | }
|
|---|
| 266 | }
|
|---|
| 267 | if (count >= 1) {
|
|---|
| 268 | CKeyState::sendKeyEvent(target, true, true, key, mask, count, button);
|
|---|
| 269 | }
|
|---|
| 270 | }
|
|---|
| 271 | else {
|
|---|
| 272 | // do key up
|
|---|
| 273 | CKeyState::sendKeyEvent(target, false, false, key, mask, 1, button);
|
|---|
| 274 | }
|
|---|
| 275 | }
|
|---|
| 276 |
|
|---|
| 277 | void
|
|---|
| 278 | CPMKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton button)
|
|---|
| 279 | {
|
|---|
| 280 | LOG((CLOG_DEBUG "fakeKeyDown: id=%#x mask=%#x button=%#x", id, mask, button));
|
|---|
| 281 | CKeyState::fakeKeyDown(id, mask, button);
|
|---|
| 282 | }
|
|---|
| 283 |
|
|---|
| 284 | void
|
|---|
| 285 | CPMKeyState::fakeKeyRepeat(KeyID id, KeyModifierMask mask, SInt32 count, KeyButton button)
|
|---|
| 286 | {
|
|---|
| 287 | LOG((CLOG_DEBUG "fakeKeyRepeat: id=%#x mask=%#x count=%d button=%#x", id, mask, count, button));
|
|---|
| 288 | CKeyState::fakeKeyRepeat(id, mask, count, button);
|
|---|
| 289 | }
|
|---|
| 290 |
|
|---|
| 291 | bool
|
|---|
| 292 | CPMKeyState::fakeCtrlAltDel()
|
|---|
| 293 | {
|
|---|
| 294 | LOG((CLOG_DEBUG "fakeCtrlAltDel:"));
|
|---|
| 295 | fakeKeyDown(kKeyDelete, KeyModifierControl | KeyModifierAlt, virtualKeyToButton(VK_DELETE));
|
|---|
| 296 | }
|
|---|
| 297 |
|
|---|
| 298 | KeyModifierMask
|
|---|
| 299 | CPMKeyState::pollActiveModifiers() const
|
|---|
| 300 | {
|
|---|
| 301 | LOG((CLOG_DEBUG "pollActiveModifiers:"));
|
|---|
| 302 | KeyModifierMask state = 0;
|
|---|
| 303 |
|
|---|
| 304 | // get non-toggle modifiers from our own shadow key state
|
|---|
| 305 | for (size_t i = 0; i < sizeof(s_modifiers) / sizeof(s_modifiers[0]); ++i) {
|
|---|
| 306 | KeyButton button = virtualKeyToButton(s_modifiers[i].m_vk);
|
|---|
| 307 | if (button != 0 && isKeyDown(button)) {
|
|---|
| 308 | state |= s_modifiers[i].m_mask;
|
|---|
| 309 | }
|
|---|
| 310 | }
|
|---|
| 311 |
|
|---|
| 312 | // we can get toggle modifiers from the system
|
|---|
| 313 | if (WinGetKeyState(HWND_DESKTOP, VK_CAPSLOCK) & 1)
|
|---|
| 314 | state |= KeyModifierCapsLock;
|
|---|
| 315 | if (WinGetKeyState(HWND_DESKTOP, VK_NUMLOCK) & 1)
|
|---|
| 316 | state |= KeyModifierNumLock;
|
|---|
| 317 | if (WinGetKeyState(HWND_DESKTOP, VK_SCRLLOCK) & 1)
|
|---|
| 318 | state |= KeyModifierScrollLock;
|
|---|
| 319 | return state;
|
|---|
| 320 | }
|
|---|
| 321 |
|
|---|
| 322 | SInt32
|
|---|
| 323 | CPMKeyState::pollActiveGroup() const
|
|---|
| 324 | {
|
|---|
| 325 | LOG((CLOG_DEBUG "pollActiveGroup:"));
|
|---|
| 326 | #if 0
|
|---|
| 327 | HKL hkl = GetKeyboardLayout(targetThread);
|
|---|
| 328 |
|
|---|
| 329 | // get group
|
|---|
| 330 | GroupMap::const_iterator i = m_groupMap.find(hkl);
|
|---|
| 331 | if (i == m_groupMap.end()) {
|
|---|
| 332 | LOG((CLOG_DEBUG1 "can't find keyboard layout %08x", hkl));
|
|---|
| 333 | return 0;
|
|---|
| 334 | }
|
|---|
| 335 |
|
|---|
| 336 | return i->second;
|
|---|
| 337 | #else
|
|---|
| 338 | return 0;
|
|---|
| 339 | #endif
|
|---|
| 340 | }
|
|---|
| 341 |
|
|---|
| 342 | void
|
|---|
| 343 | CPMKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
|
|---|
| 344 | {
|
|---|
| 345 | LOG((CLOG_DEBUG "pollPressedKeys:"));
|
|---|
| 346 | BYTE keyState[256];
|
|---|
| 347 | if (WinSetKeyboardStateTable(HWND_DESKTOP, keyState, FALSE)) {
|
|---|
| 348 | for (KeyButton i = 1; i < 256; ++i) {
|
|---|
| 349 | if ((keyState[i] & 0x80) != 0) {
|
|---|
| 350 | pressedKeys.insert(i);
|
|---|
| 351 | }
|
|---|
| 352 | }
|
|---|
| 353 | }
|
|---|
| 354 | }
|
|---|
| 355 |
|
|---|
| 356 | static USHORT
|
|---|
| 357 | convertKeyModiferMaskToTCF(const KeyModifierMask mask)
|
|---|
| 358 | {
|
|---|
| 359 | USHORT fShiftState = 0;
|
|---|
| 360 | if (mask & KeyModifierShift) fShiftState |= TCF_SHIFT;
|
|---|
| 361 | if (mask & KeyModifierControl) fShiftState |= TCF_CONTROL;
|
|---|
| 362 | if (mask & KeyModifierAlt) fShiftState |= TCF_ALT;
|
|---|
| 363 | if (mask & KeyModifierAltGr) fShiftState |= TCF_ALTGR;
|
|---|
| 364 | if (mask & KeyModifierCapsLock) fShiftState |= TCF_CAPSLOCK;
|
|---|
| 365 | if (mask & KeyModifierNumLock) fShiftState |= TCF_NUMLOCK;
|
|---|
| 366 | return fShiftState;
|
|---|
| 367 | }
|
|---|
| 368 |
|
|---|
| 369 | static KeyModifierMask
|
|---|
| 370 | convertTCFToKeyModiferMask(const USHORT fShiftState)
|
|---|
| 371 | {
|
|---|
| 372 | KeyModifierMask mask = 0;
|
|---|
| 373 | if (fShiftState & TCF_SHIFT) mask |= KeyModifierShift;
|
|---|
| 374 | if (fShiftState & TCF_CONTROL) mask |= KeyModifierControl;
|
|---|
| 375 | if (fShiftState & TCF_ALT) mask |= KeyModifierAlt;
|
|---|
| 376 | if (fShiftState & TCF_ALTGR) mask |= KeyModifierAltGr;
|
|---|
| 377 | if (fShiftState & TCF_CAPSLOCK) mask |= KeyModifierCapsLock;
|
|---|
| 378 | if (fShiftState & TCF_NUMLOCK) mask |= KeyModifierNumLock;
|
|---|
| 379 | return mask;
|
|---|
| 380 | }
|
|---|
| 381 |
|
|---|
| 382 | static bool
|
|---|
| 383 | isASCIIControlChar(const USHORT usChar)
|
|---|
| 384 | {
|
|---|
| 385 | switch (usChar) {
|
|---|
| 386 | /* Skip the ones with virtual keys. */
|
|---|
| 387 | case 0x08 + 0x40: case 0x08 + 0x60: // backspace
|
|---|
| 388 | case 0x09 + 0x40: case 0x09 + 0x60: // horizontal tab
|
|---|
| 389 | case 0x1b + 0x40: case 0x1b + 0x60: // escape
|
|---|
| 390 | case 0x0d + 0x40: case 0x0d + 0x60: // return
|
|---|
| 391 | return false;
|
|---|
| 392 | default:
|
|---|
| 393 | return (usChar >= 'A' && usChar <= '_')
|
|---|
| 394 | || (usChar >= 'a' && usChar <= 'z');
|
|---|
| 395 | }
|
|---|
| 396 | }
|
|---|
| 397 |
|
|---|
| 398 | void
|
|---|
| 399 | CPMKeyState::convertScancodes(ClientData *pData)
|
|---|
| 400 | {
|
|---|
| 401 | /*
|
|---|
| 402 | * This is a bit tricky / hackish, but what we have to do here is to
|
|---|
| 403 | * deal with extended keys and the keypad stuff. The scancode
|
|---|
| 404 | * translation tables doesn't seem to be 100% correct for these
|
|---|
| 405 | * keys, and these keys are the ones we need them the most. sigh.
|
|---|
| 406 | */
|
|---|
| 407 |
|
|---|
| 408 | //
|
|---|
| 409 | // navigation and insert/delete buttons left of the numpad:
|
|---|
| 410 | // home, up, pgup, left, right, end, down, pgdn, insert, delete
|
|---|
| 411 | //
|
|---|
| 412 | // The pm and translated scancodes are mixed up in these cases.
|
|---|
| 413 | // The keys are all extended and secondary.
|
|---|
| 414 | //
|
|---|
| 415 | if ( pData->s.xlatScan >= 0x60 /* VK_HOME */
|
|---|
| 416 | && pData->s.xlatScan <= 0x69 /* VK_DELETE */
|
|---|
| 417 | && pData->s.virtualKey != 0) {
|
|---|
| 418 | pData->s.scan = pData->s.xlatScan;
|
|---|
| 419 | pData->s.xlatScan = m_pmScanToOemScanExt[pData->s.xlatScan];
|
|---|
| 420 | pData->s.fExtendedKey = true;
|
|---|
| 421 | pData->s.fNeedNumUnlockKey = true;
|
|---|
| 422 | pData->s.fSecondary = true;
|
|---|
| 423 | return;
|
|---|
| 424 | }
|
|---|
| 425 |
|
|---|
| 426 | //
|
|---|
| 427 | // There are some virtual keys we know are extended, mark them so (like the Fxx keys).
|
|---|
| 428 | //
|
|---|
| 429 | if ( pData->s.virtualKey < sizeof(s_virtualKey) / sizeof(s_virtualKey[0])
|
|---|
| 430 | && !pData->s.fExtendedKey) {
|
|---|
| 431 | if ( s_virtualKey[pData->s.virtualKey].extended == 1
|
|---|
| 432 | || ( s_virtualKey[pData->s.virtualKey].extended == -1
|
|---|
| 433 | && ( pData->s.xlatChar == 0
|
|---|
| 434 | || pData->s.xlatChar == s_virtualKey[pData->s.virtualKey].ch))) {
|
|---|
| 435 | pData->s.fExtendedKey = true;
|
|---|
| 436 | }
|
|---|
| 437 | }
|
|---|
| 438 |
|
|---|
| 439 | //
|
|---|
| 440 | // Do the translating.
|
|---|
| 441 | //
|
|---|
| 442 | if (pData->s.xlatScan == pData->s.scan) {
|
|---|
| 443 | pData->s.scan = m_pmScanToOemScan[pData->s.xlatScan];
|
|---|
| 444 | //if (pData->s.fExtendedKey) {
|
|---|
| 445 | // pData->s.scan = m_pmScanToOemScanExt[pData->s.xlatScan];
|
|---|
| 446 | //} else {
|
|---|
| 447 | // pData->s.scan = m_pmScanToOemScan[pData->s.xlatScan];
|
|---|
| 448 | //}
|
|---|
| 449 | }
|
|---|
| 450 | }
|
|---|
| 451 |
|
|---|
| 452 |
|
|---|
| 453 | void
|
|---|
| 454 | CPMKeyState::getKeyMap(CKeyMap& keyMap)
|
|---|
| 455 | {
|
|---|
| 456 | LOG((CLOG_DEBUG "getKeyMap:"));
|
|---|
| 457 | #if 0
|
|---|
| 458 | // update keyboard groups
|
|---|
| 459 | if (getGroups(m_groups)) {
|
|---|
| 460 | m_groupMap.clear();
|
|---|
| 461 | SInt32 numGroups = (SInt32)m_groups.size();
|
|---|
| 462 | for (SInt32 g = 0; g < numGroups; ++g) {
|
|---|
| 463 | m_groupMap[m_groups[g]] = g;
|
|---|
| 464 | }
|
|---|
| 465 | }
|
|---|
| 466 | HKL activeLayout = GetKeyboardLayout(0);
|
|---|
| 467 | #endif
|
|---|
| 468 |
|
|---|
| 469 | // clear table
|
|---|
| 470 | memset(m_virtualKeyToButton, 0, sizeof(m_virtualKeyToButton));
|
|---|
| 471 | m_keyToVKMap.clear();
|
|---|
| 472 |
|
|---|
| 473 | CKeyMap::KeyItem item;
|
|---|
| 474 | for (SInt32 g = 0; g < 1; ++g) {
|
|---|
| 475 | item.m_group = g;
|
|---|
| 476 |
|
|---|
| 477 | //
|
|---|
| 478 | // Fill the scan code conversion tables.
|
|---|
| 479 | //
|
|---|
| 480 | for (unsigned i = 0; i < sizeof(m_pmScanToOemScan) / sizeof(m_pmScanToOemScan[0]); i++) {
|
|---|
| 481 | USHORT us = i;
|
|---|
| 482 | USHORT fShiftState = 0;
|
|---|
| 483 | WinTranslateChar2(0, &us, NULL, TC_SCANTOOEMSCAN, &fShiftState);
|
|---|
| 484 | m_pmScanToOemScan[i] = us;
|
|---|
| 485 | }
|
|---|
| 486 | for (unsigned i = 0; i < sizeof(m_pmScanToOemScanExt) / sizeof(m_pmScanToOemScanExt[0]); i++) {
|
|---|
| 487 | USHORT us = i;
|
|---|
| 488 | USHORT fShiftState = TCF_EXTENDEDKEY;
|
|---|
| 489 | WinTranslateChar2(0, &us, NULL, TC_SCANTOOEMSCAN, &fShiftState);
|
|---|
| 490 | m_pmScanToOemScanExt[i] = us;
|
|---|
| 491 | }
|
|---|
| 492 | for (unsigned i = 0; i < sizeof(m_oemScanToPmScan) / sizeof(m_oemScanToPmScan[0]); i++) {
|
|---|
| 493 | USHORT us = i;
|
|---|
| 494 | USHORT fShiftState = 0;
|
|---|
| 495 | WinTranslateChar2(0, &us, NULL, TC_OEMSCANTOSCAN, &fShiftState);
|
|---|
| 496 | m_oemScanToPmScan[i] = us;
|
|---|
| 497 | }
|
|---|
| 498 | for (unsigned i = 0; i < sizeof(m_oemScanToPmScanExt) / sizeof(m_oemScanToPmScanExt[0]); i++) {
|
|---|
| 499 | USHORT us = i;
|
|---|
| 500 | USHORT fShiftState = TCF_EXTENDEDKEY;
|
|---|
| 501 | WinTranslateChar2(0, &us, NULL, TC_OEMSCANTOSCAN, &fShiftState);
|
|---|
| 502 | m_oemScanToPmScanExt[i] = us;
|
|---|
| 503 | }
|
|---|
| 504 |
|
|---|
| 505 |
|
|---|
| 506 | //
|
|---|
| 507 | // map buttons (scancodes) to virtual keys
|
|---|
| 508 | //
|
|---|
| 509 | memset(m_buttonToVK, 0, sizeof(m_buttonToVK));
|
|---|
| 510 | for (KeyButton i = 1; i < 256; ++i) {
|
|---|
| 511 | USHORT usVirtualKey = i;
|
|---|
| 512 | USHORT fShiftState = 0;
|
|---|
| 513 | WinTranslateChar2(0, &usVirtualKey, NULL, TC_SCANCODETOVIRTUALKEY, &fShiftState);
|
|---|
| 514 | if ( usVirtualKey != 0
|
|---|
| 515 | && m_buttonToVK[i] == 0) {
|
|---|
| 516 | m_buttonToVK[i] = usVirtualKey;
|
|---|
| 517 | }
|
|---|
| 518 | }
|
|---|
| 519 |
|
|---|
| 520 | /// @todo the stuff down to the button loop isn't really done yet.
|
|---|
| 521 |
|
|---|
| 522 | // now map virtual keys to buttons. multiple virtual keys may map
|
|---|
| 523 | // to a single button. if the virtual key matches the one in
|
|---|
| 524 | // m_buttonToVK then we use the button as is. if not then it's
|
|---|
| 525 | // either a numpad key and we use the button as is or it's an
|
|---|
| 526 | // extended button.
|
|---|
| 527 | for (ULONG i = 1; i < 255; ++i) {
|
|---|
| 528 | // skip virtual keys we don't want
|
|---|
| 529 | switch (i) {
|
|---|
| 530 | case VK_BUTTON1:
|
|---|
| 531 | case VK_BUTTON2:
|
|---|
| 532 | case VK_BUTTON3:
|
|---|
| 533 | case VK_MENU:
|
|---|
| 534 | continue;
|
|---|
| 535 | }
|
|---|
| 536 |
|
|---|
| 537 | // get the button
|
|---|
| 538 | USHORT usButton = i;
|
|---|
| 539 | USHORT fShiftState = 0;
|
|---|
| 540 | WinTranslateChar2(0, &usButton, NULL, TC_VIRTUALKEYTOSCANCODE, &fShiftState);
|
|---|
| 541 |
|
|---|
| 542 | // add extended key if virtual keys don't match
|
|---|
| 543 | if ( usButton != 0
|
|---|
| 544 | && m_buttonToVK[usButton] != i) {
|
|---|
| 545 | m_buttonToVK[usButton | 0x100u] = i;
|
|---|
| 546 | }
|
|---|
| 547 | }
|
|---|
| 548 |
|
|---|
| 549 | // set virtual key to button table
|
|---|
| 550 | // if (GetKeyboardLayout(0) == m_groups[g]) {
|
|---|
| 551 | for (KeyButton i = 0; i < 512; ++i) {
|
|---|
| 552 | if (m_buttonToVK[i] != 0) {
|
|---|
| 553 | if (m_virtualKeyToButton[m_buttonToVK[i]] == 0) {
|
|---|
| 554 | m_virtualKeyToButton[m_buttonToVK[i]] = i;
|
|---|
| 555 | }
|
|---|
| 556 | }
|
|---|
| 557 | }
|
|---|
| 558 | // }
|
|---|
| 559 |
|
|---|
| 560 | //
|
|---|
| 561 | // Add the keys to the map.
|
|---|
| 562 | //
|
|---|
| 563 | //for (KeyButton i = 0; i < 256; ++i) {
|
|---|
| 564 | for (KeyButton i = 0; i < 256; ++i) {
|
|---|
| 565 | //
|
|---|
| 566 | // Does this translate to a virtual key or character?
|
|---|
| 567 | //
|
|---|
| 568 | USHORT usChar = i;
|
|---|
| 569 | USHORT fShiftState = 0;
|
|---|
| 570 | USHORT fCharKCFlags = WinTranslateChar2(0, &usChar, NULL, TC_SCANCODETOCHAR, &fShiftState);
|
|---|
| 571 | USHORT usVirtualKey = i;
|
|---|
| 572 | USHORT fVirtualKeyKCFlags = WinTranslateChar2(0, &usVirtualKey, NULL, TC_SCANCODETOVIRTUALKEY, &fShiftState);
|
|---|
| 573 | if (!usChar && !usVirtualKey) {
|
|---|
| 574 | // try with control down.
|
|---|
| 575 | usChar = i;
|
|---|
| 576 | fShiftState = TCF_CONTROL;
|
|---|
| 577 | fCharKCFlags = WinTranslateChar2(0, &usChar, NULL, TC_SCANCODETOCHAR, &fShiftState);
|
|---|
| 578 | usVirtualKey = i;
|
|---|
| 579 | fShiftState = TCF_CONTROL;
|
|---|
| 580 | fVirtualKeyKCFlags = WinTranslateChar2(0, &usVirtualKey, NULL, TC_SCANCODETOVIRTUALKEY, &fShiftState);
|
|---|
| 581 | }
|
|---|
| 582 | if (!usChar && !usVirtualKey) {
|
|---|
| 583 | // try with altgr down.
|
|---|
| 584 | usChar = i;
|
|---|
| 585 | fShiftState = TCF_ALTGR;
|
|---|
| 586 | fCharKCFlags = WinTranslateChar2(0, &usChar, NULL, TC_SCANCODETOCHAR, &fShiftState);
|
|---|
| 587 | usVirtualKey = i;
|
|---|
| 588 | fShiftState = TCF_ALTGR;
|
|---|
| 589 | fVirtualKeyKCFlags = WinTranslateChar2(0, &usVirtualKey, NULL, TC_SCANCODETOVIRTUALKEY, &fShiftState);
|
|---|
| 590 | }
|
|---|
| 591 | if (!usChar && !usVirtualKey) {
|
|---|
| 592 | // try with numlock toggled.
|
|---|
| 593 | usChar = i;
|
|---|
| 594 | fShiftState = TCF_NUMLOCK;
|
|---|
| 595 | fCharKCFlags = WinTranslateChar2(0, &usChar, NULL, TC_SCANCODETOCHAR, &fShiftState);
|
|---|
| 596 | usVirtualKey = i;
|
|---|
| 597 | fShiftState = TCF_NUMLOCK;
|
|---|
| 598 | fVirtualKeyKCFlags = WinTranslateChar2(0, &usVirtualKey, NULL, TC_SCANCODETOVIRTUALKEY, &fShiftState);
|
|---|
| 599 | }
|
|---|
| 600 | if ( usChar
|
|---|
| 601 | || usVirtualKey) {
|
|---|
| 602 | // initialize the item
|
|---|
| 603 | item.m_id = getKeyID(m_buttonToVK[i], i, false, usChar);
|
|---|
| 604 | item.m_button = i;
|
|---|
| 605 | item.m_required = 0;
|
|---|
| 606 | item.m_sensitive = 0;
|
|---|
| 607 | item.m_dead = false;
|
|---|
| 608 | item.m_lock = false;
|
|---|
| 609 |
|
|---|
| 610 | // get flags for modifier keys
|
|---|
| 611 | CKeyMap::initModifierKey(item);
|
|---|
| 612 | if (item.m_generates != 0) {
|
|---|
| 613 | // it's a modifier key.
|
|---|
| 614 | item.m_lock = usVirtualKey == VK_NUMLOCK
|
|---|
| 615 | || usVirtualKey == VK_SCRLLOCK
|
|---|
| 616 | || usVirtualKey == VK_CAPSLOCK;
|
|---|
| 617 | ClientData data;
|
|---|
| 618 | data.u = 0;
|
|---|
| 619 | data.s.scan = i;
|
|---|
| 620 | data.s.xlatScan = i;
|
|---|
| 621 | data.s.fShiftKey = true;
|
|---|
| 622 | data.s.xlatChar = usChar;
|
|---|
| 623 | data.s.virtualKey = usVirtualKey;
|
|---|
| 624 | convertScancodes(&data);
|
|---|
| 625 | item.m_client = data.u;
|
|---|
| 626 | addKeyEntry(keyMap, item);
|
|---|
| 627 | } else {
|
|---|
| 628 | //
|
|---|
| 629 | // Check which keys it might be sensitive to.
|
|---|
| 630 | // We assume (possibly incorrectly) that combinations doesn't matter... :)
|
|---|
| 631 | //
|
|---|
| 632 | static const struct {
|
|---|
| 633 | KeyModifierMask mask;
|
|---|
| 634 | unsigned fShiftState;
|
|---|
| 635 | } modifiers[] = {
|
|---|
| 636 | { KeyModifierShift, TCF_SHIFT},
|
|---|
| 637 | { KeyModifierControl, TCF_CONTROL },
|
|---|
| 638 | { KeyModifierAlt, TCF_ALT },
|
|---|
| 639 | { KeyModifierAltGr, TCF_ALTGR },
|
|---|
| 640 | { KeyModifierCapsLock, TCF_CAPSLOCK },
|
|---|
| 641 | { KeyModifierNumLock, TCF_NUMLOCK }
|
|---|
| 642 | };
|
|---|
| 643 | usChar = i;
|
|---|
| 644 | fShiftState = 0;
|
|---|
| 645 | fCharKCFlags = WinTranslateChar2(0, &usChar, NULL, TC_SCANCODETOCHAR, &fShiftState);
|
|---|
| 646 | usVirtualKey = i;
|
|---|
| 647 | fShiftState = 0;
|
|---|
| 648 | fVirtualKeyKCFlags = WinTranslateChar2(0, &usVirtualKey, NULL, TC_SCANCODETOVIRTUALKEY, &fShiftState);
|
|---|
| 649 | for (unsigned j = 0; j < sizeof(modifiers) / sizeof(modifiers[0]); j++) {
|
|---|
| 650 | USHORT usCh = i;
|
|---|
| 651 | fShiftState = modifiers[j].fShiftState;
|
|---|
| 652 | if ( WinTranslateChar2(0, &usCh, NULL, TC_SCANCODETOCHAR, &fShiftState) != 0
|
|---|
| 653 | && usCh != 0
|
|---|
| 654 | && usCh != usChar) {
|
|---|
| 655 | item.m_sensitive |= modifiers[j].mask;
|
|---|
| 656 | continue;
|
|---|
| 657 | }
|
|---|
| 658 | usCh = i;
|
|---|
| 659 | fShiftState = modifiers[j].fShiftState;
|
|---|
| 660 | WinTranslateChar2(0, &usCh, NULL, TC_SCANCODETOVIRTUALKEY, &fShiftState);
|
|---|
| 661 | if ( usCh != 0
|
|---|
| 662 | && usCh != usVirtualKey) {
|
|---|
| 663 | item.m_sensitive |= modifiers[j].mask;
|
|---|
| 664 | continue;
|
|---|
| 665 | }
|
|---|
| 666 | }
|
|---|
| 667 | if (isASCIIControlChar(usChar)) {
|
|---|
| 668 | item.m_sensitive |= KeyModifierControl;
|
|---|
| 669 | }
|
|---|
| 670 |
|
|---|
| 671 | //
|
|---|
| 672 | // Check for numpad keys. We need to adjust stuff then.
|
|---|
| 673 | // It think this test should be sufficient.
|
|---|
| 674 | //
|
|---|
| 675 | bool numpad = (item.m_sensitive & KeyModifierNumLock) != 0;
|
|---|
| 676 |
|
|---|
| 677 | //
|
|---|
| 678 | // Unoptimized brute force algorithm for determining the
|
|---|
| 679 | // possible combinations and adding them to the key map.
|
|---|
| 680 | //
|
|---|
| 681 | struct
|
|---|
| 682 | {
|
|---|
| 683 | UCHAR uchChar;
|
|---|
| 684 | UCHAR uchVirtualKey;
|
|---|
| 685 | } combinations[1 << TCF_MAX_BITS];
|
|---|
| 686 |
|
|---|
| 687 | for (unsigned j = 0; j < sizeof(combinations) / sizeof(combinations[0]); j++) {
|
|---|
| 688 | const KeyModifierMask mask = convertTCFToKeyModiferMask(j);
|
|---|
| 689 | if ((mask & item.m_sensitive) != mask /*|| j>3*/) {
|
|---|
| 690 | combinations[j].uchChar = combinations[j].uchVirtualKey = 0;
|
|---|
| 691 | continue;
|
|---|
| 692 | }
|
|---|
| 693 | // get any character value
|
|---|
| 694 | USHORT us = i;
|
|---|
| 695 | fShiftState = j;
|
|---|
| 696 | if (WinTranslateChar2(0, &us, NULL, TC_SCANCODETOCHAR, &fShiftState) == 0) {
|
|---|
| 697 | us = 0;
|
|---|
| 698 | }
|
|---|
| 699 | combinations[j].uchChar = us;
|
|---|
| 700 |
|
|---|
| 701 | // get any virtual key value
|
|---|
| 702 | us = i;
|
|---|
| 703 | fShiftState = j;
|
|---|
| 704 | WinTranslateChar2(0, &us, NULL, TC_SCANCODETOVIRTUALKEY, &fShiftState);
|
|---|
| 705 | combinations[j].uchVirtualKey = us;
|
|---|
| 706 |
|
|---|
| 707 | // deal with missing character translations
|
|---|
| 708 | if ( combinations[j].uchChar == 0
|
|---|
| 709 | && us != 0
|
|---|
| 710 | && us < sizeof(s_virtualKey) / sizeof(s_virtualKey[0])) {
|
|---|
| 711 | combinations[j].uchChar = s_virtualKey[us].ch;
|
|---|
| 712 | }
|
|---|
| 713 | }
|
|---|
| 714 |
|
|---|
| 715 | // 2. Eliminate duplicates.
|
|---|
| 716 | for (unsigned j = 0; j < sizeof(combinations) / sizeof(combinations[0]); j++) {
|
|---|
| 717 | unsigned best = j;
|
|---|
| 718 | KeyModifierMask bestMask = convertTCFToKeyModiferMask(j);
|
|---|
| 719 | for (unsigned k = j + 1; k < sizeof(combinations) / sizeof(combinations[0]); k++) {
|
|---|
| 720 | if ( combinations[best].uchChar == combinations[k].uchChar
|
|---|
| 721 | && combinations[best].uchVirtualKey == combinations[k].uchVirtualKey) {
|
|---|
| 722 | const KeyModifierMask mask = convertTCFToKeyModiferMask(k);
|
|---|
| 723 | // drop L/R mixes.
|
|---|
| 724 | if (mask == bestMask) {
|
|---|
| 725 | combinations[k].uchChar = combinations[k].uchVirtualKey = 0;
|
|---|
| 726 | }
|
|---|
| 727 | }
|
|---|
| 728 | }
|
|---|
| 729 | }
|
|---|
| 730 |
|
|---|
| 731 | // 3. Add the remainders.
|
|---|
| 732 | for (unsigned j = 0; j < sizeof(combinations) / sizeof(combinations[0]); j++) {
|
|---|
| 733 | if (combinations[j].uchChar || combinations[j].uchVirtualKey) {
|
|---|
| 734 | USHORT usChar = combinations[j].uchChar;
|
|---|
| 735 | USHORT usVirtualKey = combinations[j].uchVirtualKey;
|
|---|
| 736 |
|
|---|
| 737 | // update the changing item members
|
|---|
| 738 | item.m_required = convertTCFToKeyModiferMask(j);
|
|---|
| 739 | if (usVirtualKey) {
|
|---|
| 740 | switch (usVirtualKey) {
|
|---|
| 741 | case VK_BACKTAB:
|
|---|
| 742 | item.m_required |= KeyModifierShift;
|
|---|
| 743 | item.m_sensitive |= KeyModifierShift;
|
|---|
| 744 | usChar = 0; // this one is translated incorrectly
|
|---|
| 745 | break;
|
|---|
| 746 | }
|
|---|
| 747 | item.m_id = getKeyID(usVirtualKey, i, numpad, usChar);
|
|---|
| 748 | } else {
|
|---|
| 749 | item.m_id = usChar; /// @todo translate to unicode!
|
|---|
| 750 | }
|
|---|
| 751 | ClientData data;
|
|---|
| 752 | data.u = 0;
|
|---|
| 753 | data.s.scan = i;
|
|---|
| 754 | data.s.xlatScan = i;
|
|---|
| 755 | data.s.xlatChar = usChar;
|
|---|
| 756 | data.s.virtualKey = usVirtualKey;
|
|---|
| 757 | convertScancodes(&data);
|
|---|
| 758 | item.m_client = data.u;
|
|---|
| 759 | addKeyEntry(keyMap, item);
|
|---|
| 760 | }
|
|---|
| 761 | }
|
|---|
| 762 |
|
|---|
| 763 | // 4. ASCII Control Character variants. (ARG! this doesn't work, fakeMsg hacks it.)
|
|---|
| 764 | if (isASCIIControlChar(usChar)) {
|
|---|
| 765 | /* add key */
|
|---|
| 766 | item.m_id = usChar - (usChar >= 'a' ? 0x60 : 0x40);
|
|---|
| 767 | item.m_required = KeyModifierControl;
|
|---|
| 768 | ClientData data;
|
|---|
| 769 | data.u = 0;
|
|---|
| 770 | data.s.scan = i;
|
|---|
| 771 | data.s.xlatScan = i;
|
|---|
| 772 | data.s.xlatChar = item.m_id;
|
|---|
| 773 | convertScancodes(&data);
|
|---|
| 774 | item.m_client = data.u;
|
|---|
| 775 | addKeyEntry(keyMap, item);
|
|---|
| 776 | item.m_id = usChar;
|
|---|
| 777 | addKeyEntry(keyMap, item);
|
|---|
| 778 | }
|
|---|
| 779 | }
|
|---|
| 780 | }
|
|---|
| 781 | } // for buttons 0 thru 254
|
|---|
| 782 |
|
|---|
| 783 | //
|
|---|
| 784 | // Various other keys for which we don't expect the above loops to catch.
|
|---|
| 785 | //
|
|---|
| 786 | item.m_id = kKeyMenu;
|
|---|
| 787 | item.m_button = 0xee;
|
|---|
| 788 | item.m_required = 0;
|
|---|
| 789 | item.m_sensitive = 0;
|
|---|
| 790 | item.m_dead = false;
|
|---|
| 791 | item.m_lock = false;
|
|---|
| 792 | ClientData data;
|
|---|
| 793 | data.u = 0;
|
|---|
| 794 | data.s.scan = 0x7c;
|
|---|
| 795 | data.s.xlatScan = 0xee;
|
|---|
| 796 | data.s.fSecondary = true;
|
|---|
| 797 | data.s.fExtendedKey = true;
|
|---|
| 798 | data.s.fNeedNumUnlockKey = true;
|
|---|
| 799 | item.m_client = data.u;
|
|---|
| 800 | CKeyMap::initModifierKey(item);
|
|---|
| 801 | addKeyEntry(keyMap, item);
|
|---|
| 802 |
|
|---|
| 803 | item.m_id = kKeySuper_L;
|
|---|
| 804 | item.m_button = 0xec;
|
|---|
| 805 | data.s.xlatScan = 0xec;
|
|---|
| 806 | data.s.scan = 0x7e;
|
|---|
| 807 | item.m_client = data.u;
|
|---|
| 808 | CKeyMap::initModifierKey(item);
|
|---|
| 809 | addKeyEntry(keyMap, item);
|
|---|
| 810 |
|
|---|
| 811 | item.m_id = kKeySuper_R;
|
|---|
| 812 | item.m_button = 0xed;
|
|---|
| 813 | data.s.xlatScan = 0xed;
|
|---|
| 814 | data.s.scan = 0x7f;
|
|---|
| 815 | item.m_client = data.u;
|
|---|
| 816 | CKeyMap::initModifierKey(item);
|
|---|
| 817 | addKeyEntry(keyMap, item);
|
|---|
| 818 |
|
|---|
| 819 | }
|
|---|
| 820 |
|
|---|
| 821 | #if 0
|
|---|
| 822 | // restore keyboard layout
|
|---|
| 823 | ActivateKeyboardLayout(activeLayout, 0);
|
|---|
| 824 | #endif
|
|---|
| 825 | }
|
|---|
| 826 |
|
|---|
| 827 | void
|
|---|
| 828 | CPMKeyState::fakeKey(const Keystroke& keystroke)
|
|---|
| 829 | {
|
|---|
| 830 | LOG((CLOG_DEBUG "fakeKey:"));
|
|---|
| 831 | switch (keystroke.m_type) {
|
|---|
| 832 | case Keystroke::kButton: {
|
|---|
| 833 | LOG((CLOG_DEBUG1 " %03x (%08llx) %s%s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client,
|
|---|
| 834 | keystroke.m_data.m_button.m_press ? "down" : "up", keystroke.m_data.m_button.m_repeat ? " repeate" : ""));
|
|---|
| 835 | KeyButton button = keystroke.m_data.m_button.m_button;
|
|---|
| 836 |
|
|---|
| 837 | // OS/2 doesn't send key ups for key repeats
|
|---|
| 838 | if (keystroke.m_data.m_button.m_repeat &&
|
|---|
| 839 | !keystroke.m_data.m_button.m_press) {
|
|---|
| 840 | LOG((CLOG_DEBUG " discard key repeat release"));
|
|---|
| 841 | break;
|
|---|
| 842 | }
|
|---|
| 843 |
|
|---|
| 844 | // unpack the m_client packet.
|
|---|
| 845 | ClientData data;
|
|---|
| 846 | data.u = keystroke.m_data.m_button.m_client;
|
|---|
| 847 |
|
|---|
| 848 | //
|
|---|
| 849 | // synthesize message
|
|---|
| 850 | // There are two options at this point, WM_CHAR or WM_VIOCHAR. It seems
|
|---|
| 851 | // like WM_VIOCHAR is the better choice since that's the form in which
|
|---|
| 852 | // keyboard events normally arrive in the system queue.
|
|---|
| 853 | //
|
|---|
| 854 | // WM_VIOCHAR:
|
|---|
| 855 | // mp1.s1: KC_ flags.
|
|---|
| 856 | // mp1.c3: repeat
|
|---|
| 857 | // mp1.c4: scancode
|
|---|
| 858 | // mp2.c1: translated char
|
|---|
| 859 | // mp2.c2: translated scancode
|
|---|
| 860 | // mp2.s2: KDD_ flags.
|
|---|
| 861 | //
|
|---|
| 862 |
|
|---|
| 863 | // the standard bits.
|
|---|
| 864 | HAB hab = CPMUtil::getHAB();/// @todo fix this
|
|---|
| 865 | QMSG qmsg;
|
|---|
| 866 | qmsg.hwnd = NULLHANDLE;
|
|---|
| 867 | qmsg.msg = WM_VIOCHAR;
|
|---|
| 868 | qmsg.ptl.x = 0;
|
|---|
| 869 | qmsg.ptl.y = 0;
|
|---|
| 870 | qmsg.time = WinGetCurrentTime(hab);
|
|---|
| 871 | qmsg.reserved = 0;
|
|---|
| 872 |
|
|---|
| 873 | //
|
|---|
| 874 | // calc the key code flags.
|
|---|
| 875 | // PM deals with: KC_TOGGLE, KC_VIRTUALKEY, KC_CHAR, (WM_CHAR: also KC_CTRL, KC_ALT, KC_SHIFT).
|
|---|
| 876 | //
|
|---|
| 877 | USHORT fKC = 0;
|
|---|
| 878 | if (data.s.scan)
|
|---|
| 879 | fKC |= KC_SCANCODE;
|
|---|
| 880 | if (!keystroke.m_data.m_button.m_press)
|
|---|
| 881 | fKC |= KC_KEYUP;
|
|---|
| 882 | /// @todo check that PM does KC_INVALIDCOMP
|
|---|
| 883 | if (data.s.fDeadKey)
|
|---|
| 884 | fKC |= KC_DEADKEY;
|
|---|
| 885 | if (data.s.fComposite)
|
|---|
| 886 | fKC |= KC_COMPOSITE;
|
|---|
| 887 | if (!keystroke.m_data.m_button.m_press && m_lastButton == data.s.scan)
|
|---|
| 888 | fKC |= KC_LONEKEY;
|
|---|
| 889 |
|
|---|
| 890 | // we need these for calcing fKDD, so just put them in even if PM can do it for us.
|
|---|
| 891 | KeyModifierMask modifierMask = this->getActiveModifiers();
|
|---|
| 892 | if (modifierMask & KeyModifierShift)
|
|---|
| 893 | fKC |= KC_SHIFT;
|
|---|
| 894 | if (modifierMask & KeyModifierControl)
|
|---|
| 895 | fKC |= KC_CTRL;
|
|---|
| 896 | if (modifierMask & KeyModifierAlt)
|
|---|
| 897 | fKC |= KC_ALT;
|
|---|
| 898 |
|
|---|
| 899 | //
|
|---|
| 900 | // Calc the keyboard device driver flags.
|
|---|
| 901 | //
|
|---|
| 902 | USHORT fKDD;
|
|---|
| 903 | // the action
|
|---|
| 904 | if (data.s.fShiftKey)
|
|---|
| 905 | fKDD = KDD_SHIFTKEY;
|
|---|
| 906 | /** @todo Determin the KDD_ACTIONMASK value.
|
|---|
| 907 | KDD_PREFIXKEY
|
|---|
| 908 | KDD_PAUSEKEY
|
|---|
| 909 | KDD_PSEUDOPAUSE
|
|---|
| 910 | KDD_WAKEUPKEY
|
|---|
| 911 | KDD_BREAKKEY
|
|---|
| 912 | KDD_PSEUDOBREAK
|
|---|
| 913 | KDD_PRTECHOKEY
|
|---|
| 914 | KDD_PSEUDOPRECH */
|
|---|
| 915 | else
|
|---|
| 916 | fKDD = KDD_PUTINKIB;
|
|---|
| 917 | #if 0
|
|---|
| 918 | if (fKC & KC_LONEKEY) fKDD |= KDD_KC_LONEKEY;
|
|---|
| 919 | if (fKC & KC_PREVDOWN) fKDD |= KDD_KC_PREVDOWN;
|
|---|
| 920 | if (fKC & KC_KEYUP) fKDD |= KDD_KC_KEYUP | KDD_BREAK;
|
|---|
| 921 | if (fKC & KC_ALT) fKDD |= KDD_KC_ALT;
|
|---|
| 922 | if (fKC & KC_CTRL) fKDD |= KDD_KC_CTRL;
|
|---|
| 923 | if (fKC & KC_SHIFT) fKDD |= KDD_KC_SHIFT;
|
|---|
| 924 | #else
|
|---|
| 925 | if (fKC & KC_KEYUP) fKDD |= KDD_BREAK;
|
|---|
| 926 | #endif
|
|---|
| 927 | if (data.s.fSecondary) fKDD |= KDD_SECONDARY;
|
|---|
| 928 | if (data.s.fExtendedKey) fKDD |= KDD_EXTENDEDKEY;
|
|---|
| 929 |
|
|---|
| 930 | //
|
|---|
| 931 | // Adjust char for ASCII control keys. (Ctrl+C and stuff)
|
|---|
| 932 | //
|
|---|
| 933 | if ( (fKC & KC_CTRL)
|
|---|
| 934 | && isASCIIControlChar(data.s.xlatChar)) {
|
|---|
| 935 | data.s.xlatChar -= (data.s.xlatChar >= 'a' ? 0x60 : 0x40);
|
|---|
| 936 | }
|
|---|
| 937 |
|
|---|
| 938 | //
|
|---|
| 939 | // For some keys (navigation and edit keys left to the numpad),
|
|---|
| 940 | // any numlock or shift needs to be 'canceled'.
|
|---|
| 941 | // (At least this happens with my logitech diNovo keyboards.)
|
|---|
| 942 | //
|
|---|
| 943 | if ( data.s.fNeedNumUnlockKey
|
|---|
| 944 | && ( (modifierMask & (KeyModifierShift | KeyModifierNumLock)) == KeyModifierNumLock
|
|---|
| 945 | || (modifierMask & (KeyModifierShift | KeyModifierNumLock)) == KeyModifierShift)) {
|
|---|
| 946 | // need to insert a key nullifying the numlock/shift.
|
|---|
| 947 | QMSG qmsg0 = qmsg;
|
|---|
| 948 | qmsg0.mp1 = MPFROMSH2CH(fKC & KC_KEYUP, 1, 0);
|
|---|
| 949 | qmsg0.mp2 = MPFROM2SHORT(MAKESHORT(0, 0x2a), (fKDD & KDD_BREAK) | KDD_UNDEFINED | KDD_SECONDARY | KDD_EXTENDEDKEY);
|
|---|
| 950 |
|
|---|
| 951 | // this message comes first on keydown and last on keyup.
|
|---|
| 952 | if (fKC & KC_KEYUP) {
|
|---|
| 953 | QMSG tmp = qmsg;
|
|---|
| 954 | qmsg = qmsg0;
|
|---|
| 955 | qmsg0 = tmp;
|
|---|
| 956 | }
|
|---|
| 957 |
|
|---|
| 958 | LOG((CLOG_INFO "WM_VIOCHAR: fKC=%04x rep=%02x scan=%02x xlch=%02x(%c) xlscan=%02x fKDD=%04x ",
|
|---|
| 959 | SHORT1FROMMP(qmsg0.mp1), CHAR3FROMMP(qmsg0.mp1), CHAR4FROMMP(qmsg0.mp1), CHAR1FROMMP(qmsg0.mp2),
|
|---|
| 960 | isprint(CHAR1FROMMP(qmsg0.mp2)) ? CHAR1FROMMP(qmsg0.mp2) : '.', CHAR2FROMMP(qmsg0.mp2), SHORT2FROMMP(qmsg0.mp2)));
|
|---|
| 961 | const char *pszError = m_fakeMsg(hab, &qmsg0);
|
|---|
| 962 | if (pszError) {
|
|---|
| 963 | LOG((CLOG_ERR " fakeMsg failed to inject msg=%#lx mp1=%#lx mp2=%#lx: %s", qmsg0.msg, qmsg0.mp1, qmsg0.mp2, pszError));
|
|---|
| 964 | }
|
|---|
| 965 | }
|
|---|
| 966 |
|
|---|
| 967 | //
|
|---|
| 968 | // Inject the (last) message.
|
|---|
| 969 | //
|
|---|
| 970 | qmsg.mp1 = MPFROMSH2CH(fKC, 1, data.s.scan);
|
|---|
| 971 | qmsg.mp2 = MPFROM2SHORT(MAKESHORT(data.s.xlatChar, data.s.xlatScan), fKDD);
|
|---|
| 972 | LOG((CLOG_INFO "WM_VIOCHAR: fKC=%04x rep=%02x scan=%02x xlch=%02x(%c) xlscan=%02x fKDD=%04x ",
|
|---|
| 973 | SHORT1FROMMP(qmsg.mp1), CHAR3FROMMP(qmsg.mp1), CHAR4FROMMP(qmsg.mp1), CHAR1FROMMP(qmsg.mp2),
|
|---|
| 974 | isprint(CHAR1FROMMP(qmsg.mp2)) ? CHAR1FROMMP(qmsg.mp2) : '.', CHAR2FROMMP(qmsg.mp2), SHORT2FROMMP(qmsg.mp2)));
|
|---|
| 975 | const char *pszError = m_fakeMsg(hab, &qmsg);
|
|---|
| 976 | if (pszError) {
|
|---|
| 977 | LOG((CLOG_ERR " fakeMsg failed to inject msg=%#lx mp1=%#lx mp2=%#lx: %s", qmsg.msg, qmsg.mp1, qmsg.mp2, pszError));
|
|---|
| 978 | }
|
|---|
| 979 |
|
|---|
| 980 | // remember the previous key for KC_LONEKEY.
|
|---|
| 981 | if (keystroke.m_data.m_button.m_press && !keystroke.m_data.m_button.m_repeat)
|
|---|
| 982 | m_lastButton = data.s.scan;
|
|---|
| 983 | break;
|
|---|
| 984 | }
|
|---|
| 985 |
|
|---|
| 986 | case Keystroke::kGroup:
|
|---|
| 987 | #if 0
|
|---|
| 988 | // we don't restore the group. we'd like to but we can't be
|
|---|
| 989 | // sure the restoring group change will be processed after the
|
|---|
| 990 | // key events.
|
|---|
| 991 | if (!keystroke.m_data.m_group.m_restore) {
|
|---|
| 992 | if (keystroke.m_data.m_group.m_absolute) {
|
|---|
| 993 | LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group));
|
|---|
| 994 | setWindowGroup(keystroke.m_data.m_group.m_group);
|
|---|
| 995 | }
|
|---|
| 996 | else {
|
|---|
| 997 | LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group));
|
|---|
| 998 | setWindowGroup(getEffectiveGroup(pollActiveGroup(),
|
|---|
| 999 | keystroke.m_data.m_group.m_group));
|
|---|
| 1000 | }
|
|---|
| 1001 | }
|
|---|
| 1002 | #endif
|
|---|
| 1003 | break;
|
|---|
| 1004 | }
|
|---|
| 1005 | }
|
|---|
| 1006 |
|
|---|
| 1007 | KeyModifierMask&
|
|---|
| 1008 | CPMKeyState::getActiveModifiersRValue()
|
|---|
| 1009 | {
|
|---|
| 1010 | LOG((CLOG_DEBUG "getActiveModifiersRValue:"));
|
|---|
| 1011 | if (m_useSavedModifiers) {
|
|---|
| 1012 | return m_savedModifiers;
|
|---|
| 1013 | }
|
|---|
| 1014 | return CKeyState::getActiveModifiersRValue();
|
|---|
| 1015 | }
|
|---|
| 1016 |
|
|---|
| 1017 | KeyID
|
|---|
| 1018 | CPMKeyState::getKeyID(ULONG virtualKey, KeyButton button, bool numpad, USHORT usChar)
|
|---|
| 1019 | {
|
|---|
| 1020 | LOG((CLOG_DEBUG "getKeyID:"));
|
|---|
| 1021 | if (virtualKey < sizeof(s_virtualKey) / sizeof(s_virtualKey[0])) {
|
|---|
| 1022 |
|
|---|
| 1023 | //
|
|---|
| 1024 | // Numpad kludge.
|
|---|
| 1025 | //
|
|---|
| 1026 | if (numpad && s_virtualKey[virtualKey].numpad) {
|
|---|
| 1027 | switch (usChar) {
|
|---|
| 1028 | case '=': return kKeyKP_Equal;
|
|---|
| 1029 | case '*': return kKeyKP_Multiply;
|
|---|
| 1030 | case '+': return kKeyKP_Add;
|
|---|
| 1031 | //case ',':
|
|---|
| 1032 | //case '.': return kKeyKP_Separator ? kKeyKP_Decimal;
|
|---|
| 1033 | case '-': return kKeyKP_Subtract;
|
|---|
| 1034 | case '/': return kKeyKP_Divide;
|
|---|
| 1035 | case '0': return kKeyKP_0;
|
|---|
| 1036 | case '1': return kKeyKP_1;
|
|---|
| 1037 | case '2': return kKeyKP_2;
|
|---|
| 1038 | case '3': return kKeyKP_3;
|
|---|
| 1039 | case '4': return kKeyKP_4;
|
|---|
| 1040 | case '5': return kKeyKP_5;
|
|---|
| 1041 | case '6': return kKeyKP_6;
|
|---|
| 1042 | case '7': return kKeyKP_7;
|
|---|
| 1043 | case '8': return kKeyKP_8;
|
|---|
| 1044 | case '9': return kKeyKP_9;
|
|---|
| 1045 | case ' ': return kKeyKP_Space;
|
|---|
| 1046 | }
|
|---|
| 1047 | switch (s_virtualKey[virtualKey].key) {
|
|---|
| 1048 | case kKeyHome: return kKeyKP_Home;
|
|---|
| 1049 | case kKeyLeft: return kKeyKP_Left;
|
|---|
| 1050 | case kKeyUp: return kKeyKP_Up;
|
|---|
| 1051 | case kKeyRight: return kKeyKP_Right;
|
|---|
| 1052 | case kKeyDown: return kKeyKP_Down;
|
|---|
| 1053 | case kKeyPageUp: return kKeyKP_PageUp;
|
|---|
| 1054 | case kKeyPageDown: return kKeyKP_PageDown;
|
|---|
| 1055 | case kKeyEnd: return kKeyKP_End;
|
|---|
| 1056 | case kKeyBegin: return kKeyKP_Begin;
|
|---|
| 1057 | case kKeyInsert: return kKeyKP_Insert;
|
|---|
| 1058 | case kKeyDelete: return kKeyKP_Delete;
|
|---|
| 1059 | }
|
|---|
| 1060 | } else {
|
|---|
| 1061 | //
|
|---|
| 1062 | // Left/right shift and control kludge.
|
|---|
| 1063 | //
|
|---|
|
|---|