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; }