Finish porting WinGLNative to C.

This commit is contained in:
UnknownShadow200 2017-08-19 15:28:46 +10:00
parent 44ec23c931
commit d6a8b7d35c
5 changed files with 252 additions and 214 deletions

View File

@ -339,11 +339,7 @@ namespace OpenTK.Platform.Windows
if (Closing != null)
Closing(this, e);
if (!e.Cancel)
{
if (Unload != null)
Unload(this, EventArgs.Empty);
if (!e.Cancel) {
DestroyWindow();
break;
}
@ -403,7 +399,7 @@ namespace OpenTK.Platform.Windows
wc.Icon = Icon != null ? Icon.Handle : IntPtr.Zero;
#warning "This seems to resize one of the 'large' icons, rather than using a small icon directly (multi-icon files). Investigate!"
wc.IconSm = Icon != null ? new Icon(Icon, 16, 16).Handle : IntPtr.Zero;
wc.Cursor = API.LoadCursor(IntPtr.Zero, (IntPtr)32512); // CursorName.Arrow
//wc.Cursor = API.LoadCursor(IntPtr.Zero, (IntPtr)32512); // CursorName.Arrow
ushort atom = API.RegisterClassEx(ref wc);
if (atom == 0)
@ -701,8 +697,6 @@ namespace OpenTK.Platform.Windows
throw new NotImplementedException();
}
public event EventHandler<EventArgs> Load;
public event EventHandler<EventArgs> Unload;
public event EventHandler<EventArgs> Move;
public event EventHandler<EventArgs> Resize;
public event EventHandler<System.ComponentModel.CancelEventArgs> Closing;

View File

@ -10,10 +10,22 @@ bool Rectangle2D_Contains(Rectangle2D a, Int32 x, Int32 y) {
return x >= a.X && y >= a.Y && x < (a.X + a.Width) && y < (a.Y + a.Height);
}
bool Rectangle2D_Equals(Rectangle2D a, Rectangle2D b) {
return a.X == b.X && a.Y == b.Y && a.Width == b.Width && a.Height == b.Height;
}
Size2D Size2D_Make(Int32 width, Int32 height) {
Size2D s; s.Width = width; s.Height = height; return s;
}
bool Size2D_Equals(Size2D a, Size2D b) {
return a.Width == b.Width && a.Height == b.Height;
}
Point2D Point2D_Make(Int32 x, Int32 y) {
Point2D p; p.X = x; p.Y = y; return p;
}
bool Point2D_Equals(Point2D a, Point2D b) {
return a.X == b.X && a.Y == b.Y;
}

View File

@ -13,6 +13,9 @@ typedef struct Rectangle2D_ {
Int32 Width, Height;
} Rectangle2D;
/* Empty 2D rectangle. */
Rectangle2D Rectangle2D_Empty;
/* Stores a coordinate in 2D space.*/
typedef struct Point2D_ {
Int32 X, Y;
@ -30,9 +33,18 @@ Rectangle2D Rectangle2D_Make(Int32 x, Int32 y, Int32 width, Int32 height);
/* Returns whether the given rectangle contains the given point. */
bool Rectangle2D_Contains(Rectangle2D a, Int32 x, Int32 y);
/* Returns whether the two rectangles are equal. */
bool Rectangle2D_Equals(Rectangle2D a, Rectangle2D b);
/* Creates a new size. */
Size2D Size2D_Make(Int32 width, Int32 height);
/* Returns whether the two sizes are equal. */
bool Size2D_Equals(Size2D a, Size2D b);
/* Creates a new point. */
Point2D Point2D_Make(Int32 x, Int32 y);
/* Returns whether the two points are equal. */
bool Point2D_Equals(Point2D a, Point2D b);
#endif

View File

@ -1,9 +1,17 @@
#include "Typedefs.h"
#include "ErrorHandler.h"
#include "Platform.h"
#include "Window.h"
int main(int argc, char* argv[]) {
ErrorHandler_Init();
Platform_Init();
Window_Create(0, 0, 320, 320, "TEST", NULL);
Window_SetVisible(true);
while (true) {
Window_ProcessEvents();
Platform_ThreadSleep(100);
}
return 0;
}

View File

@ -14,6 +14,7 @@
HINSTANCE win_Instance;
HWND win_Handle;
HDC win_DC;
WindowState win_State = WindowState_Normal;
bool win_Exists, win_Focused;
bool mouse_outside_window = true;
@ -24,41 +25,157 @@ Rectangle2D win_Bounds;
Rectangle2D win_ClientRect;
Rectangle2D previous_bounds; // Used to restore previous size when leaving fullscreen mode.
void Window_Create(Int32 x, Int32 y, Int32 width, Int32 height, STRING_TRANSIENT String* title, DisplayDevice* device) {
win_Instance = GetModuleHandleA(NULL);
/* TODO: UngroupFromTaskbar(); */
/* Find out the final window rectangle, after the WM has added its chrome (titlebar, sidebars etc). */
RECT rect; rect.left = x; rect.top = y; rect.right = x + width; rect.bottom = y + height;
AdjustWindowRectEx(&rect, win_Style, false, win_StyleEx);
WNDCLASSEXA wc;
Platform_MemSet(&wc, 0, sizeof(WNDCLASSEXA));
wc.cbSize = sizeof(WNDCLASSEXA);
wc.style = CS_OWNDC;
wc.hInstance = win_Instance;
wc.lpfnWndProc = Window_Procedure;
wc.lpszClassName = win_ClassName;
/* TODO: Set window icons here */
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
ATOM atom = RegisterClassExA(&wc);
if (atom == 0) {
ErrorHandler_FailWithCode(GetLastError(), "Failed to register window class");
Rectangle2D Window_FromRect(RECT rect) {
Rectangle2D r;
r.X = rect.left; r.Y = rect.top;
r.Width = RECT_WIDTH(rect);
r.Height = RECT_HEIGHT(rect);
return r;
}
win_Handle = CreateWindowExA(
win_StyleEx, win_ClassName, title->buffer, win_Style,
rect.left, rect.top, RECT_WIDTH(rect), RECT_HEIGHT(rect),
NULL, NULL, win_Instance, NULL);
if (win_Handle == NULL) {
ErrorHandler_FailWithCode(GetLastError(), "Failed to create window");
}
win_Exists = true;
void Window_Destroy(void) {
if (!win_Exists) return;
DestroyWindow(win_Handle);
win_Exists = false;
}
WNDPROC Window_Procedure(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) {
void Window_ResetWindowState(void) {
suppress_resize++;
Window_SetWindowState(WindowState_Normal);
Window_ProcessEvents();
suppress_resize--;
}
bool win_hiddenBorder;
void Window_DoSetHiddenBorder(bool value) {
if (win_hiddenBorder == value) return;
/* We wish to avoid making an invisible window visible just to change the border.
However, it's a good idea to make a visible window invisible temporarily, to
avoid garbage caused by the border change. */
bool was_visible = Window_GetVisible();
/* To ensure maximized/minimized windows work correctly, reset state to normal,
change the border, then go back to maximized/minimized. */
WindowState state = win_State;
Window_ResetWindowState();
DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
style |= (value ? WS_POPUP : WS_OVERLAPPEDWINDOW);
/* Make sure client size doesn't change when changing the border style.*/
RECT rect;
rect.left = win_Bounds.X; rect.top = win_Bounds.Y;
rect.right = rect.left + win_Bounds.Width;
rect.bottom = rect.top + win_Bounds.Height;
AdjustWindowRectEx(&rect, style, false, win_StyleEx);
/* This avoids leaving garbage on the background window. */
if (was_visible) Window_SetVisible(false);
SetWindowLongA(win_Handle, GWL_STYLE, style);
SetWindowPos(win_Handle, NULL, 0, 0, RECT_WIDTH(rect), RECT_HEIGHT(rect),
SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
/* Force window to redraw update its borders, but only if it's
already visible (invisible windows will change borders when
they become visible, so no need to make them visiable prematurely).*/
if (was_visible) Window_SetVisible(true);
Window_SetWindowState(state);
}
void Window_SetHiddenBorder(bool hidden) {
suppress_resize++;
Window_DoSetHiddenBorder(hidden);
Window_ProcessEvents();
suppress_resize--;
}
void Window_EnableMouseTracking(void) {
TRACKMOUSEEVENT me;
Platform_MemSet(&me, 0, sizeof(TRACKMOUSEEVENT));
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.hwndTrack = win_Handle;
me.dwFlags = TME_LEAVE;
if (!TrackMouseEvent(&me)) {
ErrorHandler_FailWithCode(GetLastError(), "Enabling mouse tracking");
}
}
Key Window_MapKey(WPARAM key) {
if (key >= VK_F1 && key <= VK_F24) {
return Key_F1 + (key - VK_F1);
}
if (key >= '0' && key <= '9') {
return Key_Number0 + (key - '0');
}
if (key >= 'A' && key <= 'Z') {
return Key_A + (key - 'A');
}
if (key >= VK_NUMPAD0 && key <= VK_NUMPAD9) {
return Key_Keypad0 + (key - VK_NUMPAD0);
}
switch (key) {
case VK_ESCAPE: return Key_Escape;
case VK_TAB: return Key_Tab;
case VK_CAPITAL: return Key_CapsLock;
case VK_LCONTROL: return Key_ControlLeft;
case VK_LSHIFT: return Key_ShiftLeft;
case VK_LWIN: return Key_WinLeft;
case VK_LMENU: return Key_AltLeft;
case VK_SPACE: return Key_Space;
case VK_RMENU: return Key_AltRight;
case VK_RWIN: return Key_WinRight;
case VK_APPS: return Key_Menu;
case VK_RCONTROL: return Key_ControlRight;
case VK_RSHIFT: return Key_ShiftRight;
case VK_RETURN: return Key_Enter;
case VK_BACK: return Key_BackSpace;
case VK_OEM_1: return Key_Semicolon; /* Varies by keyboard: return ;: on Win2K/US */
case VK_OEM_2: return Key_Slash; /* Varies by keyboard: return /? on Win2K/US */
case VK_OEM_3: return Key_Tilde; /* Varies by keyboard: return `~ on Win2K/US */
case VK_OEM_4: return Key_BracketLeft; /* Varies by keyboard: return [{ on Win2K/US */
case VK_OEM_5: return Key_BackSlash; /* Varies by keyboard: return \| on Win2K/US */
case VK_OEM_6: return Key_BracketRight; /* Varies by keyboard: return ]} on Win2K/US */
case VK_OEM_7: return Key_Quote; /* Varies by keyboard: return '" on Win2K/US */
case VK_OEM_PLUS: return Key_Plus; /* Invariant: + */
case VK_OEM_COMMA: return Key_Comma; /* Invariant: : return */
case VK_OEM_MINUS: return Key_Minus; /* Invariant: - */
case VK_OEM_PERIOD: return Key_Period; /* Invariant: . */
case VK_HOME: return Key_Home;
case VK_END: return Key_End;
case VK_DELETE: return Key_Delete;
case VK_PRIOR: return Key_PageUp;
case VK_NEXT: return Key_PageDown;
case VK_PRINT: return Key_PrintScreen;
case VK_PAUSE: return Key_Pause;
case VK_NUMLOCK: return Key_NumLock;
case VK_SCROLL: return Key_ScrollLock;
case VK_SNAPSHOT: return Key_PrintScreen;
case VK_INSERT: return Key_Insert;
case VK_DECIMAL: return Key_KeypadDecimal;
case VK_ADD: return Key_KeypadAdd;
case VK_SUBTRACT: return Key_KeypadSubtract;
case VK_DIVIDE: return Key_KeypadDivide;
case VK_MULTIPLY: return Key_KeypadMultiply;
case VK_UP: return Key_Up;
case VK_DOWN: return Key_Down;
case VK_LEFT: return Key_Left;
case VK_RIGHT: return Key_Right;
}
return Key_Unknown;
}
LRESULT CALLBACK Window_Procedure(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) {
bool new_focused_state;
Real32 wheel_delta;
WORD mouse_x, mouse_y;
@ -73,7 +190,7 @@ WNDPROC Window_Procedure(HWND handle, UINT message, WPARAM wParam, LPARAM lParam
switch (message) {
case WM_ACTIVATE:
new_focused_state = LOWORD(wParam);
new_focused_state = LOWORD(wParam) != 0;
if (new_focused_state != win_Focused) {
win_Focused = new_focused_state;
Event_RaiseVoid(&WindowEvents_OnFocusedChanged);
@ -93,18 +210,18 @@ WNDPROC Window_Procedure(HWND handle, UINT message, WPARAM wParam, LPARAM lParam
pos = (WINDOWPOS*)lParam;
if (pos->hwnd == win_Handle) {
Point2D new_location = Point2D_Make(pos->x, pos->y);
if (Location != new_location) {
if (!Point2D_Equals(Window_GetLocation(), new_location)) {
win_Bounds.X = pos->x; win_Bounds.Y = pos->y;
Event_RaiseVoid(&WindowEvents_OnMove);
}
Size2D new_size = Size2D_Make(pos->cx, pos->cy);
if (Size != new_size) {
if (!Size2D_Equals(Window_GetSize(), new_size)) {
win_Bounds.Width = pos->cx; win_Bounds.Height = pos->cy;
RECT rect;
GetClientRect(handle, &rect);
client_rectangle = rect.ToRectangle();
win_ClientRect = Window_FromRect(rect);
SetWindowPos(win_Handle, NULL,
win_Bounds.X, win_Bounds.Y, win_Bounds.Width, win_Bounds.Height,
@ -156,7 +273,7 @@ WNDPROC Window_Procedure(HWND handle, UINT message, WPARAM wParam, LPARAM lParam
if (mouse_outside_window) {
/* Once we receive a mouse move event, it means that the mouse has re-entered the window. */
mouse_outside_window = false;
EnableMouseTracking();
Window_EnableMouseTracking();
Event_RaiseVoid(&WindowEvents_OnMouseEnter);
}
break;
@ -280,59 +397,59 @@ WNDPROC Window_Procedure(HWND handle, UINT message, WPARAM wParam, LPARAM lParam
RECT rect;
GetClientRect(handle, &rect);
win_ClientRect = rect.ToRectangle();
win_ClientRect = Window_FromRect(rect);
invisible_since_creation = true;
}
break;
case WM_CLOSE:
Event_RaiseVoid(&WindowEvents_OnClosing);
if (Unload != null)
Unload(this, EventArgs.Empty);
DestroyWindow();
Window_Destroy();
break;
case WM_DESTROY:
win_Exists = false;
UnregisterClassA(win_ClassName, win_Instance);
window.Dispose();
if (win_DC != NULL) ReleaseDC(win_Handle, win_DC);
Event_RaiseVoid(&WindowEvents_OnClosed);
break;
}
return DefWindowProcA(handle, message, wParam, lParam);
}
void Window_EnableMouseTracking(void) {
TRACKMOUSEEVENT me;
Platform_MemSet(&me, 0, sizeof(TRACKMOUSEEVENT));
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.hwndTrack = win_Handle;
me.dwFlags = TME_LEAVE;
if (!TrackMouseEvent(&me)) {
ErrorHandler_FailWithCode(GetLastError(), "Enabling mouse tracking");
}
void Window_Create(Int32 x, Int32 y, Int32 width, Int32 height, STRING_TRANSIENT String* title, DisplayDevice* device) {
win_Instance = GetModuleHandleA(NULL);
/* TODO: UngroupFromTaskbar(); */
/* Find out the final window rectangle, after the WM has added its chrome (titlebar, sidebars etc). */
RECT rect; rect.left = x; rect.top = y; rect.right = x + width; rect.bottom = y + height;
AdjustWindowRectEx(&rect, win_Style, false, win_StyleEx);
WNDCLASSEXA wc;
Platform_MemSet(&wc, 0, sizeof(WNDCLASSEXA));
wc.cbSize = sizeof(WNDCLASSEXA);
wc.style = CS_OWNDC;
wc.hInstance = win_Instance;
wc.lpfnWndProc = Window_Procedure;
wc.lpszClassName = win_ClassName;
/* TODO: Set window icons here */
wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
ATOM atom = RegisterClassExA(&wc);
if (atom == 0) {
ErrorHandler_FailWithCode(GetLastError(), "Failed to register window class");
}
void Window_Destroy(void) {
if (!win_Exists) return;
win_Handle = CreateWindowExA(
win_StyleEx, win_ClassName, title->buffer, win_Style,
rect.left, rect.top, RECT_WIDTH(rect), RECT_HEIGHT(rect),
NULL, NULL, win_Instance, NULL);
DestroyWindow(win_Handle);
win_Exists = false;
if (win_Handle == NULL) {
ErrorHandler_FailWithCode(GetLastError(), "Failed to create window");
}
void Window_SetHiddenBorder(bool hidden) {
suppress_resize++;
Window_DoSetHiddenBorder(hidden);
Window_ProcessEvents();
suppress_resize--;
}
void Window_ResetWindowState(void) {
suppress_resize++;
Window_SetWindowState(WindowState_Normal);
Window_ProcessEvents();
suppress_resize--;
win_Exists = true;
}
void Window_GetClipboardText(STRING_TRANSIENT String* value) {
@ -360,7 +477,7 @@ void Window_GetClipboardText(STRING_TRANSIENT String* value) {
String_Append(value, Convert_UnicodeToCP437(*text)); text++;
}
} else {
UInt8* text = (UInt16*)src;
UInt8* text = (UInt8*)src;
while (*text != 0) {
String_Append(value, *text); text++;
}
@ -400,6 +517,7 @@ void Window_SetClipboardText(STRING_TRANSIENT String* value) {
}
}
Rectangle2D Window_GetBounds(void) { return win_Bounds; }
void Window_SetBounds(Rectangle2D rect) {
/* Note: the bounds variable is updated when the resize/move message arrives.*/
@ -451,6 +569,7 @@ if (window.handle != IntPtr.Zero)
}*/
bool Window_GetFocused(void) { return win_Focused; }
bool Window_GetExists(void) { return win_Exists; }
bool Window_GetVisible(void) { return IsWindowVisible(win_Handle); }
void Window_SetVisible(bool visible) {
@ -460,19 +579,19 @@ void Window_SetVisible(bool visible) {
BringWindowToTop(win_Handle);
SetForegroundWindow(win_Handle);
}
} else {
}
else {
ShowWindow(win_Handle, SW_HIDE);
}
}
bool Window_GetExists(void) { return win_Exists; }
void Window_Close(void) {
PostMessageA(win_Handle, WM_CLOSE, NULL, NULL);
}
WindowState Window_GetState(void) { return win_State; }
void Window_SetState(WindowState value) {
WindowState Window_GetWindowState(void) { return win_State; }
void Window_SetWindowState(WindowState value) {
if (win_State == value) return;
DWORD command = 0;
@ -489,7 +608,7 @@ void Window_SetState(WindowState value) {
case WindowState_Maximized:
/* Reset state to avoid strange interactions with fullscreen/minimized windows. */
ResetWindowState();
Window_ResetWindowState();
command = SW_MAXIMIZE;
break;
@ -503,9 +622,9 @@ void Window_SetState(WindowState value) {
command for windows with hidden borders. */
/* Reset state to avoid strange side-effects from maximized/minimized windows. */
ResetWindowState();
previous_bounds = Bounds;
SetHiddenBorder(true);
Window_ResetWindowState();
previous_bounds = win_Bounds;
Window_SetHiddenBorder(true);
command = SW_MAXIMIZE;
SetForegroundWindow(win_Handle);
@ -515,64 +634,27 @@ void Window_SetState(WindowState value) {
if (command != 0) ShowWindow(win_Handle, command);
/* Restore previous window border or apply pending border change when leaving fullscreen mode. */
if (exiting_fullscreen) SetHiddenBorder(false);
if (exiting_fullscreen) Window_SetHiddenBorder(false);
/* Restore previous window size/location if necessary */
if (command == SW_RESTORE && previous_bounds != Rectangle.Empty) {
Bounds = previous_bounds;
previous_bounds = Rectangle.Empty;
if (command == SW_RESTORE && !Rectangle2D_Equals(previous_bounds, Rectangle2D_Empty)) {
Window_SetBounds(previous_bounds);
previous_bounds = Rectangle2D_Empty;
}
}
bool win_hiddenBorder;
void Window_DoSetHiddenBorder(bool value) {
if (win_hiddenBorder == value) return;
/* We wish to avoid making an invisible window visible just to change the border.
However, it's a good idea to make a visible window invisible temporarily, to
avoid garbage caused by the border change. */
bool was_visible = Window_GetVisible();
/* To ensure maximized/minimized windows work correctly, reset state to normal,
change the border, then go back to maximized/minimized. */
WindowState state = win_State;
Window_ResetWindowState();
DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
style |= (value ? WS_POPUP : WS_OVERLAPPEDWINDOW);
/* Make sure client size doesn't change when changing the border style.*/
Win32Rectangle rect = Win32Rectangle.From(bounds);
AdjustWindowRectEx(&rect, style, false, win_StyleEx);
/* This avoids leaving garbage on the background window. */
if (was_visible) Window_SetVisible(false);
SetWindowLongA(win_Handle, GWL_STYLE, style);
SetWindowPos(win_Handle, NULL, 0, 0, rect.Width, rect.Height,
SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
/* Force window to redraw update its borders, but only if it's
already visible (invisible windows will change borders when
they become visible, so no need to make them visiable prematurely).*/
if (was_visible) Window_SetVisible(true);
Window_SetWindowState(state);
}
Point2D Window_PointToClient(Point2D point) {
if (!ScreenToClient(win_Handle, ref point)) {
POINT p; p.x = point.X; p.y = point.Y;
if (!ScreenToClient(win_Handle, &p)) {
ErrorHandler_FailWithCode(GetLastError(), "Converting point from client to screen coordinates");
}
return point;
return Point2D_Make(p.x, p.y);
}
Point2D Window_PointToScreen(Point2D p) {
ErrorHandler_Fail("PointToScreen NOT IMPLEMENTED");
}
public event EventHandler<EventArgs> Load;
public event EventHandler<EventArgs> Unload;
MSG msg;
void Window_ProcessEvents() {
while (PeekMessageA(&msg, NULL, 0, 0, 1)) {
@ -600,73 +682,3 @@ void Window_SetCursorVisible(bool visible) {
win_cursorVisible = visible;
ShowCursor(visible ? 1 : 0);
}
Key Window_MapKey(WPARAM key) {
if (key >= VK_F1 && key <= VK_F24) {
return Key_F1 + (key - VK_F1);
}
if (key >= '0' && key <= '9') {
return Key_Number0 + (key - '0');
}
if (key >= 'A' && key <= 'Z') {
return Key_A + (key - 'A');
}
if (key >= VK_NUMPAD0 && key <= VK_NUMPAD9) {
return Key_Keypad0 + (key - VK_NUMPAD0);
}
switch (key) {
case VK_ESCAPE: return Key_Escape;
case VK_TAB: return Key_Tab;
case VK_CAPITAL: return Key_CapsLock;
case VK_LCONTROL: return Key_ControlLeft;
case VK_LSHIFT: return Key_ShiftLeft;
case VK_LWIN: return Key_WinLeft;
case VK_LMENU: return Key_AltLeft;
case VK_SPACE: return Key_Space;
case VK_RMENU: return Key_AltRight;
case VK_RWIN: return Key_WinRight;
case VK_APPS: return Key_Menu;
case VK_RCONTROL: return Key_ControlRight;
case VK_RSHIFT: return Key_ShiftRight;
case VK_RETURN: return Key_Enter;
case VK_BACK: return Key_BackSpace;
case VK_OEM_1: return Key_Semicolon; /* Varies by keyboard: return ;: on Win2K/US */
case VK_OEM_2: return Key_Slash; /* Varies by keyboard: return /? on Win2K/US */
case VK_OEM_3: return Key_Tilde; /* Varies by keyboard: return `~ on Win2K/US */
case VK_OEM_4: return Key_BracketLeft; /* Varies by keyboard: return [{ on Win2K/US */
case VK_OEM_5: return Key_BackSlash; /* Varies by keyboard: return \| on Win2K/US */
case VK_OEM_6: return Key_BracketRight; /* Varies by keyboard: return ]} on Win2K/US */
case VK_OEM_7: return Key_Quote; /* Varies by keyboard: return '" on Win2K/US */
case VK_OEM_PLUS: return Key_Plus; /* Invariant: + */
case VK_OEM_COMMA: return Key_Comma; /* Invariant: : return */
case VK_OEM_MINUS: return Key_Minus; /* Invariant: - */
case VK_OEM_PERIOD: return Key_Period; /* Invariant: . */
case VK_HOME: return Key_Home;
case VK_END: return Key_End;
case VK_DELETE: return Key_Delete;
case VK_PRIOR: return Key_PageUp;
case VK_NEXT: return Key_PageDown;
case VK_PRINT: return Key_PrintScreen;
case VK_PAUSE: return Key_Pause;
case VK_NUMLOCK: return Key_NumLock;
case VK_SCROLL: return Key_ScrollLock;
case VK_SNAPSHOT: return Key_PrintScreen;
case VK_INSERT: return Key_Insert;
case VK_DECIMAL: return Key_KeypadDecimal;
case VK_ADD: return Key_KeypadAdd;
case VK_SUBTRACT: return Key_KeypadSubtract;
case VK_DIVIDE: return Key_KeypadDivide;
case VK_MULTIPLY: return Key_KeypadMultiply;
case VK_UP: return Key_Up;
case VK_DOWN: return Key_Down;
case VK_LEFT: return Key_Left;
case VK_RIGHT: return Key_Right;
}
return Key_Unknown;
}