3 * ***** BEGIN GPL LICENSE BLOCK *****
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20 * All rights reserved.
22 * The Original Code is: all of this file.
24 * Contributor(s): none yet.
26 * ***** END GPL LICENSE BLOCK *****
32 * Copyright (C) 2001 NaN Technologies B.V.
33 * @author Maarten Gribnau
38 #include "GHOST_WindowWin32.h"
39 #include "GHOST_SystemWin32.h"
40 #include "GHOST_DropTargetWin32.h"
42 // Need glew for some defines
47 // MSVC6 still doesn't define M_PI
49 #define M_PI 3.1415926536
52 // Some more multisample defines
53 #define WGL_SAMPLE_BUFERS_ARB 0x2041
54 #define WGL_SAMPLES_ARB 0x2042
56 // win64 doesn't define GWL_USERDATA
59 #define GWL_USERDATA GWLP_USERDATA
60 #define GWL_WNDPROC GWLP_WNDPROC
64 LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
65 const int GHOST_WindowWin32::s_maxTitleLength = 128;
66 HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
67 HDC GHOST_WindowWin32::s_firstHDC = NULL;
69 static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd);
70 static int EnumPixelFormats(HDC hdc);
73 * Color and depth bit values are not to be trusted.
74 * For instance, on TNT2:
75 * When the screen color depth is set to 16 bit, we get 5 color bits
77 * When the screen color depth is set to 32 bit, we get 8 color bits
79 * Just to be safe, we request high waulity settings.
81 static PIXELFORMATDESCRIPTOR sPreferredFormat = {
82 sizeof(PIXELFORMATDESCRIPTOR), /* size */
86 PFD_SWAP_COPY | /* support swap copy */
87 PFD_DOUBLEBUFFER, /* support double-buffering */
88 PFD_TYPE_RGBA, /* color type */
89 32, /* prefered color depth */
90 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
91 0, /* no alpha buffer */
92 0, /* alpha bits (ignored) */
93 0, /* no accumulation buffer */
94 0, 0, 0, 0, /* accum bits (ignored) */
95 32, /* depth buffer */
96 0, /* no stencil buffer */
97 0, /* no auxiliary buffers */
98 PFD_MAIN_PLANE, /* main layer */
100 0, 0, 0 /* no layer, visible, damage masks */
103 GHOST_WindowWin32::GHOST_WindowWin32(
104 GHOST_SystemWin32 * system,
105 const STR_String& title,
110 GHOST_TWindowState state,
111 GHOST_TDrawingContextType type,
112 const bool stereoVisual,
113 const GHOST_TUns16 numOfAASamples,
114 GHOST_TSuccess msEnabled,
117 GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone,
118 stereoVisual,numOfAASamples),
122 m_hasMouseCaptured(false),
123 m_nPressedButtons(0),
129 m_multisample(numOfAASamples),
130 m_multisampleEnabled(msEnabled),
131 m_msPixelFormat(msPixelFormat),
138 m_normal_state(GHOST_kWindowStateNormal),
139 m_stereo(stereoVisual),
142 OSVERSIONINFOEX versionInfo;
143 bool hasMinVersionForTaskbar = false;
145 ZeroMemory(&versionInfo, sizeof(OSVERSIONINFOEX));
147 versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
149 if(!GetVersionEx((OSVERSIONINFO *)&versionInfo)) {
150 versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
151 if(GetVersionEx((OSVERSIONINFO*)&versionInfo)) {
152 if(versionInfo.dwMajorVersion>=6 && versionInfo.dwMinorVersion>=1) {
153 hasMinVersionForTaskbar = true;
157 if(versionInfo.dwMajorVersion>=6 && versionInfo.dwMinorVersion>=1) {
158 hasMinVersionForTaskbar = true;
162 if (state != GHOST_kWindowStateFullScreen) {
167 width += GetSystemMetrics(SM_CXSIZEFRAME)*2;
168 height += GetSystemMetrics(SM_CYSIZEFRAME)*2 + GetSystemMetrics(SM_CYCAPTION);
171 rect.right = left + width;
173 rect.bottom = top + height;
175 monitor.cbSize=sizeof(monitor);
178 // take taskbar into account
179 GetMonitorInfo(MonitorFromRect(&rect,MONITOR_DEFAULTTONEAREST),&monitor);
181 th = monitor.rcWork.bottom - monitor.rcWork.top;
182 tw = monitor.rcWork.right - monitor.rcWork.left;
187 left = monitor.rcWork.left;
189 else if(monitor.rcWork.right < left + (int)width)
190 left = monitor.rcWork.right - width;
191 else if(left < monitor.rcWork.left)
192 left = monitor.rcWork.left;
197 top = monitor.rcWork.top;
199 else if(monitor.rcWork.bottom < top + (int)height)
200 top = monitor.rcWork.bottom - height;
201 else if(top < monitor.rcWork.top)
202 top = monitor.rcWork.top;
204 m_hWnd = ::CreateWindow(
205 s_windowClassName, // pointer to registered class name
206 title, // pointer to window name
207 WS_OVERLAPPEDWINDOW, // window style
208 left, // horizontal position of window
209 top, // vertical position of window
210 width, // window width
211 height, // window height
212 HWND_DESKTOP, // handle to parent or owner window
213 0, // handle to menu or child-window identifier
214 ::GetModuleHandle(0), // handle to application instance
215 0); // pointer to window-creation data
218 m_hWnd = ::CreateWindow(
219 s_windowClassName, // pointer to registered class name
220 title, // pointer to window name
221 WS_POPUP | WS_MAXIMIZE, // window style
222 left, // horizontal position of window
223 top, // vertical position of window
224 width, // window width
225 height, // window height
226 HWND_DESKTOP, // handle to parent or owner window
227 0, // handle to menu or child-window identifier
228 ::GetModuleHandle(0), // handle to application instance
229 0); // pointer to window-creation data
232 // Register this window as a droptarget. Requires m_hWnd to be valid.
233 // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
234 m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
235 // Store a pointer to this class in the window structure
236 ::SetWindowLongPtr(m_hWnd, GWL_USERDATA, (LONG_PTR)this);
238 // Store the device context
239 m_hDC = ::GetDC(m_hWnd);
248 case GHOST_kWindowStateMaximized:
249 nCmdShow = SW_SHOWMAXIMIZED;
251 case GHOST_kWindowStateMinimized:
252 nCmdShow = SW_SHOWMINIMIZED;
254 case GHOST_kWindowStateNormal:
256 nCmdShow = SW_SHOWNORMAL;
259 GHOST_TSuccess success;
260 success = setDrawingContextType(type);
264 ::ShowWindow(m_hWnd, nCmdShow);
265 // Force an initial paint of the window
266 ::UpdateWindow(m_hWnd);
270 //invalidate the window
275 m_wintab = ::LoadLibrary("Wintab32.dll");
277 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
278 GHOST_WIN32_WTOpen fpWTOpen = ( GHOST_WIN32_WTOpen ) ::GetProcAddress( m_wintab, "WTOpenA" );
280 // let's see if we can initialize tablet here
281 /* check if WinTab available. */
282 if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
283 // Now init the tablet
285 AXIS TabletX, TabletY, Pressure, Orientation[3]; /* The maximum tablet size, pressure and orientation (tilt) */
287 // Open a Wintab context
289 // Get default context information
290 fpWTInfo( WTI_DEFCONTEXT, 0, &lc );
293 lc.lcPktData = PACKETDATA;
294 lc.lcPktMode = PACKETMODE;
295 lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
297 /* Set the entire tablet as active */
298 fpWTInfo(WTI_DEVICES,DVC_X,&TabletX);
299 fpWTInfo(WTI_DEVICES,DVC_Y,&TabletY);
301 /* get the max pressure, to divide into a float */
302 BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
304 m_maxPressure = Pressure.axMax;
308 /* get the max tilt axes, to divide into floats */
309 BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
311 /* does the tablet support azimuth ([0]) and altitude ([1]) */
312 if (Orientation[0].axResolution && Orientation[1].axResolution) {
313 /* all this assumes the minimum is 0 */
314 m_maxAzimuth = Orientation[0].axMax;
315 m_maxAltitude = Orientation[1].axMax;
317 else { /* no so dont do tilt stuff */
318 m_maxAzimuth = m_maxAltitude = 0;
323 m_tablet = fpWTOpen( m_hWnd, &lc, TRUE );
325 m_tabletData = new GHOST_TabletData();
326 m_tabletData->Active = GHOST_kTabletModeNone;
332 if(hasMinVersionForTaskbar)
333 CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList ,(LPVOID*)&m_Bar);
339 GHOST_WindowWin32::~GHOST_WindowWin32()
343 m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
348 GHOST_WIN32_WTClose fpWTClose = ( GHOST_WIN32_WTClose ) ::GetProcAddress( m_wintab, "WTClose" );
357 if (m_customCursor) {
358 DestroyCursor(m_customCursor);
359 m_customCursor = NULL;
362 ::wglMakeCurrent(NULL, NULL);
363 m_multisampleEnabled = GHOST_kFailure;
365 setDrawingContextType(GHOST_kDrawingContextTypeNone);
366 if (m_hDC && m_hDC != s_firstHDC) {
367 ::ReleaseDC(m_hWnd, m_hDC);
371 m_dropTarget->Release(); // frees itself.
372 ::DestroyWindow(m_hWnd);
377 GHOST_Window *GHOST_WindowWin32::getNextWindow()
382 bool GHOST_WindowWin32::getValid() const
387 HWND GHOST_WindowWin32::getHWND() const
392 void GHOST_WindowWin32::setTitle(const STR_String& title)
394 ::SetWindowText(m_hWnd, title);
398 void GHOST_WindowWin32::getTitle(STR_String& title) const
400 char buf[s_maxTitleLength];
401 ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
402 STR_String temp (buf);
407 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const
410 ::GetWindowRect(m_hWnd, &rect);
411 bounds.m_b = rect.bottom;
412 bounds.m_l = rect.left;
413 bounds.m_r = rect.right;
414 bounds.m_t = rect.top;
418 void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const
421 GHOST_TWindowState state= this->getState();
422 LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
423 int sm_cysizeframe = GetSystemMetrics(SM_CYSIZEFRAME);
424 ::GetWindowRect(m_hWnd, &rect);
426 if((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE)) {
427 if(state==GHOST_kWindowStateMaximized) {
428 // in maximized state we don't have borders on the window
429 bounds.m_b = rect.bottom-GetSystemMetrics(SM_CYCAPTION)- sm_cysizeframe*2;
430 bounds.m_l = rect.left + sm_cysizeframe;
431 bounds.m_r = rect.right - sm_cysizeframe;
432 bounds.m_t = rect.top;
434 bounds.m_b = rect.bottom-GetSystemMetrics(SM_CYCAPTION)-sm_cysizeframe*2;
435 bounds.m_l = rect.left;
436 bounds.m_r = rect.right-sm_cysizeframe*2;
437 bounds.m_t = rect.top;
440 ::GetWindowRect(m_hWnd, &rect);
441 bounds.m_b = rect.bottom;
442 bounds.m_l = rect.left;
443 bounds.m_r = rect.right;
444 bounds.m_t = rect.top;
449 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
451 GHOST_TSuccess success;
452 GHOST_Rect cBnds, wBnds;
453 getClientBounds(cBnds);
454 if (cBnds.getWidth() != (GHOST_TInt32)width) {
455 getWindowBounds(wBnds);
456 int cx = wBnds.getWidth() + width - cBnds.getWidth();
457 int cy = wBnds.getHeight();
458 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
459 GHOST_kSuccess : GHOST_kFailure;
462 success = GHOST_kSuccess;
468 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
470 GHOST_TSuccess success;
471 GHOST_Rect cBnds, wBnds;
472 getClientBounds(cBnds);
473 if (cBnds.getHeight() != (GHOST_TInt32)height) {
474 getWindowBounds(wBnds);
475 int cx = wBnds.getWidth();
476 int cy = wBnds.getHeight() + height - cBnds.getHeight();
477 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
478 GHOST_kSuccess : GHOST_kFailure;
481 success = GHOST_kSuccess;
487 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
489 GHOST_TSuccess success;
490 GHOST_Rect cBnds, wBnds;
491 getClientBounds(cBnds);
492 if ((cBnds.getWidth() != (GHOST_TInt32)width) || (cBnds.getHeight() != (GHOST_TInt32)height)) {
493 getWindowBounds(wBnds);
494 int cx = wBnds.getWidth() + width - cBnds.getWidth();
495 int cy = wBnds.getHeight() + height - cBnds.getHeight();
496 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
497 GHOST_kSuccess : GHOST_kFailure;
500 success = GHOST_kSuccess;
506 GHOST_TWindowState GHOST_WindowWin32::getState() const
508 GHOST_TWindowState state;
509 if (::IsIconic(m_hWnd)) {
510 state = GHOST_kWindowStateMinimized;
512 else if (::IsZoomed(m_hWnd)) {
513 LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
514 if((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE))
515 state = GHOST_kWindowStateMaximized;
517 state = GHOST_kWindowStateFullScreen;
520 state = GHOST_kWindowStateNormal;
526 void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
528 POINT point = { inX, inY };
529 ::ScreenToClient(m_hWnd, &point);
535 void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
537 POINT point = { inX, inY };
538 ::ClientToScreen(m_hWnd, &point);
544 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
546 GHOST_TWindowState curstate = getState();
548 wp.length = sizeof(WINDOWPLACEMENT);
549 ::GetWindowPlacement(m_hWnd, &wp);
551 if (state == GHOST_kWindowStateNormal)
552 state = m_normal_state;
554 case GHOST_kWindowStateMinimized:
555 wp.showCmd = SW_SHOWMINIMIZED;
557 case GHOST_kWindowStateMaximized:
558 ShowWindow(m_hWnd, SW_HIDE);
559 wp.showCmd = SW_SHOWMAXIMIZED;
560 SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
562 case GHOST_kWindowStateFullScreen:
563 if (curstate != state && curstate != GHOST_kWindowStateMinimized)
564 m_normal_state = curstate;
565 wp.showCmd = SW_SHOWMAXIMIZED;
566 wp.ptMaxPosition.x = 0;
567 wp.ptMaxPosition.y = 0;
568 SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
570 case GHOST_kWindowStateNormal:
572 ShowWindow(m_hWnd, SW_HIDE);
573 wp.showCmd = SW_SHOWNORMAL;
574 SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
577 return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
581 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
583 HWND hWndInsertAfter = order == GHOST_kWindowOrderTop ? HWND_TOP : HWND_BOTTOM;
584 return ::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
588 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
590 return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
594 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
596 GHOST_TSuccess success;
597 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
598 if (m_hDC && m_hGlRc) {
599 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
602 success = GHOST_kFailure;
606 success = GHOST_kSuccess;
612 GHOST_TSuccess GHOST_WindowWin32::invalidate()
614 GHOST_TSuccess success;
616 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
619 success = GHOST_kFailure;
624 GHOST_TSuccess GHOST_WindowWin32::initMultisample(PIXELFORMATDESCRIPTOR pfd)
629 HDC hDC = GetDC(getHWND());
630 float fAttributes[] = {0, 0};
632 // The attributes to look for
633 int iAttributes[] = {
634 WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
635 WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
636 WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
637 WGL_COLOR_BITS_ARB, pfd.cColorBits,
638 WGL_DEPTH_BITS_ARB, pfd.cDepthBits,
639 WGL_STENCIL_BITS_ARB, pfd.cStencilBits,
640 WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
641 WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
642 WGL_SAMPLES_ARB, m_multisample,
647 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
649 if (!wglChoosePixelFormatARB)
651 m_multisampleEnabled = GHOST_kFailure;
652 return GHOST_kFailure;
655 // See if the format is valid
656 success = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
658 if (success && numFormats >= 1)
660 m_multisampleEnabled = GHOST_kSuccess;
661 m_msPixelFormat = pixelFormat;
662 return GHOST_kSuccess;
666 // See if any formats are supported
667 while (!success && iAttributes[19] != 0)
669 iAttributes[19] /= 2;
671 success = wglChoosePixelFormatARB(m_hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
673 if (success && numFormats >= 1)
675 m_multisampleEnabled = GHOST_kSuccess;
676 m_msPixelFormat = pixelFormat;
677 return GHOST_kSuccess;
680 success = GHOST_kFailure;
684 // No available pixel format...
685 return GHOST_kFailure;
688 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
690 GHOST_TSuccess success;
692 case GHOST_kDrawingContextTypeOpenGL:
694 // If this window has multisample enabled, use the supplied format
695 if (m_multisampleEnabled)
697 if (SetPixelFormat(m_hDC, m_msPixelFormat, &sPreferredFormat)==FALSE)
699 success = GHOST_kFailure;
703 // Create the context
704 m_hGlRc = ::wglCreateContext(m_hDC);
707 ::wglCopyContext(s_firsthGLRc, m_hGlRc, GL_ALL_ATTRIB_BITS);
708 wglShareLists(s_firsthGLRc, m_hGlRc);
710 s_firsthGLRc = m_hGlRc;
713 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
716 printf("Failed to get a context....\n");
717 success = GHOST_kFailure;
723 sPreferredFormat.dwFlags |= PFD_STEREO;
725 // Attempt to match device context pixel format to the preferred format
726 int iPixelFormat = EnumPixelFormats(m_hDC);
727 if (iPixelFormat == 0) {
728 success = GHOST_kFailure;
731 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
732 success = GHOST_kFailure;
735 // For debugging only: retrieve the pixel format chosen
736 PIXELFORMATDESCRIPTOR preferredFormat;
737 ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat);
739 // Create the context
740 m_hGlRc = ::wglCreateContext(m_hDC);
743 ::wglShareLists(s_firsthGLRc, m_hGlRc);
745 s_firsthGLRc = m_hGlRc;
748 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
751 printf("Failed to get a context....\n");
752 success = GHOST_kFailure;
755 // Attempt to enable multisample
756 if (m_multisample && WGL_ARB_multisample && !m_multisampleEnabled)
758 success = initMultisample(preferredFormat);
763 // Make sure we don't screw up the context
764 m_drawingContextType = GHOST_kDrawingContextTypeOpenGL;
765 removeDrawingContext();
767 // Create a new window
768 GHOST_TWindowState new_state = getState();
770 m_nextWindow = new GHOST_WindowWin32((GHOST_SystemWin32*)GHOST_ISystem::getSystem(),
780 m_multisampleEnabled,
783 // Return failure so we can trash this window.
784 success = GHOST_kFailure;
793 case GHOST_kDrawingContextTypeNone:
794 success = GHOST_kSuccess;
798 success = GHOST_kFailure;
803 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
805 GHOST_TSuccess success;
806 switch (m_drawingContextType) {
807 case GHOST_kDrawingContextTypeOpenGL:
809 bool first = m_hGlRc == s_firsthGLRc;
810 success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
817 success = GHOST_kFailure;
820 case GHOST_kDrawingContextTypeNone:
821 success = GHOST_kSuccess;
824 success = GHOST_kFailure;
829 void GHOST_WindowWin32::lostMouseCapture()
831 if (m_hasMouseCaptured) {
832 m_hasMouseCaptured = false;
833 m_nPressedButtons = 0;
837 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
840 if (!m_hasMouseCaptured) {
841 ::SetCapture(m_hWnd);
842 m_hasMouseCaptured = true;
846 if (m_nPressedButtons) {
848 if (!m_nPressedButtons) {
850 m_hasMouseCaptured = false;
857 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
860 while (::ShowCursor(FALSE) >= 0);
863 while (::ShowCursor(TRUE) < 0);
866 if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
867 ::SetCursor( m_customCursor );
869 // Convert GHOST cursor to Windows OEM cursor
873 case GHOST_kStandardCursorDefault: id = IDC_ARROW; break;
874 case GHOST_kStandardCursorRightArrow: id = IDC_ARROW; break;
875 case GHOST_kStandardCursorLeftArrow: id = IDC_ARROW; break;
876 case GHOST_kStandardCursorInfo: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
877 case GHOST_kStandardCursorDestroy: id = IDC_NO; break; // Slashed circle
878 case GHOST_kStandardCursorHelp: id = IDC_HELP; break; // Arrow and question mark
879 case GHOST_kStandardCursorCycle: id = IDC_NO; break; // Slashed circle
880 case GHOST_kStandardCursorSpray: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
881 case GHOST_kStandardCursorWait: id = IDC_WAIT; break; // Hourglass
882 case GHOST_kStandardCursorText: id = IDC_IBEAM; break; // I-beam
883 case GHOST_kStandardCursorCrosshair: id = IDC_CROSS; break; // Crosshair
884 case GHOST_kStandardCursorUpDown: id = IDC_SIZENS; break; // Double-pointed arrow pointing north and south
885 case GHOST_kStandardCursorLeftRight: id = IDC_SIZEWE; break; // Double-pointed arrow pointing west and east
886 case GHOST_kStandardCursorTopSide: id = IDC_UPARROW; break; // Vertical arrow
887 case GHOST_kStandardCursorBottomSide: id = IDC_SIZENS; break;
888 case GHOST_kStandardCursorLeftSide: id = IDC_SIZEWE; break;
889 case GHOST_kStandardCursorTopLeftCorner: id = IDC_SIZENWSE; break;
890 case GHOST_kStandardCursorTopRightCorner: id = IDC_SIZENESW; break;
891 case GHOST_kStandardCursorBottomRightCorner: id = IDC_SIZENWSE; break;
892 case GHOST_kStandardCursorBottomLeftCorner: id = IDC_SIZENESW; break;
893 case GHOST_kStandardCursorPencil: id = IDC_ARROW; break;
894 case GHOST_kStandardCursorCopy: id = IDC_ARROW; break;
900 ::SetCursor(::LoadCursor(0, id));
905 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
907 if (::GetForegroundWindow() == m_hWnd) {
908 loadCursor(visible, getCursorShape());
911 return GHOST_kSuccess;
914 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
916 if(mode != GHOST_kGrabDisable) {
917 if(mode != GHOST_kGrabNormal) {
918 m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
919 setCursorGrabAccum(0, 0);
921 if(mode == GHOST_kGrabHide)
922 setWindowCursorVisibility(false);
924 registerMouseClickEvent(true);
927 if (m_cursorGrab==GHOST_kGrabHide) {
928 m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
929 setWindowCursorVisibility(true);
931 if(m_cursorGrab != GHOST_kGrabNormal) {
932 /* use to generate a mouse move event, otherwise the last event
933 * blender gets can be outside the screen causing menus not to show
934 * properly unless the user moves the mouse */
936 m_system->getCursorPosition(pos[0], pos[1]);
937 m_system->setCursorPosition(pos[0], pos[1]);
940 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
941 setCursorGrabAccum(0, 0);
942 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
943 registerMouseClickEvent(false);
946 return GHOST_kSuccess;
949 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
951 if (m_customCursor) {
952 DestroyCursor(m_customCursor);
953 m_customCursor = NULL;
956 if (::GetForegroundWindow() == m_hWnd) {
957 loadCursor(getCursorVisibility(), cursorShape);
960 return GHOST_kSuccess;
963 void GHOST_WindowWin32::processWin32TabletInitEvent()
966 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
968 // let's see if we can initialize tablet here
969 /* check if WinTab available. */
971 AXIS Pressure, Orientation[3]; /* The maximum tablet size */
973 BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
975 m_maxPressure = Pressure.axMax;
979 BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
981 /* does the tablet support azimuth ([0]) and altitude ([1]) */
982 if (Orientation[0].axResolution && Orientation[1].axResolution) {
983 m_maxAzimuth = Orientation[0].axMax;
984 m_maxAltitude = Orientation[1].axMax;
986 else { /* no so dont do tilt stuff */
987 m_maxAzimuth = m_maxAltitude = 0;
991 m_tabletData->Active = GHOST_kTabletModeNone;
996 void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
1000 GHOST_WIN32_WTPacket fpWTPacket = ( GHOST_WIN32_WTPacket ) ::GetProcAddress( m_wintab, "WTPacket" );
1002 if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
1004 switch (pkt.pkCursor) {
1005 case 0: /* first device */
1006 case 3: /* second device */
1007 m_tabletData->Active = GHOST_kTabletModeNone; /* puck - not yet supported */
1011 m_tabletData->Active = GHOST_kTabletModeStylus; /* stylus */
1015 m_tabletData->Active = GHOST_kTabletModeEraser; /* eraser */
1018 if (m_maxPressure > 0) {
1019 m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
1021 m_tabletData->Pressure = 1.0f;
1024 if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
1025 ORIENTATION ort = pkt.pkOrientation;
1027 float altRad, azmRad; /* in radians */
1030 from the wintab spec:
1031 orAzimuth Specifies the clockwise rotation of the
1032 cursor about the z axis through a full circular range.
1034 orAltitude Specifies the angle with the x-y plane
1035 through a signed, semicircular range. Positive values
1036 specify an angle upward toward the positive z axis;
1037 negative values specify an angle downward toward the negative z axis.
1039 wintab.h defines .orAltitude as a UINT but documents .orAltitude
1040 as positive for upward angles and negative for downward angles.
1041 WACOM uses negative altitude values to show that the pen is inverted;
1042 therefore we cast .orAltitude as an (int) and then use the absolute value.
1045 /* convert raw fixed point data to radians */
1046 altRad = (float)((fabs((float)ort.orAltitude)/(float)m_maxAltitude) * M_PI/2.0);
1047 azmRad = (float)(((float)ort.orAzimuth/(float)m_maxAzimuth) * M_PI*2.0);
1049 /* find length of the stylus' projected vector on the XY plane */
1050 vecLen = cos(altRad);
1052 /* from there calculate X and Y components based on azimuth */
1053 m_tabletData->Xtilt = sin(azmRad) * vecLen;
1054 m_tabletData->Ytilt = (float)(sin(M_PI/2.0 - azmRad) * vecLen);
1057 m_tabletData->Xtilt = 0.0f;
1058 m_tabletData->Ytilt = 0.0f;
1066 /** Reverse the bits in a GHOST_TUns8 */
1067 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1069 ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
1070 ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
1071 ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
1075 /** Reverse the bits in a GHOST_TUns16 */
1076 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1078 shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
1079 shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
1080 shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
1081 shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
1084 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
1085 GHOST_TUns8 mask[16][2], int hotX, int hotY)
1087 return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask,
1088 16, 16, hotX, hotY, 0, 1);
1091 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap,
1092 GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY,
1093 int fg_color, int bg_color)
1095 GHOST_TUns32 andData[32];
1096 GHOST_TUns32 xorData[32];
1097 GHOST_TUns32 fullBitRow, fullMaskRow;
1100 cols=sizeX/8; /* Num of whole bytes per row (width of bm/mask) */
1101 if (sizeX%8) cols++;
1103 if (m_customCursor) {
1104 DestroyCursor(m_customCursor);
1105 m_customCursor = NULL;
1108 memset(&andData, 0xFF, sizeof(andData));
1109 memset(&xorData, 0, sizeof(xorData));
1111 for (y=0; y<sizeY; y++) {
1114 for (x=cols-1; x>=0; x--){
1117 fullBitRow |= uns8ReverseBits(bitmap[cols*y + x]);
1118 fullMaskRow |= uns8ReverseBits( mask[cols*y + x]);
1120 xorData[y]= fullBitRow & fullMaskRow;
1121 andData[y]= ~fullMaskRow;
1124 m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
1125 if (!m_customCursor) {
1126 return GHOST_kFailure;
1129 if (::GetForegroundWindow() == m_hWnd) {
1130 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1133 return GHOST_kSuccess;
1137 GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress)
1139 /*SetProgressValue sets state to TBPF_NORMAL automaticly*/
1140 if(m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd,10000*progress,10000))
1141 return GHOST_kSuccess;
1143 return GHOST_kFailure;
1146 GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
1148 if(m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd,TBPF_NOPROGRESS))
1149 return GHOST_kSuccess;
1151 return GHOST_kFailure;
1154 /* Ron Fosner's code for weighting pixel formats and forcing software.
1155 See http://www.opengl.org/resources/faq/technical/weight.cpp */
1157 static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) {
1160 /* assume desktop color depth is 32 bits per pixel */
1162 /* cull unusable pixel formats */
1163 /* if no formats can be found, can we determine why it was rejected? */
1164 if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ||
1165 !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
1166 !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */
1167 ( pfd.cDepthBits <= 8 ) ||
1168 !(pfd.iPixelType == PFD_TYPE_RGBA))
1171 weight = 1; /* it's usable */
1173 /* the bigger the depth buffer the better */
1174 /* give no weight to a 16-bit depth buffer, because those are crap */
1175 weight += pfd.cDepthBits - 16;
1177 weight += pfd.cColorBits - 8;
1179 /* want swap copy capability -- it matters a lot */
1180 if(pfd.dwFlags & PFD_SWAP_COPY) weight += 16;
1182 /* but if it's a generic (not accelerated) view, it's really bad */
1183 if(pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10;
1188 /* A modification of Ron Fosner's replacement for ChoosePixelFormat */
1189 /* returns 0 on error, else returns the pixel format number to be used */
1190 static int EnumPixelFormats(HDC hdc) {
1192 int i, n, w, weight = 0;
1193 PIXELFORMATDESCRIPTOR pfd;
1195 /* we need a device context to do anything */
1198 iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */
1200 /* obtain detailed information about
1201 the device context's first pixel format */
1202 n = 1+::DescribePixelFormat(hdc, iPixelFormat,
1203 sizeof(PIXELFORMATDESCRIPTOR), &pfd);
1205 /* choose a pixel format using the useless Windows function in case
1206 we come up empty handed */
1207 iPixelFormat = ::ChoosePixelFormat( hdc, &sPreferredFormat );
1209 if(!iPixelFormat) return 0; /* couldn't find one to use */
1211 for(i=1; i<=n; i++) { /* not the idiom, but it's right */
1212 ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
1213 w = WeightPixelFormat(pfd);
1214 // be strict on stereo
1215 if (!((sPreferredFormat.dwFlags ^ pfd.dwFlags) & PFD_STEREO)) {
1223 // we could find the correct stereo setting, just find any suitable format
1224 for(i=1; i<=n; i++) { /* not the idiom, but it's right */
1225 ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
1226 w = WeightPixelFormat(pfd);
1233 return iPixelFormat;