summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon TURNEY <jon.turney@dronecode.org.uk>2012-11-30 14:05:00 +0000
committerJon TURNEY <jon.turney@dronecode.org.uk>2012-11-30 14:26:25 +0000
commitb0255ec4a0ff509326d528c4370ab8960715bfb2 (patch)
tree727d9fa12371913aead4e9070f4ae5ea6d186424
parent2e3122b5230284422be0001cd6a52c16ef707784 (diff)
Create window in message pump thread, so we can process it's messsages
Also, add keyboard handling
-rw-r--r--TODO38
-rw-r--r--src/Makefile.am4
-rw-r--r--src/global.h22
-rw-r--r--src/main.c38
-rw-r--r--src/scancodes.h194
-rw-r--r--src/win.c321
-rw-r--r--src/winkeybd.c290
-rw-r--r--src/winkeybd.h303
8 files changed, 1187 insertions, 23 deletions
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..aa90a01
--- /dev/null
+++ b/TODO
@@ -0,0 +1,38 @@
+TODO for XtoW
+=============
+
+new features
+
+* we always have access to full contents of window (when mapped), so previewing, thumbnailing, etc. works correctly
+* we have access to a window pixmap with alpha channel, so windows are composed into native desktop
+
+features missing compared to XWin's integrated WM
+
+those that probably need libxcwm support:
+* Convert icon found in icon property to native icon for window
+* SHAPE extension support
+* Change the native cursor to the X cursor image when it's over an X window
+* WM_DISPLAYCHANGE should use RANDR to change the display size
+* multiple native screens should be known to XINERAMA
+* the ability to ring the bell
+* accelerate OpenGL? (requires cunning plan)
+
+those that just need writing:
+* 'keyhook' option, so we can catch all keypress and consider if we want to process them, rather than only
+getting the ones that the windows shell isn't using (e.g. Windows key, Menu key, Alt-Tab)
+* 3 button mouse emulation (if anyone actually needs this anymore...)
+* Set the X keyboard configuration to match the native one
+* Synchronize the X server modifier key state with the native one at startup, and whenever an X window gains focus (as modifiers might have been latched or unlatched without us noticing)
+* Faster way of getting the bits onto the screen than GDI BitBlt() (e.g. DirectDraw)
+
+other stuff:
+
+* lcwm doesn't adopt any existing windows when it starts
+* lcwm doesn't understand about InputOnly windows (probably should just ignore them)
+* support the _NET_WM_WINDOW_OPACITY property using layered windows on XP, even if we can't use alpha
+
+* Libraryize the code which is currently copied out of the Xserver
+- Initially, make it a library in the xserver codebase, so we can share it?
+- XWin on MinGW is going to be around for a while, and I don't need the maintainance headache
+
+* Implement -version command line option
diff --git a/src/Makefile.am b/src/Makefile.am
index 1ca1a0d..25f8a28 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,6 +7,8 @@ xtow_SOURCES = \
win.c \
debug.c \
debug.h \
- winmessages.h
+ winkeybd.c \
+ winmessages.h \
+ scancodes.h
xtow_LDADD = $(XCWM_LIBS) -lgdi32
diff --git a/src/global.h b/src/global.h
new file mode 100644
index 0000000..14d13ff
--- /dev/null
+++ b/src/global.h
@@ -0,0 +1,22 @@
+//
+// Copyright © Jon TURNEY 2012
+//
+// This file is part of XtoW.
+//
+// XtoW 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.
+//
+// XtoW 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 XtoW. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <xcwm/xcwm.h>
+
+extern xcwm_context_t *context;
diff --git a/src/main.c b/src/main.c
index 01eb40f..60ca79f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,6 +24,13 @@
#include <xcwm/xcwm.h>
#include "debug.h"
+#include "global.h"
+
+#define WM_XCWM_CREATE WM_USER
+#define WM_XCWM_DESTROY (WM_USER+1)
+
+xcwm_context_t *context;
+DWORD msgPumpThread;
void
eventHandler(const xcwm_event_t *event)
@@ -36,11 +43,20 @@ eventHandler(const xcwm_event_t *event)
switch (type)
{
case XCWM_EVENT_WINDOW_CREATE:
- winCreateWindowsWindow(window);
+ /*
+ Windows windows are owned by the thread they are created by,
+ and only the thread which owns the window can receive messages for it.
+ So, we must arrange for the thread which we want to run the Windows
+ message pump in to create the window...
+ */
+ PostThreadMessage(msgPumpThread, WM_XCWM_CREATE, 0, (LPARAM)window);
break;
case XCWM_EVENT_WINDOW_DESTROY:
- winDestroyWindowsWindow(window);
+ /*
+ Only the owner thread is allowed to destroy a window
+ */
+ PostThreadMessage(msgPumpThread, WM_XCWM_DESTROY, 0, (LPARAM)window);
break;
case XCWM_EVENT_WINDOW_NAME:
@@ -102,16 +118,26 @@ int main(int argc, char **argv)
DEBUG("screen is '%s'\n", screen);
- xcwm_context_t *context = xcwm_context_open(screen);
+ // ensure this thread has a message queue
+ MSG msg;
+ PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
+ msgPumpThread = GetCurrentThreadId();
+
+ // create the global xcwm context
+ context = xcwm_context_open(screen);
// spawn the event loop thread, and set the callback function
xcwm_event_start_loop(context, eventHandler);
- // Pump windows message queue
- MSG msg;
+ // pump windows message queue
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
- DispatchMessage(&msg);
+ if (msg.message == WM_XCWM_CREATE)
+ winCreateWindowsWindow(msg.lParam);
+ else if (msg.message == WM_XCWM_DESTROY)
+ winDestroyWindowsWindow(msg.lParam);
+ else
+ DispatchMessage(&msg);
}
// Shutdown:
diff --git a/src/scancodes.h b/src/scancodes.h
new file mode 100644
index 0000000..3215a4b
--- /dev/null
+++ b/src/scancodes.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Thomas Roell not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. Thomas Roell makes no representations
+ * about the suitability of this software for any purpose. It is provided
+ * "as is" without express or implied warranty.
+ *
+ * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef SCANCODES_H
+#define SCANCODES_H
+
+/*
+ * NOTE: The AT/MF keyboards can generate (via the 8042) two (MF: three)
+ * sets of scancodes. Set3 can only be generated by a MF keyboard.
+ * Set2 sends a makecode for keypress, and the same code prefixed by a
+ * F0 for keyrelease. This is a little bit ugly to handle. Thus we use
+ * here for X386 the PC/XT compatible Set1. This set uses 8bit scancodes.
+ * Bit 7 ist set if the key is released. The code E0 switches to a
+ * different meaning to add the new MF cursorkeys, while not breaking old
+ * applications. E1 is another special prefix. Since I assume that there
+ * will be further versions of PC/XT scancode compatible keyboards, we
+ * may be in trouble one day.
+ *
+ * IDEA: 1) Use Set2 on AT84 keyboards and translate it to MF Set3.
+ * 2) Use the keyboards native set and translate it to common keysyms.
+ */
+
+/*
+ * definition of the AT84/MF101/MF102 Keyboard:
+ * ============================================================
+ * Defined Key Cap Glyphs Pressed value
+ * Key Name Main Also (hex) (dec)
+ * ---------------- ---------- ------- ------ ------
+ */
+
+#define KEY_Escape /* Escape 0x01 */ 1
+#define KEY_1 /* 1 ! 0x02 */ 2
+#define KEY_2 /* 2 @ 0x03 */ 3
+#define KEY_3 /* 3 # 0x04 */ 4
+#define KEY_4 /* 4 $ 0x05 */ 5
+#define KEY_5 /* 5 % 0x06 */ 6
+#define KEY_6 /* 6 ^ 0x07 */ 7
+#define KEY_7 /* 7 & 0x08 */ 8
+#define KEY_8 /* 8 * 0x09 */ 9
+#define KEY_9 /* 9 ( 0x0a */ 10
+#define KEY_0 /* 0 ) 0x0b */ 11
+#define KEY_Minus /* - (Minus) _ (Under) 0x0c */ 12
+#define KEY_Equal /* = (Equal) + 0x0d */ 13
+#define KEY_BackSpace /* Back Space 0x0e */ 14
+#define KEY_Tab /* Tab 0x0f */ 15
+#define KEY_Q /* Q 0x10 */ 16
+#define KEY_W /* W 0x11 */ 17
+#define KEY_E /* E 0x12 */ 18
+#define KEY_R /* R 0x13 */ 19
+#define KEY_T /* T 0x14 */ 20
+#define KEY_Y /* Y 0x15 */ 21
+#define KEY_U /* U 0x16 */ 22
+#define KEY_I /* I 0x17 */ 23
+#define KEY_O /* O 0x18 */ 24
+#define KEY_P /* P 0x19 */ 25
+#define KEY_LBrace /* [ { 0x1a */ 26
+#define KEY_RBrace /* ] } 0x1b */ 27
+#define KEY_Enter /* Enter 0x1c */ 28
+#define KEY_LCtrl /* Ctrl(left) 0x1d */ 29
+#define KEY_A /* A 0x1e */ 30
+#define KEY_S /* S 0x1f */ 31
+#define KEY_D /* D 0x20 */ 32
+#define KEY_F /* F 0x21 */ 33
+#define KEY_G /* G 0x22 */ 34
+#define KEY_H /* H 0x23 */ 35
+#define KEY_J /* J 0x24 */ 36
+#define KEY_K /* K 0x25 */ 37
+#define KEY_L /* L 0x26 */ 38
+#define KEY_SemiColon /* ;(SemiColon) :(Colon) 0x27 */ 39
+#define KEY_Quote /* ' (Apostr) " (Quote) 0x28 */ 40
+#define KEY_Tilde /* ` (Accent) ~ (Tilde) 0x29 */ 41
+#define KEY_ShiftL /* Shift(left) 0x2a */ 42
+#define KEY_BSlash /* \(BckSlash) |(VertBar)0x2b */ 43
+#define KEY_Z /* Z 0x2c */ 44
+#define KEY_X /* X 0x2d */ 45
+#define KEY_C /* C 0x2e */ 46
+#define KEY_V /* V 0x2f */ 47
+#define KEY_B /* B 0x30 */ 48
+#define KEY_N /* N 0x31 */ 49
+#define KEY_M /* M 0x32 */ 50
+#define KEY_Comma /* , (Comma) < (Less) 0x33 */ 51
+#define KEY_Period /* . (Period) >(Greater)0x34 */ 52
+#define KEY_Slash /* / (Slash) ? 0x35 */ 53
+#define KEY_ShiftR /* Shift(right) 0x36 */ 54
+#define KEY_KP_Multiply /* * 0x37 */ 55
+#define KEY_Alt /* Alt(left) 0x38 */ 56
+#define KEY_Space /* (SpaceBar) 0x39 */ 57
+#define KEY_CapsLock /* CapsLock 0x3a */ 58
+#define KEY_F1 /* F1 0x3b */ 59
+#define KEY_F2 /* F2 0x3c */ 60
+#define KEY_F3 /* F3 0x3d */ 61
+#define KEY_F4 /* F4 0x3e */ 62
+#define KEY_F5 /* F5 0x3f */ 63
+#define KEY_F6 /* F6 0x40 */ 64
+#define KEY_F7 /* F7 0x41 */ 65
+#define KEY_F8 /* F8 0x42 */ 66
+#define KEY_F9 /* F9 0x43 */ 67
+#define KEY_F10 /* F10 0x44 */ 68
+#define KEY_NumLock /* NumLock 0x45 */ 69
+#define KEY_ScrollLock /* ScrollLock 0x46 */ 70
+#define KEY_KP_7 /* 7 Home 0x47 */ 71
+#define KEY_KP_8 /* 8 Up 0x48 */ 72
+#define KEY_KP_9 /* 9 PgUp 0x49 */ 73
+#define KEY_KP_Minus /* - (Minus) 0x4a */ 74
+#define KEY_KP_4 /* 4 Left 0x4b */ 75
+#define KEY_KP_5 /* 5 0x4c */ 76
+#define KEY_KP_6 /* 6 Right 0x4d */ 77
+#define KEY_KP_Plus /* + (Plus) 0x4e */ 78
+#define KEY_KP_1 /* 1 End 0x4f */ 79
+#define KEY_KP_2 /* 2 Down 0x50 */ 80
+#define KEY_KP_3 /* 3 PgDown 0x51 */ 81
+#define KEY_KP_0 /* 0 Insert 0x52 */ 82
+#define KEY_KP_Decimal /* . (Decimal) Delete 0x53 */ 83
+#define KEY_SysReqest /* SysReqest 0x54 */ 84
+#define KEY_Fn /* Fn 0x55 */ 85
+#define KEY_Less /* < (Less) >(Greater) 0x56 */ 86
+#define KEY_F11 /* F11 0x57 */ 87
+#define KEY_F12 /* F12 0x58 */ 88
+
+#define KEY_Prefix0 /* special 0x60 */ 96
+#define KEY_Prefix1 /* specail 0x61 */ 97
+
+/*
+ * The 'scancodes' below are generated by the server, because the MF101/102
+ * keyboard sends them as sequence of other scancodes
+ */
+#define KEY_Home /* Home 0x59 */ 89
+#define KEY_Up /* Up 0x5a */ 90
+#define KEY_PgUp /* PgUp 0x5b */ 91
+#define KEY_Left /* Left 0x5c */ 92
+#define KEY_Begin /* Begin 0x5d */ 93
+#define KEY_Right /* Right 0x5e */ 94
+#define KEY_End /* End 0x5f */ 95
+#define KEY_Down /* Down 0x60 */ 96
+#define KEY_PgDown /* PgDown 0x61 */ 97
+#define KEY_Insert /* Insert 0x62 */ 98
+#define KEY_Delete /* Delete 0x63 */ 99
+#define KEY_KP_Enter /* Enter 0x64 */ 100
+#define KEY_RCtrl /* Ctrl(right) 0x65 */ 101
+#define KEY_Pause /* Pause 0x66 */ 102
+#define KEY_Print /* Print 0x67 */ 103
+#define KEY_KP_Divide /* Divide 0x68 */ 104
+#define KEY_AltLang /* AtlLang(right) 0x69 */ 105
+#define KEY_Break /* Break 0x6a */ 106
+#define KEY_LMeta /* Left Meta 0x6b */ 107
+#define KEY_RMeta /* Right Meta 0x6c */ 108
+#define KEY_Menu /* Menu 0x6d */ 109
+#define KEY_F13 /* F13 0x6e */ 110
+#define KEY_F14 /* F14 0x6f */ 111
+#define KEY_F15 /* F15 0x70 */ 112
+#define KEY_F16 /* F16 0x71 */ 113
+#define KEY_F17 /* F17 0x72 */ 114
+#define KEY_KP_DEC /* KP_DEC 0x73 */ 115
+#define KEY_KP_Equal /* Equal (Keypad) 0x76 */ 118
+#define KEY_XFER /* Kanji Transfer 0x79 */ 121
+#define KEY_NFER /* No Kanji Transfer 0x7b */ 123
+#define KEY_Yen /* Yen 0x7d */ 125
+#define KEY_HKTG /* Hirugana/Katakana tog 0xc8 */ 200
+#define KEY_BSlash2 /* \ _ 0xcb */ 203
+
+#define KEY_Mute /* Audio Mute */ 152
+#define KEY_AudioLower /* Audio Lower */ 168
+#define KEY_AudioRaise /* Audio Raise */ 166
+
+#define KEY_NEXTSONG /* Media next */ 145
+#define KEY_PLAYPAUSE /* Media play/pause toggle */ 154
+#define KEY_PREVIOUSSONG /* Media previous */ 136
+#define KEY_STOPCD /* Media stop */ 156
+
+/* These are for "notused" and "unknown" entries in translation maps. */
+#define KEY_NOTUSED 0
+#define KEY_UNKNOWN 255
+
+#endif /* SCANCODES_H */
diff --git a/src/win.c b/src/win.c
index 527fa9a..fba4ae0 100644
--- a/src/win.c
+++ b/src/win.c
@@ -23,8 +23,10 @@
//
#include <windows.h>
+#include <windowsx.h>
#include <stdbool.h>
#include <stdio.h>
+#include <assert.h>
#include <xcwm/xcwm.h>
#include "debug.h"
@@ -100,6 +102,20 @@ ValidateSizing (HWND hwnd, xcwm_window_t *window,
}
/*
+ Convert coordinates pt from client area of hWnd to X server
+ */
+static void
+ClientToXCoord(HWND hWnd, POINT *pt)
+{
+ /* Translate the client area mouse coordinates to screen (virtual desktop) coordinates */
+ ClientToScreen(hWnd, pt);
+
+ /* Translate from screen coordinates to X coordinates */
+ pt->x -= GetSystemMetrics(SM_XVIRTUALSCREEN);
+ pt->y -= GetSystemMetrics(SM_YVIRTUALSCREEN);
+}
+
+/*
* winAdjustXWindow
*
* Move and resize X window with respect to corresponding Windows window.
@@ -114,7 +130,7 @@ int
winAdjustXWindow (xcwm_window_t *window, HWND hWnd)
{
RECT rcWin;
- LONG x, y, w, h;
+ LONG w, h;
#define WIDTH(rc) (rc.right - rc.left)
#define HEIGHT(rc) (rc.bottom - rc.top)
@@ -134,16 +150,14 @@ winAdjustXWindow (xcwm_window_t *window, HWND hWnd)
w = WIDTH(rcWin);
h = HEIGHT(rcWin);
- /* Get virtual desktop coordinates of top-left of client area,
- and transform to X screen coordinates */
+ /* Transform virtual desktop coordinates of top-left of client area,
+ to X screen coordinates */
POINT p = {0,0};
- ClientToScreen(hWnd, &p);
- x = p.x - GetSystemMetrics(SM_XVIRTUALSCREEN);
- y = p.y - GetSystemMetrics(SM_YVIRTUALSCREEN);
+ ClientToXCoord(hWnd, &p);
- DEBUG("ConfigureWindow to %ldx%ld @ (%ld, %ld)\n", w, h, x, y);
+ DEBUG("ConfigureWindow to %ldx%ld @ (%ld, %ld)\n", w, h, p.x, p.y);
- xcwm_window_configure(window, x, y, w, h);
+ xcwm_window_configure(window, p.x, p.y, h, w);
return 1;
@@ -198,7 +212,9 @@ UpdateImage(xcwm_window_t *window)
damage.right = dmgRect->x + dmgRect->width;
damage.bottom = dmgRect->y + dmgRect->height;
- InvalidateRect(hWnd, &damage, TRUE);
+ DEBUG("UpdateImage: invalidating %dx%d @ %d,%d on HWND 0x08%x\n", dmgRect->width, dmgRect->height, damage.left, damage.top, hWnd);
+ // InvalidateRect(hWnd, &damage, TRUE);
+ InvalidateRect(hWnd, NULL, TRUE); // invalidate whole window for a test
}
#if 0
@@ -493,6 +509,59 @@ BumpWindowPosition(HWND hWnd)
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
+#define WIN_POLLING_MOUSE_TIMER_ID 2
+#define MOUSE_POLLING_INTERVAL 50
+
+static UINT_PTR g_uipMousePollingTimerID = 0;
+
+static void
+winStartMousePolling(void)
+{
+ /*
+ * Timer to poll mouse position. This is needed to make
+ * programs like xeyes follow the mouse properly when the
+ * mouse pointer is outside of any X window.
+ */
+ if (g_uipMousePollingTimerID == 0)
+ g_uipMousePollingTimerID = SetTimer(NULL,
+ WIN_POLLING_MOUSE_TIMER_ID,
+ MOUSE_POLLING_INTERVAL, NULL);
+}
+
+static bool g_fButton[3] = { FALSE, FALSE, FALSE };
+
+int
+winMouseButtonsHandle(xcwm_window_t *window, bool press, int iButton, HWND hWnd, LPARAM lParam)
+{
+ /* 3 button emulation code would go here, if we thought anyone actually needed it anymore... */
+
+ g_fButton[iButton] = press;
+ if (press)
+ {
+ SetCapture(hWnd);
+ }
+ else
+ {
+ ReleaseCapture();
+ winStartMousePolling();
+ }
+
+ /* XXX: this looks like it would do the wrong thing when multiple mouse buttons are pressed and
+ only one released */
+
+ /* Unpack the client area mouse coordinates */
+ POINT ptMouse;
+ ptMouse.x = GET_X_LPARAM(lParam);
+ ptMouse.y = GET_Y_LPARAM(lParam);
+
+ /* Translate from client area coordinates to X coordinates */
+ ClientToXCoord(hWnd, &ptMouse);
+
+ xcwm_input_mouse_button_event(window, ptMouse.x, ptMouse.y, iButton, press);
+
+ return 0;
+}
+
static void
winDebugWin32Message(const char* function, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
@@ -528,6 +597,8 @@ LRESULT CALLBACK
winTopLevelWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static bool hasEnteredSizeMove = FALSE;
+ /* XXX: a global is wrong if WM_MOUSEMOVE of the new window is delivered before WM_MOUSELEAVE of the old? Use HWND instead? */
+ static bool s_fTracking = FALSE;
winDebugWin32Message("winTopLevelWindowProc", hWnd, message, wParam, lParam);
@@ -556,9 +627,36 @@ winTopLevelWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
/* If focus is going to another Windows app, no X window has the X input focus */
if (!wParam)
{
- xcwm_window_set_input_focus(NULL);
+ xcb_set_input_focus(window->context->conn, XCB_INPUT_FOCUS_NONE,
+ XCB_WINDOW_NONE, XCB_CURRENT_TIME);
+ }
+ return 0;
+
+ case WM_SETFOCUS:
+ {
+ /* Get the parent window for transient handling */
+ HWND hParent = GetParent(hWnd);
+
+ if (hParent && IsIconic(hParent))
+ ShowWindow(hParent, SW_RESTORE);
}
+ /* Synchronize all latching key states */
+ // winRestoreModeKeyStates();
+
+ return 0;
+
+ case WM_KILLFOCUS:
+ /* Pop any pressed keys since we are losing keyboard focus */
+ winKeybdReleaseKeys();
+
+ /* Drop the X focus as well, but only if the Windows focus is going to another window */
+ if (!wParam)
+ xcb_set_input_focus(window->context->conn, XCB_INPUT_FOCUS_NONE,
+ XCB_WINDOW_NONE, XCB_CURRENT_TIME);
+
+ return 0;
+
case WM_MOUSEACTIVATE:
/* ??? override-redirect windows should not mouse-activate */
break;
@@ -604,7 +702,10 @@ winTopLevelWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
return 0;
case WM_SHOWWINDOW:
- /* ??? */
+ /* If window is shown, start mouse polling in case focus isn't on it */
+ if (wParam)
+ winStartMousePolling();
+
break;
case WM_WINDOWPOSCHANGED:
@@ -704,10 +805,7 @@ winTopLevelWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
}
else
{
- /* XXX: just eat it for now... */
- ValidateRect(hWnd, &(ps.rcPaint));
-
- /* stop xcwm processing events so it doesn't can't change
+ /* stop xcwm processing events so it can't change
this damage while we are using it */
xcwm_event_get_thread_lock();
@@ -718,7 +816,58 @@ winTopLevelWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
xcwm_rect_t *winRect = xcwm_window_get_full_rect(window);
xcwm_rect_t *dmgRect = xcwm_window_get_damaged_rect(window);
- // convert xcwm_image_t to something we can draw...
+ DEBUG("full_rect is %ldx%ld @ (%ld, %ld)\n", winRect->width, winRect->height, winRect->x, winRect->y);
+ DEBUG("damaged rect is %ldx%ld @ (%ld, %ld)\n", dmgRect->width, dmgRect->height, dmgRect->x, dmgRect->y);
+ DEBUG("invalidated rect is %ldx%ld @ (%ld, %ld)\n", ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, ps.rcPaint.left, ps.rcPaint.top);
+
+ assert(image->image->scanline_pad = 32); // DIBs are always 32 bit aligned
+ assert(((int)image->image->data % 4) == 0); // ?
+
+ // XXX: probably should use BITMAPV5HEADER ?
+ // describe the bitmap format we are given
+ BITMAPINFO diBmi;
+ memset(&diBmi, 0, sizeof(diBmi));
+ diBmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ diBmi.bmiHeader.biWidth = image->width;
+ diBmi.bmiHeader.biHeight = -image->height;
+ diBmi.bmiHeader.biPlanes = 1;
+ diBmi.bmiHeader.biBitCount = image->image->bpp;
+ diBmi.bmiHeader.biCompression = BI_RGB;
+ diBmi.bmiHeader.biSizeImage = 0; // image->image->size;
+ diBmi.bmiHeader.biClrUsed = 0;
+ diBmi.bmiHeader.biClrImportant = 0;
+ // diBmi.bmiColors unused unless biBitCount is 8 or less, or biCompression is BI_BITFIELDS, or biClrUsed is non-zero...
+
+#if 0
+ int lines = SetDIBitsToDevice(hdcUpdate, dmgRect->x, dmgRect->y, dmgRect->width, dmgRect->height,
+ 0, 0, 0, image->height, image->image->data, &diBmi, DIB_RGB_COLORS);
+#endif
+
+ int lines = StretchDIBits(hdcUpdate,
+ dmgRect->x, dmgRect->y, dmgRect->width, dmgRect->height,
+ 0, 0, image->width, image->height,
+ image->image->data, &diBmi, DIB_RGB_COLORS, SRCCOPY);
+
+ DEBUG("returned %d\n", lines);
+ if (lines == 0)
+ fprintf(stderr, "failed: 0x%08x\n", GetLastError());
+
+#if 0
+ // create compatible DC, create a device compatible bitmap from the image, and select it into the DC
+ HDC hdcMem = CreateCompatibleDC(hdcUpdate);
+ HBITMAP hBitmap = CreateDIBitmap(hdcMem, &(diBmi.bmiHeader), CBM_INIT, image->image->data, &diBmi, DIB_RGB_COLORS);
+ HBITMAP hStockBitmap = SelectObject(hdcMem, hBitmap);
+
+ // transfer the bits from our compatible DC to the display
+ if (!BitBlt(hdcUpdate, dmgRect->x, dmgRect->y, dmgRect->width, dmgRect->height, hdcMem, 0, 0, SRCCOPY))
+ {
+ fprintf(stderr, "winTopLevelWindowProc: BitBlt failed: 0x%08x\n", GetLastError());
+ }
+
+ SelectObject(hdcMem, hStockBitmap);
+ DeleteObject(hBitmap);
+ DeleteDC(hdcMem);
+#endif
// Remove the damage
xcwm_window_remove_damage(window);
@@ -730,6 +879,142 @@ winTopLevelWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
return 0;
}
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_KEYUP:
+ {
+ bool press = (message == WM_SYSKEYDOWN) || (message == WM_KEYDOWN);
+
+ /*
+ * Don't do anything for the Windows keys, as focus will soon
+ * be returned to Windows.
+ */
+ if (wParam == VK_LWIN || wParam == VK_RWIN)
+ break;
+
+ /* Discard fake Ctrl_L events that precede AltGR on non-US keyboards */
+ if (winIsFakeCtrl_L(message, wParam, lParam))
+ return 0;
+
+ /*
+ * Discard presses generated from Windows auto-repeat
+ */
+ if (press && (lParam & (1 << 30)))
+ {
+ switch (wParam) {
+ /* ago: Pressing LControl while RControl is pressed is
+ * Indicated as repeat. Fix this!
+ */
+ case VK_CONTROL:
+ case VK_SHIFT:
+ if (winCheckKeyPressed(wParam, lParam))
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ /* Translate Windows key code to X scan code */
+ int iScanCode;
+ winTranslateKey(wParam, lParam, &iScanCode);
+
+ /* Ignore press repeats for CapsLock */
+ if (press && (wParam == VK_CAPITAL))
+ lParam = 1;
+
+ /* Send the key event(s) */
+ int i;
+ for (i = 0; i < LOWORD(lParam); ++i)
+ winSendKeyEvent(iScanCode, TRUE);
+
+ /* Release all pressed shift keys */
+ if (!press && (wParam == VK_SHIFT))
+ winFixShiftKeys(iScanCode);
+ }
+
+ return 0;
+
+ case WM_MOUSEMOVE:
+ {
+ /* Have we requested WM_MOUSELEAVE when mouse leaves window? */
+ if (!s_fTracking)
+ {
+ TRACKMOUSEEVENT tme;
+
+ /* Setup data structure */
+ memset(&tme, 0, sizeof(tme));
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hWnd;
+
+ /* Call the tracking function */
+ if (!TrackMouseEvent(&tme))
+ fprintf(stderr, "winTopLevelWindowProc: TrackMouseEvent failed\n");
+
+ /* Flag that we are tracking now */
+ s_fTracking = TRUE;
+ }
+
+ /* Kill the timer used to poll mouse position */
+ if (g_uipMousePollingTimerID != 0)
+ {
+ KillTimer(NULL, WIN_POLLING_MOUSE_TIMER_ID);
+ g_uipMousePollingTimerID = 0;
+ }
+
+ /* Unpack the client area mouse coordinates */
+ POINT ptMouse;
+ ptMouse.x = GET_X_LPARAM(lParam);
+ ptMouse.y = GET_Y_LPARAM(lParam);
+
+ /* Translate the client area mouse coordinates to X coordinates */
+ ClientToXCoord(hWnd, &ptMouse);
+
+ /* Deliver absolute cursor position to X Server */
+ xcwm_input_mouse_motion(xcwm_window_get_context(window), ptMouse.x, ptMouse.y);
+
+ return 0;
+ }
+
+ case WM_MOUSELEAVE:
+ /* Mouse has left our client area */
+ /* Flag that we are no longer tracking */
+ s_fTracking = FALSE;
+ winStartMousePolling();
+ return 0;
+
+ case WM_LBUTTONDBLCLK:
+ case WM_LBUTTONDOWN:
+ return winMouseButtonsHandle(window, TRUE, 0, hWnd, lParam);
+
+ case WM_LBUTTONUP:
+ return winMouseButtonsHandle(window, FALSE, 0, hWnd, lParam);
+
+ case WM_MBUTTONDBLCLK:
+ case WM_MBUTTONDOWN:
+ return winMouseButtonsHandle(window, TRUE, 1, hWnd, lParam);
+
+ case WM_MBUTTONUP:
+ return winMouseButtonsHandle(window, FALSE, 1, hWnd, lParam);
+
+ case WM_RBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ return winMouseButtonsHandle(window, TRUE, 2, hWnd, lParam);
+
+ case WM_RBUTTONUP:
+ return winMouseButtonsHandle(window, FALSE, 2, hWnd, lParam);
+
+ case WM_XBUTTONDBLCLK:
+ case WM_XBUTTONDOWN:
+ return winMouseButtonsHandle(window, TRUE, HIWORD(wParam) + 5, hWnd, lParam);
+
+ case WM_XBUTTONUP:
+ return winMouseButtonsHandle(window, FALSE, HIWORD(wParam) + 5, hWnd, lParam);
+
+ case WM_MOUSEWHEEL:
+ return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
@@ -910,6 +1195,10 @@ winCreateWindowsWindow(xcwm_window_t *window)
/* zstyle = SWP_TOPMOST; */
}
+ /* XXX: set reasonable style, for now */
+ SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_APPWINDOW);
+ SetWindowLongPtr(hWnd, GWL_STYLE, GetWindowLongPtr(hWnd, GWL_STYLE) | WS_SYSMENU | WS_BORDER | WS_CAPTION);
+
/* Apply all properties which effect the window appearance or behaviour */
UpdateName(window);
/* UpdateIcon(); */
diff --git a/src/winkeybd.c b/src/winkeybd.c
new file mode 100644
index 0000000..6444785
--- /dev/null
+++ b/src/winkeybd.c
@@ -0,0 +1,290 @@
+/* Copyright (c) 2012 Jon TURNEY
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <windows.h>
+#include <stdbool.h>
+
+#include "debug.h"
+#include "global.h"
+#include "winkeybd.h"
+
+static bool g_winKeyState[NUM_KEYCODES];
+
+/*
+ * Translate a Windows WM_[SYS]KEY(UP/DOWN) message
+ * into an ASCII scan code.
+ *
+ * We do this ourselves, rather than letting Windows handle it,
+ * because Windows tends to munge the handling of special keys,
+ * like AltGr on European keyboards.
+ */
+
+void
+winTranslateKey(WPARAM wParam, LPARAM lParam, int *piScanCode)
+{
+ int iKeyFixup = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 1];
+ int iKeyFixupEx = g_iKeyMap[wParam * WIN_KEYMAP_COLS + 2];
+ int iParam = HIWORD(lParam);
+ int iParamScanCode = LOBYTE(iParam);
+
+ DEBUG("winTranslateKey: wParam %08x lParam %08x\n", wParam, lParam);
+
+ /* WM_ key messages faked by Vista speech recognition (WSR) don't have a
+ * scan code.
+ *
+ * Vocola 3 (Rick Mohr's supplement to WSR) uses
+ * System.Windows.Forms.SendKeys.SendWait(), which appears always to give a
+ * scan code of 1
+ */
+ if (iParamScanCode <= 1) {
+ if (VK_PRIOR <= wParam && wParam <= VK_DOWN)
+ /* Trigger special case table to translate to extended
+ * keycode, otherwise if num_lock is on, we can get keypad
+ * numbers instead of navigation keys. */
+ iParam |= KF_EXTENDED;
+ else
+ iParamScanCode = MapVirtualKeyEx(wParam,
+ /*MAPVK_VK_TO_VSC */ 0,
+ GetKeyboardLayout(0));
+ }
+
+ /* Branch on special extended, special non-extended, or normal key */
+ if ((iParam & KF_EXTENDED) && iKeyFixupEx)
+ *piScanCode = iKeyFixupEx;
+ else if (iKeyFixup)
+ *piScanCode = iKeyFixup;
+ else if (wParam == 0 && iParamScanCode == 0x70)
+ *piScanCode = KEY_HKTG;
+ else
+ switch (iParamScanCode) {
+ case 0x70:
+ *piScanCode = KEY_HKTG;
+ break;
+ case 0x73:
+ *piScanCode = KEY_BSlash2;
+ break;
+ default:
+ *piScanCode = iParamScanCode;
+ break;
+ }
+}
+
+/*
+ * Lift any keys that are pressed
+ */
+void
+winKeybdReleaseKeys(void)
+{
+ int i;
+
+ /* Loop through all keys */
+ for (i = 0; i < NUM_KEYCODES; ++i) {
+ /* Pop key if pressed */
+ if (g_winKeyState[i])
+ winSendKeyEvent(i, FALSE);
+
+ /* Reset pressed flag for keys */
+ g_winKeyState[i] = FALSE;
+ }
+}
+
+/*
+ * Take a raw X key code and send an up or down event for it.
+ *
+ * Thanks to VNC for inspiration, though it is a simple function.
+ */
+void
+winSendKeyEvent(DWORD dwKey, bool fDown)
+{
+ /*
+ * When alt-tabing between screens we can get phantom key up messages
+ * Here we only pass them through it we think we should!
+ */
+ if (g_winKeyState[dwKey] == FALSE && fDown == FALSE)
+ return;
+
+ /* Update the keyState map */
+ g_winKeyState[dwKey] = fDown;
+
+ xcwm_input_key_event(context, dwKey + MIN_KEYCODE, fDown);
+
+ DEBUG("winSendKeyEvent: dwKey: %d, fDown: %d\n", dwKey, fDown);
+}
+
+/*
+ * Look for the lovely fake Control_L press/release generated by Windows
+ * when AltGr is pressed/released on a non-U.S. keyboard.
+ */
+
+bool
+winIsFakeCtrl_L(UINT message, WPARAM wParam, LPARAM lParam)
+{
+ MSG msgNext;
+ LONG lTime;
+ bool fReturn;
+
+ static bool lastWasControlL = FALSE;
+ static UINT lastMessage;
+ static LONG lastTime;
+
+ /*
+ * Fake Ctrl_L presses will be followed by an Alt_R press
+ * with the same timestamp as the Ctrl_L press.
+ */
+ if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
+ && wParam == VK_CONTROL && (HIWORD(lParam) & KF_EXTENDED) == 0) {
+ /* Got a Ctrl_L press */
+
+ /* Get time of current message */
+ lTime = GetMessageTime();
+
+ /* Look for next press message */
+ fReturn = PeekMessage(&msgNext, NULL,
+ WM_KEYDOWN, WM_SYSKEYDOWN, PM_NOREMOVE);
+
+ if (fReturn && msgNext.message != WM_KEYDOWN &&
+ msgNext.message != WM_SYSKEYDOWN)
+ fReturn = 0;
+
+ if (!fReturn) {
+ lastWasControlL = TRUE;
+ lastMessage = message;
+ lastTime = lTime;
+ }
+ else {
+ lastWasControlL = FALSE;
+ }
+
+ /* Is next press an Alt_R with the same timestamp? */
+ if (fReturn && msgNext.wParam == VK_MENU
+ && msgNext.time == lTime
+ && (HIWORD(msgNext.lParam) & KF_EXTENDED)) {
+ /*
+ * Next key press is Alt_R with same timestamp as current
+ * Ctrl_L message. Therefore, this Ctrl_L press is a fake
+ * event, so discard it.
+ */
+ return TRUE;
+ }
+ }
+ /*
+ * Sometimes, the Alt_R press message is not yet posted when the
+ * fake Ctrl_L press message arrives (even though it has the
+ * same timestamp), so check for an Alt_R press message that has
+ * arrived since the last Ctrl_L message.
+ */
+ else if ((message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
+ && wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
+ /* Got a Alt_R press */
+
+ if (lastWasControlL) {
+ lTime = GetMessageTime();
+
+ if (lastTime == lTime) {
+ /* Undo the fake Ctrl_L press by sending a fake Ctrl_L release */
+ winSendKeyEvent(KEY_LCtrl, FALSE);
+ }
+ lastWasControlL = FALSE;
+ }
+ }
+ /*
+ * Fake Ctrl_L releases will be followed by an Alt_R release
+ * with the same timestamp as the Ctrl_L release.
+ */
+ else if ((message == WM_KEYUP || message == WM_SYSKEYUP)
+ && wParam == VK_CONTROL && (HIWORD(lParam) & KF_EXTENDED) == 0) {
+ /* Got a Ctrl_L release */
+
+ /* Get time of current message */
+ lTime = GetMessageTime();
+
+ /* Look for next release message */
+ fReturn = PeekMessage(&msgNext, NULL,
+ WM_KEYUP, WM_SYSKEYUP, PM_NOREMOVE);
+
+ if (fReturn && msgNext.message != WM_KEYUP &&
+ msgNext.message != WM_SYSKEYUP)
+ fReturn = 0;
+
+ lastWasControlL = FALSE;
+
+ /* Is next press an Alt_R with the same timestamp? */
+ if (fReturn
+ && (msgNext.message == WM_KEYUP || msgNext.message == WM_SYSKEYUP)
+ && msgNext.wParam == VK_MENU
+ && msgNext.time == lTime
+ && (HIWORD(msgNext.lParam) & KF_EXTENDED)) {
+ /*
+ * Next key release is Alt_R with same timestamp as current
+ * Ctrl_L message. Therefore, this Ctrl_L release is a fake
+ * event, so discard it.
+ */
+ return TRUE;
+ }
+ }
+ else {
+ /* On any other press or release message, we don't have a
+ potentially fake Ctrl_L to worry about anymore... */
+ lastWasControlL = FALSE;
+ }
+
+ /* Not a fake control left press/release */
+ return FALSE;
+}
+
+BOOL
+winCheckKeyPressed(WPARAM wParam, LPARAM lParam)
+{
+ switch (wParam) {
+ case VK_CONTROL:
+ if ((lParam & 0x1ff0000) == 0x11d0000 && g_winKeyState[KEY_RCtrl])
+ return TRUE;
+ if ((lParam & 0x1ff0000) == 0x01d0000 && g_winKeyState[KEY_LCtrl])
+ return TRUE;
+ break;
+ case VK_SHIFT:
+ if ((lParam & 0x1ff0000) == 0x0360000 && g_winKeyState[KEY_ShiftR])
+ return TRUE;
+ if ((lParam & 0x1ff0000) == 0x02a0000 && g_winKeyState[KEY_ShiftL])
+ return TRUE;
+ break;
+ default:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Only one shift release message is sent even if both are pressed.
+ * Fix this here
+ */
+void
+winFixShiftKeys(int iScanCode)
+{
+ if (GetKeyState(VK_SHIFT) & 0x8000)
+ return;
+
+ if (iScanCode == KEY_ShiftL && g_winKeyState[KEY_ShiftR])
+ winSendKeyEvent(KEY_ShiftR, FALSE);
+ if (iScanCode == KEY_ShiftR && g_winKeyState[KEY_ShiftL])
+ winSendKeyEvent(KEY_ShiftL, FALSE);
+}
diff --git a/src/winkeybd.h b/src/winkeybd.h
new file mode 100644
index 0000000..287fbe8
--- /dev/null
+++ b/src/winkeybd.h
@@ -0,0 +1,303 @@
+/*
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef WINKEYBD_H
+#define WINKEYBD_H
+
+/*
+ * We need symbols for the scan codes of keys.
+ */
+#include "scancodes.h"
+
+#define NUM_KEYCODES 248
+#define MIN_KEYCODE 8
+#define MAX_KEYCODE (NUM_KEYCODES + MIN_KEYCODE - 1)
+
+#define VK_FN 0xFF
+
+#define WIN_KEYMAP_COLS 3
+
+/* Rows 160 through 165 correspond to software-generated codes, which
+ * may not be associated with the appropriate scan code.
+ */
+const int
+ g_iKeyMap[] = {
+ /* count Windows VK, ASCII, ASCII when extended VK */
+ /* 0 */ 0, 0, 0,
+ /* 1 */ 0, 0, 0,
+ /* 2 */ 0, 0, 0,
+ /* 3 */ VK_CANCEL, 0, KEY_Break,
+ /* 4 */ 0, 0, 0,
+ /* 5 */ 0, 0, 0,
+ /* 6 */ 0, 0, 0,
+ /* 7 */ 0, 0, 0,
+ /* 8 */ 0, 0, 0,
+ /* 9 */ 0, 0, 0,
+ /* 10 */ 0, 0, 0,
+ /* 11 */ 0, 0, 0,
+ /* 12 */ 0, 0, 0,
+ /* 13 */ VK_RETURN, 0, KEY_KP_Enter,
+ /* 14 */ 0, 0, 0,
+ /* 15 */ 0, 0, 0,
+ /* 16 */ VK_SHIFT, 0, 0,
+ /* 17 */ VK_CONTROL, 0, KEY_RCtrl,
+ /* 18 */ VK_MENU, 0, KEY_AltLang,
+ /* 19 */ VK_PAUSE, KEY_Pause, 0,
+ /* 20 */ 0, 0, 0,
+ /* 21 */ 0, 0, 0,
+ /* 22 */ 0, 0, 0,
+ /* 23 */ 0, 0, 0,
+ /* 24 */ 0, 0, 0,
+ /* 25 */ 0, 0, 0,
+ /* 26 */ 0, 0, 0,
+ /* 27 */ 0, 0, 0,
+ /* 28 */ 0, 0, 0,
+ /* 29 */ 0, 0, 0,
+ /* 30 */ 0, 0, 0,
+ /* 31 */ 0, 0, 0,
+ /* 32 */ 0, 0, 0,
+ /* 33 */ VK_PRIOR, 0, KEY_PgUp,
+ /* 34 */ VK_NEXT, 0, KEY_PgDown,
+ /* 35 */ VK_END, 0, KEY_End,
+ /* 36 */ VK_HOME, 0, KEY_Home,
+ /* 37 */ VK_LEFT, 0, KEY_Left,
+ /* 38 */ VK_UP, 0, KEY_Up,
+ /* 39 */ VK_RIGHT, 0, KEY_Right,
+ /* 40 */ VK_DOWN, 0, KEY_Down,
+ /* 41 */ 0, 0, 0,
+ /* 42 */ 0, 0, 0,
+ /* 43 */ 0, 0, 0,
+ /* 44 */ VK_SNAPSHOT, 0, KEY_Print,
+ /* 45 */ VK_INSERT, 0, KEY_Insert,
+ /* 46 */ VK_DELETE, 0, KEY_Delete,
+ /* 47 */ 0, 0, 0,
+ /* 48 */ 0, 0, 0,
+ /* 49 */ 0, 0, 0,
+ /* 50 */ 0, 0, 0,
+ /* 51 */ 0, 0, 0,
+ /* 52 */ 0, 0, 0,
+ /* 53 */ 0, 0, 0,
+ /* 54 */ 0, 0, 0,
+ /* 55 */ 0, 0, 0,
+ /* 56 */ 0, 0, 0,
+ /* 57 */ 0, 0, 0,
+ /* 58 */ 0, 0, 0,
+ /* 59 */ 0, 0, 0,
+ /* 60 */ 0, 0, 0,
+ /* 61 */ 0, 0, 0,
+ /* 62 */ 0, 0, 0,
+ /* 63 */ 0, 0, 0,
+ /* 64 */ 0, 0, 0,
+ /* 65 */ 0, 0, 0,
+ /* 66 */ 0, 0, 0,
+ /* 67 */ 0, 0, 0,
+ /* 68 */ 0, 0, 0,
+ /* 69 */ 0, 0, 0,
+ /* 70 */ 0, 0, 0,
+ /* 71 */ 0, 0, 0,
+ /* 72 */ 0, 0, 0,
+ /* 73 */ 0, 0, 0,
+ /* 74 */ 0, 0, 0,
+ /* 75 */ 0, 0, 0,
+ /* 76 */ 0, 0, 0,
+ /* 77 */ 0, 0, 0,
+ /* 78 */ 0, 0, 0,
+ /* 79 */ 0, 0, 0,
+ /* 80 */ 0, 0, 0,
+ /* 81 */ 0, 0, 0,
+ /* 82 */ 0, 0, 0,
+ /* 83 */ 0, 0, 0,
+ /* 84 */ 0, 0, 0,
+ /* 85 */ 0, 0, 0,
+ /* 86 */ 0, 0, 0,
+ /* 87 */ 0, 0, 0,
+ /* 88 */ 0, 0, 0,
+ /* 89 */ 0, 0, 0,
+ /* 90 */ 0, 0, 0,
+ /* 91 */ VK_LWIN, KEY_LMeta, 0,
+ /* 92 */ VK_RWIN, KEY_RMeta, 0,
+ /* 93 */ VK_APPS, KEY_Menu, 0,
+ /* 94 */ 0, 0, 0,
+ /* 95 */ 0, 0, 0,
+ /* 96 */ 0, 0, 0,
+ /* 97 */ 0, 0, 0,
+ /* 98 */ 0, 0, 0,
+ /* 99 */ 0, 0, 0,
+ /* 100 */ 0, 0, 0,
+ /* 101 */ 0, 0, 0,
+ /* 102 */ 0, 0, 0,
+ /* 103 */ 0, 0, 0,
+ /* 104 */ 0, 0, 0,
+ /* 105 */ 0, 0, 0,
+ /* 106 */ 0, 0, 0,
+ /* 107 */ 0, 0, 0,
+ /* 108 */ 0, 0, 0,
+ /* 109 */ 0, 0, 0,
+ /* 110 */ 0, 0, 0,
+ /* 111 */ VK_DIVIDE, 0, KEY_KP_Divide,
+ /* 112 */ 0, 0, 0,
+ /* 113 */ 0, 0, 0,
+ /* 114 */ 0, 0, 0,
+ /* 115 */ 0, 0, 0,
+ /* 116 */ 0, 0, 0,
+ /* 117 */ 0, 0, 0,
+ /* 118 */ 0, 0, 0,
+ /* 119 */ 0, 0, 0,
+ /* 120 */ 0, 0, 0,
+ /* 121 */ 0, 0, 0,
+ /* 122 */ 0, 0, 0,
+ /* 123 */ 0, 0, 0,
+ /* 124 */ 0, 0, 0,
+ /* 125 */ 0, 0, 0,
+ /* 126 */ 0, 0, 0,
+ /* 127 */ 0, 0, 0,
+ /* 128 */ 0, 0, 0,
+ /* 129 */ 0, 0, 0,
+ /* 130 */ 0, 0, 0,
+ /* 131 */ 0, 0, 0,
+ /* 132 */ 0, 0, 0,
+ /* 133 */ 0, 0, 0,
+ /* 134 */ 0, 0, 0,
+ /* 135 */ 0, 0, 0,
+ /* 136 */ 0, 0, 0,
+ /* 137 */ 0, 0, 0,
+ /* 138 */ 0, 0, 0,
+ /* 139 */ 0, 0, 0,
+ /* 140 */ 0, 0, 0,
+ /* 141 */ 0, 0, 0,
+ /* 142 */ 0, 0, 0,
+ /* 143 */ 0, 0, 0,
+ /* 144 */ 0, 0, 0,
+ /* 145 */ 0, 0, 0,
+ /* 146 */ 0, 0, 0,
+ /* 147 */ 0, 0, 0,
+ /* 148 */ 0, 0, 0,
+ /* 149 */ 0, 0, 0,
+ /* 150 */ 0, 0, 0,
+ /* 151 */ 0, 0, 0,
+ /* 152 */ 0, 0, 0,
+ /* 153 */ 0, 0, 0,
+ /* 154 */ 0, 0, 0,
+ /* 155 */ 0, 0, 0,
+ /* 156 */ 0, 0, 0,
+ /* 157 */ 0, 0, 0,
+ /* 158 */ 0, 0, 0,
+ /* 159 */ 0, 0, 0,
+ /* 160 */ VK_LSHIFT, KEY_ShiftL, 0,
+ /* 161 */ VK_RSHIFT, KEY_ShiftR, 0,
+ /* 162 */ VK_LCONTROL, KEY_LCtrl, 0,
+ /* 163 */ VK_RCONTROL, KEY_RCtrl, 0,
+ /* 164 */ VK_LMENU, KEY_Alt, 0,
+ /* 165 */ VK_RMENU, KEY_AltLang, 0,
+ /* 166 */ 0, 0, 0,
+ /* 167 */ 0, 0, 0,
+ /* 168 */ 0, 0, 0,
+ /* 169 */ 0, 0, 0,
+ /* 170 */ 0, 0, 0,
+ /* 171 */ 0, 0, 0,
+ /* 172 */ 0, 0, 0,
+ /* 173 */ VK_VOLUME_MUTE, 0, KEY_Mute,
+ /* 174 */ VK_VOLUME_DOWN, 0, KEY_AudioLower,
+ /* 175 */ VK_VOLUME_UP, 0, KEY_AudioRaise,
+ /* 176 */ VK_MEDIA_NEXT_TRACK, 0, KEY_NEXTSONG,
+ /* 177 */ VK_MEDIA_PREV_TRACK, 0, KEY_PREVIOUSSONG,
+ /* 178 */ VK_MEDIA_STOP, 0, KEY_STOPCD,
+ /* 179 */ VK_MEDIA_PLAY_PAUSE, 0, KEY_PLAYPAUSE,
+ /* 180 */ 0, 0, 0,
+ /* 181 */ 0, 0, 0,
+ /* 182 */ 0, 0, 0,
+ /* 183 */ 0, 0, 0,
+ /* 184 */ 0, 0, 0,
+ /* 185 */ 0, 0, 0,
+ /* 186 */ 0, 0, 0,
+ /* 187 */ 0, 0, 0,
+ /* 188 */ 0, 0, 0,
+ /* 189 */ 0, 0, 0,
+ /* 190 */ 0, 0, 0,
+ /* 191 */ 0, 0, 0,
+ /* 192 */ 0, 0, 0,
+ /* 193 */ 0, 0, 0,
+ /* 194 */ 0, 0, 0,
+ /* 195 */ 0, 0, 0,
+ /* 196 */ 0, 0, 0,
+ /* 197 */ 0, 0, 0,
+ /* 198 */ 0, 0, 0,
+ /* 199 */ 0, 0, 0,
+ /* 200 */ 0, 0, 0,
+ /* 201 */ 0, 0, 0,
+ /* 202 */ 0, 0, 0,
+ /* 203 */ 0, 0, 0,
+ /* 204 */ 0, 0, 0,
+ /* 205 */ 0, 0, 0,
+ /* 206 */ 0, 0, 0,
+ /* 207 */ 0, 0, 0,
+ /* 208 */ 0, 0, 0,
+ /* 209 */ 0, 0, 0,
+ /* 210 */ 0, 0, 0,
+ /* 211 */ 0, 0, 0,
+ /* 212 */ 0, 0, 0,
+ /* 213 */ 0, 0, 0,
+ /* 214 */ 0, 0, 0,
+ /* 215 */ 0, 0, 0,
+ /* 216 */ 0, 0, 0,
+ /* 217 */ 0, 0, 0,
+ /* 218 */ 0, 0, 0,
+ /* 219 */ 0, 0, 0,
+ /* 220 */ 0, 0, 0,
+ /* 221 */ 0, 0, 0,
+ /* 222 */ 0, 0, 0,
+ /* 223 */ VK_OEM_8, 0, KEY_RCtrl, /* at least on Canadian Multilingual Standard layout */
+ /* 224 */ 0, 0, 0,
+ /* 225 */ 0, 0, 0,
+ /* 226 */ 0, 0, 0,
+ /* 227 */ 0, 0, 0,
+ /* 228 */ 0, 0, 0,
+ /* 229 */ 0, 0, 0,
+ /* 230 */ 0, 0, 0,
+ /* 231 */ 0, 0, 0,
+ /* 232 */ 0, 0, 0,
+ /* 233 */ 0, 0, 0,
+ /* 234 */ 0, 0, 0,
+ /* 235 */ 0, 0, 0,
+ /* 236 */ 0, 0, 0,
+ /* 237 */ 0, 0, 0,
+ /* 238 */ 0, 0, 0,
+ /* 239 */ 0, 0, 0,
+ /* 240 */ 0, 0, 0,
+ /* 241 */ 0, 0, 0,
+ /* 242 */ 0, 0, 0,
+ /* 243 */ 0, 0, 0,
+ /* 244 */ 0, 0, 0,
+ /* 245 */ 0, 0, 0,
+ /* 246 */ 0, 0, 0,
+ /* 247 */ 0, 0, 0,
+ /* 248 */ 0, 0, 0,
+ /* 249 */ 0, 0, 0,
+ /* 250 */ 0, 0, 0,
+ /* 251 */ 0, 0, 0,
+ /* 252 */ 0, 0, 0,
+ /* 253 */ 0, 0, 0,
+ /* 254 */ 0, 0, 0,
+ /* 255 */ VK_FN, 0, KEY_Fn /* Most keyboards don't generate a scancode for Fn, but a few do... */
+};
+
+#endif /* WINKEYBD_H */