changed for the new fullscreen button (windows only)
[blender.git] / intern / ghost / intern / GHOST_WindowWin32.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
4  *
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
11  * about this.
12  *
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.
17  *
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.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 /**
33
34  * $Id$
35  * Copyright (C) 2001 NaN Technologies B.V.
36  * @author      Maarten Gribnau
37  * @date        May 10, 2001
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43
44 #include <string.h>
45 #include "GHOST_WindowWin32.h"
46 #include <GL/gl.h>
47
48 LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
49 const int GHOST_WindowWin32::s_maxTitleLength = 128;
50 HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
51
52 /*
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
56  * and 16 depth bits.
57  * When the screen color depth is set to 32 bit, we get 8 color bits
58  * and 24 depth bits.
59  * Just to be safe, we request high waulity settings.
60  */
61 static PIXELFORMATDESCRIPTOR sPreferredFormat = {
62         sizeof(PIXELFORMATDESCRIPTOR),  /* size */
63         1,                              /* version */
64         PFD_SUPPORT_OPENGL |
65         PFD_DRAW_TO_WINDOW |
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 */
78         0,                              /* reserved */
79         0, 0, 0                         /* no layer, visible, damage masks */
80 };
81
82 GHOST_WindowWin32::GHOST_WindowWin32(
83         const STR_String& title,
84         GHOST_TInt32 left,
85         GHOST_TInt32 top,
86         GHOST_TUns32 width,
87         GHOST_TUns32 height,
88         GHOST_TWindowState state,
89         GHOST_TDrawingContextType type,
90         const bool stereoVisual)
91 :
92         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone,
93         stereoVisual),
94         m_hDC(0),
95         m_hGlRc(0),
96         m_hasMouseCaptured(false),
97         m_nPressedButtons(0),
98         m_customCursor(0)
99 {
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);
104
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
117         }
118         else {
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
131         }
132         if (m_hWnd) {
133                 // Store a pointer to this class in the window structure
134                 LONG result = ::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
135
136                 // Store the device context
137                 m_hDC = ::GetDC(m_hWnd);
138
139                 // Show the window
140                 int nCmdShow;
141                 switch (state) {
142                         case GHOST_kWindowStateMaximized:
143                                 nCmdShow = SW_SHOWMAXIMIZED;
144                                 break;
145                         case GHOST_kWindowStateMinimized:
146                                 nCmdShow = SW_SHOWMINIMIZED;
147                                 break;
148                         case GHOST_kWindowStateNormal:
149                         default:
150                                 nCmdShow = SW_SHOWNORMAL;
151                                 break;
152                 }
153                 setDrawingContextType(type);
154                 ::ShowWindow(m_hWnd, nCmdShow);
155                 // Force an initial paint of the window
156                 ::UpdateWindow(m_hWnd);
157         }
158 }
159
160
161 GHOST_WindowWin32::~GHOST_WindowWin32()
162 {
163         if (m_customCursor) {
164                 DestroyCursor(m_customCursor);
165                 m_customCursor = NULL;
166         }
167
168         setDrawingContextType(GHOST_kDrawingContextTypeNone);
169         if (m_hDC) {
170                 ::ReleaseDC(m_hWnd, m_hDC);
171                 m_hDC = 0;
172         }
173         if (m_hWnd) {
174                 ::DestroyWindow(m_hWnd);
175                 m_hWnd = 0;
176         }
177 }
178
179 bool GHOST_WindowWin32::getValid() const
180 {
181         return m_hWnd != 0;
182 }
183
184
185 void GHOST_WindowWin32::setTitle(const STR_String& title)
186 {
187         ::SetWindowText(m_hWnd, title);
188 }
189
190
191 void GHOST_WindowWin32::getTitle(STR_String& title) const
192 {
193         char buf[s_maxTitleLength];
194         ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
195         STR_String temp (buf);
196         title = buf;
197 }
198
199
200 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const
201 {
202         RECT rect;
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;
208 }
209
210
211 void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const
212 {
213         RECT rect;
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;
219 }
220
221
222 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
223 {
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;
233         }
234         else {
235                 success = GHOST_kSuccess;
236         }
237         return success;
238 }
239
240
241 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
242 {
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;
252         }
253         else {
254                 success = GHOST_kSuccess;
255         }
256         return success;
257 }
258
259
260 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
261 {
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;
271         }
272         else {
273                 success = GHOST_kSuccess;
274         }
275         return success;
276 }
277
278
279 GHOST_TWindowState GHOST_WindowWin32::getState() const
280 {
281         GHOST_TWindowState state;
282         if (::IsIconic(m_hWnd)) {
283                 state = GHOST_kWindowStateMinimized;
284         }
285         else if (::IsZoomed(m_hWnd)) {
286                 state = GHOST_kWindowStateMaximized;
287         }
288         else {
289                 state = GHOST_kWindowStateNormal;
290         }
291         return state;
292 }
293
294
295 void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
296 {
297         POINT point = { inX, inY };
298         ::ScreenToClient(m_hWnd, &point);
299         outX = point.x;
300         outY = point.y;
301 }
302
303
304 void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
305 {
306         POINT point = { inX, inY };
307         ::ClientToScreen(m_hWnd, &point);
308         outX = point.x;
309         outY = point.y;
310 }
311
312
313 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
314 {
315         WINDOWPLACEMENT wp;
316         wp.length = sizeof(WINDOWPLACEMENT);
317         ::GetWindowPlacement(m_hWnd, &wp);
318         switch (state) {
319         case GHOST_kWindowStateMinimized: 
320                 wp.showCmd = SW_SHOWMINIMIZED; 
321                 break;
322         case GHOST_kWindowStateMaximized: 
323                 ShowWindow(m_hWnd, SW_HIDE); //fe. HACK!
324                                 //Solves redraw problems when switching from fullscreen to normal.
325                                 
326                 wp.showCmd = SW_SHOWMAXIMIZED; 
327                 SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
328                 break;
329         case GHOST_kWindowStateFullScreen:
330                 wp.showCmd = SW_SHOWMAXIMIZED;
331                 SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
332                 break;
333         case GHOST_kWindowStateNormal: 
334         default: 
335                 wp.showCmd = SW_SHOWNORMAL; 
336                 break;
337         }
338         return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
339 }
340
341
342 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
343 {
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;
346 }
347
348
349 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
350 {
351         return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
352 }
353
354
355 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
356 {
357         GHOST_TSuccess success;
358         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
359                 if (m_hDC && m_hGlRc) {
360                         success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
361                 }
362                 else {
363                         success = GHOST_kFailure;
364                 }
365         }
366         else {
367                 success = GHOST_kSuccess;
368         }
369         return success;
370 }
371
372
373 GHOST_TSuccess GHOST_WindowWin32::invalidate()
374 {
375         GHOST_TSuccess success;
376         if (m_hWnd) {
377                 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
378         }
379         else {
380                 success = GHOST_kFailure;
381         }
382         return success;
383 }
384
385
386 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
387 {
388         GHOST_TSuccess success;
389         switch (type) {
390         case GHOST_kDrawingContextTypeOpenGL:
391                 {
392                 if(m_stereoVisual)
393                         sPreferredFormat.dwFlags |= PFD_STEREO;
394
395                 // Attempt to match device context pixel format to the preferred format
396                 int iPixelFormat = ::ChoosePixelFormat(m_hDC, &sPreferredFormat);
397                 if (iPixelFormat == 0) {
398                         success = GHOST_kFailure;
399                         break;
400                 }
401                 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
402                         success = GHOST_kFailure;
403                         break;
404                 }
405                 // For debugging only: retrieve the pixel format chosen
406                 PIXELFORMATDESCRIPTOR preferredFormat;
407                 ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat);
408                 // Create the context
409                 m_hGlRc = ::wglCreateContext(m_hDC);
410                 if (m_hGlRc) {
411                         if (s_firsthGLRc) {
412                                 wglShareLists(s_firsthGLRc, m_hGlRc);
413                         } else {
414                                 s_firsthGLRc = m_hGlRc;
415                         }
416
417                         success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
418                 }
419                 else {
420                         success = GHOST_kFailure;
421                 }
422                 }
423                 break;
424
425         case GHOST_kDrawingContextTypeNone:
426                 success = GHOST_kSuccess;
427                 break;
428
429         default:
430                 success = GHOST_kFailure;
431         }
432         return success;
433 }
434
435
436 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
437 {
438         GHOST_TSuccess success;
439         switch (m_drawingContextType) {
440         case GHOST_kDrawingContextTypeOpenGL:
441                 if (m_hGlRc) {
442                         success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
443                         if (m_hGlRc == s_firsthGLRc) {
444                                 s_firsthGLRc = NULL;
445                         }
446                         m_hGlRc = 0;
447                 }
448                 else {
449                         success = GHOST_kFailure;
450                 }
451                 break;
452         case GHOST_kDrawingContextTypeNone:
453                 success = GHOST_kSuccess;
454                 break;
455         default:
456                 success = GHOST_kFailure;
457         }
458         return success;
459 }
460
461 void GHOST_WindowWin32::lostMouseCapture()
462 {
463         if (m_hasMouseCaptured) {
464                 m_hasMouseCaptured = false;
465                 m_nPressedButtons = 0;
466         }
467 }
468
469 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
470 {
471         if (press) {
472                 if (!m_hasMouseCaptured) {
473                         ::SetCapture(m_hWnd);
474                         m_hasMouseCaptured = true;
475                 }
476                 m_nPressedButtons++;
477         } else {
478                 if (m_nPressedButtons) {
479                         m_nPressedButtons--;
480                         if (!m_nPressedButtons) {
481                                 ::ReleaseCapture();
482                                 m_hasMouseCaptured = false;
483                         }
484                 }
485         }
486 }
487
488
489 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
490 {
491         if (!visible) {
492                 while (::ShowCursor(FALSE) >= 0);
493         }
494         else {
495                 while (::ShowCursor(TRUE) < 0);
496         }
497
498         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
499                 ::SetCursor( m_customCursor );
500         } else {
501                 // Convert GHOST cursor to Windows OEM cursor
502                 bool success = true;
503                 LPCSTR id;
504                 switch (cursor) {
505                         case GHOST_kStandardCursorDefault:                              id = IDC_ARROW;
506                         case GHOST_kStandardCursorRightArrow:                   id = IDC_ARROW;         break;
507                         case GHOST_kStandardCursorLeftArrow:                    id = IDC_ARROW;         break;
508                         case GHOST_kStandardCursorInfo:                                 id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
509                         case GHOST_kStandardCursorDestroy:                              id = IDC_NO;            break;  // Slashed circle
510                         case GHOST_kStandardCursorHelp:                                 id = IDC_HELP;          break;  // Arrow and question mark
511                         case GHOST_kStandardCursorCycle:                                id = IDC_NO;            break;  // Slashed circle
512                         case GHOST_kStandardCursorSpray:                                id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
513                         case GHOST_kStandardCursorWait:                                 id = IDC_WAIT;          break;  // Hourglass
514                         case GHOST_kStandardCursorText:                                 id = IDC_IBEAM;         break;  // I-beam
515                         case GHOST_kStandardCursorCrosshair:                    id = IDC_CROSS;         break;  // Crosshair
516                         case GHOST_kStandardCursorUpDown:                               id = IDC_SIZENS;        break;  // Double-pointed arrow pointing north and south
517                         case GHOST_kStandardCursorLeftRight:                    id = IDC_SIZEWE;        break;  // Double-pointed arrow pointing west and east
518                         case GHOST_kStandardCursorTopSide:                              id = IDC_UPARROW;       break;  // Vertical arrow
519                         case GHOST_kStandardCursorBottomSide:                   id = IDC_SIZENS;        break;
520                         case GHOST_kStandardCursorLeftSide:                             id = IDC_SIZEWE;        break;
521                         case GHOST_kStandardCursorTopLeftCorner:                id = IDC_SIZENWSE;      break;
522                         case GHOST_kStandardCursorTopRightCorner:               id = IDC_SIZENESW;      break;
523                         case GHOST_kStandardCursorBottomRightCorner:    id = IDC_SIZENWSE;      break;
524                         case GHOST_kStandardCursorBottomLeftCorner:             id = IDC_SIZENESW;      break;
525                 default:
526                         success = false;
527                 }
528                 
529                 if (success) {
530                         HCURSOR hCursor = ::SetCursor(::LoadCursor(0, id));
531                 }
532         }
533 }
534
535 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
536 {
537         if (::GetForegroundWindow() == m_hWnd) {
538                 loadCursor(visible, getCursorShape());
539         }
540
541         return GHOST_kSuccess;
542 }
543
544 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
545 {
546         if (m_customCursor) {
547                 DestroyCursor(m_customCursor);
548                 m_customCursor = NULL;
549         }
550
551         if (::GetForegroundWindow() == m_hWnd) {
552                 loadCursor(getCursorVisibility(), cursorShape);
553         }
554
555         return GHOST_kSuccess;
556 }
557
558 /** Reverse the bits in a GHOST_TUns16 */
559 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
560 {
561         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
562         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
563         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
564         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
565         return shrt;
566 }
567
568 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY)
569 {
570         GHOST_TUns32 andData[32];
571         GHOST_TUns32 xorData[32];
572         int y;
573
574         if (m_customCursor) {
575                 DestroyCursor(m_customCursor);
576                 m_customCursor = NULL;
577         }
578
579         memset(&andData, 0xFF, sizeof(andData));
580         memset(&xorData, 0, sizeof(xorData));
581
582         for (y=0; y<16; y++) {
583                 GHOST_TUns32 fullBitRow = uns16ReverseBits((bitmap[y][0]<<8) | (bitmap[y][1]<<0));
584                 GHOST_TUns32 fullMaskRow = uns16ReverseBits((mask[y][0]<<8) | (mask[y][1]<<0));
585
586                 xorData[y]= fullBitRow & fullMaskRow;
587                 andData[y]= ~fullMaskRow;
588         }
589
590         m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
591         if (!m_customCursor) {
592                 return GHOST_kFailure;
593         }
594
595         if (::GetForegroundWindow() == m_hWnd) {
596                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
597         }
598
599         return GHOST_kSuccess;
600 }
601