Changeset View
Standalone View
intern/ghost/intern/GHOST_WindowWin32.cpp
| Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
| #ifdef WIN32_COMPOSITING | #ifdef WIN32_COMPOSITING | ||||
| #include <Dwmapi.h> | #include <Dwmapi.h> | ||||
| #endif | #endif | ||||
| #include <math.h> | #include <math.h> | ||||
| #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; | ||||
| /* force NVidia Optimus to used dedicated graphics */ | /* force NVidia Optimus to used dedicated graphics */ | ||||
| Show All 19 Lines | : GHOST_Window(width, height, state, | ||||
| m_inLiveResize(false), | m_inLiveResize(false), | ||||
| m_system(system), | m_system(system), | ||||
| m_hDC(0), | m_hDC(0), | ||||
| m_hasMouseCaptured(false), | m_hasMouseCaptured(false), | ||||
| m_hasGrabMouse(false), | m_hasGrabMouse(false), | ||||
| m_nPressedButtons(0), | m_nPressedButtons(0), | ||||
| m_customCursor(0), | m_customCursor(0), | ||||
| m_wantAlphaBackground(alphaBackground), | m_wantAlphaBackground(alphaBackground), | ||||
| m_wintab(NULL), | |||||
| m_tabletData(NULL), | |||||
| m_tablet(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) | ||||
| { | { | ||||
| if (state != GHOST_kWindowStateFullScreen) { | if (state != GHOST_kWindowStateFullScreen) { | ||||
| RECT rect; | RECT rect; | ||||
| MONITORINFO monitor; | MONITORINFO monitor; | ||||
| GHOST_TUns32 tw, th; | GHOST_TUns32 tw, th; | ||||
| ▲ Show 20 Lines • Show All 173 Lines • ▼ Show 20 Lines | if (parentwindowhwnd != 0) { | ||||
| RAWINPUTDEVICE device = {0}; | RAWINPUTDEVICE device = {0}; | ||||
| device.usUsagePage = 0x01; /* usUsagePage & usUsage for keyboard*/ | device.usUsagePage = 0x01; /* usUsagePage & usUsage for keyboard*/ | ||||
| device.usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */ | device.usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */ | ||||
| device.dwFlags |= RIDEV_INPUTSINK; // makes WM_INPUT is visible for ghost when has parent window | device.dwFlags |= RIDEV_INPUTSINK; // makes WM_INPUT is visible for ghost when has parent window | ||||
| device.hwndTarget = m_hWnd; | device.hwndTarget = m_hWnd; | ||||
| RegisterRawInputDevices(&device, 1, sizeof(device)); | RegisterRawInputDevices(&device, 1, sizeof(device)); | ||||
| } | } | ||||
| m_wintab = ::LoadLibrary("Wintab32.dll"); | // Initialize tablet data | ||||
| if (m_wintab) { | memset(&m_tabletData, 0, sizeof(m_tabletData)); | ||||
| GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA"); | m_tabletData.Active = GHOST_kTabletModeNone; | ||||
| GHOST_WIN32_WTOpen fpWTOpen = (GHOST_WIN32_WTOpen) ::GetProcAddress(m_wintab, "WTOpenA"); | |||||
| // Initialize Windows Ink | |||||
| 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"); | |||||
| } | |||||
| // Initialize Wintab | |||||
| memset(&m_wintab, 0, sizeof(m_wintab)); | |||||
| m_wintab.handle = ::LoadLibrary("Wintab32.dll"); | |||||
| if (m_wintab.handle) { | |||||
| // Get API functions | |||||
| m_wintab.info = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab.handle, "WTInfoA"); | |||||
| m_wintab.open = (GHOST_WIN32_WTOpen) ::GetProcAddress(m_wintab.handle, "WTOpenA"); | |||||
| m_wintab.close = (GHOST_WIN32_WTClose) ::GetProcAddress(m_wintab.handle, "WTClose"); | |||||
| m_wintab.packet = (GHOST_WIN32_WTPacket) ::GetProcAddress(m_wintab.handle, "WTPacket"); | |||||
| m_wintab.enable = (GHOST_WIN32_WTEnable) ::GetProcAddress(m_wintab.handle, "WTEnable"); | |||||
| m_wintab.overlap = (GHOST_WIN32_WTOverlap) ::GetProcAddress(m_wintab.handle, "WTOverlap"); | |||||
| // Let's see if we can initialize tablet here. | // Let's see if we can initialize tablet here. | ||||
| // Check if WinTab available by getting system context info. | // Check if WinTab available by getting system context info. | ||||
| LOGCONTEXT lc = { 0 }; | LOGCONTEXT lc = { 0 }; | ||||
| lc.lcOptions |= CXO_SYSTEM; | lc.lcOptions |= CXO_SYSTEM; | ||||
| if (fpWTInfo && fpWTInfo(WTI_DEFSYSCTX, 0, &lc)) { | if (m_wintab.open && m_wintab.info && m_wintab.info(WTI_DEFSYSCTX, 0, &lc)) { | ||||
| // Now init the tablet | // Now init the tablet | ||||
| /* The maximum tablet size, pressure and orientation (tilt) */ | /* The maximum tablet size, pressure and orientation (tilt) */ | ||||
| AXIS TabletX, TabletY, Pressure, Orientation[3]; | AXIS TabletX, TabletY, Pressure, Orientation[3]; | ||||
| // Open a Wintab context | // Open a Wintab context | ||||
| // Open the context | // Open the context | ||||
| lc.lcPktData = PACKETDATA; | lc.lcPktData = PACKETDATA; | ||||
| lc.lcPktMode = PACKETMODE; | lc.lcPktMode = PACKETMODE; | ||||
| lc.lcOptions |= CXO_MESSAGES; | lc.lcOptions |= CXO_MESSAGES; | ||||
| lc.lcMoveMask = PACKETDATA; | lc.lcMoveMask = PACKETDATA; | ||||
| /* Set the entire tablet as active */ | /* Set the entire tablet as active */ | ||||
| fpWTInfo(WTI_DEVICES, DVC_X, &TabletX); | m_wintab.info(WTI_DEVICES, DVC_X, &TabletX); | ||||
| fpWTInfo(WTI_DEVICES, DVC_Y, &TabletY); | m_wintab.info(WTI_DEVICES, DVC_Y, &TabletY); | ||||
| /* get the max pressure, to divide into a float */ | /* get the max pressure, to divide into a float */ | ||||
| BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure); | BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure); | ||||
| if (pressureSupport) | if (pressureSupport) | ||||
| m_maxPressure = Pressure.axMax; | m_wintab.maxPressure = Pressure.axMax; | ||||
| else | else | ||||
| m_maxPressure = 0; | m_wintab.maxPressure = 0; | ||||
| /* get the max tilt axes, to divide into floats */ | /* get the max tilt axes, to divide into floats */ | ||||
| BOOL tiltSupport = fpWTInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation); | BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation); | ||||
| if (tiltSupport) { | if (tiltSupport) { | ||||
| /* does the tablet support azimuth ([0]) and altitude ([1]) */ | /* does the tablet support azimuth ([0]) and altitude ([1]) */ | ||||
| if (Orientation[0].axResolution && Orientation[1].axResolution) { | if (Orientation[0].axResolution && Orientation[1].axResolution) { | ||||
| /* all this assumes the minimum is 0 */ | /* all this assumes the minimum is 0 */ | ||||
| m_maxAzimuth = Orientation[0].axMax; | m_wintab.maxAzimuth = Orientation[0].axMax; | ||||
| m_maxAltitude = Orientation[1].axMax; | m_wintab.maxAltitude = Orientation[1].axMax; | ||||
| } | } | ||||
| else { /* no so dont do tilt stuff */ | else { /* no so dont do tilt stuff */ | ||||
| m_maxAzimuth = m_maxAltitude = 0; | m_wintab.maxAzimuth = m_wintab.maxAltitude = 0; | ||||
| } | } | ||||
| } | } | ||||
| 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_wintab.tablet = m_wintab.open(m_hWnd, &lc, FALSE); | ||||
| if (m_tablet) { | if (m_wintab.enable && m_wintab.tablet) { | ||||
| m_tabletData = new GHOST_TabletData(); | m_wintab.enable(m_wintab.tablet, TRUE); | ||||
| m_tabletData->Active = GHOST_kTabletModeNone; | |||||
| } | |||||
| GHOST_WIN32_WTEnable fpWTEnable = (GHOST_WIN32_WTEnable) ::GetProcAddress(m_wintab, "WTEnable"); | |||||
| if (fpWTEnable) { | |||||
| fpWTEnable(m_tablet, TRUE); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar); | CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar); | ||||
| } | } | ||||
| GHOST_WindowWin32::~GHOST_WindowWin32() | GHOST_WindowWin32::~GHOST_WindowWin32() | ||||
| { | { | ||||
| 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.handle) { | ||||
| GHOST_WIN32_WTClose fpWTClose = (GHOST_WIN32_WTClose) ::GetProcAddress(m_wintab, "WTClose"); | if (m_wintab.close && m_wintab.tablet) { | ||||
| if (fpWTClose) { | m_wintab.close(m_wintab.tablet); | ||||
| if (m_tablet) | |||||
| fpWTClose(m_tablet); | |||||
| delete m_tabletData; | |||||
| m_tabletData = NULL; | |||||
| } | } | ||||
| FreeLibrary(m_wintab.handle); | |||||
| memset(&m_wintab, 0, sizeof(m_wintab)); | |||||
| } | |||||
| if (m_user32) { | |||||
| FreeLibrary(m_user32); | |||||
| m_user32 = NULL; | |||||
| m_fpGetPointerInfo = NULL; | |||||
| m_fpGetPointerPenInfo = NULL; | |||||
| } | } | ||||
| if (m_customCursor) { | if (m_customCursor) { | ||||
| DestroyCursor(m_customCursor); | DestroyCursor(m_customCursor); | ||||
| m_customCursor = NULL; | m_customCursor = NULL; | ||||
| } | } | ||||
| 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) { | ||||
| if (m_dropTarget) { | if (m_dropTarget) { | ||||
| // Disable DragDrop | // Disable DragDrop | ||||
| 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); | ||||
| m_hWnd = 0; | m_hWnd = 0; | ||||
| } | } | ||||
| if (m_user32) { | if (m_user32) { | ||||
| FreeLibrary(m_user32); | FreeLibrary(m_user32); | ||||
| ▲ Show 20 Lines • Show All 441 Lines • ▼ Show 20 Lines | GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode) | ||||
| } | } | ||||
| else { | else { | ||||
| if (m_cursorGrab == GHOST_kGrabHide) { | if (m_cursorGrab == GHOST_kGrabHide) { | ||||
| m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]); | m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]); | ||||
| setWindowCursorVisibility(true); | setWindowCursorVisibility(true); | ||||
| } | } | ||||
| if (m_cursorGrab != GHOST_kGrabNormal) { | if (m_cursorGrab != GHOST_kGrabNormal) { | ||||
| /* use to generate a mouse move event, otherwise the last event | /* use to generate a mouse move event, otherwise the last event | ||||
| * blender gets can be outside the screen causing menus not to show | * blender gets can be outside the screen causing menus not to show | ||||
LazyDodo: please do this only once and cache the result. | |||||
| * properly unless the user moves the mouse */ | * properly unless the user moves the mouse */ | ||||
| GHOST_TInt32 pos[2]; | GHOST_TInt32 pos[2]; | ||||
| m_system->getCursorPosition(pos[0], pos[1]); | m_system->getCursorPosition(pos[0], pos[1]); | ||||
| m_system->setCursorPosition(pos[0], pos[1]); | m_system->setCursorPosition(pos[0], pos[1]); | ||||
| } | } | ||||
| /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */ | /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */ | ||||
| setCursorGrabAccum(0, 0); | setCursorGrabAccum(0, 0); | ||||
| m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */ | m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */ | ||||
| registerMouseClickEvent(3); | registerMouseClickEvent(3); | ||||
| } | } | ||||
Not Done Inline ActionsThis memory leaks in GHOST_WindowWin32::~GHOST_WindowWin32 if no wintab is present. The code there needs to be updated. brecht: This memory leaks in `GHOST_WindowWin32::~GHOST_WindowWin32` if no wintab is present. The code… | |||||
| return GHOST_kSuccess; | return GHOST_kSuccess; | ||||
| } | } | ||||
| GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape) | GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape) | ||||
| { | { | ||||
| if (m_customCursor) { | if (m_customCursor) { | ||||
| DestroyCursor(m_customCursor); | DestroyCursor(m_customCursor); | ||||
| m_customCursor = NULL; | m_customCursor = NULL; | ||||
| } | } | ||||
| if (::GetForegroundWindow() == m_hWnd) { | if (::GetForegroundWindow() == m_hWnd) { | ||||
| loadCursor(getCursorVisibility(), cursorShape); | loadCursor(getCursorVisibility(), cursorShape); | ||||
| } | } | ||||
| return GHOST_kSuccess; | return GHOST_kSuccess; | ||||
| } | } | ||||
| void GHOST_WindowWin32::processWin32PointerEvent(WPARAM wParam) | |||||
| { | |||||
| if (!m_system->useTabletAPI(GHOST_kTabletNative)) { | |||||
| return; // Other tablet API specified by user | |||||
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_fpGetPointerInfo || !m_fpGetPointerPenInfo) { | |||||
| 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 | |||||
| } | |||||
| m_tabletData.Active = GHOST_kTabletModeNone; | |||||
Not Done Inline Actionsthe within -> to within brecht: the within -> to within | |||||
| 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_system->useTabletAPI(GHOST_kTabletWintab)) { | ||||
| return; | return; | ||||
| } | } | ||||
| GHOST_WIN32_WTEnable fpWTEnable = (GHOST_WIN32_WTEnable) ::GetProcAddress(m_wintab, "WTEnable"); | if (m_wintab.enable && m_wintab.tablet) { | ||||
| GHOST_WIN32_WTOverlap fpWTOverlap = (GHOST_WIN32_WTOverlap) ::GetProcAddress(m_wintab, "WTOverlap"); | m_wintab.enable(m_wintab.tablet, state); | ||||
| if (fpWTEnable) { | if (m_wintab.overlap && state) { | ||||
| fpWTEnable(m_tablet, state); | m_wintab.overlap(m_wintab.tablet, TRUE); | ||||
| if (fpWTOverlap && state) { | |||||
| fpWTOverlap(m_tablet, TRUE); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void GHOST_WindowWin32::processWin32TabletInitEvent() | void GHOST_WindowWin32::processWin32TabletInitEvent() | ||||
| { | { | ||||
| if (m_wintab && m_tabletData) { | if (!m_system->useTabletAPI(GHOST_kTabletWintab)) { | ||||
| GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA"); | return; | ||||
| } | |||||
| // let's see if we can initialize tablet here | // Let's see if we can initialize tablet here | ||||
| /* check if WinTab available. */ | if (m_wintab.info && m_wintab.tablet) { | ||||
| if (fpWTInfo) { | |||||
| AXIS Pressure, Orientation[3]; /* The maximum tablet size */ | AXIS Pressure, Orientation[3]; /* The maximum tablet size */ | ||||
| BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure); | BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure); | ||||
| if (pressureSupport) | if (pressureSupport) | ||||
| m_maxPressure = Pressure.axMax; | m_wintab.maxPressure = Pressure.axMax; | ||||
| else | else | ||||
| m_maxPressure = 0; | m_wintab.maxPressure = 0; | ||||
| BOOL tiltSupport = fpWTInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation); | BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation); | ||||
| if (tiltSupport) { | if (tiltSupport) { | ||||
| /* does the tablet support azimuth ([0]) and altitude ([1]) */ | /* does the tablet support azimuth ([0]) and altitude ([1]) */ | ||||
| if (Orientation[0].axResolution && Orientation[1].axResolution) { | if (Orientation[0].axResolution && Orientation[1].axResolution) { | ||||
| m_maxAzimuth = Orientation[0].axMax; | m_wintab.maxAzimuth = Orientation[0].axMax; | ||||
| m_maxAltitude = Orientation[1].axMax; | m_wintab.maxAltitude = Orientation[1].axMax; | ||||
| } | } | ||||
| else { /* no so dont do tilt stuff */ | else { /* no so dont do tilt stuff */ | ||||
| m_maxAzimuth = m_maxAltitude = 0; | m_wintab.maxAzimuth = m_wintab.maxAltitude = 0; | ||||
| } | } | ||||
| } | } | ||||
| m_tabletData->Active = GHOST_kTabletModeNone; | m_tabletData.Active = GHOST_kTabletModeNone; | ||||
| } | |||||
| } | } | ||||
| m_tabletData.Active = GHOST_kTabletModeNone; | |||||
| } | } | ||||
| void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam) | void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam) | ||||
| { | { | ||||
| if (!m_system->useTabletAPI(GHOST_kTabletWintab)) { | |||||
| return; | |||||
| } | |||||
| if (m_wintab.packet && m_wintab.tablet) { | |||||
| PACKET pkt; | PACKET pkt; | ||||
| if (m_wintab) { | if (m_wintab.packet((HCTX)lParam, wParam, &pkt)) { | ||||
| GHOST_WIN32_WTPacket fpWTPacket = (GHOST_WIN32_WTPacket) ::GetProcAddress(m_wintab, "WTPacket"); | |||||
| if (fpWTPacket) { | |||||
| if (fpWTPacket((HCTX)lParam, wParam, &pkt)) { | |||||
| if (m_tabletData) { | |||||
| switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */ | switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */ | ||||
| case 0: | case 0: | ||||
| m_tabletData->Active = GHOST_kTabletModeNone; /* puck - not yet supported */ | m_tabletData.Active = GHOST_kTabletModeNone; /* puck - not yet supported */ | ||||
| break; | break; | ||||
| case 1: | case 1: | ||||
| m_tabletData->Active = GHOST_kTabletModeStylus; /* stylus */ | m_tabletData.Active = GHOST_kTabletModeStylus; /* stylus */ | ||||
| break; | break; | ||||
| case 2: | case 2: | ||||
| m_tabletData->Active = GHOST_kTabletModeEraser; /* eraser */ | m_tabletData.Active = GHOST_kTabletModeEraser; /* eraser */ | ||||
| break; | break; | ||||
| } | } | ||||
| if (m_maxPressure > 0) { | |||||
| m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure; | if (m_wintab.maxPressure > 0) { | ||||
| m_tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_wintab.maxPressure; | |||||
| } | } | ||||
| else { | else { | ||||
| m_tabletData->Pressure = 1.0f; | m_tabletData.Pressure = 1.0f; | ||||
| } | } | ||||
| if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) { | if ((m_wintab.maxAzimuth > 0) && (m_wintab.maxAltitude > 0)) { | ||||
| ORIENTATION ort = pkt.pkOrientation; | ORIENTATION ort = pkt.pkOrientation; | ||||
| float vecLen; | float vecLen; | ||||
| float altRad, azmRad; /* in radians */ | float altRad, azmRad; /* in radians */ | ||||
| /* | /* | ||||
| * from the wintab spec: | * from the wintab spec: | ||||
| * orAzimuth Specifies the clockwise rotation of the | * orAzimuth Specifies the clockwise rotation of the | ||||
| * cursor about the z axis through a full circular range. | * cursor about the z axis through a full circular range. | ||||
| * | * | ||||
| * orAltitude Specifies the angle with the x-y plane | * orAltitude Specifies the angle with the x-y plane | ||||
| * through a signed, semicircular range. Positive values | * through a signed, semicircular range. Positive values | ||||
| * specify an angle upward toward the positive z axis; | * specify an angle upward toward the positive z axis; | ||||
| * negative values specify an angle downward toward the negative z axis. | * negative values specify an angle downward toward the negative z axis. | ||||
| * | * | ||||
| * wintab.h defines .orAltitude as a UINT but documents .orAltitude | * wintab.h defines .orAltitude as a UINT but documents .orAltitude | ||||
| * as positive for upward angles and negative for downward angles. | * as positive for upward angles and negative for downward angles. | ||||
| * WACOM uses negative altitude values to show that the pen is inverted; | * WACOM uses negative altitude values to show that the pen is inverted; | ||||
| * therefore we cast .orAltitude as an (int) and then use the absolute value. | * therefore we cast .orAltitude as an (int) and then use the absolute value. | ||||
| */ | */ | ||||
| /* convert raw fixed point data to radians */ | /* convert raw fixed point data to radians */ | ||||
| altRad = (float)((fabs((float)ort.orAltitude) / (float)m_maxAltitude) * M_PI / 2.0); | altRad = (float)((fabs((float)ort.orAltitude) / (float)m_wintab.maxAltitude) * M_PI / 2.0); | ||||
| azmRad = (float)(((float)ort.orAzimuth / (float)m_maxAzimuth) * M_PI * 2.0); | azmRad = (float)(((float)ort.orAzimuth / (float)m_wintab.maxAzimuth) * M_PI * 2.0); | ||||
| /* find length of the stylus' projected vector on the XY plane */ | /* find length of the stylus' projected vector on the XY plane */ | ||||
| vecLen = cos(altRad); | vecLen = cos(altRad); | ||||
| /* from there calculate X and Y components based on azimuth */ | /* from there calculate X and Y components based on azimuth */ | ||||
| m_tabletData->Xtilt = sin(azmRad) * vecLen; | m_tabletData.Xtilt = sin(azmRad) * vecLen; | ||||
| m_tabletData->Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen); | m_tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen); | ||||
| } | } | ||||
| else { | else { | ||||
| m_tabletData->Xtilt = 0.0f; | m_tabletData.Xtilt = 0.0f; | ||||
| m_tabletData->Ytilt = 0.0f; | m_tabletData.Ytilt = 0.0f; | ||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void GHOST_WindowWin32::bringTabletContextToFront() | void GHOST_WindowWin32::bringTabletContextToFront() | ||||
| { | { | ||||
| if (m_wintab) { | if (!m_system->useTabletAPI(GHOST_kTabletWintab)) { | ||||
| GHOST_WIN32_WTOverlap fpWTOverlap = (GHOST_WIN32_WTOverlap) ::GetProcAddress(m_wintab, "WTOverlap"); | return; | ||||
| if (fpWTOverlap) { | |||||
| fpWTOverlap(m_tablet, TRUE); | |||||
| } | } | ||||
| if (m_wintab.overlap && m_wintab.tablet) { | |||||
| m_wintab.overlap(m_wintab.tablet, TRUE); | |||||
| } | } | ||||
| } | } | ||||
| GHOST_TUns16 GHOST_WindowWin32::getDPIHint() | GHOST_TUns16 GHOST_WindowWin32::getDPIHint() | ||||
| { | { | ||||
| 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"); | ||||
| ▲ Show 20 Lines • Show All 114 Lines • Show Last 20 Lines | |||||
please do this only once and cache the result.