diff options
-rw-r--r-- | TODO | 38 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/global.h | 22 | ||||
-rw-r--r-- | src/main.c | 38 | ||||
-rw-r--r-- | src/scancodes.h | 194 | ||||
-rw-r--r-- | src/win.c | 321 | ||||
-rw-r--r-- | src/winkeybd.c | 290 | ||||
-rw-r--r-- | src/winkeybd.h | 303 |
8 files changed, 1187 insertions, 23 deletions
@@ -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; @@ -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 */ @@ -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 */ |