3 * ***** BEGIN GPL/BL DUAL 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. The Blender
9 * Foundation also sells licenses for use in proprietary software under
10 * the Blender License. See http://www.blender.org/BL/ for information
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23 * All rights reserved.
25 * The Original Code is: all of this file.
27 * Contributor(s): none yet.
29 * ***** END GPL/BL DUAL LICENSE BLOCK *****
35 * Copyright (C) 2001 NaN Technologies B.V.
36 * @author Maarten Gribnau
45 #include "GHOST_WindowWin32.h"
49 LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
50 const int GHOST_WindowWin32::s_maxTitleLength = 128;
51 HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
53 static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd);
54 static int EnumPixelFormats(HDC hdc);
57 * Color and depth bit values are not to be trusted.
58 * For instance, on TNT2:
59 * When the screen color depth is set to 16 bit, we get 5 color bits
61 * When the screen color depth is set to 32 bit, we get 8 color bits
63 * Just to be safe, we request high waulity settings.
65 static PIXELFORMATDESCRIPTOR sPreferredFormat = {
66 sizeof(PIXELFORMATDESCRIPTOR), /* size */
70 PFD_SWAP_COPY | /* support swap copy */
71 PFD_DOUBLEBUFFER, /* support double-buffering */
72 PFD_TYPE_RGBA, /* color type */
73 32, /* prefered color depth */
74 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
75 0, /* no alpha buffer */
76 0, /* alpha bits (ignored) */
77 0, /* no accumulation buffer */
78 0, 0, 0, 0, /* accum bits (ignored) */
79 32, /* depth buffer */
80 0, /* no stencil buffer */
81 0, /* no auxiliary buffers */
82 PFD_MAIN_PLANE, /* main layer */
84 0, 0, 0 /* no layer, visible, damage masks */
87 GHOST_WindowWin32::GHOST_WindowWin32(
88 const STR_String& title,
93 GHOST_TWindowState state,
94 GHOST_TDrawingContextType type,
95 const bool stereoVisual)
97 GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone,
101 m_hasMouseCaptured(false),
102 m_nPressedButtons(0),
109 if (state != GHOST_kWindowStateFullScreen) {
110 /* Convert client size into window size */
111 width += GetSystemMetrics(SM_CXSIZEFRAME)*2;
112 height += GetSystemMetrics(SM_CYSIZEFRAME)*2 + GetSystemMetrics(SM_CYCAPTION);
114 m_hWnd = ::CreateWindow(
115 s_windowClassName, // pointer to registered class name
116 title, // pointer to window name
117 WS_OVERLAPPEDWINDOW, // window style
118 left, // horizontal position of window
119 top, // vertical position of window
120 width, // window width
121 height, // window height
122 0, // handle to parent or owner window
123 0, // handle to menu or child-window identifier
124 ::GetModuleHandle(0), // handle to application instance
125 0); // pointer to window-creation data
128 m_hWnd = ::CreateWindow(
129 s_windowClassName, // pointer to registered class name
130 title, // pointer to window name
131 WS_POPUP | WS_MAXIMIZE, // window style
132 left, // horizontal position of window
133 top, // vertical position of window
134 width, // window width
135 height, // window height
136 0, // handle to parent or owner window
137 0, // handle to menu or child-window identifier
138 ::GetModuleHandle(0), // handle to application instance
139 0); // pointer to window-creation data
142 // Store a pointer to this class in the window structure
143 LONG result = ::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
145 // Store the device context
146 m_hDC = ::GetDC(m_hWnd);
151 case GHOST_kWindowStateMaximized:
152 nCmdShow = SW_SHOWMAXIMIZED;
154 case GHOST_kWindowStateMinimized:
155 nCmdShow = SW_SHOWMINIMIZED;
157 case GHOST_kWindowStateNormal:
159 nCmdShow = SW_SHOWNORMAL;
162 setDrawingContextType(type);
163 ::ShowWindow(m_hWnd, nCmdShow);
164 // Force an initial paint of the window
165 ::UpdateWindow(m_hWnd);
168 m_wintab = ::LoadLibrary("Wintab32.dll");
170 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
171 GHOST_WIN32_WTOpen fpWTOpen = ( GHOST_WIN32_WTOpen ) ::GetProcAddress( m_wintab, "WTOpenA" );
173 // let's see if we can initialize tablet here
174 /* check if WinTab available. */
175 if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
176 // Now init the tablet
178 AXIS TabletX, TabletY, Pressure, Orientation[3]; /* The maximum tablet size, pressure and orientation (tilt) */
180 // Open a Wintab context
182 // Get default context information
183 fpWTInfo( WTI_DEFCONTEXT, 0, &lc );
186 lc.lcPktData = PACKETDATA;
187 lc.lcPktMode = PACKETMODE;
188 lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
190 /* Set the entire tablet as active */
191 fpWTInfo(WTI_DEVICES,DVC_X,&TabletX);
192 fpWTInfo(WTI_DEVICES,DVC_Y,&TabletY);
194 /* get the max pressure, to divide into a float */
195 BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
197 m_maxPressure = Pressure.axMax;
201 /* get the max tilt axes, to divide into floats */
202 BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
204 /* does the tablet support azimuth ([0]) and altitude ([1]) */
205 if (Orientation[0].axResolution && Orientation[1].axResolution) {
206 /* all this assumes the minimum is 0 */
207 m_maxAzimuth = Orientation[0].axMax;
208 m_maxAltitude = Orientation[1].axMax;
210 else { /* no so dont do tilt stuff */
211 m_maxAzimuth = m_maxAltitude = 0;
217 lc.lcInExtX = TabletX.axMax;
218 lc.lcInExtY = TabletY.axMax;
221 m_tablet = fpWTOpen( m_hWnd, &lc, TRUE );
223 m_tabletData = new GHOST_TabletData();
224 m_tabletData->Active = 0;
232 GHOST_WindowWin32::~GHOST_WindowWin32()
235 GHOST_WIN32_WTClose fpWTClose = ( GHOST_WIN32_WTClose ) ::GetProcAddress( m_wintab, "WTClose" );
244 if (m_customCursor) {
245 DestroyCursor(m_customCursor);
246 m_customCursor = NULL;
249 setDrawingContextType(GHOST_kDrawingContextTypeNone);
251 ::ReleaseDC(m_hWnd, m_hDC);
255 ::DestroyWindow(m_hWnd);
260 bool GHOST_WindowWin32::getValid() const
266 void GHOST_WindowWin32::setTitle(const STR_String& title)
268 ::SetWindowText(m_hWnd, title);
272 void GHOST_WindowWin32::getTitle(STR_String& title) const
274 char buf[s_maxTitleLength];
275 ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
276 STR_String temp (buf);
281 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const
284 ::GetWindowRect(m_hWnd, &rect);
285 bounds.m_b = rect.bottom;
286 bounds.m_l = rect.left;
287 bounds.m_r = rect.right;
288 bounds.m_t = rect.top;
292 void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const
295 ::GetClientRect(m_hWnd, &rect);
296 bounds.m_b = rect.bottom;
297 bounds.m_l = rect.left;
298 bounds.m_r = rect.right;
299 bounds.m_t = rect.top;
303 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
305 GHOST_TSuccess success;
306 GHOST_Rect cBnds, wBnds;
307 getClientBounds(cBnds);
308 if (cBnds.getWidth() != width) {
309 getWindowBounds(wBnds);
310 int cx = wBnds.getWidth() + width - cBnds.getWidth();
311 int cy = wBnds.getHeight();
312 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
313 GHOST_kSuccess : GHOST_kFailure;
316 success = GHOST_kSuccess;
322 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
324 GHOST_TSuccess success;
325 GHOST_Rect cBnds, wBnds;
326 getClientBounds(cBnds);
327 if (cBnds.getHeight() != height) {
328 getWindowBounds(wBnds);
329 int cx = wBnds.getWidth();
330 int cy = wBnds.getHeight() + height - cBnds.getHeight();
331 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
332 GHOST_kSuccess : GHOST_kFailure;
335 success = GHOST_kSuccess;
341 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
343 GHOST_TSuccess success;
344 GHOST_Rect cBnds, wBnds;
345 getClientBounds(cBnds);
346 if ((cBnds.getWidth() != width) || (cBnds.getHeight() != height)) {
347 getWindowBounds(wBnds);
348 int cx = wBnds.getWidth() + width - cBnds.getWidth();
349 int cy = wBnds.getHeight() + height - cBnds.getHeight();
350 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
351 GHOST_kSuccess : GHOST_kFailure;
354 success = GHOST_kSuccess;
360 GHOST_TWindowState GHOST_WindowWin32::getState() const
362 GHOST_TWindowState state;
363 if (::IsIconic(m_hWnd)) {
364 state = GHOST_kWindowStateMinimized;
366 else if (::IsZoomed(m_hWnd)) {
367 state = GHOST_kWindowStateMaximized;
370 state = GHOST_kWindowStateNormal;
376 void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
378 POINT point = { inX, inY };
379 ::ScreenToClient(m_hWnd, &point);
385 void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
387 POINT point = { inX, inY };
388 ::ClientToScreen(m_hWnd, &point);
394 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
397 wp.length = sizeof(WINDOWPLACEMENT);
398 ::GetWindowPlacement(m_hWnd, &wp);
400 case GHOST_kWindowStateMinimized:
401 wp.showCmd = SW_SHOWMINIMIZED;
403 case GHOST_kWindowStateMaximized:
404 ShowWindow(m_hWnd, SW_HIDE); //fe. HACK!
405 //Solves redraw problems when switching from fullscreen to normal.
407 wp.showCmd = SW_SHOWMAXIMIZED;
408 SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
410 case GHOST_kWindowStateFullScreen:
411 wp.showCmd = SW_SHOWMAXIMIZED;
412 SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
414 case GHOST_kWindowStateNormal:
416 wp.showCmd = SW_SHOWNORMAL;
419 return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
423 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
425 HWND hWndInsertAfter = order == GHOST_kWindowOrderTop ? HWND_TOP : HWND_BOTTOM;
426 return ::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
430 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
432 // adding a glFinish() here is to prevent Geforce in 'full scene antialias' mode
433 // from antialising the Blender window. Officially a swapbuffers does a glFinish
434 // itself, so this feels really like a hack... but it won't harm. (ton)
436 return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
440 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
442 GHOST_TSuccess success;
443 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
444 if (m_hDC && m_hGlRc) {
445 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
448 success = GHOST_kFailure;
452 success = GHOST_kSuccess;
458 GHOST_TSuccess GHOST_WindowWin32::invalidate()
460 GHOST_TSuccess success;
462 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
465 success = GHOST_kFailure;
471 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
473 GHOST_TSuccess success;
475 case GHOST_kDrawingContextTypeOpenGL:
478 sPreferredFormat.dwFlags |= PFD_STEREO;
480 // Attempt to match device context pixel format to the preferred format
481 int iPixelFormat = EnumPixelFormats(m_hDC);
482 if (iPixelFormat == 0) {
483 success = GHOST_kFailure;
486 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
487 success = GHOST_kFailure;
490 // For debugging only: retrieve the pixel format chosen
491 PIXELFORMATDESCRIPTOR preferredFormat;
492 ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat);
493 // Create the context
494 m_hGlRc = ::wglCreateContext(m_hDC);
497 wglShareLists(s_firsthGLRc, m_hGlRc);
499 s_firsthGLRc = m_hGlRc;
502 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
505 success = GHOST_kFailure;
510 case GHOST_kDrawingContextTypeNone:
511 success = GHOST_kSuccess;
515 success = GHOST_kFailure;
521 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
523 GHOST_TSuccess success;
524 switch (m_drawingContextType) {
525 case GHOST_kDrawingContextTypeOpenGL:
527 success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
528 if (m_hGlRc == s_firsthGLRc) {
534 success = GHOST_kFailure;
537 case GHOST_kDrawingContextTypeNone:
538 success = GHOST_kSuccess;
541 success = GHOST_kFailure;
546 void GHOST_WindowWin32::lostMouseCapture()
548 if (m_hasMouseCaptured) {
549 m_hasMouseCaptured = false;
550 m_nPressedButtons = 0;
554 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
557 if (!m_hasMouseCaptured) {
558 ::SetCapture(m_hWnd);
559 m_hasMouseCaptured = true;
563 if (m_nPressedButtons) {
565 if (!m_nPressedButtons) {
567 m_hasMouseCaptured = false;
574 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
577 while (::ShowCursor(FALSE) >= 0);
580 while (::ShowCursor(TRUE) < 0);
583 if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
584 ::SetCursor( m_customCursor );
586 // Convert GHOST cursor to Windows OEM cursor
590 case GHOST_kStandardCursorDefault: id = IDC_ARROW; break;
591 case GHOST_kStandardCursorRightArrow: id = IDC_ARROW; break;
592 case GHOST_kStandardCursorLeftArrow: id = IDC_ARROW; break;
593 case GHOST_kStandardCursorInfo: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
594 case GHOST_kStandardCursorDestroy: id = IDC_NO; break; // Slashed circle
595 case GHOST_kStandardCursorHelp: id = IDC_HELP; break; // Arrow and question mark
596 case GHOST_kStandardCursorCycle: id = IDC_NO; break; // Slashed circle
597 case GHOST_kStandardCursorSpray: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
598 case GHOST_kStandardCursorWait: id = IDC_WAIT; break; // Hourglass
599 case GHOST_kStandardCursorText: id = IDC_IBEAM; break; // I-beam
600 case GHOST_kStandardCursorCrosshair: id = IDC_CROSS; break; // Crosshair
601 case GHOST_kStandardCursorUpDown: id = IDC_SIZENS; break; // Double-pointed arrow pointing north and south
602 case GHOST_kStandardCursorLeftRight: id = IDC_SIZEWE; break; // Double-pointed arrow pointing west and east
603 case GHOST_kStandardCursorTopSide: id = IDC_UPARROW; break; // Vertical arrow
604 case GHOST_kStandardCursorBottomSide: id = IDC_SIZENS; break;
605 case GHOST_kStandardCursorLeftSide: id = IDC_SIZEWE; break;
606 case GHOST_kStandardCursorTopLeftCorner: id = IDC_SIZENWSE; break;
607 case GHOST_kStandardCursorTopRightCorner: id = IDC_SIZENESW; break;
608 case GHOST_kStandardCursorBottomRightCorner: id = IDC_SIZENWSE; break;
609 case GHOST_kStandardCursorBottomLeftCorner: id = IDC_SIZENESW; break;
610 case GHOST_kStandardCursorPencil: id = IDC_ARROW; break;
616 HCURSOR hCursor = ::SetCursor(::LoadCursor(0, id));
621 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
623 if (::GetForegroundWindow() == m_hWnd) {
624 loadCursor(visible, getCursorShape());
627 return GHOST_kSuccess;
630 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
632 if (m_customCursor) {
633 DestroyCursor(m_customCursor);
634 m_customCursor = NULL;
637 if (::GetForegroundWindow() == m_hWnd) {
638 loadCursor(getCursorVisibility(), cursorShape);
641 return GHOST_kSuccess;
643 void GHOST_WindowWin32::processWin32TabletInitEvent()
646 GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
648 // let's see if we can initialize tablet here
649 /* check if WinTab available. */
651 AXIS Pressure, Orientation[3]; /* The maximum tablet size */
653 BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
655 m_maxPressure = Pressure.axMax;
659 BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
661 /* does the tablet support azimuth ([0]) and altitude ([1]) */
662 if (Orientation[0].axResolution && Orientation[1].axResolution) {
663 m_maxAzimuth = Orientation[0].axMax;
664 m_maxAltitude = Orientation[1].axMax;
666 else { /* no so dont do tilt stuff */
667 m_maxAzimuth = m_maxAltitude = 0;
671 m_tabletData->Active = 0;
676 void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
680 GHOST_WIN32_WTPacket fpWTPacket = ( GHOST_WIN32_WTPacket ) ::GetProcAddress( m_wintab, "WTPacket" );
682 if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
684 switch (pkt.pkCursor) {
685 case 0: /* first device */
686 case 3: /* second device */
687 m_tabletData->Active = 0; /* puck - not yet supported */
691 m_tabletData->Active = 1; /* stylus */
695 m_tabletData->Active = 2; /* eraser */
698 if (m_maxPressure > 0) {
699 m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
701 m_tabletData->Pressure = 1.0f;
704 if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
705 ORIENTATION ort = pkt.pkOrientation;
707 float altRad, azmRad; /* in radians */
710 from the wintab spec:
711 orAzimuth Specifies the clockwise rotation of the
712 cursor about the z axis through a full circular range.
714 orAltitude Specifies the angle with the x-y plane
715 through a signed, semicircular range. Positive values
716 specify an angle upward toward the positive z axis;
717 negative values specify an angle downward toward the negative z axis.
719 wintab.h defines .orAltitude as a UINT but documents .orAltitude
720 as positive for upward angles and negative for downward angles.
721 WACOM uses negative altitude values to show that the pen is inverted;
722 therefore we cast .orAltitude as an (int) and then use the absolute value.
725 /* convert raw fixed point data to radians */
726 altRad = (fabs((float)ort.orAltitude)/(float)m_maxAltitude) * M_PI/2.0;
727 azmRad = ((float)ort.orAzimuth/(float)m_maxAzimuth) * M_PI*2.0;
729 /* find length of the stylus' projected vector on the XY plane */
730 vecLen = cos(altRad);
732 /* from there calculate X and Y components based on azimuth */
733 m_tabletData->Xtilt = sin(azmRad) * vecLen;
734 m_tabletData->Ytilt = sin(M_PI/2.0 - azmRad) * vecLen;
737 m_tabletData->Xtilt = 0.0f;
738 m_tabletData->Ytilt = 0.0f;
746 /** Reverse the bits in a GHOST_TUns8 */
747 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
749 ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
750 ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
751 ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
755 /** Reverse the bits in a GHOST_TUns16 */
756 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
758 shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
759 shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
760 shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
761 shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
764 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
765 GHOST_TUns8 mask[16][2], int hotX, int hotY)
767 return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask,
768 16, 16, hotX, hotY, 0, 1);
771 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap,
772 GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY,
773 int fg_color, int bg_color)
775 GHOST_TUns32 andData[32];
776 GHOST_TUns32 xorData[32];
777 GHOST_TUns32 fullBitRow, fullMaskRow;
780 cols=sizeX/8; /* Num of whole bytes per row (width of bm/mask) */
783 if (m_customCursor) {
784 DestroyCursor(m_customCursor);
785 m_customCursor = NULL;
788 memset(&andData, 0xFF, sizeof(andData));
789 memset(&xorData, 0, sizeof(xorData));
791 for (y=0; y<sizeY; y++) {
794 for (x=cols-1; x>=0; x--){
797 fullBitRow |= uns8ReverseBits(bitmap[cols*y + x]);
798 fullMaskRow |= uns8ReverseBits( mask[cols*y + x]);
800 xorData[y]= fullBitRow & fullMaskRow;
801 andData[y]= ~fullMaskRow;
804 m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
805 if (!m_customCursor) {
806 return GHOST_kFailure;
809 if (::GetForegroundWindow() == m_hWnd) {
810 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
813 return GHOST_kSuccess;
817 /* Ron Fosner's code for weighting pixel formats and forcing software.
818 See http://www.opengl.org/resources/faq/technical/weight.cpp */
820 static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) {
823 /* assume desktop color depth is 32 bits per pixel */
825 /* cull unusable pixel formats */
826 /* if no formats can be found, can we determine why it was rejected? */
827 if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ||
828 !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
829 !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */
830 ( pfd.cDepthBits <= 8 ) ||
831 !(pfd.iPixelType == PFD_TYPE_RGBA) )
834 weight = 1; /* it's usable */
836 /* the bigger the depth buffer the better */
837 /* give no weight to a 16-bit depth buffer, because those are crap */
838 weight += pfd.cDepthBits - 16;
840 weight += pfd.cColorBits - 8;
842 /* want swap copy capability -- it matters a lot */
843 if(pfd.dwFlags & PFD_SWAP_COPY) weight += 16;
845 /* but if it's a generic (not accelerated) view, it's really bad */
846 if(pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10;
851 /* A modification of Ron Fosner's replacement for ChoosePixelFormat */
852 /* returns 0 on error, else returns the pixel format number to be used */
853 static int EnumPixelFormats(HDC hdc) {
855 int i, n, w, weight = 0;
856 PIXELFORMATDESCRIPTOR pfd, pfd_fallback;
858 /* we need a device context to do anything */
861 iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */
863 /* obtain detailed information about
864 the device context's first pixel format */
865 n = 1+::DescribePixelFormat(hdc, iPixelFormat,
866 sizeof(PIXELFORMATDESCRIPTOR), &pfd);
868 /* choose a pixel format using the useless Windows function in case
869 we come up empty handed */
870 iPixelFormat = ::ChoosePixelFormat( hdc, &sPreferredFormat );
872 if(!iPixelFormat) return 0; /* couldn't find one to use */
874 for(i=1; i<=n; i++) { /* not the idiom, but it's right */
875 ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
876 w = WeightPixelFormat(pfd);