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 if (state != GHOST_kWindowStateFullScreen) {
147 width += GetSystemMetrics(SM_CXSIZEFRAME)*2;
148 height += GetSystemMetrics(SM_CYSIZEFRAME)*2 + GetSystemMetrics(SM_CYCAPTION);
151 rect.right = left + width;
153 rect.bottom = top + height;
155 monitor.cbSize=sizeof(monitor);
158 // take taskbar into account
159 GetMonitorInfo(MonitorFromRect(&rect,MONITOR_DEFAULTTONEAREST),&monitor);
161 th = monitor.rcWork.bottom - monitor.rcWork.top;
162 tw = monitor.rcWork.right - monitor.rcWork.left;
167 left = monitor.rcWork.left;
169 else if(monitor.rcWork.right < left + (int)width)
170 left = monitor.rcWork.right - width;
171 else if(left < monitor.rcWork.left)
172 left = monitor.rcWork.left;
177 top = monitor.rcWork.top;
179 else if(monitor.rcWork.bottom < top + (int)height)
180 top = monitor.rcWork.bottom - height;
181 else if(top < monitor.rcWork.top)
182 top = monitor.rcWork.top;
184 m_hWnd = ::CreateWindow(
185 s_windowClassName, // pointer to registered class name
186 title, // pointer to window name
187 WS_OVERLAPPEDWINDOW, // window style
188 left, // horizontal position of window
189 top, // vertical position of window
190 width, // window width
191 height, // window height
192 HWND_DESKTOP, // handle to parent or owner window
193 0, // handle to menu or child-window identifier
194 ::GetModuleHandle(0), // handle to application instance
195 0); // pointer to window-creation data
198 m_hWnd = ::CreateWindow(
199 s_windowClassName, // pointer to registered class name
200 title, // pointer to window name
201 WS_POPUP | WS_MAXIMIZE, // window style
202 left, // horizontal position of window
203 top, // vertical position of window
204 width, // window width
205 height, // window height
206 HWND_DESKTOP, // handle to parent or owner window
207 0, // handle to menu or child-window identifier
208 ::GetModuleHandle(0), // handle to application instance
209 0); // pointer to window-creation data
212 // Register this window as a droptarget. Requires m_hWnd to be valid.
213 // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
214 m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
215 // Store a pointer to this class in the window structure
216 ::SetWindowLongPtr(m_hWnd, GWL_USERDATA, (LONG_PTR)this);
218 // Store the device context
219 m_hDC = ::GetDC(m_hWnd);
228 case GHOST_kWindowStateMaximized:
229 nCmdShow = SW_SHOWMAXIMIZED;
231 case GHOST_kWindowStateMinimized:
232 nCmdShow = SW_SHOWMINIMIZED;
234 case GHOST_kWindowStateNormal:
236 nCmdShow = SW_SHOWNORMAL;
239 GHOST_TSuccess success;
240 success = setDrawingContextType(type);
244 ::ShowWindow(m_hWnd, nCmdShow);
245 // Force an initial paint of the window
246 ::UpdateWindow(m_hWnd);
250 //invalidate the window
255 m_wintab = ::LoadLibrary("Wintab32.dll");
257 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
258 GHOST_WIN32_WTOpen fpWTOpen = ( GHOST_WIN32_WTOpen ) ::GetProcAddress( m_wintab, "WTOpenA" );
260 // let's see if we can initialize tablet here
261 /* check if WinTab available. */
262 if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
263 // Now init the tablet
265 AXIS TabletX, TabletY, Pressure, Orientation[3]; /* The maximum tablet size, pressure and orientation (tilt) */
267 // Open a Wintab context
269 // Get default context information
270 fpWTInfo( WTI_DEFCONTEXT, 0, &lc );
273 lc.lcPktData = PACKETDATA;
274 lc.lcPktMode = PACKETMODE;
275 lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
277 /* Set the entire tablet as active */
278 fpWTInfo(WTI_DEVICES,DVC_X,&TabletX);
279 fpWTInfo(WTI_DEVICES,DVC_Y,&TabletY);
281 /* get the max pressure, to divide into a float */
282 BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
284 m_maxPressure = Pressure.axMax;
288 /* get the max tilt axes, to divide into floats */
289 BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
291 /* does the tablet support azimuth ([0]) and altitude ([1]) */
292 if (Orientation[0].axResolution && Orientation[1].axResolution) {
293 /* all this assumes the minimum is 0 */
294 m_maxAzimuth = Orientation[0].axMax;
295 m_maxAltitude = Orientation[1].axMax;
297 else { /* no so dont do tilt stuff */
298 m_maxAzimuth = m_maxAltitude = 0;
303 m_tablet = fpWTOpen( m_hWnd, &lc, TRUE );
305 m_tabletData = new GHOST_TabletData();
306 m_tabletData->Active = GHOST_kTabletModeNone;
314 GHOST_WindowWin32::~GHOST_WindowWin32()
317 GHOST_WIN32_WTClose fpWTClose = ( GHOST_WIN32_WTClose ) ::GetProcAddress( m_wintab, "WTClose" );
326 if (m_customCursor) {
327 DestroyCursor(m_customCursor);
328 m_customCursor = NULL;
331 ::wglMakeCurrent(NULL, NULL);
332 m_multisampleEnabled = GHOST_kFailure;
334 setDrawingContextType(GHOST_kDrawingContextTypeNone);
335 if (m_hDC && m_hDC != s_firstHDC) {
336 ::ReleaseDC(m_hWnd, m_hDC);
340 m_dropTarget->Release(); // frees itself.
341 ::DestroyWindow(m_hWnd);
346 GHOST_Window *GHOST_WindowWin32::getNextWindow()
351 bool GHOST_WindowWin32::getValid() const
356 HWND GHOST_WindowWin32::getHWND() const
361 void GHOST_WindowWin32::setTitle(const STR_String& title)
363 ::SetWindowText(m_hWnd, title);
367 void GHOST_WindowWin32::getTitle(STR_String& title) const
369 char buf[s_maxTitleLength];
370 ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
371 STR_String temp (buf);
376 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const
379 ::GetWindowRect(m_hWnd, &rect);
380 bounds.m_b = rect.bottom;
381 bounds.m_l = rect.left;
382 bounds.m_r = rect.right;
383 bounds.m_t = rect.top;
387 void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const
390 GHOST_TWindowState state= this->getState();
391 LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
392 int sm_cysizeframe = GetSystemMetrics(SM_CYSIZEFRAME);
393 ::GetWindowRect(m_hWnd, &rect);
395 if((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE)) {
396 if(state==GHOST_kWindowStateMaximized) {
397 // in maximized state we don't have borders on the window
398 bounds.m_b = rect.bottom-GetSystemMetrics(SM_CYCAPTION)- sm_cysizeframe*2;
399 bounds.m_l = rect.left + sm_cysizeframe;
400 bounds.m_r = rect.right - sm_cysizeframe;
401 bounds.m_t = rect.top;
403 bounds.m_b = rect.bottom-GetSystemMetrics(SM_CYCAPTION)-sm_cysizeframe*2;
404 bounds.m_l = rect.left;
405 bounds.m_r = rect.right-sm_cysizeframe*2;
406 bounds.m_t = rect.top;
409 ::GetWindowRect(m_hWnd, &rect);
410 bounds.m_b = rect.bottom;
411 bounds.m_l = rect.left;
412 bounds.m_r = rect.right;
413 bounds.m_t = rect.top;
418 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
420 GHOST_TSuccess success;
421 GHOST_Rect cBnds, wBnds;
422 getClientBounds(cBnds);
423 if (cBnds.getWidth() != (GHOST_TInt32)width) {
424 getWindowBounds(wBnds);
425 int cx = wBnds.getWidth() + width - cBnds.getWidth();
426 int cy = wBnds.getHeight();
427 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
428 GHOST_kSuccess : GHOST_kFailure;
431 success = GHOST_kSuccess;
437 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
439 GHOST_TSuccess success;
440 GHOST_Rect cBnds, wBnds;
441 getClientBounds(cBnds);
442 if (cBnds.getHeight() != (GHOST_TInt32)height) {
443 getWindowBounds(wBnds);
444 int cx = wBnds.getWidth();
445 int cy = wBnds.getHeight() + height - cBnds.getHeight();
446 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
447 GHOST_kSuccess : GHOST_kFailure;
450 success = GHOST_kSuccess;
456 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
458 GHOST_TSuccess success;
459 GHOST_Rect cBnds, wBnds;
460 getClientBounds(cBnds);
461 if ((cBnds.getWidth() != (GHOST_TInt32)width) || (cBnds.getHeight() != (GHOST_TInt32)height)) {
462 getWindowBounds(wBnds);
463 int cx = wBnds.getWidth() + width - cBnds.getWidth();
464 int cy = wBnds.getHeight() + height - cBnds.getHeight();
465 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
466 GHOST_kSuccess : GHOST_kFailure;
469 success = GHOST_kSuccess;
475 GHOST_TWindowState GHOST_WindowWin32::getState() const
477 GHOST_TWindowState state;
478 if (::IsIconic(m_hWnd)) {
479 state = GHOST_kWindowStateMinimized;
481 else if (::IsZoomed(m_hWnd)) {
482 LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
483 if((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE))
484 state = GHOST_kWindowStateMaximized;
486 state = GHOST_kWindowStateFullScreen;
489 state = GHOST_kWindowStateNormal;
495 void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
497 POINT point = { inX, inY };
498 ::ScreenToClient(m_hWnd, &point);
504 void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
506 POINT point = { inX, inY };
507 ::ClientToScreen(m_hWnd, &point);
513 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
515 GHOST_TWindowState curstate = getState();
517 wp.length = sizeof(WINDOWPLACEMENT);
518 ::GetWindowPlacement(m_hWnd, &wp);
520 if (state == GHOST_kWindowStateNormal)
521 state = m_normal_state;
523 case GHOST_kWindowStateMinimized:
524 wp.showCmd = SW_SHOWMINIMIZED;
526 case GHOST_kWindowStateMaximized:
527 ShowWindow(m_hWnd, SW_HIDE);
528 wp.showCmd = SW_SHOWMAXIMIZED;
529 SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
531 case GHOST_kWindowStateFullScreen:
532 if (curstate != state && curstate != GHOST_kWindowStateMinimized)
533 m_normal_state = curstate;
534 wp.showCmd = SW_SHOWMAXIMIZED;
535 wp.ptMaxPosition.x = 0;
536 wp.ptMaxPosition.y = 0;
537 SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
539 case GHOST_kWindowStateNormal:
541 ShowWindow(m_hWnd, SW_HIDE);
542 wp.showCmd = SW_SHOWNORMAL;
543 SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
546 return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
550 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
552 HWND hWndInsertAfter = order == GHOST_kWindowOrderTop ? HWND_TOP : HWND_BOTTOM;
553 return ::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
557 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
559 return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
563 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
565 GHOST_TSuccess success;
566 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
567 if (m_hDC && m_hGlRc) {
568 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
571 success = GHOST_kFailure;
575 success = GHOST_kSuccess;
581 GHOST_TSuccess GHOST_WindowWin32::invalidate()
583 GHOST_TSuccess success;
585 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
588 success = GHOST_kFailure;
593 GHOST_TSuccess GHOST_WindowWin32::initMultisample(PIXELFORMATDESCRIPTOR pfd)
598 HDC hDC = GetDC(getHWND());
599 float fAttributes[] = {0, 0};
601 // The attributes to look for
602 int iAttributes[] = {
603 WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
604 WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
605 WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
606 WGL_COLOR_BITS_ARB, pfd.cColorBits,
607 WGL_DEPTH_BITS_ARB, pfd.cDepthBits,
608 WGL_STENCIL_BITS_ARB, pfd.cStencilBits,
609 WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
610 WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
611 WGL_SAMPLES_ARB, m_multisample,
616 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
618 if (!wglChoosePixelFormatARB)
620 m_multisampleEnabled = GHOST_kFailure;
621 return GHOST_kFailure;
624 // See if the format is valid
625 success = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
627 if (success && numFormats >= 1)
629 m_multisampleEnabled = GHOST_kSuccess;
630 m_msPixelFormat = pixelFormat;
631 return GHOST_kSuccess;
635 // See if any formats are supported
636 while (!success && iAttributes[19] != 0)
638 iAttributes[19] /= 2;
640 success = wglChoosePixelFormatARB(m_hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
642 if (success && numFormats >= 1)
644 m_multisampleEnabled = GHOST_kSuccess;
645 m_msPixelFormat = pixelFormat;
646 return GHOST_kSuccess;
649 success = GHOST_kFailure;
653 // No available pixel format...
654 return GHOST_kFailure;
657 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
659 GHOST_TSuccess success;
661 case GHOST_kDrawingContextTypeOpenGL:
663 // If this window has multisample enabled, use the supplied format
664 if (m_multisampleEnabled)
666 if (SetPixelFormat(m_hDC, m_msPixelFormat, &sPreferredFormat)==FALSE)
668 success = GHOST_kFailure;
672 // Create the context
673 m_hGlRc = ::wglCreateContext(m_hDC);
676 ::wglCopyContext(s_firsthGLRc, m_hGlRc, GL_ALL_ATTRIB_BITS);
677 wglShareLists(s_firsthGLRc, m_hGlRc);
679 s_firsthGLRc = m_hGlRc;
682 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
685 printf("Failed to get a context....\n");
686 success = GHOST_kFailure;
692 sPreferredFormat.dwFlags |= PFD_STEREO;
694 // Attempt to match device context pixel format to the preferred format
695 int iPixelFormat = EnumPixelFormats(m_hDC);
696 if (iPixelFormat == 0) {
697 success = GHOST_kFailure;
700 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
701 success = GHOST_kFailure;
704 // For debugging only: retrieve the pixel format chosen
705 PIXELFORMATDESCRIPTOR preferredFormat;
706 ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat);
708 // Create the context
709 m_hGlRc = ::wglCreateContext(m_hDC);
712 ::wglShareLists(s_firsthGLRc, m_hGlRc);
714 s_firsthGLRc = m_hGlRc;
717 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
720 printf("Failed to get a context....\n");
721 success = GHOST_kFailure;
724 // Attempt to enable multisample
725 if (m_multisample && WGL_ARB_multisample && !m_multisampleEnabled)
727 success = initMultisample(preferredFormat);
732 // Make sure we don't screw up the context
733 m_drawingContextType = GHOST_kDrawingContextTypeOpenGL;
734 removeDrawingContext();
736 // Create a new window
737 GHOST_TWindowState new_state = getState();
739 m_nextWindow = new GHOST_WindowWin32((GHOST_SystemWin32*)GHOST_ISystem::getSystem(),
749 m_multisampleEnabled,
752 // Return failure so we can trash this window.
753 success = GHOST_kFailure;
762 case GHOST_kDrawingContextTypeNone:
763 success = GHOST_kSuccess;
767 success = GHOST_kFailure;
772 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
774 GHOST_TSuccess success;
775 switch (m_drawingContextType) {
776 case GHOST_kDrawingContextTypeOpenGL:
778 bool first = m_hGlRc == s_firsthGLRc;
779 success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
786 success = GHOST_kFailure;
789 case GHOST_kDrawingContextTypeNone:
790 success = GHOST_kSuccess;
793 success = GHOST_kFailure;
798 void GHOST_WindowWin32::lostMouseCapture()
800 if (m_hasMouseCaptured) {
801 m_hasMouseCaptured = false;
802 m_nPressedButtons = 0;
806 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
809 if (!m_hasMouseCaptured) {
810 ::SetCapture(m_hWnd);
811 m_hasMouseCaptured = true;
815 if (m_nPressedButtons) {
817 if (!m_nPressedButtons) {
819 m_hasMouseCaptured = false;
826 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
829 while (::ShowCursor(FALSE) >= 0);
832 while (::ShowCursor(TRUE) < 0);
835 if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
836 ::SetCursor( m_customCursor );
838 // Convert GHOST cursor to Windows OEM cursor
842 case GHOST_kStandardCursorDefault: id = IDC_ARROW; break;
843 case GHOST_kStandardCursorRightArrow: id = IDC_ARROW; break;
844 case GHOST_kStandardCursorLeftArrow: id = IDC_ARROW; break;
845 case GHOST_kStandardCursorInfo: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
846 case GHOST_kStandardCursorDestroy: id = IDC_NO; break; // Slashed circle
847 case GHOST_kStandardCursorHelp: id = IDC_HELP; break; // Arrow and question mark
848 case GHOST_kStandardCursorCycle: id = IDC_NO; break; // Slashed circle
849 case GHOST_kStandardCursorSpray: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
850 case GHOST_kStandardCursorWait: id = IDC_WAIT; break; // Hourglass
851 case GHOST_kStandardCursorText: id = IDC_IBEAM; break; // I-beam
852 case GHOST_kStandardCursorCrosshair: id = IDC_CROSS; break; // Crosshair
853 case GHOST_kStandardCursorUpDown: id = IDC_SIZENS; break; // Double-pointed arrow pointing north and south
854 case GHOST_kStandardCursorLeftRight: id = IDC_SIZEWE; break; // Double-pointed arrow pointing west and east
855 case GHOST_kStandardCursorTopSide: id = IDC_UPARROW; break; // Vertical arrow
856 case GHOST_kStandardCursorBottomSide: id = IDC_SIZENS; break;
857 case GHOST_kStandardCursorLeftSide: id = IDC_SIZEWE; break;
858 case GHOST_kStandardCursorTopLeftCorner: id = IDC_SIZENWSE; break;
859 case GHOST_kStandardCursorTopRightCorner: id = IDC_SIZENESW; break;
860 case GHOST_kStandardCursorBottomRightCorner: id = IDC_SIZENWSE; break;
861 case GHOST_kStandardCursorBottomLeftCorner: id = IDC_SIZENESW; break;
862 case GHOST_kStandardCursorPencil: id = IDC_ARROW; break;
863 case GHOST_kStandardCursorCopy: id = IDC_ARROW; break;
869 ::SetCursor(::LoadCursor(0, id));
874 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
876 if (::GetForegroundWindow() == m_hWnd) {
877 loadCursor(visible, getCursorShape());
880 return GHOST_kSuccess;
883 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
885 if(mode != GHOST_kGrabDisable) {
886 if(mode != GHOST_kGrabNormal) {
887 m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
888 setCursorGrabAccum(0, 0);
890 if(mode == GHOST_kGrabHide)
891 setWindowCursorVisibility(false);
893 registerMouseClickEvent(true);
896 if (m_cursorGrab==GHOST_kGrabHide) {
897 m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
898 setWindowCursorVisibility(true);
900 if(m_cursorGrab != GHOST_kGrabNormal) {
901 /* use to generate a mouse move event, otherwise the last event
902 * blender gets can be outside the screen causing menus not to show
903 * properly unless the user moves the mouse */
905 m_system->getCursorPosition(pos[0], pos[1]);
906 m_system->setCursorPosition(pos[0], pos[1]);
909 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
910 setCursorGrabAccum(0, 0);
911 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
912 registerMouseClickEvent(false);
915 return GHOST_kSuccess;
918 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
920 if (m_customCursor) {
921 DestroyCursor(m_customCursor);
922 m_customCursor = NULL;
925 if (::GetForegroundWindow() == m_hWnd) {
926 loadCursor(getCursorVisibility(), cursorShape);
929 return GHOST_kSuccess;
932 void GHOST_WindowWin32::processWin32TabletInitEvent()
935 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
937 // let's see if we can initialize tablet here
938 /* check if WinTab available. */
940 AXIS Pressure, Orientation[3]; /* The maximum tablet size */
942 BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
944 m_maxPressure = Pressure.axMax;
948 BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
950 /* does the tablet support azimuth ([0]) and altitude ([1]) */
951 if (Orientation[0].axResolution && Orientation[1].axResolution) {
952 m_maxAzimuth = Orientation[0].axMax;
953 m_maxAltitude = Orientation[1].axMax;
955 else { /* no so dont do tilt stuff */
956 m_maxAzimuth = m_maxAltitude = 0;
960 m_tabletData->Active = GHOST_kTabletModeNone;
965 void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
969 GHOST_WIN32_WTPacket fpWTPacket = ( GHOST_WIN32_WTPacket ) ::GetProcAddress( m_wintab, "WTPacket" );
971 if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
973 switch (pkt.pkCursor) {
974 case 0: /* first device */
975 case 3: /* second device */
976 m_tabletData->Active = GHOST_kTabletModeNone; /* puck - not yet supported */
980 m_tabletData->Active = GHOST_kTabletModeStylus; /* stylus */
984 m_tabletData->Active = GHOST_kTabletModeEraser; /* eraser */
987 if (m_maxPressure > 0) {
988 m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
990 m_tabletData->Pressure = 1.0f;
993 if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
994 ORIENTATION ort = pkt.pkOrientation;
996 float altRad, azmRad; /* in radians */
999 from the wintab spec:
1000 orAzimuth Specifies the clockwise rotation of the
1001 cursor about the z axis through a full circular range.
1003 orAltitude Specifies the angle with the x-y plane
1004 through a signed, semicircular range. Positive values
1005 specify an angle upward toward the positive z axis;
1006 negative values specify an angle downward toward the negative z axis.
1008 wintab.h defines .orAltitude as a UINT but documents .orAltitude
1009 as positive for upward angles and negative for downward angles.
1010 WACOM uses negative altitude values to show that the pen is inverted;
1011 therefore we cast .orAltitude as an (int) and then use the absolute value.
1014 /* convert raw fixed point data to radians */
1015 altRad = (float)((fabs((float)ort.orAltitude)/(float)m_maxAltitude) * M_PI/2.0);
1016 azmRad = (float)(((float)ort.orAzimuth/(float)m_maxAzimuth) * M_PI*2.0);
1018 /* find length of the stylus' projected vector on the XY plane */
1019 vecLen = cos(altRad);
1021 /* from there calculate X and Y components based on azimuth */
1022 m_tabletData->Xtilt = sin(azmRad) * vecLen;
1023 m_tabletData->Ytilt = (float)(sin(M_PI/2.0 - azmRad) * vecLen);
1026 m_tabletData->Xtilt = 0.0f;
1027 m_tabletData->Ytilt = 0.0f;
1035 /** Reverse the bits in a GHOST_TUns8 */
1036 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1038 ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
1039 ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
1040 ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
1044 /** Reverse the bits in a GHOST_TUns16 */
1045 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1047 shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
1048 shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
1049 shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
1050 shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
1053 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
1054 GHOST_TUns8 mask[16][2], int hotX, int hotY)
1056 return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask,
1057 16, 16, hotX, hotY, 0, 1);
1060 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap,
1061 GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY,
1062 int fg_color, int bg_color)
1064 GHOST_TUns32 andData[32];
1065 GHOST_TUns32 xorData[32];
1066 GHOST_TUns32 fullBitRow, fullMaskRow;
1069 cols=sizeX/8; /* Num of whole bytes per row (width of bm/mask) */
1070 if (sizeX%8) cols++;
1072 if (m_customCursor) {
1073 DestroyCursor(m_customCursor);
1074 m_customCursor = NULL;
1077 memset(&andData, 0xFF, sizeof(andData));
1078 memset(&xorData, 0, sizeof(xorData));
1080 for (y=0; y<sizeY; y++) {
1083 for (x=cols-1; x>=0; x--){
1086 fullBitRow |= uns8ReverseBits(bitmap[cols*y + x]);
1087 fullMaskRow |= uns8ReverseBits( mask[cols*y + x]);
1089 xorData[y]= fullBitRow & fullMaskRow;
1090 andData[y]= ~fullMaskRow;
1093 m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
1094 if (!m_customCursor) {
1095 return GHOST_kFailure;
1098 if (::GetForegroundWindow() == m_hWnd) {
1099 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1102 return GHOST_kSuccess;
1106 /* Ron Fosner's code for weighting pixel formats and forcing software.
1107 See http://www.opengl.org/resources/faq/technical/weight.cpp */
1109 static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) {
1112 /* assume desktop color depth is 32 bits per pixel */
1114 /* cull unusable pixel formats */
1115 /* if no formats can be found, can we determine why it was rejected? */
1116 if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ||
1117 !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
1118 !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */
1119 ( pfd.cDepthBits <= 8 ) ||
1120 !(pfd.iPixelType == PFD_TYPE_RGBA))
1123 weight = 1; /* it's usable */
1125 /* the bigger the depth buffer the better */
1126 /* give no weight to a 16-bit depth buffer, because those are crap */
1127 weight += pfd.cDepthBits - 16;
1129 weight += pfd.cColorBits - 8;
1131 /* want swap copy capability -- it matters a lot */
1132 if(pfd.dwFlags & PFD_SWAP_COPY) weight += 16;
1134 /* but if it's a generic (not accelerated) view, it's really bad */
1135 if(pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10;
1140 /* A modification of Ron Fosner's replacement for ChoosePixelFormat */
1141 /* returns 0 on error, else returns the pixel format number to be used */
1142 static int EnumPixelFormats(HDC hdc) {
1144 int i, n, w, weight = 0;
1145 PIXELFORMATDESCRIPTOR pfd;
1147 /* we need a device context to do anything */
1150 iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */
1152 /* obtain detailed information about
1153 the device context's first pixel format */
1154 n = 1+::DescribePixelFormat(hdc, iPixelFormat,
1155 sizeof(PIXELFORMATDESCRIPTOR), &pfd);
1157 /* choose a pixel format using the useless Windows function in case
1158 we come up empty handed */
1159 iPixelFormat = ::ChoosePixelFormat( hdc, &sPreferredFormat );
1161 if(!iPixelFormat) return 0; /* couldn't find one to use */
1163 for(i=1; i<=n; i++) { /* not the idiom, but it's right */
1164 ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
1165 w = WeightPixelFormat(pfd);
1166 // be strict on stereo
1167 if (!((sPreferredFormat.dwFlags ^ pfd.dwFlags) & PFD_STEREO)) {
1175 // we could find the correct stereo setting, just find any suitable format
1176 for(i=1; i<=n; i++) { /* not the idiom, but it's right */
1177 ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
1178 w = WeightPixelFormat(pfd);
1185 return iPixelFormat;