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"
48 LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
49 const int GHOST_WindowWin32::s_maxTitleLength = 128;
50 HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
53 * Color and depth bit values are not to be trusted.
54 * For instance, on TNT2:
55 * When the screen color depth is set to 16 bit, we get 5 color bits
57 * When the screen color depth is set to 32 bit, we get 8 color bits
59 * Just to be safe, we request high waulity settings.
61 static PIXELFORMATDESCRIPTOR sPreferredFormat = {
62 sizeof(PIXELFORMATDESCRIPTOR), /* size */
66 PFD_DOUBLEBUFFER, /* support double-buffering */
67 PFD_TYPE_RGBA, /* color type */
68 32, /* prefered color depth */
69 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
70 0, /* no alpha buffer */
71 0, /* alpha bits (ignored) */
72 0, /* no accumulation buffer */
73 0, 0, 0, 0, /* accum bits (ignored) */
74 32, /* depth buffer */
75 0, /* no stencil buffer */
76 0, /* no auxiliary buffers */
77 PFD_MAIN_PLANE, /* main layer */
79 0, 0, 0 /* no layer, visible, damage masks */
82 GHOST_WindowWin32::GHOST_WindowWin32(
83 const STR_String& title,
88 GHOST_TWindowState state,
89 GHOST_TDrawingContextType type,
90 const bool stereoVisual)
92 GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone,
96 m_hasMouseCaptured(false),
100 if (state != GHOST_kWindowStateFullScreen) {
101 /* Convert client size into window size */
102 width += GetSystemMetrics(SM_CXSIZEFRAME)*2;
103 height += GetSystemMetrics(SM_CYSIZEFRAME)*2 + GetSystemMetrics(SM_CYCAPTION);
105 m_hWnd = ::CreateWindow(
106 s_windowClassName, // pointer to registered class name
107 title, // pointer to window name
108 WS_OVERLAPPEDWINDOW, // window style
109 left, // horizontal position of window
110 top, // vertical position of window
111 width, // window width
112 height, // window height
113 0, // handle to parent or owner window
114 0, // handle to menu or child-window identifier
115 ::GetModuleHandle(0), // handle to application instance
116 0); // pointer to window-creation data
119 m_hWnd = ::CreateWindow(
120 s_windowClassName, // pointer to registered class name
121 title, // pointer to window name
122 WS_POPUP | WS_MAXIMIZE, // window style
123 left, // horizontal position of window
124 top, // vertical position of window
125 width, // window width
126 height, // window height
127 0, // handle to parent or owner window
128 0, // handle to menu or child-window identifier
129 ::GetModuleHandle(0), // handle to application instance
130 0); // pointer to window-creation data
133 // Store a pointer to this class in the window structure
134 LONG result = ::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
136 // Store the device context
137 m_hDC = ::GetDC(m_hWnd);
142 case GHOST_kWindowStateMaximized:
143 nCmdShow = SW_SHOWMAXIMIZED;
145 case GHOST_kWindowStateMinimized:
146 nCmdShow = SW_SHOWMINIMIZED;
148 case GHOST_kWindowStateNormal:
150 nCmdShow = SW_SHOWNORMAL;
153 setDrawingContextType(type);
154 ::ShowWindow(m_hWnd, nCmdShow);
155 // Force an initial paint of the window
156 ::UpdateWindow(m_hWnd);
161 GHOST_WindowWin32::~GHOST_WindowWin32()
163 if (m_customCursor) {
164 DestroyCursor(m_customCursor);
165 m_customCursor = NULL;
168 setDrawingContextType(GHOST_kDrawingContextTypeNone);
170 ::ReleaseDC(m_hWnd, m_hDC);
174 ::DestroyWindow(m_hWnd);
179 bool GHOST_WindowWin32::getValid() const
185 void GHOST_WindowWin32::setTitle(const STR_String& title)
187 ::SetWindowText(m_hWnd, title);
191 void GHOST_WindowWin32::getTitle(STR_String& title) const
193 char buf[s_maxTitleLength];
194 ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
195 STR_String temp (buf);
200 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const
203 ::GetWindowRect(m_hWnd, &rect);
204 bounds.m_b = rect.bottom;
205 bounds.m_l = rect.left;
206 bounds.m_r = rect.right;
207 bounds.m_t = rect.top;
211 void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const
214 ::GetClientRect(m_hWnd, &rect);
215 bounds.m_b = rect.bottom;
216 bounds.m_l = rect.left;
217 bounds.m_r = rect.right;
218 bounds.m_t = rect.top;
222 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
224 GHOST_TSuccess success;
225 GHOST_Rect cBnds, wBnds;
226 getClientBounds(cBnds);
227 if (cBnds.getWidth() != width) {
228 getWindowBounds(wBnds);
229 int cx = wBnds.getWidth() + width - cBnds.getWidth();
230 int cy = wBnds.getHeight();
231 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
232 GHOST_kSuccess : GHOST_kFailure;
235 success = GHOST_kSuccess;
241 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
243 GHOST_TSuccess success;
244 GHOST_Rect cBnds, wBnds;
245 getClientBounds(cBnds);
246 if (cBnds.getHeight() != height) {
247 getWindowBounds(wBnds);
248 int cx = wBnds.getWidth();
249 int cy = wBnds.getHeight() + height - cBnds.getHeight();
250 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
251 GHOST_kSuccess : GHOST_kFailure;
254 success = GHOST_kSuccess;
260 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
262 GHOST_TSuccess success;
263 GHOST_Rect cBnds, wBnds;
264 getClientBounds(cBnds);
265 if ((cBnds.getWidth() != width) || (cBnds.getHeight() != height)) {
266 getWindowBounds(wBnds);
267 int cx = wBnds.getWidth() + width - cBnds.getWidth();
268 int cy = wBnds.getHeight() + height - cBnds.getHeight();
269 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
270 GHOST_kSuccess : GHOST_kFailure;
273 success = GHOST_kSuccess;
279 GHOST_TWindowState GHOST_WindowWin32::getState() const
281 GHOST_TWindowState state;
282 if (::IsIconic(m_hWnd)) {
283 state = GHOST_kWindowStateMinimized;
285 else if (::IsZoomed(m_hWnd)) {
286 state = GHOST_kWindowStateMaximized;
289 state = GHOST_kWindowStateNormal;
295 void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
297 POINT point = { inX, inY };
298 ::ScreenToClient(m_hWnd, &point);
304 void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
306 POINT point = { inX, inY };
307 ::ClientToScreen(m_hWnd, &point);
313 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
316 wp.length = sizeof(WINDOWPLACEMENT);
317 ::GetWindowPlacement(m_hWnd, &wp);
319 case GHOST_kWindowStateMinimized:
320 wp.showCmd = SW_SHOWMINIMIZED;
322 case GHOST_kWindowStateMaximized:
323 ShowWindow(m_hWnd, SW_HIDE); //fe. HACK!
324 //Solves redraw problems when switching from fullscreen to normal.
326 wp.showCmd = SW_SHOWMAXIMIZED;
327 SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
329 case GHOST_kWindowStateFullScreen:
330 wp.showCmd = SW_SHOWMAXIMIZED;
331 SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
333 case GHOST_kWindowStateNormal:
335 wp.showCmd = SW_SHOWNORMAL;
338 return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
342 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
344 HWND hWndInsertAfter = order == GHOST_kWindowOrderTop ? HWND_TOP : HWND_BOTTOM;
345 return ::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
349 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
351 // adding a glFinish() here is to prevent Geforce in 'full scene antialias' mode
352 // from antialising the Blender window. Officially a swapbuffers does a glFinish
353 // itself, so this feels really like a hack... but it won't harm. (ton)
355 return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
359 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
361 GHOST_TSuccess success;
362 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
363 if (m_hDC && m_hGlRc) {
364 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
367 success = GHOST_kFailure;
371 success = GHOST_kSuccess;
377 GHOST_TSuccess GHOST_WindowWin32::invalidate()
379 GHOST_TSuccess success;
381 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
384 success = GHOST_kFailure;
390 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
392 GHOST_TSuccess success;
394 case GHOST_kDrawingContextTypeOpenGL:
397 sPreferredFormat.dwFlags |= PFD_STEREO;
399 // Attempt to match device context pixel format to the preferred format
400 int iPixelFormat = ::ChoosePixelFormat(m_hDC, &sPreferredFormat);
401 if (iPixelFormat == 0) {
402 success = GHOST_kFailure;
405 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
406 success = GHOST_kFailure;
409 // For debugging only: retrieve the pixel format chosen
410 PIXELFORMATDESCRIPTOR preferredFormat;
411 ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat);
412 // Create the context
413 m_hGlRc = ::wglCreateContext(m_hDC);
416 wglShareLists(s_firsthGLRc, m_hGlRc);
418 s_firsthGLRc = m_hGlRc;
421 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
424 success = GHOST_kFailure;
429 case GHOST_kDrawingContextTypeNone:
430 success = GHOST_kSuccess;
434 success = GHOST_kFailure;
440 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
442 GHOST_TSuccess success;
443 switch (m_drawingContextType) {
444 case GHOST_kDrawingContextTypeOpenGL:
446 success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
447 if (m_hGlRc == s_firsthGLRc) {
453 success = GHOST_kFailure;
456 case GHOST_kDrawingContextTypeNone:
457 success = GHOST_kSuccess;
460 success = GHOST_kFailure;
465 void GHOST_WindowWin32::lostMouseCapture()
467 if (m_hasMouseCaptured) {
468 m_hasMouseCaptured = false;
469 m_nPressedButtons = 0;
473 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
476 if (!m_hasMouseCaptured) {
477 ::SetCapture(m_hWnd);
478 m_hasMouseCaptured = true;
482 if (m_nPressedButtons) {
484 if (!m_nPressedButtons) {
486 m_hasMouseCaptured = false;
493 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
496 while (::ShowCursor(FALSE) >= 0);
499 while (::ShowCursor(TRUE) < 0);
502 if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
503 ::SetCursor( m_customCursor );
505 // Convert GHOST cursor to Windows OEM cursor
509 case GHOST_kStandardCursorDefault: id = IDC_ARROW; break;
510 case GHOST_kStandardCursorRightArrow: id = IDC_ARROW; break;
511 case GHOST_kStandardCursorLeftArrow: id = IDC_ARROW; break;
512 case GHOST_kStandardCursorInfo: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
513 case GHOST_kStandardCursorDestroy: id = IDC_NO; break; // Slashed circle
514 case GHOST_kStandardCursorHelp: id = IDC_HELP; break; // Arrow and question mark
515 case GHOST_kStandardCursorCycle: id = IDC_NO; break; // Slashed circle
516 case GHOST_kStandardCursorSpray: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
517 case GHOST_kStandardCursorWait: id = IDC_WAIT; break; // Hourglass
518 case GHOST_kStandardCursorText: id = IDC_IBEAM; break; // I-beam
519 case GHOST_kStandardCursorCrosshair: id = IDC_CROSS; break; // Crosshair
520 case GHOST_kStandardCursorUpDown: id = IDC_SIZENS; break; // Double-pointed arrow pointing north and south
521 case GHOST_kStandardCursorLeftRight: id = IDC_SIZEWE; break; // Double-pointed arrow pointing west and east
522 case GHOST_kStandardCursorTopSide: id = IDC_UPARROW; break; // Vertical arrow
523 case GHOST_kStandardCursorBottomSide: id = IDC_SIZENS; break;
524 case GHOST_kStandardCursorLeftSide: id = IDC_SIZEWE; break;
525 case GHOST_kStandardCursorTopLeftCorner: id = IDC_SIZENWSE; break;
526 case GHOST_kStandardCursorTopRightCorner: id = IDC_SIZENESW; break;
527 case GHOST_kStandardCursorBottomRightCorner: id = IDC_SIZENWSE; break;
528 case GHOST_kStandardCursorBottomLeftCorner: id = IDC_SIZENESW; break;
529 case GHOST_kStandardCursorPencil: id = IDC_ARROW; break;
535 HCURSOR hCursor = ::SetCursor(::LoadCursor(0, id));
540 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
542 if (::GetForegroundWindow() == m_hWnd) {
543 loadCursor(visible, getCursorShape());
546 return GHOST_kSuccess;
549 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
551 if (m_customCursor) {
552 DestroyCursor(m_customCursor);
553 m_customCursor = NULL;
556 if (::GetForegroundWindow() == m_hWnd) {
557 loadCursor(getCursorVisibility(), cursorShape);
560 return GHOST_kSuccess;
563 /** Reverse the bits in a GHOST_TUns16 */
564 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
566 shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
567 shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
568 shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
569 shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
573 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY)
575 GHOST_TUns32 andData[32];
576 GHOST_TUns32 xorData[32];
579 if (m_customCursor) {
580 DestroyCursor(m_customCursor);
581 m_customCursor = NULL;
584 memset(&andData, 0xFF, sizeof(andData));
585 memset(&xorData, 0, sizeof(xorData));
587 for (y=0; y<16; y++) {
588 GHOST_TUns32 fullBitRow = uns16ReverseBits((bitmap[y][0]<<8) | (bitmap[y][1]<<0));
589 GHOST_TUns32 fullMaskRow = uns16ReverseBits((mask[y][0]<<8) | (mask[y][1]<<0));
591 xorData[y]= fullBitRow & fullMaskRow;
592 andData[y]= ~fullMaskRow;
595 m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
596 if (!m_customCursor) {
597 return GHOST_kFailure;
600 if (::GetForegroundWindow() == m_hWnd) {
601 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
604 return GHOST_kSuccess;