barrier/client/CMSWindowsSecondaryScreen.cpp
crs a5ae8011e2 win32 changes. replaced log dialog hack with a windows console
window.  now attaching thread input queues as necessary. shifted
code around so toggling toggle keys is immediately reflected by
secondary screen's keyboard.  now setting extended key flag for
keys that need it.  fixed handling of shift + caps-lock.  added
handling of keys that should distinguish between left and right
but don't.  fixed get/set of active window on leave/enter of
primary screen.  replaced 1x1 primary window with a full screen
window to work around a problem with losing key events.  changed
calculation of mouse move deltas.
2002-05-22 17:01:17 +00:00

1424 lines
42 KiB
C++

#include "CMSWindowsSecondaryScreen.h"
#include "CMSWindowsClipboard.h"
#include "CClient.h"
#include "CClipboard.h"
#include "CThread.h"
#include "CLog.h"
#include <assert.h>
#include <ctype.h>
//
// CMSWindowsSecondaryScreen
//
CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen() :
m_client(NULL),
m_window(NULL),
m_nextClipboardWindow(NULL)
{
// do nothing
}
CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
{
assert(m_window == NULL);
}
void CMSWindowsSecondaryScreen::run()
{
// change our priority
CThread::getCurrentThread().setPriority(-7);
// save thread id
m_threadID = GetCurrentThreadId();
// run event loop
log((CLOG_INFO "entering event loop"));
doRun();
log((CLOG_INFO "exiting event loop"));
}
void CMSWindowsSecondaryScreen::stop()
{
log((CLOG_INFO "requesting event loop stop"));
doStop();
}
void CMSWindowsSecondaryScreen::open(CClient* client)
{
assert(m_client == NULL);
assert(client != NULL);
log((CLOG_INFO "opening screen"));
// set the client
m_client = client;
// open the display
openDisplay();
// update key state
updateKeys();
updateModifiers();
// assume primary has all clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id)
grabClipboard(id);
}
void CMSWindowsSecondaryScreen::close()
{
assert(m_client != NULL);
log((CLOG_INFO "closing screen"));
// close the display
closeDisplay();
// done with client
m_client = NULL;
}
void CMSWindowsSecondaryScreen::enter(
SInt32 x, SInt32 y, KeyModifierMask mask)
{
assert(m_window != NULL);
log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask));
// attach thread input queues
AttachThreadInput(GetCurrentThreadId(), m_threadID, TRUE);
// update our keyboard state to reflect the local state
updateKeys();
updateModifiers();
// toggle modifiers that don't match the desired state
if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) {
toggleKey(VK_CAPITAL, KeyModifierCapsLock);
}
if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) {
toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock);
}
if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) {
toggleKey(VK_SCROLL, KeyModifierScrollLock);
}
// warp to requested location
SInt32 w, h;
getScreenSize(&w, &h);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
(DWORD)((65535.99 * x) / (w - 1)),
(DWORD)((65535.99 * y) / (h - 1)),
0, 0);
// show cursor
log((CLOG_INFO "show cursor"));
ShowWindow(m_window, SW_HIDE);
}
void CMSWindowsSecondaryScreen::leave()
{
assert(m_window != NULL);
log((CLOG_INFO "leaving screen"));
// move hider window under the mouse (rather than moving the mouse
// somewhere else on the screen)
POINT point;
GetCursorPos(&point);
MoveWindow(m_window, point.x, point.y, 1, 1, FALSE);
// raise and show the hider window. take activation.
log((CLOG_INFO "hide cursor"));
ShowWindow(m_window, SW_SHOWNORMAL);
// if we think we own the clipboard but we don't then somebody
// grabbed the clipboard on this screen without us knowing.
// tell the server that this screen grabbed the clipboard.
//
// this works around bugs in the clipboard viewer chain.
// sometimes NT will simply never send WM_DRAWCLIPBOARD
// messages for no apparent reason and rebooting fixes the
// problem. since we don't want a broken clipboard until the
// next reboot we do this double check. clipboard ownership
// won't be reflected on other screens until we leave but at
// least the clipboard itself will work.
HWND clipboardOwner = GetClipboardOwner();
if (m_clipboardOwner != clipboardOwner) {
m_clipboardOwner = clipboardOwner;
if (m_clipboardOwner != m_window) {
m_client->onClipboardChanged(kClipboardClipboard);
m_client->onClipboardChanged(kClipboardSelection);
}
}
}
void CMSWindowsSecondaryScreen::keyDown(
KeyID key, KeyModifierMask mask)
{
Keystrokes keys;
UINT virtualKey;
// get the sequence of keys to simulate key press and the final
// modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kPress);
if (keys.empty())
return;
// generate key events
doKeystrokes(keys, 1);
// note that key is now down
m_keys[virtualKey] |= 0x80;
switch (virtualKey) {
case VK_LSHIFT:
case VK_RSHIFT:
m_keys[VK_SHIFT] |= 0x80;
break;
case VK_LCONTROL:
case VK_RCONTROL:
m_keys[VK_CONTROL] |= 0x80;
break;
case VK_LMENU:
case VK_RMENU:
m_keys[VK_MENU] |= 0x80;
break;
}
}
void CMSWindowsSecondaryScreen::keyRepeat(
KeyID key, KeyModifierMask mask, SInt32 count)
{
Keystrokes keys;
UINT virtualKey;
// get the sequence of keys to simulate key repeat and the final
// modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kRepeat);
if (keys.empty())
return;
// generate key events
doKeystrokes(keys, count);
}
void CMSWindowsSecondaryScreen::keyUp(
KeyID key, KeyModifierMask mask)
{
Keystrokes keys;
UINT virtualKey;
// get the sequence of keys to simulate key release and the final
// modifier state.
m_mask = mapKey(keys, virtualKey, key, mask, kRelease);
if (keys.empty())
return;
// generate key events
doKeystrokes(keys, 1);
// note that key is now up
m_keys[virtualKey] &= ~0x80;
switch (virtualKey) {
case VK_LSHIFT:
if ((m_keys[VK_RSHIFT] & 0x80) == 0) {
m_keys[VK_SHIFT] &= ~0x80;
}
break;
case VK_RSHIFT:
if ((m_keys[VK_LSHIFT] & 0x80) == 0) {
m_keys[VK_SHIFT] &= ~0x80;
}
break;
case VK_LCONTROL:
if ((m_keys[VK_RCONTROL] & 0x80) == 0) {
m_keys[VK_CONTROL] &= ~0x80;
}
break;
case VK_RCONTROL:
if ((m_keys[VK_LCONTROL] & 0x80) == 0) {
m_keys[VK_CONTROL] &= ~0x80;
}
break;
case VK_LMENU:
if ((m_keys[VK_RMENU] & 0x80) == 0) {
m_keys[VK_MENU] &= ~0x80;
}
break;
case VK_RMENU:
if ((m_keys[VK_LMENU] & 0x80) == 0) {
m_keys[VK_MENU] &= ~0x80;
}
break;
}
}
void CMSWindowsSecondaryScreen::mouseDown(ButtonID button)
{
// map button id to button flag
DWORD flags = mapButton(button, true);
// send event
if (flags != 0)
mouse_event(flags, 0, 0, 0, 0);
}
void CMSWindowsSecondaryScreen::mouseUp(ButtonID button)
{
// map button id to button flag
DWORD flags = mapButton(button, false);
// send event
if (flags != 0)
mouse_event(flags, 0, 0, 0, 0);
}
void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y)
{
SInt32 w, h;
getScreenSize(&w, &h);
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
(SInt32)(65535.99 * x / (w - 1)),
(SInt32)(65535.99 * y / (h - 1)),
0, 0);
}
void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta)
{
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0);
}
void CMSWindowsSecondaryScreen::setClipboard(
ClipboardID id, const IClipboard* src)
{
assert(m_window != NULL);
CMSWindowsClipboard dst(m_window);
CClipboard::copy(&dst, src);
}
void CMSWindowsSecondaryScreen::grabClipboard(ClipboardID id)
{
assert(m_window != NULL);
CMSWindowsClipboard clipboard(m_window);
if (clipboard.open(0)) {
clipboard.close();
}
}
void CMSWindowsSecondaryScreen::getSize(
SInt32* width, SInt32* height) const
{
getScreenSize(width, height);
}
SInt32 CMSWindowsSecondaryScreen::getJumpZoneSize() const
{
return 0;
}
void CMSWindowsSecondaryScreen::getClipboard(
ClipboardID id, IClipboard* dst) const
{
assert(m_window != NULL);
CMSWindowsClipboard src(m_window);
CClipboard::copy(dst, &src);
}
void CMSWindowsSecondaryScreen::onOpenDisplay()
{
assert(m_window == NULL);
// initialize clipboard owner to current owner. we don't want
// to take ownership of the clipboard just by starting up.
m_clipboardOwner = GetClipboardOwner();
// create the cursor hiding window. this window is used to hide the
// cursor when it's not on the screen. the window is hidden as soon
// as the cursor enters the screen or the display's real cursor is
// moved.
m_window = CreateWindowEx(WS_EX_TOPMOST |
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW,
(LPCTSTR)getClass(), "Synergy",
WS_POPUP,
0, 0, 1, 1, NULL, NULL,
getInstance(),
NULL);
// hide the cursor
leave();
// install our clipboard snooper
m_nextClipboardWindow = SetClipboardViewer(m_window);
}
void CMSWindowsSecondaryScreen::onCloseDisplay()
{
assert(m_window != NULL);
// remove clipboard snooper
ChangeClipboardChain(m_window, m_nextClipboardWindow);
m_nextClipboardWindow = NULL;
// destroy window
DestroyWindow(m_window);
m_window = NULL;
}
bool CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg)
{
return false;
}
LRESULT CMSWindowsSecondaryScreen::onEvent(
HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch (msg) {
// FIXME -- handle display changes
case WM_PAINT:
ValidateRect(hwnd, NULL);
return 0;
case WM_ACTIVATEAPP:
if (wParam == FALSE) {
// some other app activated. hide the hider window.
log((CLOG_INFO "show cursor"));
ShowWindow(m_window, SW_HIDE);
}
break;
case WM_DRAWCLIPBOARD:
log((CLOG_DEBUG "clipboard was taken"));
// first pass it on
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
// now notify client that somebody changed the clipboard (unless
// we're now the owner, in which case it's because we took
// ownership).
m_clipboardOwner = GetClipboardOwner();
if (m_clipboardOwner != m_window) {
m_client->onClipboardChanged(kClipboardClipboard);
m_client->onClipboardChanged(kClipboardSelection);
}
return 0;
case WM_CHANGECBCHAIN:
if (m_nextClipboardWindow == (HWND)wParam)
m_nextClipboardWindow = (HWND)lParam;
else
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// these tables map KeyID (a X windows KeySym) to virtual key codes.
// if the key is an extended key then the entry is the virtual key
// code | 0x100. keys that map to normal characters have a 0 entry
// and the conversion is done elsewhere.
static const UINT g_latin1[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_latin2[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_latin3[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_latin4[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_latin5[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_latin6[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_latin7[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_latin8[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_latin9[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_terminal[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
static const UINT g_function[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x18 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x20 */ VK_TAB, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
};
/* XK_KP_Space to XK_KP_Equal */
static const UINT g_miscellany[] =
{
/* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x08 */ VK_BACK, VK_TAB, 0, VK_CLEAR, 0, VK_RETURN, 0, 0,
/* 0x10 */ 0, 0, 0, VK_PAUSE, VK_SCROLL, 0/*sys-req*/, 0, 0,
/* 0x18 */ 0, 0, 0, VK_ESCAPE, 0, 0, 0, 0,
/* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x28 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x50 */ VK_HOME|0x100, VK_LEFT|0x100, VK_UP|0x100, VK_RIGHT|0x100,
/* 0x54 */ VK_DOWN|0x100, VK_PRIOR|0x100, VK_NEXT|0x100, VK_END|0x100,
/* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x60 */ VK_SELECT|0x100, VK_SNAPSHOT|0x100, VK_EXECUTE|0x100, VK_INSERT|0x100,
/* 0x64 */ 0, 0, 0, VK_APPS|0x100,
/* 0x68 */ 0, 0, VK_HELP|0x100, VK_CANCEL|0x100, 0, 0, 0, 0,
/* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x78 */ 0, 0, 0, 0, 0, 0, VK_MODECHANGE|0x100, VK_NUMLOCK|0x100,
/* 0x80 */ VK_SPACE, 0, 0, 0, 0, 0, 0, 0,
/* 0x88 */ 0, VK_TAB, 0, 0, 0, VK_RETURN|0x100, 0, 0,
/* 0x90 */ 0, 0, 0, 0, 0, VK_HOME, VK_LEFT, VK_UP,
/* 0x98 */ VK_RIGHT, VK_DOWN, VK_PRIOR, VK_NEXT,
/* 0x9c */ VK_END, 0, VK_INSERT, VK_DELETE,
/* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xa8 */ 0, 0, VK_MULTIPLY, VK_ADD,
/* 0xac */ VK_SEPARATOR, VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE|0x100,
/* 0xb0 */ VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3,
/* 0xb4 */ VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
/* 0xb8 */ VK_NUMPAD8, VK_NUMPAD9, 0, 0, 0, 0, VK_F1, VK_F2,
/* 0xc0 */ VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_F10,
/* 0xc8 */ VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18,
/* 0xd0 */ VK_F19, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, 0, 0,
/* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xe0 */ 0, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL,
/* 0xe4 */ VK_RCONTROL|0x100, VK_CAPITAL, 0, VK_LWIN|0x100,
/* 0xe8 */ VK_RWIN|0x100, VK_LMENU, VK_RMENU|0x100, 0, 0, 0, 0, 0,
/* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, VK_DELETE|0x100
};
static const UINT* g_katakana = NULL;
static const UINT* g_arabic = NULL;
static const UINT* g_cyrillic = NULL;
static const UINT* g_greek = NULL;
static const UINT* g_technical = NULL;
static const UINT* g_special = NULL;
static const UINT* g_publishing = NULL;
static const UINT* g_apl = NULL;
static const UINT* g_hebrew = NULL;
static const UINT* g_thai = NULL;
static const UINT* g_korean = NULL;
static const UINT* g_armenian = NULL;
static const UINT* g_georgian = NULL;
static const UINT* g_azeri = NULL;
static const UINT* g_vietnamese = NULL;
static const UINT* g_currency = NULL;
static const UINT* g_mapTable[] =
{
/* 0x00 */ g_latin1, g_latin2, g_latin3, g_latin4,
/* 0x04 */ g_katakana, g_arabic, g_cyrillic, g_greek,
/* 0x08 */ g_technical, g_special, g_publishing, g_apl,
/* 0x0c */ g_hebrew, g_thai, g_korean, NULL,
/* 0x10 */ NULL, NULL, g_latin8, g_latin9,
/* 0x14 */ g_armenian, g_georgian, g_azeri, NULL,
/* 0x18 */ NULL, NULL, NULL, NULL, NULL, NULL, g_vietnamese, NULL,
/* 0x20 */ g_currency, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x28 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x30 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x38 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x40 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x48 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x50 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x58 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x60 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x68 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x70 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x78 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x80 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x88 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x90 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0x98 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xa0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xa8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xb0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xb8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xc0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xc8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xd0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xd8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xe0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xe8 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xf0 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 0xf8 */ NULL, NULL, NULL, NULL,
/* 0xfc */ NULL, g_terminal, g_function, g_miscellany
};
DWORD CMSWindowsSecondaryScreen::mapButton(
ButtonID button, bool press) const
{
// map button id to button flag
switch (button) {
case kButtonLeft:
return press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
case kButtonMiddle:
return press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
case kButtonRight:
return press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
default:
return 0;
}
}
KeyModifierMask CMSWindowsSecondaryScreen::mapKey(
Keystrokes& keys,
UINT& virtualKey,
KeyID id, KeyModifierMask mask,
EKeyAction action) const
{
// lookup the key table
const UInt32 mapID = ((id >> 8) & 0xff);
const UINT* map = g_mapTable[mapID];
if (map == NULL) {
// unknown key
return m_mask;
}
// look up virtual key for id. default output mask carries over
// the current toggle modifier states and includes desired shift,
// control, alt, and meta states.
const UInt32 code = (id & 0xff);
virtualKey = map[code];
KeyModifierMask outMask = (m_mask &
(KeyModifierCapsLock |
KeyModifierNumLock |
KeyModifierScrollLock));
outMask |= (mask &
(KeyModifierShift |
KeyModifierControl |
KeyModifierAlt |
KeyModifierMeta));
log((CLOG_DEBUG2 "key id %d -> virtual key %d", id, virtualKey));
// extract extended key flag
const bool isExtended = ((virtualKey & 0x100) != 0);
virtualKey &= ~0x100;
// if not in map then ask system to convert ascii character
if (virtualKey == 0) {
if (mapID != 0) {
// not ascii
log((CLOG_DEBUG2 "not ascii"));
return m_mask;
}
// translate. return no keys if unknown key.
SHORT vk = VkKeyScan(static_cast<TCHAR>(code));
if (vk == 0xffff) {
log((CLOG_DEBUG2 "no virtual key for character %d", code));
return m_mask;
}
// use whatever shift state VkKeyScan says
// FIXME -- also for control and alt, but it's more difficult
// to determine if control and alt must be off or if it just
// doesn't matter.
outMask &= ~KeyModifierShift;
// convert system modifier mask to our mask
if (HIBYTE(vk) & 1)
outMask |= KeyModifierShift;
if (HIBYTE(vk) & 2)
outMask |= KeyModifierControl;
if (HIBYTE(vk) & 4)
outMask |= KeyModifierAlt;
log((CLOG_DEBUG2 "character %d to virtual key %d mask 0x%04x", code, LOBYTE(vk), outMask));
// handle combination of caps-lock and shift. if caps-lock is
// off locally then use shift as necessary. if caps-lock is on
// locally then it reverses the meaning of shift for keys that
// are subject to case conversion.
if ((outMask & KeyModifierCapsLock) != 0) {
if (tolower(code) != toupper(code)) {
log((CLOG_DEBUG2 "flip shift"));
outMask ^= KeyModifierShift;
}
}
// get virtual key
virtualKey = LOBYTE(vk);
}
// if in map then figure out correct modifier state
else {
// check numeric keypad. note that while KeyID distinguishes
// between the keypad movement keys (e.g. Home, left arrow),
// the virtual keys do not. however, the virtual keys do
// distinguish between keypad numbers and operators (e.g.
// add, multiply) and their main keyboard counterparts.
// therefore, we can ignore the num-lock state for movement
// virtual keys but not for numeric keys.
if (virtualKey >= VK_NUMPAD0 && virtualKey <= VK_DIVIDE) {
// set required shift state based on current numlock state
if ((outMask & KeyModifierNumLock) == 0) {
if ((m_mask & KeyModifierNumLock) == 0) {
log((CLOG_DEBUG2 "turn on num lock for keypad key"));
outMask |= KeyModifierNumLock;
}
else {
log((CLOG_DEBUG2 "turn on shift for keypad key"));
outMask |= KeyModifierShift;
}
}
}
// check for ISO_Left_Tab
else if (id == 0xfe20) {
outMask |= KeyModifierShift;
}
}
// a list of modifier key info
class CModifierInfo {
public:
KeyModifierMask m_mask;
UINT m_virtualKey;
UINT m_virtualKey2;
bool m_isToggle;
};
static const CModifierInfo s_modifier[] = {
{ KeyModifierShift, VK_LSHIFT, VK_RSHIFT, false },
{ KeyModifierControl, VK_LCONTROL, VK_RCONTROL | 0x100,false },
{ KeyModifierAlt, VK_LMENU, VK_RMENU | 0x100, false },
{ KeyModifierMeta, VK_LWIN | 0x100, VK_RWIN | 0x100, false },
{ KeyModifierCapsLock, VK_CAPITAL, 0, true },
{ KeyModifierNumLock, VK_NUMLOCK | 0x100, 0, true },
{ KeyModifierScrollLock,VK_SCROLL, 0, true }
};
static const unsigned int s_numModifiers =
sizeof(s_modifier) / sizeof(s_modifier[0]);
// note if the key is a modifier
unsigned int modifierIndex;
switch (virtualKey) {
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
modifierIndex = 0;
break;
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL:
modifierIndex = 1;
break;
case VK_MENU:
case VK_LMENU:
case VK_RMENU:
modifierIndex = 2;
break;
case VK_LWIN:
case VK_RWIN:
modifierIndex = 3;
break;
case VK_CAPITAL:
modifierIndex = 4;
break;
case VK_NUMLOCK:
modifierIndex = 5;
break;
case VK_SCROLL:
modifierIndex = 6;
break;
default:
modifierIndex = s_numModifiers;
break;
}
const bool isModifier = (modifierIndex != s_numModifiers);
// add the key events required to get to the modifier state
// necessary to generate an event yielding id. also save the
// key events required to restore the state. if the key is
// a modifier key then skip this because modifiers should not
// modify modifiers.
Keystrokes undo;
Keystroke keystroke;
if (outMask != m_mask && !isModifier) {
for (unsigned int i = 0; i < s_numModifiers; ++i) {
KeyModifierMask bit = s_modifier[i].m_mask;
if ((outMask & bit) != (m_mask & bit)) {
if ((outMask & bit) != 0) {
// modifier is not active but should be. if the
// modifier is a toggle then toggle it on with a
// press/release, otherwise activate it with a
// press.
keystroke.m_virtualKey = s_modifier[i].m_virtualKey;
keystroke.m_press = true;
keystroke.m_repeat = false;
keys.push_back(keystroke);
if (s_modifier[i].m_isToggle) {
keystroke.m_press = false;
keys.push_back(keystroke);
undo.push_back(keystroke);
keystroke.m_press = true;
undo.push_back(keystroke);
}
else {
keystroke.m_press = false;
undo.push_back(keystroke);
}
}
else {
// modifier is active but should not be. if the
// modifier is a toggle then toggle it off with a
// press/release, otherwise deactivate it with a
// release. we must check each keycode for the
// modifier if not a toggle.
if (s_modifier[i].m_isToggle) {
keystroke.m_virtualKey = s_modifier[i].m_virtualKey;
keystroke.m_press = true;
keystroke.m_repeat = false;
keys.push_back(keystroke);
keystroke.m_press = false;
keys.push_back(keystroke);
undo.push_back(keystroke);
keystroke.m_press = true;
undo.push_back(keystroke);
}
else {
UINT key = s_modifier[i].m_virtualKey;
if ((m_keys[key & 0xff] & 0x80) != 0) {
keystroke.m_virtualKey = key;
keystroke.m_press = false;
keystroke.m_repeat = false;
keys.push_back(keystroke);
keystroke.m_press = true;
undo.push_back(keystroke);
}
key = s_modifier[i].m_virtualKey2;
if (key != 0 && (m_keys[key & 0xff] & 0x80) != 0) {
keystroke.m_virtualKey = key;
keystroke.m_press = false;
keystroke.m_repeat = false;
keys.push_back(keystroke);
keystroke.m_press = true;
undo.push_back(keystroke);
}
}
}
}
}
}
// add the key event
keystroke.m_virtualKey = virtualKey;
if (isExtended)
keystroke.m_virtualKey |= 0x100;
switch (action) {
case kPress:
keystroke.m_press = true;
keystroke.m_repeat = false;
keys.push_back(keystroke);
break;
case kRelease:
keystroke.m_press = false;
keystroke.m_repeat = false;
keys.push_back(keystroke);
break;
case kRepeat:
keystroke.m_press = true;
keystroke.m_repeat = true;
keys.push_back(keystroke);
break;
}
// add key events to restore the modifier state. apply events in
// the reverse order that they're stored in undo.
while (!undo.empty()) {
keys.push_back(undo.back());
undo.pop_back();
}
// if the key is a modifier key then compute the modifier mask after
// this key is pressed.
mask = m_mask;
if (isModifier && action != kRepeat) {
// toggle keys modify the state on release. other keys set
// the bit on press and clear the bit on release.
const CModifierInfo& modifier = s_modifier[modifierIndex];
if (modifier.m_isToggle) {
if (action == kRelease) {
mask ^= modifier.m_mask;
}
}
else if (action == kPress) {
mask |= modifier.m_mask;
}
else {
// can't reset bit until all keys that set it are released.
// scan those keys to see if any are pressed.
bool down = false;
if (virtualKey != (modifier.m_virtualKey & 0xff) &&
(m_keys[modifier.m_virtualKey & 0xff] & 0x80) != 0) {
down = true;
}
if (modifier.m_virtualKey2 != 0 &&
virtualKey != (modifier.m_virtualKey2 & 0xff) &&
(m_keys[modifier.m_virtualKey2 & 0xff] & 0x80) != 0) {
down = true;
}
if (!down)
mask &= ~modifier.m_mask;
}
}
log((CLOG_DEBUG2 "previous modifiers 0x%04x, final modifiers 0x%04x", m_mask, mask));
return mask;
}
void CMSWindowsSecondaryScreen::doKeystrokes(
const Keystrokes& keys, SInt32 count)
{
// do nothing if no keys or no repeats
if (count < 1 || keys.empty())
return;
// generate key events
for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) {
if (k->m_repeat) {
// repeat from here up to but not including the next key
// with m_repeat == false count times.
Keystrokes::const_iterator start = k;
for (; count > 0; --count) {
// send repeating events
for (k = start; k != keys.end() && k->m_repeat; ++k) {
sendKeyEvent(k->m_virtualKey, k->m_press);
}
}
// note -- k is now on the first non-repeat key after the
// repeat keys, exactly where we'd like to continue from.
}
else {
// send event
sendKeyEvent(k->m_virtualKey, k->m_press);
// next key
++k;
}
}
}
void CMSWindowsSecondaryScreen::updateKeys()
{
// clear key state
memset(m_keys, 0, sizeof(m_keys));
// we only care about the modifier key states
m_keys[VK_LSHIFT] = static_cast<BYTE>(GetKeyState(VK_LSHIFT));
m_keys[VK_RSHIFT] = static_cast<BYTE>(GetKeyState(VK_RSHIFT));
m_keys[VK_SHIFT] = static_cast<BYTE>(GetKeyState(VK_SHIFT));
m_keys[VK_LCONTROL] = static_cast<BYTE>(GetKeyState(VK_LCONTROL));
m_keys[VK_RCONTROL] = static_cast<BYTE>(GetKeyState(VK_RCONTROL));
m_keys[VK_CONTROL] = static_cast<BYTE>(GetKeyState(VK_CONTROL));
m_keys[VK_LMENU] = static_cast<BYTE>(GetKeyState(VK_LMENU));
m_keys[VK_RMENU] = static_cast<BYTE>(GetKeyState(VK_RMENU));
m_keys[VK_MENU] = static_cast<BYTE>(GetKeyState(VK_MENU));
m_keys[VK_LWIN] = static_cast<BYTE>(GetKeyState(VK_LWIN));
m_keys[VK_RWIN] = static_cast<BYTE>(GetKeyState(VK_RWIN));
m_keys[VK_APPS] = static_cast<BYTE>(GetKeyState(VK_APPS));
m_keys[VK_CAPITAL] = static_cast<BYTE>(GetKeyState(VK_CAPITAL));
m_keys[VK_NUMLOCK] = static_cast<BYTE>(GetKeyState(VK_NUMLOCK));
m_keys[VK_SCROLL] = static_cast<BYTE>(GetKeyState(VK_SCROLL));
}
void CMSWindowsSecondaryScreen::updateModifiers()
{
// update active modifier mask
m_mask = 0;
if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0)
m_mask |= KeyModifierShift;
if ((m_keys[VK_LCONTROL] & 0x80) != 0 || (m_keys[VK_RCONTROL] & 0x80) != 0)
m_mask |= KeyModifierControl;
if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0)
m_mask |= KeyModifierAlt;
if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0)
m_mask |= KeyModifierMeta;
if ((m_keys[VK_CAPITAL] & 0x01) != 0)
m_mask |= KeyModifierCapsLock;
if ((m_keys[VK_NUMLOCK] & 0x01) != 0)
m_mask |= KeyModifierNumLock;
if ((m_keys[VK_SCROLL] & 0x01) != 0)
m_mask |= KeyModifierScrollLock;
log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask));
}
void CMSWindowsSecondaryScreen::toggleKey(
UINT virtualKey, KeyModifierMask mask)
{
// send key events to simulate a press and release
sendKeyEvent(virtualKey, true);
sendKeyEvent(virtualKey, false);
// toggle shadow state
m_mask ^= mask;
m_keys[virtualKey & 0xff] ^= 0x01;
}
UINT CMSWindowsSecondaryScreen::virtualKeyToScanCode(
UINT& virtualKey)
{
// try mapping given virtual key
UINT code = MapVirtualKey(virtualKey & 0xff, 0);
if (code != 0)
return code;
// no dice. if the virtual key distinguishes between left/right
// then try the one that doesn't distinguish sides. windows (or
// keyboard drivers) are inconsistent in their treatment of these
// virtual keys. the following behaviors have been observed:
//
// win2k (gateway desktop):
// MapVirtualKey(vk, 0):
// VK_SHIFT == VK_LSHIFT != VK_RSHIFT
// VK_CONTROL == VK_LCONTROL == VK_RCONTROL
// VK_MENU == VK_LMENU == VK_RMENU
// MapVirtualKey(sc, 3):
// VK_LSHIFT and VK_RSHIFT mapped independently
// VK_LCONTROL is mapped but not VK_RCONTROL
// VK_LMENU is mapped but not VK_RMENU
//
// win me (sony vaio laptop):
// MapVirtualKey(vk, 0):
// VK_SHIFT mapped; VK_LSHIFT, VK_RSHIFT not mapped
// VK_CONTROL mapped; VK_LCONTROL, VK_RCONTROL not mapped
// VK_MENU mapped; VK_LMENU, VK_RMENU not mapped
// MapVirtualKey(sc, 3):
// all scan codes unmapped (function apparently unimplemented)
switch (virtualKey & 0xff) {
case VK_LSHIFT:
case VK_RSHIFT:
virtualKey = VK_SHIFT;
return MapVirtualKey(VK_SHIFT, 0);
case VK_LCONTROL:
case VK_RCONTROL:
virtualKey = VK_CONTROL;
return MapVirtualKey(VK_CONTROL, 0);
case VK_LMENU:
case VK_RMENU:
virtualKey = VK_MENU;
return MapVirtualKey(VK_MENU, 0);
default:
return 0;
}
}
bool CMSWindowsSecondaryScreen::isExtendedKey(
UINT virtualKey)
{
// see if we've already encoded the extended flag
if ((virtualKey & 0x100) != 0)
return true;
// check known virtual keys
switch (virtualKey & 0xff) {
case VK_NUMLOCK:
case VK_RCONTROL:
case VK_RMENU:
case VK_LWIN:
case VK_RWIN:
case VK_APPS:
return true;
default:
return false;
}
}
void CMSWindowsSecondaryScreen::sendKeyEvent(
UINT virtualKey, bool press)
{
DWORD flags = 0;
if (isExtendedKey(virtualKey))
flags |= KEYEVENTF_EXTENDEDKEY;
if (!press)
flags |= KEYEVENTF_KEYUP;
const UINT code = virtualKeyToScanCode(virtualKey);
keybd_event(static_cast<BYTE>(virtualKey & 0xff),
static_cast<BYTE>(code), flags, 0);
log((CLOG_DEBUG2 "send key %d, 0x%04x, %s%s", virtualKey & 0xff, code, ((flags & KEYEVENTF_KEYUP) ? "release" : "press"), ((flags & KEYEVENTF_EXTENDEDKEY) ? " extended" : "")));
}