Changeset View
Standalone View
intern/ghost/intern/GHOST_WindowWin32.cpp
| Context not available. | |||||
| #include <string.h> | #include <string.h> | ||||
| #include <assert.h> | #include <assert.h> | ||||
| #ifndef GET_POINTERID_WPARAM | |||||
| #define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam)) | |||||
| #endif // GET_POINTERID_WPARAM | |||||
| const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass"; | const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass"; | ||||
| const int GHOST_WindowWin32::s_maxTitleLength = 128; | const int GHOST_WindowWin32::s_maxTitleLength = 128; | ||||
| Context not available. | |||||
| m_maxPressure(0), | m_maxPressure(0), | ||||
| m_normal_state(GHOST_kWindowStateNormal), | m_normal_state(GHOST_kWindowStateNormal), | ||||
| m_user32(NULL), | m_user32(NULL), | ||||
| m_fpGetPointerInfo(NULL), | |||||
| m_fpGetPointerPenInfo(NULL), | |||||
| m_parentWindowHwnd(parentwindowhwnd), | m_parentWindowHwnd(parentwindowhwnd), | ||||
| m_debug_context(is_debug) | m_debug_context(is_debug), | ||||
| m_NativePressureEnabled(true), | |||||
| m_WintabPressureEnabled(true) | |||||
| { | { | ||||
| if (state != GHOST_kWindowStateFullScreen) { | if (state != GHOST_kWindowStateFullScreen) { | ||||
| RECT rect; | RECT rect; | ||||
| Context not available. | |||||
| RegisterRawInputDevices(&device, 1, sizeof(device)); | RegisterRawInputDevices(&device, 1, sizeof(device)); | ||||
| } | } | ||||
| m_user32 = ::LoadLibrary("user32.dll"); | |||||
| if (m_user32) { | |||||
| m_fpGetPointerInfo = (GHOST_WIN32_GetPointerInfo) ::GetProcAddress(m_user32, "GetPointerInfo"); | |||||
| m_fpGetPointerPenInfo = (GHOST_WIN32_GetPointerPenInfo) ::GetProcAddress(m_user32, "GetPointerPenInfo"); | |||||
| } | |||||
| m_wintab = ::LoadLibrary("Wintab32.dll"); | m_wintab = ::LoadLibrary("Wintab32.dll"); | ||||
| if (m_wintab) { | if (m_wintab) { | ||||
| GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA"); | GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA"); | ||||
| Context not available. | |||||
| if (fpWTOpen) { | if (fpWTOpen) { | ||||
| // The Wintab spec says we must open the context disabled if we are using cursor masks. | // The Wintab spec says we must open the context disabled if we are using cursor masks. | ||||
| m_tablet = fpWTOpen(m_hWnd, &lc, FALSE); | m_tablet = fpWTOpen(m_hWnd, &lc, FALSE); | ||||
| if (m_tablet) { | if (m_tablet && !m_tabletData) { | ||||
| m_tabletData = new GHOST_TabletData(); | m_tabletData = new GHOST_TabletData(); | ||||
| m_tabletData->Active = GHOST_kTabletModeNone; | m_tabletData->Active = GHOST_kTabletModeNone; | ||||
| } | } | ||||
| Context not available. | |||||
| if (m_Bar) { | if (m_Bar) { | ||||
| m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS); | m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS); | ||||
| m_Bar->Release(); | m_Bar->Release(); | ||||
| m_Bar = NULL; | |||||
| } | } | ||||
| if (m_wintab) { | if (m_wintab) { | ||||
| GHOST_WIN32_WTClose fpWTClose = (GHOST_WIN32_WTClose) ::GetProcAddress(m_wintab, "WTClose"); | GHOST_WIN32_WTClose fpWTClose = (GHOST_WIN32_WTClose) ::GetProcAddress(m_wintab, "WTClose"); | ||||
| if (fpWTClose) { | if (fpWTClose) { | ||||
| if (m_tablet) | if (m_tablet) { | ||||
| fpWTClose(m_tablet); | fpWTClose(m_tablet); | ||||
| delete m_tabletData; | m_tablet = NULL; | ||||
| m_tabletData = NULL; | } | ||||
| fpWTClose = NULL; | |||||
| } | } | ||||
| FreeLibrary(m_wintab); | |||||
| m_wintab = NULL; | |||||
| } | |||||
| if (m_tabletData) { | |||||
| delete m_tabletData; | |||||
| m_tabletData = NULL; | |||||
| } | |||||
| if (m_user32) { | |||||
| FreeLibrary(m_user32); | |||||
| m_user32 = NULL; | |||||
| m_fpGetPointerInfo = NULL; | |||||
| m_fpGetPointerPenInfo = NULL; | |||||
| } | } | ||||
| if (m_customCursor) { | if (m_customCursor) { | ||||
| Context not available. | |||||
| if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) { | if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) { | ||||
| ::ReleaseDC(m_hWnd, m_hDC); | ::ReleaseDC(m_hWnd, m_hDC); | ||||
| m_hDC = NULL; | |||||
| } | } | ||||
| if (m_hWnd) { | if (m_hWnd) { | ||||
| Context not available. | |||||
| RevokeDragDrop(m_hWnd); | RevokeDragDrop(m_hWnd); | ||||
| // Release our reference of the DropTarget and it will delete itself eventually. | // Release our reference of the DropTarget and it will delete itself eventually. | ||||
| m_dropTarget->Release(); | m_dropTarget->Release(); | ||||
| m_dropTarget = NULL; | |||||
| } | } | ||||
| ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, NULL); | ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, NULL); | ||||
| ::DestroyWindow(m_hWnd); | ::DestroyWindow(m_hWnd); | ||||
brecht: This memory leaks in `GHOST_WindowWin32::~GHOST_WindowWin32` if no wintab is present. The code… | |||||
Not Done Inline Actionsplease do this only once and cache the result. LazyDodo: please do this only once and cache the result. | |||||
| Context not available. | |||||
| return GHOST_kSuccess; | return GHOST_kSuccess; | ||||
| } | } | ||||
| void GHOST_WindowWin32::setWintabPressureEnabled(bool enabled) | |||||
| { | |||||
| if (enabled != m_WintabPressureEnabled) { | |||||
| m_WintabPressureEnabled = enabled; | |||||
| if (m_tabletData) { | |||||
| m_tabletData->Active = GHOST_kTabletModeNone; | |||||
| } | |||||
| } | |||||
| } | |||||
| void GHOST_WindowWin32::setNativePressureEnabled(bool enabled) | |||||
| { | |||||
| if (enabled != m_NativePressureEnabled) { | |||||
| m_NativePressureEnabled = enabled; | |||||
Not Done Inline ActionsIs the pointerPenInfo.pressure > 0 test correct? Are there case where the mask is set but no valid pressure is provided? It seems like it could cause full pressure when there is none. brecht: Is the `pointerPenInfo.pressure > 0` test correct? Are there case where the mask is set but no… | |||||
Done Inline ActionsWith the Microsoft Surface Pen if you hover the pen within 1cm of the screen you'll start getting WM_POINTERUPDATE events with the PEN_MASK_PRESSURE mask set and zero pressure. When the pen is touching the screen the pressure field will have a value between 1 and 1024. After taking a look at the WinTab code I made it so that if the pen is not physically touching the screen GHOST_kTabletModeNone is set. With this implementation if the user switches from the pen to the mouse it will switch from GHOST_kTabletModeStylus to GHOST_kTabletModeNone and they'll be able to draw at full pressure with the mouse. That said I have realised that the test for PEN_FLAG_ERASER should happen after this, otherwise if their is a user with a pressure sensitive eraser it may start drawing a line instead. chris_82: With the Microsoft Surface Pen if you hover the pen within 1cm of the screen you'll start… | |||||
Done Inline ActionsMakes sense then. You could leave a comment explaining this in the code. brecht: Makes sense then. You could leave a comment explaining this in the code. | |||||
| if (m_tabletData) { | |||||
| m_tabletData->Active = GHOST_kTabletModeNone; | |||||
| } | |||||
| } | |||||
| } | |||||
| void GHOST_WindowWin32::processWin32PointerEvent(WPARAM wParam) | |||||
| { | |||||
| if (!m_NativePressureEnabled) { | |||||
| return; // Native pressure disabled by user | |||||
| } | |||||
| if (!m_fpGetPointerInfo || !m_fpGetPointerPenInfo) { | |||||
Not Done Inline Actionsthe within -> to within brecht: the within -> to within | |||||
| return; // OS version does not support pointer API | |||||
| } | |||||
| UINT32 pointerId = GET_POINTERID_WPARAM(wParam); | |||||
| POINTER_INFO pointerInfo; | |||||
| if (!m_fpGetPointerInfo(pointerId, &pointerInfo)) { | |||||
| return; // Invalid pointer info | |||||
| } | |||||
| if (!m_tabletData) { | |||||
| m_tabletData = new GHOST_TabletData(); | |||||
| m_tabletData->Active = GHOST_kTabletModeNone; | |||||
| } | |||||
| m_tabletData->Active = GHOST_kTabletModeNone; | |||||
| m_tabletData->Pressure = 1.0f; | |||||
| m_tabletData->Xtilt = 0.0f; | |||||
| m_tabletData->Ytilt = 0.0f; | |||||
| if (pointerInfo.pointerType & PT_POINTER) { | |||||
| POINTER_PEN_INFO pointerPenInfo; | |||||
| if (!m_fpGetPointerPenInfo(pointerId, &pointerPenInfo)) { | |||||
| return; | |||||
| } | |||||
| // With the Microsoft Surface Pen if you hover the within 1cm of the screen the WM_POINTERUPDATE | |||||
| // event will fire with PEN_MASK_PRESSURE mask set and zero pressure. In this case we disable | |||||
| // tablet mode until the pen is physically touching. This enables the user to switch to the | |||||
| // mouse and draw at full pressure. | |||||
| if (pointerPenInfo.penMask & PEN_MASK_PRESSURE && pointerPenInfo.pressure > 0) { | |||||
| m_tabletData->Active = GHOST_kTabletModeStylus; | |||||
| m_tabletData->Pressure = pointerPenInfo.pressure / 1024.0f; | |||||
| } | |||||
| if (pointerPenInfo.penFlags & PEN_FLAG_ERASER) { | |||||
| m_tabletData->Active = GHOST_kTabletModeEraser; | |||||
| } | |||||
| if (pointerPenInfo.penFlags & PEN_MASK_TILT_X) { | |||||
| m_tabletData->Xtilt = fmin(fabs(pointerPenInfo.tiltX / 90), 1.0f); | |||||
| } | |||||
| if (pointerPenInfo.penFlags & PEN_MASK_TILT_Y) { | |||||
| m_tabletData->Ytilt = fmin(fabs(pointerPenInfo.tiltY / 90), 1.0f); | |||||
| } | |||||
| } | |||||
| } | |||||
| void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state) | void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state) | ||||
| { | { | ||||
| if (!m_tablet) { | if (!m_tablet) { | ||||
| Context not available. | |||||
| void GHOST_WindowWin32::processWin32TabletInitEvent() | void GHOST_WindowWin32::processWin32TabletInitEvent() | ||||
| { | { | ||||
| if (m_wintab && m_tabletData) { | if (m_WintabPressureEnabled && m_wintab && m_tabletData) { | ||||
| GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA"); | GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA"); | ||||
| // let's see if we can initialize tablet here | // let's see if we can initialize tablet here | ||||
| Context not available. | |||||
| void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam) | void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam) | ||||
| { | { | ||||
| if (!m_WintabPressureEnabled) { | |||||
| return; // Wintab pressure disabled by user | |||||
| } | |||||
| PACKET pkt; | PACKET pkt; | ||||
| if (m_wintab) { | if (m_wintab) { | ||||
| GHOST_WIN32_WTPacket fpWTPacket = (GHOST_WIN32_WTPacket) ::GetProcAddress(m_wintab, "WTPacket"); | GHOST_WIN32_WTPacket fpWTPacket = (GHOST_WIN32_WTPacket) ::GetProcAddress(m_wintab, "WTPacket"); | ||||
| Context not available. | |||||
| GHOST_TUns16 GHOST_WindowWin32::getDPIHint() | GHOST_TUns16 GHOST_WindowWin32::getDPIHint() | ||||
| { | { | ||||
| if (!m_user32) { | |||||
| m_user32 = ::LoadLibrary("user32.dll"); | |||||
| } | |||||
| if (m_user32) { | if (m_user32) { | ||||
| GHOST_WIN32_GetDpiForWindow fpGetDpiForWindow = (GHOST_WIN32_GetDpiForWindow) ::GetProcAddress(m_user32, "GetDpiForWindow"); | GHOST_WIN32_GetDpiForWindow fpGetDpiForWindow = (GHOST_WIN32_GetDpiForWindow) ::GetProcAddress(m_user32, "GetDpiForWindow"); | ||||
| Context not available. | |||||
This memory leaks in GHOST_WindowWin32::~GHOST_WindowWin32 if no wintab is present. The code there needs to be updated.