Changeset View
Changeset View
Standalone View
Standalone View
intern/ghost/intern/GHOST_WindowWin32.cpp
| Show All 15 Lines | |||||
| * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. | * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. | ||||
| * All rights reserved. | * All rights reserved. | ||||
| */ | */ | ||||
| /** \file | /** \file | ||||
| * \ingroup GHOST | * \ingroup GHOST | ||||
| */ | */ | ||||
| #define _USE_MATH_DEFINES | |||||
| #include "GHOST_WindowWin32.h" | #include "GHOST_WindowWin32.h" | ||||
| #include "GHOST_ContextD3D.h" | #include "GHOST_ContextD3D.h" | ||||
| #include "GHOST_ContextNone.h" | #include "GHOST_ContextNone.h" | ||||
| #include "GHOST_DropTargetWin32.h" | #include "GHOST_DropTargetWin32.h" | ||||
| #include "GHOST_SystemWin32.h" | #include "GHOST_SystemWin32.h" | ||||
| #include "GHOST_WindowManager.h" | #include "GHOST_WindowManager.h" | ||||
| #include "utf_winfunc.h" | #include "utf_winfunc.h" | ||||
| #include "utfconv.h" | #include "utfconv.h" | ||||
| Show All 33 Lines | GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, | ||||
| GHOST_TWindowState state, | GHOST_TWindowState state, | ||||
| GHOST_TDrawingContextType type, | GHOST_TDrawingContextType type, | ||||
| bool wantStereoVisual, | bool wantStereoVisual, | ||||
| bool alphaBackground, | bool alphaBackground, | ||||
| GHOST_WindowWin32 *parentwindow, | GHOST_WindowWin32 *parentwindow, | ||||
| bool is_debug, | bool is_debug, | ||||
| bool dialog) | bool dialog) | ||||
| : GHOST_Window(width, height, state, wantStereoVisual, false), | : GHOST_Window(width, height, state, wantStereoVisual, false), | ||||
| m_tabletInRange(false), | m_mousePresent(false), | ||||
| m_inLiveResize(false), | m_inLiveResize(false), | ||||
| m_system(system), | m_system(system), | ||||
| m_hDC(0), | m_hDC(0), | ||||
| m_isDialog(dialog), | m_isDialog(dialog), | ||||
| 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_lastPointerTabletData(GHOST_TABLET_DATA_NONE), | |||||
| m_normal_state(GHOST_kWindowStateNormal), | m_normal_state(GHOST_kWindowStateNormal), | ||||
| m_user32(NULL), | m_user32(NULL), | ||||
| m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), | m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP), | ||||
| m_debug_context(is_debug) | m_debug_context(is_debug) | ||||
| { | { | ||||
| wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); | wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); | ||||
| RECT win_rect = {left, top, (long)(left + width), (long)(top + height)}; | RECT win_rect = {left, top, (long)(left + width), (long)(top + height)}; | ||||
| // Initialize tablet variables | |||||
| memset(&m_wintab, 0, sizeof(m_wintab)); | |||||
| m_tabletData = GHOST_TABLET_DATA_NONE; | |||||
| DWORD style = parentwindow ? | DWORD style = parentwindow ? | ||||
| WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX : | WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX : | ||||
| WS_OVERLAPPEDWINDOW; | WS_OVERLAPPEDWINDOW; | ||||
| if (state == GHOST_kWindowStateFullScreen) { | if (state == GHOST_kWindowStateFullScreen) { | ||||
| style |= WS_MAXIMIZE; | style |= WS_MAXIMIZE; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | #endif | ||||
| else { | else { | ||||
| // invalidate the window | // invalidate the window | ||||
| ::DestroyWindow(m_hWnd); | ::DestroyWindow(m_hWnd); | ||||
| m_hWnd = NULL; | m_hWnd = NULL; | ||||
| } | } | ||||
| } | } | ||||
| // Initialize Wintab | // Initialize Wintab | ||||
| m_wintab.handle = ::LoadLibrary("Wintab32.dll"); | if (system->getTabletAPI() != GHOST_kTabletNative) { | ||||
| if (m_wintab.handle && m_system->getTabletAPI() != GHOST_kTabletNative) { | loadWintab(GHOST_kWindowStateMinimized != state); | ||||
| // 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. | |||||
| // Check if WinTab available by getting system context info. | |||||
| LOGCONTEXT lc = {0}; | |||||
| lc.lcOptions |= CXO_SYSTEM; | |||||
| if (m_wintab.open && m_wintab.info && m_wintab.info(WTI_DEFSYSCTX, 0, &lc)) { | |||||
| // Now init the tablet | |||||
| /* The maximum tablet size, pressure and orientation (tilt) */ | |||||
| AXIS TabletX, TabletY, Pressure, Orientation[3]; | |||||
| // Open a Wintab context | |||||
| // Open the context | |||||
| lc.lcPktData = PACKETDATA; | |||||
| lc.lcPktMode = PACKETMODE; | |||||
| lc.lcOptions |= CXO_MESSAGES; | |||||
| lc.lcMoveMask = PACKETDATA; | |||||
| /* Set the entire tablet as active */ | |||||
| m_wintab.info(WTI_DEVICES, DVC_X, &TabletX); | |||||
| m_wintab.info(WTI_DEVICES, DVC_Y, &TabletY); | |||||
| /* get the max pressure, to divide into a float */ | |||||
| BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure); | |||||
| if (pressureSupport) | |||||
| m_wintab.maxPressure = Pressure.axMax; | |||||
| else | |||||
| m_wintab.maxPressure = 0; | |||||
| /* get the max tilt axes, to divide into floats */ | |||||
| BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation); | |||||
| if (tiltSupport) { | |||||
| /* does the tablet support azimuth ([0]) and altitude ([1]) */ | |||||
| if (Orientation[0].axResolution && Orientation[1].axResolution) { | |||||
| /* all this assumes the minimum is 0 */ | |||||
| m_wintab.maxAzimuth = Orientation[0].axMax; | |||||
| m_wintab.maxAltitude = Orientation[1].axMax; | |||||
| } | |||||
| else { /* No so don't do tilt stuff. */ | |||||
| m_wintab.maxAzimuth = m_wintab.maxAltitude = 0; | |||||
| } | |||||
| } | } | ||||
| // The Wintab spec says we must open the context disabled if we are using cursor masks. | |||||
| m_wintab.tablet = m_wintab.open(m_hWnd, &lc, FALSE); | |||||
| if (m_wintab.enable && m_wintab.tablet) { | |||||
| m_wintab.enable(m_wintab.tablet, TRUE); | |||||
| } | |||||
| } | |||||
| } | |||||
| CoCreateInstance( | CoCreateInstance( | ||||
| CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar); | 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; | m_Bar = NULL; | ||||
| } | } | ||||
| if (m_wintab.handle) { | closeWintab(); | ||||
| if (m_wintab.close && m_wintab.tablet) { | |||||
| m_wintab.close(m_wintab.tablet); | |||||
| } | |||||
| FreeLibrary(m_wintab.handle); | |||||
| memset(&m_wintab, 0, sizeof(m_wintab)); | |||||
| } | |||||
| if (m_user32) { | if (m_user32) { | ||||
| FreeLibrary(m_user32); | FreeLibrary(m_user32); | ||||
| m_user32 = NULL; | m_user32 = NULL; | ||||
| } | } | ||||
| if (m_customCursor) { | if (m_customCursor) { | ||||
| DestroyCursor(m_customCursor); | DestroyCursor(m_customCursor); | ||||
| ▲ Show 20 Lines • Show All 600 Lines • ▼ Show 20 Lines | |||||
| GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorShape) | GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorShape) | ||||
| { | { | ||||
| return (getStandardCursor(cursorShape)) ? GHOST_kSuccess : GHOST_kFailure; | return (getStandardCursor(cursorShape)) ? GHOST_kSuccess : GHOST_kFailure; | ||||
| } | } | ||||
| GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( | GHOST_TSuccess GHOST_WindowWin32::getPointerInfo( | ||||
| std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM lParam) | std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM lParam) | ||||
| { | { | ||||
| if (!useTabletAPI(GHOST_kTabletNative)) { | |||||
| return GHOST_kFailure; | |||||
| } | |||||
| GHOST_TInt32 pointerId = GET_POINTERID_WPARAM(wParam); | GHOST_TInt32 pointerId = GET_POINTERID_WPARAM(wParam); | ||||
| GHOST_TInt32 isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam); | GHOST_TInt32 isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam); | ||||
| GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); | GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem(); | ||||
| GHOST_TUns32 outCount; | GHOST_TUns32 outCount; | ||||
| if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) { | if (!(GetPointerInfoHistory(pointerId, &outCount, NULL))) { | ||||
| return GHOST_kFailure; | return GHOST_kFailure; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | if (pointerPenInfo[i].penMask & PEN_MASK_TILT_X) { | ||||
| outPointerInfo[i].tabletData.Xtilt = fmin(fabs(pointerPenInfo[i].tiltX / 90.0f), 1.0f); | outPointerInfo[i].tabletData.Xtilt = fmin(fabs(pointerPenInfo[i].tiltX / 90.0f), 1.0f); | ||||
| } | } | ||||
| if (pointerPenInfo[i].penMask & PEN_MASK_TILT_Y) { | if (pointerPenInfo[i].penMask & PEN_MASK_TILT_Y) { | ||||
| outPointerInfo[i].tabletData.Ytilt = fmin(fabs(pointerPenInfo[i].tiltY / 90.0f), 1.0f); | outPointerInfo[i].tabletData.Ytilt = fmin(fabs(pointerPenInfo[i].tiltY / 90.0f), 1.0f); | ||||
| } | } | ||||
| } | } | ||||
| return GHOST_kSuccess; | if (!outPointerInfo.empty()) { | ||||
| if (pointerPenInfo.back().pointerInfo.pointerFlags & POINTER_FLAG_INRANGE) { | |||||
| m_lastPointerTabletData = outPointerInfo.back().tabletData; | |||||
| } | } | ||||
| else { | |||||
| void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state) | m_lastPointerTabletData = GHOST_TABLET_DATA_NONE; | ||||
| { | |||||
| if (!useTabletAPI(GHOST_kTabletWintab)) { | |||||
| return; | |||||
| } | |||||
| if (m_wintab.enable && m_wintab.tablet) { | |||||
| m_wintab.enable(m_wintab.tablet, state); | |||||
| if (m_wintab.overlap && state) { | |||||
| m_wintab.overlap(m_wintab.tablet, TRUE); | |||||
| } | } | ||||
| } | } | ||||
| return GHOST_kSuccess; | |||||
| } | } | ||||
| bool GHOST_WindowWin32::useTabletAPI(GHOST_TTabletAPI api) const | void GHOST_WindowWin32::resetPointerInfo() | ||||
| { | { | ||||
| if (m_system->getTabletAPI() == api) { | m_lastPointerTabletData = GHOST_TABLET_DATA_NONE; | ||||
| return true; | |||||
| } | |||||
| else if (m_system->getTabletAPI() == GHOST_kTabletAutomatic) { | |||||
| if (m_wintab.tablet) | |||||
| return api == GHOST_kTabletWintab; | |||||
| else | |||||
| return api == GHOST_kTabletNative; | |||||
| } | |||||
| else { | |||||
| return false; | |||||
| } | |||||
| } | } | ||||
| void GHOST_WindowWin32::processWin32TabletInitEvent() | GHOST_Wintab *GHOST_WindowWin32::getWintab() const | ||||
| { | { | ||||
| if (!useTabletAPI(GHOST_kTabletWintab)) { | return m_wintab; | ||||
| return; | |||||
| } | } | ||||
| // Let's see if we can initialize tablet here | void GHOST_WindowWin32::loadWintab(bool enable) | ||||
| if (m_wintab.info && m_wintab.tablet) { | { | ||||
| AXIS Pressure, Orientation[3]; /* The maximum tablet size */ | if (!m_wintab) { | ||||
| if (m_wintab = GHOST_Wintab::loadWintab(m_hWnd)) { | |||||
| if (enable) { | |||||
| m_wintab->enable(); | |||||
| BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure); | /* Focus Wintab if cursor is inside this window. This ensures Wintab is enabled when the | ||||
| if (pressureSupport) | * tablet is used to change the Tablet API. */ | ||||
| m_wintab.maxPressure = Pressure.axMax; | GHOST_TInt32 x, y; | ||||
| else | if (m_system->getCursorPosition(x, y)) { | ||||
| m_wintab.maxPressure = 0; | GHOST_Rect rect; | ||||
| getClientBounds(rect); | |||||
| BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation); | if (rect.isInside(x, y)) { | ||||
| if (tiltSupport) { | m_wintab->gainFocus(); | ||||
| /* does the tablet support azimuth ([0]) and altitude ([1]) */ | } | ||||
| if (Orientation[0].axResolution && Orientation[1].axResolution) { | |||||
| m_wintab.maxAzimuth = Orientation[0].axMax; | |||||
| m_wintab.maxAltitude = Orientation[1].axMax; | |||||
| } | } | ||||
| else { /* No so don't do tilt stuff. */ | |||||
| m_wintab.maxAzimuth = m_wintab.maxAltitude = 0; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| m_tabletData.Active = GHOST_kTabletModeNone; | |||||
| } | } | ||||
| void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam) | void GHOST_WindowWin32::closeWintab() | ||||
| { | { | ||||
| if (!useTabletAPI(GHOST_kTabletWintab)) { | delete m_wintab; | ||||
| return; | m_wintab = NULL; | ||||
| } | } | ||||
| if (m_wintab.packet && m_wintab.tablet) { | bool GHOST_WindowWin32::useTabletAPI(GHOST_TTabletAPI api) const | ||||
| PACKET pkt; | { | ||||
| if (m_wintab.packet((HCTX)lParam, wParam, &pkt)) { | if (m_system->getTabletAPI() == api) { | ||||
| switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */ | return true; | ||||
| case 0: | |||||
| m_tabletData.Active = GHOST_kTabletModeNone; /* puck - not yet supported */ | |||||
| break; | |||||
| case 1: | |||||
| m_tabletData.Active = GHOST_kTabletModeStylus; /* stylus */ | |||||
| break; | |||||
| case 2: | |||||
| m_tabletData.Active = GHOST_kTabletModeEraser; /* eraser */ | |||||
| break; | |||||
| } | } | ||||
| else if (m_system->getTabletAPI() == GHOST_kTabletAutomatic) { | |||||
| if (m_wintab.maxPressure > 0) { | if (m_wintab && m_wintab->devicesPresent()) { | ||||
| m_tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_wintab.maxPressure; | return api == GHOST_kTabletWintab; | ||||
| } | } | ||||
| else { | else { | ||||
| m_tabletData.Pressure = 1.0f; | return api == GHOST_kTabletNative; | ||||
| } | } | ||||
| if ((m_wintab.maxAzimuth > 0) && (m_wintab.maxAltitude > 0)) { | |||||
| ORIENTATION ort = pkt.pkOrientation; | |||||
| float vecLen; | |||||
| float altRad, azmRad; /* in radians */ | |||||
| /* | |||||
| * from the wintab spec: | |||||
| * orAzimuth Specifies the clockwise rotation of the | |||||
| * cursor about the z axis through a full circular range. | |||||
| * | |||||
| * orAltitude Specifies the angle with the x-y plane | |||||
| * through a signed, semicircular range. Positive values | |||||
| * specify an angle upward toward the positive z axis; | |||||
| * negative values specify an angle downward toward the negative z axis. | |||||
| * | |||||
| * wintab.h defines .orAltitude as a UINT but documents .orAltitude | |||||
| * as positive for upward angles and negative for downward angles. | |||||
| * 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. | |||||
| */ | |||||
| /* convert raw fixed point data to radians */ | |||||
| altRad = (float)((fabs((float)ort.orAltitude) / (float)m_wintab.maxAltitude) * 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 */ | |||||
| vecLen = cos(altRad); | |||||
| /* from there calculate X and Y components based on azimuth */ | |||||
| m_tabletData.Xtilt = sin(azmRad) * vecLen; | |||||
| m_tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen); | |||||
| } | } | ||||
| else { | else { | ||||
| m_tabletData.Xtilt = 0.0f; | return false; | ||||
| m_tabletData.Ytilt = 0.0f; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| void GHOST_WindowWin32::bringTabletContextToFront() | GHOST_TabletData GHOST_WindowWin32::getTabletData() | ||||
| { | { | ||||
| if (!useTabletAPI(GHOST_kTabletWintab)) { | if (useTabletAPI(GHOST_kTabletWintab)) { | ||||
| return; | return m_wintab->getLastTabletData(); | ||||
| } | } | ||||
| else { | |||||
| if (m_wintab.overlap && m_wintab.tablet) { | return m_lastPointerTabletData; | ||||
| 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( | GHOST_WIN32_GetDpiForWindow fpGetDpiForWindow = (GHOST_WIN32_GetDpiForWindow)::GetProcAddress( | ||||
| m_user32, "GetDpiForWindow"); | m_user32, "GetDpiForWindow"); | ||||
| ▲ Show 20 Lines • Show All 109 Lines • Show Last 20 Lines | |||||