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: wp.showCmd = SW_SHOWMAXIMIZED; break;
320 case GHOST_kWindowStateMaximized: wp.showCmd = SW_SHOWMINIMIZED; break;
321 case GHOST_kWindowStateNormal: default: wp.showCmd = SW_SHOWNORMAL; break;
323 return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
327 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
329 HWND hWndInsertAfter = order == GHOST_kWindowOrderTop ? HWND_TOP : HWND_BOTTOM;
330 return ::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
334 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
336 return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
340 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
342 GHOST_TSuccess success;
343 if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
344 if (m_hDC && m_hGlRc) {
345 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
348 success = GHOST_kFailure;
352 success = GHOST_kSuccess;
358 GHOST_TSuccess GHOST_WindowWin32::invalidate()
360 GHOST_TSuccess success;
362 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
365 success = GHOST_kFailure;
371 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
373 GHOST_TSuccess success;
375 case GHOST_kDrawingContextTypeOpenGL:
378 sPreferredFormat.dwFlags |= PFD_STEREO;
380 // Attempt to match device context pixel format to the preferred format
381 int iPixelFormat = ::ChoosePixelFormat(m_hDC, &sPreferredFormat);
382 if (iPixelFormat == 0) {
383 success = GHOST_kFailure;
386 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
387 success = GHOST_kFailure;
390 // For debugging only: retrieve the pixel format chosen
391 PIXELFORMATDESCRIPTOR preferredFormat;
392 ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat);
393 // Create the context
394 m_hGlRc = ::wglCreateContext(m_hDC);
397 wglShareLists(s_firsthGLRc, m_hGlRc);
399 s_firsthGLRc = m_hGlRc;
402 success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
405 success = GHOST_kFailure;
410 case GHOST_kDrawingContextTypeNone:
411 success = GHOST_kSuccess;
415 success = GHOST_kFailure;
421 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
423 GHOST_TSuccess success;
424 switch (m_drawingContextType) {
425 case GHOST_kDrawingContextTypeOpenGL:
427 success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
428 if (m_hGlRc == s_firsthGLRc) {
434 success = GHOST_kFailure;
437 case GHOST_kDrawingContextTypeNone:
438 success = GHOST_kSuccess;
441 success = GHOST_kFailure;
446 void GHOST_WindowWin32::lostMouseCapture()
448 if (m_hasMouseCaptured) {
449 m_hasMouseCaptured = false;
450 m_nPressedButtons = 0;
454 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
457 if (!m_hasMouseCaptured) {
458 ::SetCapture(m_hWnd);
459 m_hasMouseCaptured = true;
463 if (m_nPressedButtons) {
465 if (!m_nPressedButtons) {
467 m_hasMouseCaptured = false;
474 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
477 while (::ShowCursor(FALSE) >= 0);
480 while (::ShowCursor(TRUE) < 0);
483 if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
484 ::SetCursor( m_customCursor );
486 // Convert GHOST cursor to Windows OEM cursor
490 case GHOST_kStandardCursorDefault: id = IDC_ARROW;
491 case GHOST_kStandardCursorRightArrow: id = IDC_ARROW; break;
492 case GHOST_kStandardCursorLeftArrow: id = IDC_ARROW; break;
493 case GHOST_kStandardCursorInfo: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
494 case GHOST_kStandardCursorDestroy: id = IDC_NO; break; // Slashed circle
495 case GHOST_kStandardCursorHelp: id = IDC_HELP; break; // Arrow and question mark
496 case GHOST_kStandardCursorCycle: id = IDC_NO; break; // Slashed circle
497 case GHOST_kStandardCursorSpray: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
498 case GHOST_kStandardCursorWait: id = IDC_WAIT; break; // Hourglass
499 case GHOST_kStandardCursorText: id = IDC_IBEAM; break; // I-beam
500 case GHOST_kStandardCursorCrosshair: id = IDC_CROSS; break; // Crosshair
501 case GHOST_kStandardCursorUpDown: id = IDC_SIZENS; break; // Double-pointed arrow pointing north and south
502 case GHOST_kStandardCursorLeftRight: id = IDC_SIZEWE; break; // Double-pointed arrow pointing west and east
503 case GHOST_kStandardCursorTopSide: id = IDC_UPARROW; break; // Vertical arrow
504 case GHOST_kStandardCursorBottomSide: id = IDC_SIZENS; break;
505 case GHOST_kStandardCursorLeftSide: id = IDC_SIZEWE; break;
506 case GHOST_kStandardCursorTopLeftCorner: id = IDC_SIZENWSE; break;
507 case GHOST_kStandardCursorTopRightCorner: id = IDC_SIZENESW; break;
508 case GHOST_kStandardCursorBottomRightCorner: id = IDC_SIZENWSE; break;
509 case GHOST_kStandardCursorBottomLeftCorner: id = IDC_SIZENESW; break;
515 HCURSOR hCursor = ::SetCursor(::LoadCursor(0, id));
520 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
522 if (::GetForegroundWindow() == m_hWnd) {
523 loadCursor(visible, getCursorShape());
526 return GHOST_kSuccess;
529 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
531 if (m_customCursor) {
532 DestroyCursor(m_customCursor);
533 m_customCursor = NULL;
536 if (::GetForegroundWindow() == m_hWnd) {
537 loadCursor(getCursorVisibility(), cursorShape);
540 return GHOST_kSuccess;
543 /** Reverse the bits in a GHOST_TUns16 */
544 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
546 shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
547 shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
548 shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
549 shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
553 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY)
555 GHOST_TUns32 andData[32];
556 GHOST_TUns32 xorData[32];
559 if (m_customCursor) {
560 DestroyCursor(m_customCursor);
561 m_customCursor = NULL;
564 memset(&andData, 0xFF, sizeof(andData));
565 memset(&xorData, 0, sizeof(xorData));
567 for (y=0; y<16; y++) {
568 GHOST_TUns32 fullBitRow = uns16ReverseBits((bitmap[y][0]<<8) | (bitmap[y][1]<<0));
569 GHOST_TUns32 fullMaskRow = uns16ReverseBits((mask[y][0]<<8) | (mask[y][1]<<0));
571 xorData[y]= fullBitRow & fullMaskRow;
572 andData[y]= ~fullMaskRow;
575 m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
576 if (!m_customCursor) {
577 return GHOST_kFailure;
580 if (::GetForegroundWindow() == m_hWnd) {
581 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
584 return GHOST_kSuccess;