/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): Maarten Gribnau. * * ***** END GPL LICENSE BLOCK ***** */ /** \file ghost/intern/GHOST_WindowWin32.cpp * \ingroup GHOST */ #include #include "GHOST_WindowWin32.h" #include "GHOST_SystemWin32.h" #include "GHOST_DropTargetWin32.h" #include "utfconv.h" #include "utf_winfunc.h" // Need glew for some defines #include #include #include // MSVC6 still doesn't define M_PI #ifndef M_PI # define M_PI 3.1415926536 #endif // Some more multisample defines #define WGL_SAMPLE_BUFFERS_ARB 0x2041 #define WGL_SAMPLES_ARB 0x2042 // win64 doesn't define GWL_USERDATA #ifdef WIN32 /* why? - probably should remove */ # ifndef GWL_USERDATA # define GWL_USERDATA GWLP_USERDATA # define GWL_WNDPROC GWLP_WNDPROC # endif #endif const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass"; const int GHOST_WindowWin32::s_maxTitleLength = 128; HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL; HDC GHOST_WindowWin32::s_firstHDC = NULL; static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd); static int EnumPixelFormats(HDC hdc); /* * Color and depth bit values are not to be trusted. * For instance, on TNT2: * When the screen color depth is set to 16 bit, we get 5 color bits * and 16 depth bits. * When the screen color depth is set to 32 bit, we get 8 color bits * and 24 depth bits. * Just to be safe, we request high waulity settings. */ static PIXELFORMATDESCRIPTOR sPreferredFormat = { sizeof(PIXELFORMATDESCRIPTOR), /* size */ 1, /* version */ PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_SWAP_COPY | /* support swap copy */ PFD_DOUBLEBUFFER, /* support double-buffering */ PFD_TYPE_RGBA, /* color type */ 32, /* prefered color depth */ 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ 0, /* no alpha buffer */ 0, /* alpha bits (ignored) */ 0, /* no accumulation buffer */ 0, 0, 0, 0, /* accum bits (ignored) */ 32, /* depth buffer */ 0, /* no stencil buffer */ 0, /* no auxiliary buffers */ PFD_MAIN_PLANE, /* main layer */ 0, /* reserved */ 0, 0, 0 /* no layer, visible, damage masks */ }; /* Intel videocards don't work fine with multiple contexts and * have to share the same context for all windows. * But if we just share context for all windows it could work incorrect * with multiple videocards configuration. Suppose, that Intel videocards * can't be in multiple-devices configuration. */ static int is_crappy_intel_card(void) { int crappy = 0; const char *vendor = (const char *)glGetString(GL_VENDOR); if (strstr(vendor, "Intel")) crappy = 1; return crappy; } GHOST_WindowWin32::GHOST_WindowWin32( GHOST_SystemWin32 *system, const STR_String& title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height, GHOST_TWindowState state, GHOST_TDrawingContextType type, const bool stereoVisual, const GHOST_TUns16 numOfAASamples, GHOST_TEmbedderWindowID parentwindowhwnd, GHOST_TSuccess msEnabled, int msPixelFormat) : GHOST_Window(width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual, numOfAASamples), m_system(system), m_hDC(0), m_hGlRc(0), m_hasMouseCaptured(false), m_hasGrabMouse(false), m_nPressedButtons(0), m_customCursor(0), m_wintab(NULL), m_tabletData(NULL), m_tablet(0), m_maxPressure(0), m_multisample(numOfAASamples), m_multisampleEnabled(msEnabled), m_msPixelFormat(msPixelFormat), //For recreation m_title(title), m_left(left), m_top(top), m_width(width), m_height(height), m_normal_state(GHOST_kWindowStateNormal), m_stereo(stereoVisual), m_nextWindow(NULL), m_parentWindowHwnd(parentwindowhwnd) { OSVERSIONINFOEX versionInfo; bool hasMinVersionForTaskbar = false; ZeroMemory(&versionInfo, sizeof(OSVERSIONINFOEX)); versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if (!GetVersionEx((OSVERSIONINFO *)&versionInfo)) { versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx((OSVERSIONINFO *)&versionInfo)) { if ((versionInfo.dwMajorVersion == 6 && versionInfo.dwMinorVersion >= 1) || versionInfo.dwMajorVersion >= 7) { hasMinVersionForTaskbar = true; } } } else { if ((versionInfo.dwMajorVersion == 6 && versionInfo.dwMinorVersion >= 1) || versionInfo.dwMajorVersion >= 7) { hasMinVersionForTaskbar = true; } } if (state != GHOST_kWindowStateFullScreen) { RECT rect; MONITORINFO monitor; GHOST_TUns32 tw, th; width += GetSystemMetrics(SM_CXSIZEFRAME) * 2; height += GetSystemMetrics(SM_CYSIZEFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION); rect.left = left; rect.right = left + width; rect.top = top; rect.bottom = top + height; monitor.cbSize = sizeof(monitor); monitor.dwFlags = 0; // take taskbar into account GetMonitorInfo(MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST), &monitor); th = monitor.rcWork.bottom - monitor.rcWork.top; tw = monitor.rcWork.right - monitor.rcWork.left; if (tw < width) { width = tw; left = monitor.rcWork.left; } else if (monitor.rcWork.right < left + (int)width) left = monitor.rcWork.right - width; else if (left < monitor.rcWork.left) left = monitor.rcWork.left; if (th < height) { height = th; top = monitor.rcWork.top; } else if (monitor.rcWork.bottom < top + (int)height) top = monitor.rcWork.bottom - height; else if (top < monitor.rcWork.top) top = monitor.rcWork.top; int wintype = WS_OVERLAPPEDWINDOW; if (m_parentWindowHwnd != 0) { wintype = WS_CHILD; GetWindowRect((HWND)m_parentWindowHwnd, &rect); left = 0; top = 0; width = rect.right - rect.left; height = rect.bottom - rect.top; } wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0); m_hWnd = ::CreateWindowW( s_windowClassName, // pointer to registered class name title_16, // pointer to window name wintype, // window style left, // horizontal position of window top, // vertical position of window width, // window width height, // window height (HWND) m_parentWindowHwnd, // handle to parent or owner window 0, // handle to menu or child-window identifier ::GetModuleHandle(0), // handle to application instance 0); // pointer to window-creation data free(title_16); } else { wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0); m_hWnd = ::CreateWindowW( s_windowClassName, // pointer to registered class name title_16, // pointer to window name WS_POPUP | WS_MAXIMIZE, // window style left, // horizontal position of window top, // vertical position of window width, // window width height, // window height HWND_DESKTOP, // handle to parent or owner window 0, // handle to menu or child-window identifier ::GetModuleHandle(0), // handle to application instance 0); // pointer to window-creation data free(title_16); } if (m_hWnd) { // Register this window as a droptarget. Requires m_hWnd to be valid. // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32. m_dropTarget = new GHOST_DropTargetWin32(this, m_system); if (m_dropTarget) { ::RegisterDragDrop(m_hWnd, m_dropTarget); } // Store a pointer to this class in the window structure ::SetWindowLongPtr(m_hWnd, GWL_USERDATA, (LONG_PTR) this); // Store the device context m_hDC = ::GetDC(m_hWnd); if (!s_firstHDC) { s_firstHDC = m_hDC; } // Show the window int nCmdShow; switch (state) { case GHOST_kWindowStateMaximized: nCmdShow = SW_SHOWMAXIMIZED; break; case GHOST_kWindowStateMinimized: nCmdShow = SW_SHOWMINIMIZED; break; case GHOST_kWindowStateNormal: default: nCmdShow = SW_SHOWNORMAL; break; } GHOST_TSuccess success; success = setDrawingContextType(type); if (success) { ::ShowWindow(m_hWnd, nCmdShow); // Force an initial paint of the window ::UpdateWindow(m_hWnd); } else { //invalidate the window m_hWnd = 0; } } if (parentwindowhwnd != 0) { RAWINPUTDEVICE device = {0}; device.usUsagePage = 0x01; /* usUsagePage & usUsage for keyboard*/ 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.hwndTarget = m_hWnd; RegisterRawInputDevices(&device, 1, sizeof(device)); } m_wintab = ::LoadLibrary("Wintab32.dll"); if (m_wintab) { GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA"); GHOST_WIN32_WTOpen fpWTOpen = (GHOST_WIN32_WTOpen) ::GetProcAddress(m_wintab, "WTOpenA"); // let's see if we can initialize tablet here /* check if WinTab available. */ if (fpWTInfo && fpWTInfo(0, 0, NULL)) { // Now init the tablet LOGCONTEXT lc; AXIS TabletX, TabletY, Pressure, Orientation[3]; /* The maximum tablet size, pressure and orientation (tilt) */ // Open a Wintab context // Get default context information fpWTInfo(WTI_DEFCONTEXT, 0, &lc); // Open the context lc.lcPktData = PACKETDATA; lc.lcPktMode = PACKETMODE; lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM; /* Set the entire tablet as active */ fpWTInfo(WTI_DEVICES, DVC_X, &TabletX); fpWTInfo(WTI_DEVICES, DVC_Y, &TabletY); /* get the max pressure, to divide into a float */ BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure); if (pressureSupport) m_maxPressure = Pressure.axMax; else m_maxPressure = 0; /* get the max tilt axes, to divide into floats */ BOOL tiltSupport = fpWTInfo(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_maxAzimuth = Orientation[0].axMax; m_maxAltitude = Orientation[1].axMax; } else { /* no so dont do tilt stuff */ m_maxAzimuth = m_maxAltitude = 0; } } if (fpWTOpen) { m_tablet = fpWTOpen(m_hWnd, &lc, TRUE); if (m_tablet) { m_tabletData = new GHOST_TabletData(); m_tabletData->Active = GHOST_kTabletModeNone; } } } } if (hasMinVersionForTaskbar) CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList, (LPVOID *)&m_Bar); else m_Bar = NULL; } GHOST_WindowWin32::~GHOST_WindowWin32() { if (m_Bar) { m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS); m_Bar->Release(); }; if (m_wintab) { GHOST_WIN32_WTClose fpWTClose = (GHOST_WIN32_WTClose) ::GetProcAddress(m_wintab, "WTClose"); if (fpWTClose) { if (m_tablet) fpWTClose(m_tablet); if (m_tabletData) delete m_tabletData; m_tabletData = NULL; } } if (m_customCursor) { DestroyCursor(m_customCursor); m_customCursor = NULL; } ::wglMakeCurrent(NULL, NULL); m_multisampleEnabled = GHOST_kFailure; m_multisample = 0; setDrawingContextType(GHOST_kDrawingContextTypeNone); if (m_hDC && m_hDC != s_firstHDC) { ::ReleaseDC(m_hWnd, m_hDC); m_hDC = 0; } if (m_hWnd) { if (m_dropTarget) { // Disable DragDrop RevokeDragDrop(m_hWnd); // Release our reference of the DropTarget and it will delete itself eventually. m_dropTarget->Release(); } ::DestroyWindow(m_hWnd); m_hWnd = 0; } } GHOST_Window *GHOST_WindowWin32::getNextWindow() { return m_nextWindow; } bool GHOST_WindowWin32::getValid() const { return m_hWnd != 0; } HWND GHOST_WindowWin32::getHWND() const { return m_hWnd; } void GHOST_WindowWin32::setTitle(const STR_String& title) { wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0); ::SetWindowTextW(m_hWnd, (wchar_t *)title_16); free(title_16); } void GHOST_WindowWin32::getTitle(STR_String& title) const { char buf[s_maxTitleLength]; /*CHANGE + never used yet*/ ::GetWindowText(m_hWnd, buf, s_maxTitleLength); STR_String temp(buf); title = buf; } void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const { RECT rect; ::GetWindowRect(m_hWnd, &rect); bounds.m_b = rect.bottom; bounds.m_l = rect.left; bounds.m_r = rect.right; bounds.m_t = rect.top; } void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const { RECT rect; GHOST_TWindowState state = this->getState(); LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE); int sm_cysizeframe = GetSystemMetrics(SM_CYSIZEFRAME); ::GetWindowRect(m_hWnd, &rect); if ((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE)) { if (state == GHOST_kWindowStateMaximized) { // in maximized state we don't have borders on the window bounds.m_b = rect.bottom - GetSystemMetrics(SM_CYCAPTION) - sm_cysizeframe * 2; bounds.m_l = rect.left + sm_cysizeframe; bounds.m_r = rect.right - sm_cysizeframe; bounds.m_t = rect.top; } else if (state == GHOST_kWindowStateEmbedded) { bounds.m_b = rect.bottom; bounds.m_l = rect.left; bounds.m_r = rect.right; bounds.m_t = rect.top; } else { bounds.m_b = rect.bottom - GetSystemMetrics(SM_CYCAPTION) - sm_cysizeframe * 2; bounds.m_l = rect.left; bounds.m_r = rect.right - sm_cysizeframe * 2; bounds.m_t = rect.top; } } else { bounds.m_b = rect.bottom; bounds.m_l = rect.left; bounds.m_r = rect.right; bounds.m_t = rect.top; } } GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width) { GHOST_TSuccess success; GHOST_Rect cBnds, wBnds; getClientBounds(cBnds); if (cBnds.getWidth() != (GHOST_TInt32)width) { getWindowBounds(wBnds); int cx = wBnds.getWidth() + width - cBnds.getWidth(); int cy = wBnds.getHeight(); success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ? GHOST_kSuccess : GHOST_kFailure; } else { success = GHOST_kSuccess; } return success; } GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height) { GHOST_TSuccess success; GHOST_Rect cBnds, wBnds; getClientBounds(cBnds); if (cBnds.getHeight() != (GHOST_TInt32)height) { getWindowBounds(wBnds); int cx = wBnds.getWidth(); int cy = wBnds.getHeight() + height - cBnds.getHeight(); success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ? GHOST_kSuccess : GHOST_kFailure; } else { success = GHOST_kSuccess; } return success; } GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height) { GHOST_TSuccess success; GHOST_Rect cBnds, wBnds; getClientBounds(cBnds); if ((cBnds.getWidth() != (GHOST_TInt32)width) || (cBnds.getHeight() != (GHOST_TInt32)height)) { getWindowBounds(wBnds); int cx = wBnds.getWidth() + width - cBnds.getWidth(); int cy = wBnds.getHeight() + height - cBnds.getHeight(); success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ? GHOST_kSuccess : GHOST_kFailure; } else { success = GHOST_kSuccess; } return success; } GHOST_TWindowState GHOST_WindowWin32::getState() const { GHOST_TWindowState state; // XXX 27.04.2011 // we need to find a way to combine parented windows + resizing if we simply set the // state as GHOST_kWindowStateEmbedded we will need to check for them somewhere else. // It's also strange that in Windows is the only platform we need to make this separation. if (m_parentWindowHwnd != 0) { state = GHOST_kWindowStateEmbedded; return state; } if (::IsIconic(m_hWnd)) { state = GHOST_kWindowStateMinimized; } else if (::IsZoomed(m_hWnd)) { LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE); if ((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE)) state = GHOST_kWindowStateMaximized; else state = GHOST_kWindowStateFullScreen; } else { state = GHOST_kWindowStateNormal; } return state; } void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const { POINT point = { inX, inY }; ::ScreenToClient(m_hWnd, &point); outX = point.x; outY = point.y; } void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const { POINT point = { inX, inY }; ::ClientToScreen(m_hWnd, &point); outX = point.x; outY = point.y; } GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state) { GHOST_TWindowState curstate = getState(); WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); ::GetWindowPlacement(m_hWnd, &wp); if (state == GHOST_kWindowStateNormal) state = m_normal_state; switch (state) { case GHOST_kWindowStateMinimized: wp.showCmd = SW_SHOWMINIMIZED; break; case GHOST_kWindowStateMaximized: wp.showCmd = SW_SHOWMAXIMIZED; SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); break; case GHOST_kWindowStateFullScreen: if (curstate != state && curstate != GHOST_kWindowStateMinimized) m_normal_state = curstate; wp.showCmd = SW_SHOWMAXIMIZED; wp.ptMaxPosition.x = 0; wp.ptMaxPosition.y = 0; SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE); break; case GHOST_kWindowStateEmbedded: SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_CHILD); break; case GHOST_kWindowStateNormal: default: wp.showCmd = SW_SHOWNORMAL; SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); break; } SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); /*Clears window cache for SetWindowLongPtr */ return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure; } GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order) { HWND hWndInsertAfter = order == GHOST_kWindowOrderTop ? HWND_TOP : HWND_BOTTOM; return ::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == TRUE ? GHOST_kSuccess : GHOST_kFailure; } GHOST_TSuccess GHOST_WindowWin32::swapBuffers() { HDC hDC = m_hDC; if (is_crappy_intel_card()) hDC = ::wglGetCurrentDC(); return ::SwapBuffers(hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure; } GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext() { GHOST_TSuccess success; if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) { if (m_hDC && m_hGlRc) { success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure; } else { success = GHOST_kFailure; } } else { success = GHOST_kSuccess; } return success; } GHOST_TSuccess GHOST_WindowWin32::invalidate() { GHOST_TSuccess success; if (m_hWnd) { success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure; } else { success = GHOST_kFailure; } return success; } GHOST_TSuccess GHOST_WindowWin32::initMultisample(PIXELFORMATDESCRIPTOR pfd) { int pixelFormat; bool success = FALSE; UINT numFormats; HDC hDC = GetDC(getHWND()); float fAttributes[] = {0, 0}; UINT nMaxFormats = 1; // The attributes to look for int iAttributes[] = { WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, WGL_SUPPORT_OPENGL_ARB, GL_TRUE, WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, WGL_COLOR_BITS_ARB, pfd.cColorBits, WGL_DEPTH_BITS_ARB, pfd.cDepthBits, WGL_ALPHA_BITS_ARB, pfd.cAlphaBits, WGL_STENCIL_BITS_ARB, pfd.cStencilBits, WGL_DOUBLE_BUFFER_ARB, GL_TRUE, WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, WGL_SAMPLES_ARB, m_multisample, 0, 0 }; // Get the function PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); if (!wglChoosePixelFormatARB) { m_multisampleEnabled = GHOST_kFailure; return GHOST_kFailure; } // iAttributes[17] is the initial multisample. If not valid try to use the closest valid value under it. while (iAttributes[17] > 0) { // See if the format is valid success = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, nMaxFormats, &pixelFormat, &numFormats); GHOST_PRINTF("WGL_SAMPLES_ARB = %i --> success = %i, %i formats\n", iAttributes[17], success, numFormats); if (success && numFormats >= 1 && m_multisampleEnabled == GHOST_kFailure) { GHOST_PRINTF("valid pixel format with %i multisamples\n", iAttributes[17]); m_multisampleEnabled = GHOST_kSuccess; m_msPixelFormat = pixelFormat; } iAttributes[17] -= 1; success = GHOST_kFailure; } if (m_multisampleEnabled == GHOST_kSuccess) { return GHOST_kSuccess; } GHOST_PRINT("no available pixel format\n"); return GHOST_kFailure; } GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type) { GHOST_TSuccess success; switch (type) { case GHOST_kDrawingContextTypeOpenGL: { // If this window has multisample enabled, use the supplied format if (m_multisampleEnabled) { if (SetPixelFormat(m_hDC, m_msPixelFormat, &sPreferredFormat) == FALSE) { success = GHOST_kFailure; break; } // Create the context m_hGlRc = ::wglCreateContext(m_hDC); if (m_hGlRc) { if (::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE) { if (s_firsthGLRc) { if (is_crappy_intel_card()) { if (::wglMakeCurrent(NULL, NULL) == TRUE) { ::wglDeleteContext(m_hGlRc); m_hGlRc = s_firsthGLRc; } else { ::wglDeleteContext(m_hGlRc); m_hGlRc = NULL; } } else { ::wglCopyContext(s_firsthGLRc, m_hGlRc, GL_ALL_ATTRIB_BITS); ::wglShareLists(s_firsthGLRc, m_hGlRc); } } else { s_firsthGLRc = m_hGlRc; } if (m_hGlRc) { success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure; } else { success = GHOST_kFailure; } } else { success = GHOST_kFailure; } } else { success = GHOST_kFailure; } if (success == GHOST_kFailure) { printf("Failed to get a context....\n"); } } else { if (m_stereoVisual) sPreferredFormat.dwFlags |= PFD_STEREO; // Attempt to match device context pixel format to the preferred format int iPixelFormat = EnumPixelFormats(m_hDC); if (iPixelFormat == 0) { success = GHOST_kFailure; break; } if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) { success = GHOST_kFailure; break; } // For debugging only: retrieve the pixel format chosen PIXELFORMATDESCRIPTOR preferredFormat; ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat); // Create the context m_hGlRc = ::wglCreateContext(m_hDC); if (m_hGlRc) { if (::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE) { if (s_firsthGLRc) { if (is_crappy_intel_card()) { if (::wglMakeCurrent(NULL, NULL) == TRUE) { ::wglDeleteContext(m_hGlRc); m_hGlRc = s_firsthGLRc; } else { ::wglDeleteContext(m_hGlRc); m_hGlRc = NULL; } } else { ::wglShareLists(s_firsthGLRc, m_hGlRc); } } else { s_firsthGLRc = m_hGlRc; } if (m_hGlRc) { success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure; } else { success = GHOST_kFailure; } } else { success = GHOST_kFailure; } } else { success = GHOST_kFailure; } if (success == GHOST_kFailure) { printf("Failed to get a context....\n"); } // Attempt to enable multisample if (m_multisample && WGL_ARB_multisample && !m_multisampleEnabled && !is_crappy_intel_card()) { success = initMultisample(preferredFormat); if (success) { // Make sure we don't screw up the context if (m_hGlRc == s_firsthGLRc) s_firsthGLRc = NULL; m_drawingContextType = GHOST_kDrawingContextTypeOpenGL; removeDrawingContext(); // Create a new window GHOST_TWindowState new_state = getState(); m_nextWindow = new GHOST_WindowWin32((GHOST_SystemWin32 *)GHOST_ISystem::getSystem(), m_title, m_left, m_top, m_width, m_height, new_state, type, m_stereo, m_multisample, m_parentWindowHwnd, m_multisampleEnabled, m_msPixelFormat); // Return failure so we can trash this window. success = GHOST_kFailure; break; } else { m_multisampleEnabled = GHOST_kSuccess; printf("Multisample failed to initialized\n"); success = GHOST_kSuccess; } } } } break; case GHOST_kDrawingContextTypeNone: success = GHOST_kSuccess; break; default: success = GHOST_kFailure; } return success; } GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext() { GHOST_TSuccess success; switch (m_drawingContextType) { case GHOST_kDrawingContextTypeOpenGL: // we shouldn't remove the drawing context if it's the first OpenGL context // If we do, we get corrupted drawing. See #19997 if (m_hGlRc && m_hGlRc != s_firsthGLRc) { success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure; m_hGlRc = 0; } else { success = GHOST_kFailure; } break; case GHOST_kDrawingContextTypeNone: success = GHOST_kSuccess; break; default: success = GHOST_kFailure; } return success; } void GHOST_WindowWin32::lostMouseCapture() { if (m_hasMouseCaptured) { m_hasGrabMouse = false; m_nPressedButtons = 0; m_hasMouseCaptured = false; }; } void GHOST_WindowWin32::registerMouseClickEvent(int press) { switch (press) { case 0: m_nPressedButtons++; break; case 1: if (m_nPressedButtons) m_nPressedButtons--; break; case 2: m_hasGrabMouse = true; break; case 3: m_hasGrabMouse = false; break; } if (!m_nPressedButtons && !m_hasGrabMouse && m_hasMouseCaptured) { ::ReleaseCapture(); m_hasMouseCaptured = false; } else if ((m_nPressedButtons || m_hasGrabMouse) && !m_hasMouseCaptured) { ::SetCapture(m_hWnd); m_hasMouseCaptured = true; } } void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const { if (!visible) { while (::ShowCursor(FALSE) >= 0) ; } else { while (::ShowCursor(TRUE) < 0) ; } if (cursor == GHOST_kStandardCursorCustom && m_customCursor) { ::SetCursor(m_customCursor); } else { // Convert GHOST cursor to Windows OEM cursor bool success = true; LPCSTR id; switch (cursor) { case GHOST_kStandardCursorDefault: id = IDC_ARROW; break; case GHOST_kStandardCursorRightArrow: id = IDC_ARROW; break; case GHOST_kStandardCursorLeftArrow: id = IDC_ARROW; break; case GHOST_kStandardCursorInfo: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west case GHOST_kStandardCursorDestroy: id = IDC_NO; break; // Slashed circle case GHOST_kStandardCursorHelp: id = IDC_HELP; break; // Arrow and question mark case GHOST_kStandardCursorCycle: id = IDC_NO; break; // Slashed circle case GHOST_kStandardCursorSpray: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west case GHOST_kStandardCursorWait: id = IDC_WAIT; break; // Hourglass case GHOST_kStandardCursorText: id = IDC_IBEAM; break; // I-beam case GHOST_kStandardCursorCrosshair: id = IDC_CROSS; break; // Crosshair case GHOST_kStandardCursorUpDown: id = IDC_SIZENS; break; // Double-pointed arrow pointing north and south case GHOST_kStandardCursorLeftRight: id = IDC_SIZEWE; break; // Double-pointed arrow pointing west and east case GHOST_kStandardCursorTopSide: id = IDC_UPARROW; break; // Vertical arrow case GHOST_kStandardCursorBottomSide: id = IDC_SIZENS; break; case GHOST_kStandardCursorLeftSide: id = IDC_SIZEWE; break; case GHOST_kStandardCursorTopLeftCorner: id = IDC_SIZENWSE; break; case GHOST_kStandardCursorTopRightCorner: id = IDC_SIZENESW; break; case GHOST_kStandardCursorBottomRightCorner: id = IDC_SIZENWSE; break; case GHOST_kStandardCursorBottomLeftCorner: id = IDC_SIZENESW; break; case GHOST_kStandardCursorPencil: id = IDC_ARROW; break; case GHOST_kStandardCursorCopy: id = IDC_ARROW; break; default: success = false; } if (success) { ::SetCursor(::LoadCursor(0, id)); } } } GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible) { if (::GetForegroundWindow() == m_hWnd) { loadCursor(visible, getCursorShape()); } return GHOST_kSuccess; } GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode) { if (mode != GHOST_kGrabDisable) { if (mode != GHOST_kGrabNormal) { m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]); setCursorGrabAccum(0, 0); if (mode == GHOST_kGrabHide) setWindowCursorVisibility(false); } registerMouseClickEvent(2); } else { if (m_cursorGrab == GHOST_kGrabHide) { m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]); setWindowCursorVisibility(true); } if (m_cursorGrab != GHOST_kGrabNormal) { /* use to generate a mouse move event, otherwise the last event * blender gets can be outside the screen causing menus not to show * properly unless the user moves the mouse */ GHOST_TInt32 pos[2]; m_system->getCursorPosition(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 */ setCursorGrabAccum(0, 0); m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */ registerMouseClickEvent(3); } return GHOST_kSuccess; } GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape) { if (m_customCursor) { DestroyCursor(m_customCursor); m_customCursor = NULL; } if (::GetForegroundWindow() == m_hWnd) { loadCursor(getCursorVisibility(), cursorShape); } return GHOST_kSuccess; } void GHOST_WindowWin32::processWin32TabletInitEvent() { if (m_wintab) { GHOST_WIN32_WTInfo fpWTInfo = (GHOST_WIN32_WTInfo) ::GetProcAddress(m_wintab, "WTInfoA"); // let's see if we can initialize tablet here /* check if WinTab available. */ if (fpWTInfo) { AXIS Pressure, Orientation[3]; /* The maximum tablet size */ BOOL pressureSupport = fpWTInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure); if (pressureSupport) m_maxPressure = Pressure.axMax; else m_maxPressure = 0; BOOL tiltSupport = fpWTInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation); if (tiltSupport) { /* does the tablet support azimuth ([0]) and altitude ([1]) */ if (Orientation[0].axResolution && Orientation[1].axResolution) { m_maxAzimuth = Orientation[0].axMax; m_maxAltitude = Orientation[1].axMax; } else { /* no so dont do tilt stuff */ m_maxAzimuth = m_maxAltitude = 0; } } m_tabletData->Active = GHOST_kTabletModeNone; } } } void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam) { PACKET pkt; if (m_wintab) { 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) { case 0: /* first device */ case 3: /* second device */ m_tabletData->Active = GHOST_kTabletModeNone; /* puck - not yet supported */ break; case 1: case 4: m_tabletData->Active = GHOST_kTabletModeStylus; /* stylus */ break; case 2: case 5: m_tabletData->Active = GHOST_kTabletModeEraser; /* eraser */ break; } if (m_maxPressure > 0) { m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure; } else { m_tabletData->Pressure = 1.0f; } if ((m_maxAzimuth > 0) && (m_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_maxAltitude) * M_PI / 2.0); azmRad = (float)(((float)ort.orAzimuth / (float)m_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 { m_tabletData->Xtilt = 0.0f; m_tabletData->Ytilt = 0.0f; } } } } } } void GHOST_WindowWin32::bringTabletContextToFront() { if (m_wintab) { GHOST_WIN32_WTOverlap fpWTOverlap = (GHOST_WIN32_WTOverlap) ::GetProcAddress(m_wintab, "WTOverlap"); if (fpWTOverlap) { fpWTOverlap(m_tablet, TRUE); } } } /** Reverse the bits in a GHOST_TUns8 */ static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch) { ch = ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA); ch = ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC); ch = ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0); return ch; } #if 0 /* UNUSED */ /** Reverse the bits in a GHOST_TUns16 */ static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt) { shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA); shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC); shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0); shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00); return shrt; } #endif GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY) { return setWindowCustomCursorShape((GHOST_TUns8 *)bitmap, (GHOST_TUns8 *)mask, 16, 16, hotX, hotY, 0, 1); } GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY, int fg_color, int bg_color) { GHOST_TUns32 andData[32]; GHOST_TUns32 xorData[32]; GHOST_TUns32 fullBitRow, fullMaskRow; int x, y, cols; cols = sizeX / 8; /* Num of whole bytes per row (width of bm/mask) */ if (sizeX % 8) cols++; if (m_customCursor) { DestroyCursor(m_customCursor); m_customCursor = NULL; } memset(&andData, 0xFF, sizeof(andData)); memset(&xorData, 0, sizeof(xorData)); for (y = 0; y < sizeY; y++) { fullBitRow = 0; fullMaskRow = 0; for (x = cols - 1; x >= 0; x--) { fullBitRow <<= 8; fullMaskRow <<= 8; fullBitRow |= uns8ReverseBits(bitmap[cols * y + x]); fullMaskRow |= uns8ReverseBits(mask[cols * y + x]); } xorData[y] = fullBitRow & fullMaskRow; andData[y] = ~fullMaskRow; } m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData); if (!m_customCursor) { return GHOST_kFailure; } if (::GetForegroundWindow() == m_hWnd) { loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom); } return GHOST_kSuccess; } GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress) { /*SetProgressValue sets state to TBPF_NORMAL automaticly*/ if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000)) return GHOST_kSuccess; return GHOST_kFailure; } GHOST_TSuccess GHOST_WindowWin32::endProgressBar() { if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS)) return GHOST_kSuccess; return GHOST_kFailure; } /* Ron Fosner's code for weighting pixel formats and forcing software. * See http://www.opengl.org/resources/faq/technical/weight.cpp */ static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) { int weight = 0; /* assume desktop color depth is 32 bits per pixel */ /* cull unusable pixel formats */ /* if no formats can be found, can we determine why it was rejected? */ if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL) || !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */ (pfd.cDepthBits <= 8) || !(pfd.iPixelType == PFD_TYPE_RGBA)) { return 0; } weight = 1; /* it's usable */ /* the bigger the depth buffer the better */ /* give no weight to a 16-bit depth buffer, because those are crap */ weight += pfd.cDepthBits - 16; weight += pfd.cColorBits - 8; if (pfd.cAlphaBits > 0) weight ++; /* want swap copy capability -- it matters a lot */ if (pfd.dwFlags & PFD_SWAP_COPY) weight += 16; /* but if it's a generic (not accelerated) view, it's really bad */ if (pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10; return weight; } /* A modification of Ron Fosner's replacement for ChoosePixelFormat */ /* returns 0 on error, else returns the pixel format number to be used */ static int EnumPixelFormats(HDC hdc) { int iPixelFormat; int i, n, w, weight = 0; PIXELFORMATDESCRIPTOR pfd; /* we need a device context to do anything */ if (!hdc) return 0; iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */ /* obtain detailed information about * the device context's first pixel format */ n = 1 + ::DescribePixelFormat(hdc, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); /* choose a pixel format using the useless Windows function in case * we come up empty handed */ iPixelFormat = ::ChoosePixelFormat(hdc, &sPreferredFormat); if (!iPixelFormat) return 0; /* couldn't find one to use */ for (i = 1; i <= n; i++) { /* not the idiom, but it's right */ ::DescribePixelFormat(hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd); w = WeightPixelFormat(pfd); // be strict on stereo if (!((sPreferredFormat.dwFlags ^ pfd.dwFlags) & PFD_STEREO)) { if (w > weight) { weight = w; iPixelFormat = i; } } } if (weight == 0) { // we could find the correct stereo setting, just find any suitable format for (i = 1; i <= n; i++) { /* not the idiom, but it's right */ ::DescribePixelFormat(hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd); w = WeightPixelFormat(pfd); if (w > weight) { weight = w; iPixelFormat = i; } } } return iPixelFormat; }