GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, RAWINPUT const& raw) { int keyDown=0; char vk; GHOST_SystemWin32 * system = (GHOST_SystemWin32 *)getSystem(); GHOST_TKey key = system->hardKey(window, raw, &keyDown, &vk); GHOST_EventKey* event; if (key != GHOST_kKeyUnknown) { char utf8_char[6] = {0} ; wchar_t utf16[2]={0}; BYTE state[256]; GetKeyboardState((PBYTE)state); if(ToUnicodeEx(vk, 0, state, utf16, 2, 0, system->m_keylayout)) WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)utf16, 1, (LPSTR) utf8_char, 5, NULL,NULL); else *utf8_char = 0; if(!keyDown) utf8_char[0] = '\0'; event = new GHOST_EventKey(system->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, (*utf8_char & 0x80)?'?':*utf8_char, utf8_char); #ifdef GHOST_DEBUG std::cout << ascii << std::endl; #endif } else { event = 0; } return event; }
GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, RAWINPUT const& raw) { int keyDown=0; char vk; GHOST_SystemWin32 * system = (GHOST_SystemWin32 *)getSystem(); GHOST_TKey key = system->hardKey(window, raw, &keyDown, &vk); GHOST_EventKey* event; if (key != GHOST_kKeyUnknown) { char ascii = '\0'; unsigned short utf16[2]={0}; BYTE state[256]; GetKeyboardState((PBYTE)state); if(ToAsciiEx(vk, 0, state, utf16, 0, system->m_keylayout)) WideCharToMultiByte(CP_ACP, 0x00000400, (wchar_t*)utf16, 1, (LPSTR) &ascii, 1, NULL,NULL); event = new GHOST_EventKey(system->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, ascii); #ifdef GHOST_DEBUG std::cout << ascii << std::endl; #endif } else { event = 0; } return event; }
GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow) { GHOST_TInt32 x_screen, y_screen; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *) getSystem(); GHOST_WindowWin32 *window = (GHOST_WindowWin32 *) Iwindow; system->getCursorPosition(x_screen, y_screen); /* TODO: CHECK IF THIS IS A TABLET EVENT */ bool is_tablet = false; if (is_tablet == false && window->getCursorGrabModeIsWarp()) { GHOST_TInt32 x_new = x_screen; GHOST_TInt32 y_new = y_screen; GHOST_TInt32 x_accum, y_accum; GHOST_Rect bounds; /* fallback to window bounds */ if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) { window->getClientBounds(bounds); } /* could also clamp to screen bounds * wrap with a window outside the view will fail atm */ bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */ window->getCursorGrabAccum(x_accum, y_accum); if (x_new != x_screen || y_new != y_screen) { /* when wrapping we don't need to add an event because the * setCursorPosition call will cause a new event after */ system->setCursorPosition(x_new, y_new); /* wrap */ window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new)); } else { return new GHOST_EventCursor(system->getMilliSeconds(), GHOST_kEventCursorMove, window, x_screen + x_accum, y_screen + y_accum ); } } else { return new GHOST_EventCursor(system->getMilliSeconds(), GHOST_kEventCursorMove, window, x_screen, y_screen ); } return NULL; }
GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, GHOST_IWindow *window, int mouseX, int mouseY, void *data) { GHOST_SystemWin32 *system = ((GHOST_SystemWin32 *)getSystem()); return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data) ); }
GHOST_EventKey *GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, RAWINPUT const& raw) { int keyDown = 0; char vk; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); GHOST_TKey key = system->hardKey(window, raw, &keyDown, &vk); GHOST_EventKey *event; if (key != GHOST_kKeyUnknown) { char utf8_char[6] = {0}; char ascii = 0; wchar_t utf16[3] = {0}; BYTE state[256] = {0}; int r; GetKeyboardState((PBYTE)state); // don't call ToUnicodeEx on dead keys as it clears the buffer and so won't allow diacritical composition. if (MapVirtualKeyW(vk,2) != 0) { // todo: ToUnicodeEx can respond with up to 4 utf16 chars (only 2 here). Could be up to 24 utf8 bytes. if ((r = ToUnicodeEx(vk, raw.data.keyboard.MakeCode, state, utf16, 2, 0, system->m_keylayout))) { if ((r > 0 && r < 3)) { utf16[r] = 0; conv_utf_16_to_8(utf16, utf8_char, 6); } else if (r == -1) { utf8_char[0] = '\0'; } } } if (!keyDown) { utf8_char[0] = '\0'; ascii = '\0'; } else { ascii = utf8_char[0] & 0x80 ? '?' : utf8_char[0]; } event = new GHOST_EventKey(system->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, ascii, utf8_char); #ifdef GHOST_DEBUG std::cout << ascii << std::endl; #endif } else { event = 0; } return event; }
LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { GHOST_Event *event = 0; bool eventHandled = false; LRESULT lResult = 0; GHOST_SystemWin32 *system = ((GHOST_SystemWin32 *)getSystem()); GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized"); if (hwnd) { GHOST_WindowWin32 *window = (GHOST_WindowWin32 *)::GetWindowLongPtr(hwnd, GWL_USERDATA); if (window) { switch (msg) { // we need to check if new key layout has AltGr case WM_INPUTLANGCHANGE: system->handleKeyboardChange(); break; //////////////////////////////////////////////////////////////////////// // Keyboard events, processed //////////////////////////////////////////////////////////////////////// case WM_INPUT: { // check WM_INPUT from input sink when ghost window is not in the foreground if (wParam == RIM_INPUTSINK) { if (GetFocus() != hwnd) // WM_INPUT message not for this window return 0; } //else wParam == RIM_INPUT RAWINPUT raw; RAWINPUT *raw_ptr = &raw; UINT rawSize = sizeof(RAWINPUT); GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER)); switch (raw.header.dwType) { case RIM_TYPEKEYBOARD: event = processKeyEvent(window, raw); if (!event) { GHOST_PRINT("GHOST_SystemWin32::wndProc: key event "); GHOST_PRINT(msg); GHOST_PRINT(" key ignored\n"); } break; #ifdef WITH_INPUT_NDOF case RIM_TYPEHID: if (system->processNDOF(raw)) eventHandled = true; break; #endif } break; } //////////////////////////////////////////////////////////////////////// // Keyboard events, ignored //////////////////////////////////////////////////////////////////////// case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: /* These functions were replaced by WM_INPUT*/ case WM_CHAR: /* The WM_CHAR message is posted to the window with the keyboard focus when * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR * contains the character code of the key that was pressed. */ case WM_DEADCHAR: /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR * specifies a character code generated by a dead key. A dead key is a key that * generates a character, such as the umlaut (double-dot), that is combined with * another character to form a composite character. For example, the umlaut-O * character (Ö) is generated by typing the dead key for the umlaut character, and * then typing the O key. */ break; case WM_SYSDEADCHAR: /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when * a WM_SYSKEYDOWN message is translated by the TranslateMessage function. * WM_SYSDEADCHAR specifies the character code of a system dead key - that is, * a dead key that is pressed while holding down the alt key. */ case WM_SYSCHAR: /* The WM_SYSCHAR message is sent to the window with the keyboard focus when * a WM_SYSCHAR message is translated by the TranslateMessage function. * WM_SYSCHAR specifies the character code of a dead key - that is, * a dead key that is pressed while holding down the alt key. * To prevent the sound, DefWindowProc must be avoided by return */ break; case WM_SYSCOMMAND: /* The WM_SYSCHAR message is sent to the window when system commands such as * maximize, minimize or close the window are triggered. Also it is sent when ALT * button is press for menu. To prevent this we must return preventing DefWindowProc. */ if (wParam == SC_KEYMENU) { eventHandled = true; }// else /* XXX Disable for now due to area resizing issue. bug# 34990 */ /*if((wParam&0xfff0)==SC_SIZE) { window->registerMouseClickEvent(0); window->m_wsh.startSizing(wParam); eventHandled = true; }*/ break; //////////////////////////////////////////////////////////////////////// // Tablet events, processed //////////////////////////////////////////////////////////////////////// case WT_PACKET: ((GHOST_WindowWin32 *)window)->processWin32TabletEvent(wParam, lParam); break; case WT_CSRCHANGE: case WT_PROXIMITY: ((GHOST_WindowWin32 *)window)->processWin32TabletInitEvent(); break; //////////////////////////////////////////////////////////////////////// // Mouse events, processed //////////////////////////////////////////////////////////////////////// case WM_LBUTTONDOWN: window->registerMouseClickEvent(0); event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft); break; case WM_MBUTTONDOWN: window->registerMouseClickEvent(0); event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle); break; case WM_RBUTTONDOWN: window->registerMouseClickEvent(0); event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight); break; case WM_XBUTTONDOWN: window->registerMouseClickEvent(0); if ((short) HIWORD(wParam) == XBUTTON1) { event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4); } else if ((short) HIWORD(wParam) == XBUTTON2) { event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5); } break; case WM_LBUTTONUP: window->registerMouseClickEvent(1); if(window->m_wsh.isWinChanges()) window->m_wsh.accept(); else event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft); break; case WM_MBUTTONUP: window->registerMouseClickEvent(1); event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle); break; case WM_RBUTTONUP: window->registerMouseClickEvent(1); event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight); break; case WM_XBUTTONUP: window->registerMouseClickEvent(1); if ((short) HIWORD(wParam) == XBUTTON1) { event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4); } else if ((short) HIWORD(wParam) == XBUTTON2) { event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5); } break; case WM_MOUSEMOVE: if(window->m_wsh.isWinChanges()) window->m_wsh.updateWindowSize(); else event = processCursorEvent(GHOST_kEventCursorMove, window); break; case WM_MOUSEWHEEL: /* The WM_MOUSEWHEEL message is sent to the focus window * when the mouse wheel is rotated. The DefWindowProc * function propagates the message to the window's parent. * There should be no internal forwarding of the message, * since DefWindowProc propagates it up the parent chain * until it finds a window that processes it. */ event = processWheelEvent(window, wParam, lParam); break; case WM_SETCURSOR: /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor * to move within a window and mouse input is not captured. * This means we have to set the cursor shape every time the mouse moves! * The DefWindowProc function uses this message to set the cursor to an * arrow if it is not in the client area. */ if (LOWORD(lParam) == HTCLIENT) { // Load the current cursor window->loadCursor(window->getCursorVisibility(), window->getCursorShape()); // Bypass call to DefWindowProc return 0; } else { // Outside of client area show standard cursor window->loadCursor(true, GHOST_kStandardCursorDefault); } break; //////////////////////////////////////////////////////////////////////// // Mouse events, ignored //////////////////////////////////////////////////////////////////////// case WM_NCMOUSEMOVE: /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved * within the nonclient area of the window. This message is posted to the window * that contains the cursor. If a window has captured the mouse, this message is not posted. */ case WM_NCHITTEST: /* The WM_NCHITTEST message is sent to a window when the cursor moves, or * when a mouse button is pressed or released. If the mouse is not captured, * the message is sent to the window beneath the cursor. Otherwise, the message * is sent to the window that has captured the mouse. */ break; //////////////////////////////////////////////////////////////////////// // Window events, processed //////////////////////////////////////////////////////////////////////// case WM_CLOSE: /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */ event = processWindowEvent(GHOST_kEventWindowClose, window); break; case WM_ACTIVATE: /* The WM_ACTIVATE message is sent to both the window being activated and the window being * deactivated. If the windows use the same input queue, the message is sent synchronously, * first to the window procedure of the top-level window being deactivated, then to the window * procedure of the top-level window being activated. If the windows use different input queues, * the message is sent asynchronously, so the window is activated immediately. */ { GHOST_ModifierKeys modifiers; modifiers.clear(); system->storeModifierKeys(modifiers); event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window); /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL * will not be dispatched to OUR active window if we minimize one of OUR windows. */ if(LOWORD(wParam)==WA_INACTIVE) window->lostMouseCapture(); lResult = ::DefWindowProc(hwnd, msg, wParam, lParam); break; } case WM_PAINT: /* An application sends the WM_PAINT message when the system or another application * makes a request to paint a portion of an application's window. The message is sent * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage * function when the application obtains a WM_PAINT message by using the GetMessage or * PeekMessage function. */ event = processWindowEvent(GHOST_kEventWindowUpdate, window); ::ValidateRect(hwnd, NULL); break; case WM_GETMINMAXINFO: /* The WM_GETMINMAXINFO message is sent to a window when the size or * position of the window is about to change. An application can use * this message to override the window's default maximized size and * position, or its default minimum or maximum tracking size. */ processMinMaxInfo((MINMAXINFO *) lParam); /* Let DefWindowProc handle it. */ break; case WM_SIZE: /* The WM_SIZE message is sent to a window after its size has changed. * The WM_SIZE and WM_MOVE messages are not sent if an application handles the * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient * to perform any move or size change processing during the WM_WINDOWPOSCHANGED * message without calling DefWindowProc. */ event = processWindowEvent(GHOST_kEventWindowSize, window); break; case WM_CAPTURECHANGED: window->lostMouseCapture(); break; case WM_MOVING: /* The WM_MOVING message is sent to a window that the user is moving. By processing * this message, an application can monitor the size and position of the drag rectangle * and, if needed, change its size or position. */ case WM_MOVE: /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient * to perform any move or size change processing during the WM_WINDOWPOSCHANGED * message without calling DefWindowProc. */ event = processWindowEvent(GHOST_kEventWindowMove, window); break; //////////////////////////////////////////////////////////////////////// // Window events, ignored //////////////////////////////////////////////////////////////////////// case WM_WINDOWPOSCHANGED: /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place * in the Z order has changed as a result of a call to the SetWindowPos function or * another window-management function. * The WM_SIZE and WM_MOVE messages are not sent if an application handles the * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient * to perform any move or size change processing during the WM_WINDOWPOSCHANGED * message without calling DefWindowProc. */ case WM_ERASEBKGND: /* An application sends the WM_ERASEBKGND message when the window background must be * erased (for example, when a window is resized). The message is sent to prepare an * invalidated portion of a window for painting. */ case WM_NCPAINT: /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */ case WM_NCACTIVATE: /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed * to indicate an active or inactive state. */ case WM_DESTROY: /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window * procedure of the window being destroyed after the window is removed from the screen. * This message is sent first to the window being destroyed and then to the child windows * (if any) as they are destroyed. During the processing of the message, it can be assumed * that all child windows still exist. */ case WM_NCDESTROY: /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY * message. WM_DESTROY is used to free the allocated memory object associated with the window. */ break; case WM_KILLFOCUS: /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. * We want to prevent this if a window is still active and it loses focus to nowhere*/ if (!wParam && hwnd == GetActiveWindow()) SetFocus(hwnd); case WM_SHOWWINDOW: /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */ case WM_WINDOWPOSCHANGING: /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in * the Z order is about to change as a result of a call to the SetWindowPos function or * another window-management function. */ case WM_SETFOCUS: /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */ case WM_ENTERSIZEMOVE: /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving * or sizing modal loop. The window enters the moving or sizing modal loop when the user * clicks the window's title bar or sizing border, or when the window passes the * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when * DefWindowProc returns. */ break; //////////////////////////////////////////////////////////////////////// // Other events //////////////////////////////////////////////////////////////////////// case WM_GETTEXT: /* An application sends a WM_GETTEXT message to copy the text that * corresponds to a window into a buffer provided by the caller. */ case WM_ACTIVATEAPP: /* The WM_ACTIVATEAPP message is sent when a window belonging to a * different application than the active window is about to be activated. * The message is sent to the application whose window is being activated * and to the application whose window is being deactivated. */ case WM_TIMER: /* The WIN32 docs say: * The WM_TIMER message is posted to the installing thread's message queue * when a timer expires. You can process the message by providing a WM_TIMER * case in the window procedure. Otherwise, the default window procedure will * call the TimerProc callback function specified in the call to the SetTimer * function used to install the timer. * * In GHOST, we let DefWindowProc call the timer callback. */ break; case WM_CANCELMODE: if(window->m_wsh.isWinChanges()) window->m_wsh.cancel(); } } else { // Event found for a window before the pointer to the class has been set. GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n"); /* These are events we typically miss at this point: * WM_GETMINMAXINFO 0x24 * WM_NCCREATE 0x81 * WM_NCCALCSIZE 0x83 * WM_CREATE 0x01 * We let DefWindowProc do the work. */ } } else { // Events without valid hwnd GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n"); } if (event) { system->pushEvent(event); eventHandled = true; } if (!eventHandled) lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam); return lResult; }
GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, RAWINPUT const& raw, int *keyDown, char *vk) { GHOST_TKey key = GHOST_kKeyUnknown; if (!keyDown) return GHOST_kKeyUnknown; GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); GHOST_ModifierKeys modifiers; system->retrieveModifierKeys(modifiers); // RI_KEY_BREAK doesn't work for sticky keys release, so we also // check for the up message unsigned int msg = raw.data.keyboard.Message; *keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP; key = this->convertKey(window, raw.data.keyboard.VKey, raw.data.keyboard.MakeCode, (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0))); // extra handling of modifier keys: don't send repeats out from GHOST if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt) { bool changed = false; GHOST_TModifierKeyMask modifier; switch (key) { case GHOST_kKeyLeftShift: { changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown); modifier = GHOST_kModifierKeyLeftShift; } break; case GHOST_kKeyRightShift: { changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown); modifier = GHOST_kModifierKeyRightShift; } break; case GHOST_kKeyLeftControl: { changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown); modifier = GHOST_kModifierKeyLeftControl; } break; case GHOST_kKeyRightControl: { changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown); modifier = GHOST_kModifierKeyRightControl; } break; case GHOST_kKeyLeftAlt: { changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown); modifier = GHOST_kModifierKeyLeftAlt; } break; case GHOST_kKeyRightAlt: { changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown); modifier = GHOST_kModifierKeyRightAlt; } break; default: break; } if (changed) { modifiers.set(modifier, (bool)*keyDown); system->storeModifierKeys(modifiers); } else { key = GHOST_kKeyUnknown; } } if (vk) *vk = raw.data.keyboard.VKey; return key; }
GHOST_TKey GHOST_SystemWin32::convertKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam) const { GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem(); bool isExtended = (lParam&(1<<24))?true:false; GHOST_TKey key; GHOST_ModifierKeys oldModifiers, newModifiers; system->retrieveModifierKeys(oldModifiers); system->getModifierKeys(newModifiers); //std::cout << wParam << " " << system->m_curKeyStatus[wParam] << " shift pressed: " << system->shiftPressed() << std::endl; if ((wParam >= '0') && (wParam <= '9')) { // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) key = (GHOST_TKey)(wParam - '0' + GHOST_kKey0); } else if ((wParam >= 'A') && (wParam <= 'Z')) { // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) key = (GHOST_TKey)(wParam - 'A' + GHOST_kKeyA); } else if ((wParam >= VK_F1) && (wParam <= VK_F24)) { key = (GHOST_TKey)(wParam - VK_F1 + GHOST_kKeyF1); } else { switch (wParam) { case VK_RETURN: key = isExtended?GHOST_kKeyNumpadEnter:GHOST_kKeyEnter; break; case VK_BACK: key = GHOST_kKeyBackSpace; break; case VK_TAB: key = GHOST_kKeyTab; break; case VK_ESCAPE: key = GHOST_kKeyEsc; break; case VK_SPACE: key = GHOST_kKeySpace; break; case VK_PRIOR: key = GHOST_kKeyUpPage; break; case VK_NEXT: key = GHOST_kKeyDownPage; break; case VK_END: key = GHOST_kKeyEnd; break; case VK_HOME: { if(system->m_curKeyStatus[VK_NUMPAD7] && system->shiftPressed()) key = GHOST_kKeyNumpad7; else key = GHOST_kKeyHome; } break; case VK_INSERT: key = GHOST_kKeyInsert; break; case VK_DELETE: key = GHOST_kKeyDelete; break; case VK_LEFT: key = GHOST_kKeyLeftArrow; break; case VK_RIGHT: key = GHOST_kKeyRightArrow; break; case VK_UP: key = GHOST_kKeyUpArrow; break; case VK_DOWN: key = GHOST_kKeyDownArrow; break; case VK_NUMPAD0: key = GHOST_kKeyNumpad0; break; case VK_NUMPAD1: key = GHOST_kKeyNumpad1; break; case VK_NUMPAD2: key = GHOST_kKeyNumpad2; break; case VK_NUMPAD3: key = GHOST_kKeyNumpad3; break; case VK_NUMPAD4: key = GHOST_kKeyNumpad4; break; case VK_NUMPAD5: key = GHOST_kKeyNumpad5; break; case VK_NUMPAD6: key = GHOST_kKeyNumpad6; break; case VK_NUMPAD7: key = GHOST_kKeyNumpad7; break; case VK_NUMPAD8: key = GHOST_kKeyNumpad8; break; case VK_NUMPAD9: key = GHOST_kKeyNumpad9; break; case VK_SNAPSHOT: key = GHOST_kKeyPrintScreen; break; case VK_PAUSE: key = GHOST_kKeyPause; break; case VK_MULTIPLY: key = GHOST_kKeyNumpadAsterisk; break; case VK_SUBTRACT: key = GHOST_kKeyNumpadMinus; break; case VK_DECIMAL: key = GHOST_kKeyNumpadPeriod; break; case VK_DIVIDE: key = GHOST_kKeyNumpadSlash; break; case VK_ADD: key = GHOST_kKeyNumpadPlus; break; case VK_SEMICOLON: key = GHOST_kKeySemicolon; break; case VK_EQUALS: key = GHOST_kKeyEqual; break; case VK_COMMA: key = GHOST_kKeyComma; break; case VK_MINUS: key = GHOST_kKeyMinus; break; case VK_PERIOD: key = GHOST_kKeyPeriod; break; case VK_SLASH: key = GHOST_kKeySlash; break; case VK_BACK_QUOTE: key = GHOST_kKeyAccentGrave; break; case VK_OPEN_BRACKET: key = GHOST_kKeyLeftBracket; break; case VK_BACK_SLASH: key = GHOST_kKeyBackslash; break; case VK_CLOSE_BRACKET: key = GHOST_kKeyRightBracket; break; case VK_QUOTE: key = GHOST_kKeyQuote; break; case VK_GR_LESS: key = GHOST_kKeyGrLess; break; case VK_SHIFT: { bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftShift) != newModifiers.get(GHOST_kModifierKeyLeftShift); if(lchanged) { key = GHOST_kKeyLeftShift; } else { bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightShift) != newModifiers.get(GHOST_kModifierKeyRightShift); if(rchanged) { key = GHOST_kKeyRightShift; } else { key = GHOST_kKeyUnknown; } } } break; case VK_CONTROL: { bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftControl) != newModifiers.get(GHOST_kModifierKeyLeftControl); if(lchanged) { key = GHOST_kKeyLeftControl; } else { bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightControl) != newModifiers.get(GHOST_kModifierKeyRightControl); if(rchanged) { key = GHOST_kKeyRightControl; } else { key = GHOST_kKeyUnknown; } } } break; case VK_MENU: { if(m_hasAltGr && isExtended) { // We have here an extended RAlt, which is AltGr. The keyboard driver on Windows sends before this a LControl, so // to be able to input characters created with AltGr (normal on German, French, Finnish and other keyboards) we // push an extra LControl up event. This ensures we don't have a 'hanging' ctrl event in Blender windowmanager // when typing in Text editor or Console. GHOST_Event *extra = new GHOST_EventKey(getSystem()->getMilliSeconds(), GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl, '\0'); ((GHOST_SystemWin32*)getSystem())->pushEvent(extra); newModifiers.set(GHOST_kModifierKeyRightControl, false); newModifiers.set(GHOST_kModifierKeyLeftControl, false); } bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftAlt) != newModifiers.get(GHOST_kModifierKeyLeftAlt); if(lchanged) { key = GHOST_kKeyLeftAlt; } else { bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightAlt) != newModifiers.get(GHOST_kModifierKeyRightAlt); if(rchanged) { key = GHOST_kKeyRightAlt; } else { key = GHOST_kKeyUnknown; } } } break; case VK_LWIN: case VK_RWIN: key = GHOST_kKeyOS; break; case VK_NUMLOCK: key = GHOST_kKeyNumLock; break; case VK_SCROLL: key = GHOST_kKeyScrollLock; break; case VK_CAPITAL: key = GHOST_kKeyCapsLock; break; case VK_OEM_8: key = ((GHOST_SystemWin32*)getSystem())->processSpecialKey(window, wParam, lParam); break; default: key = GHOST_kKeyUnknown; break; } } ((GHOST_SystemWin32*)getSystem())->storeModifierKeys(newModifiers); return key; }