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_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_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; 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*)::GetWindowLong(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_KEYDOWN: case WM_SYSKEYDOWN: event = processKeyEvent(window, true, wParam, lParam); if (!event) { GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ") GHOST_PRINT(msg) GHOST_PRINT(" key ignored\n") } break; case WM_KEYUP: case WM_SYSKEYUP: event = processKeyEvent(window, false, wParam, lParam); if (!event) { GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ") GHOST_PRINT(msg) GHOST_PRINT(" key ignored\n") } break; //////////////////////////////////////////////////////////////////////// // Keyboard events, ignored //////////////////////////////////////////////////////////////////////// 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. */ 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. */ 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(true); event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft); break; case WM_MBUTTONDOWN: window->registerMouseClickEvent(true); event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle); break; case WM_RBUTTONDOWN: window->registerMouseClickEvent(true); event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight); break; case WM_XBUTTONDOWN: window->registerMouseClickEvent(true); 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(false); event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft); break; case WM_MBUTTONUP: window->registerMouseClickEvent(false); event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle); break; case WM_RBUTTONUP: window->registerMouseClickEvent(false); event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight); break; case WM_XBUTTONUP: window->registerMouseClickEvent(false); 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: 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. */ 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. */ 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. */ case WM_KILLFOCUS: /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. */ 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_BLND_NDOF_AXIS: { GHOST_TEventNDOFData ndofdata; system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata); system->m_eventManager-> pushEvent(new GHOST_EventNDOF( system->getMilliSeconds(), GHOST_kEventNDOFMotion, window, ndofdata)); } break; case WM_BLND_NDOF_BTN: { GHOST_TEventNDOFData ndofdata; system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata); system->m_eventManager-> pushEvent(new GHOST_EventNDOF( system->getMilliSeconds(), GHOST_kEventNDOFButton, window, ndofdata)); } break; } } 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. */ } }