Yes I did it again ;)
[blender-staging.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: wp.showCmd = SW_SHOWMAXIMIZED; break;
320         case GHOST_kWindowStateMaximized: wp.showCmd = SW_SHOWMINIMIZED; break;
321         case GHOST_kWindowStateNormal: default: wp.showCmd = SW_SHOWNORMAL; break;
322         }
323         return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
324 }
325
326
327 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
328 {
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;
331 }
332
333
334 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
335 {
336         return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
337 }
338
339
340 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
341 {
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;
346                 }
347                 else {
348                         success = GHOST_kFailure;
349                 }
350         }
351         else {
352                 success = GHOST_kSuccess;
353         }
354         return success;
355 }
356
357
358 GHOST_TSuccess GHOST_WindowWin32::invalidate()
359 {
360         GHOST_TSuccess success;
361         if (m_hWnd) {
362                 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
363         }
364         else {
365                 success = GHOST_kFailure;
366         }
367         return success;
368 }
369
370
371 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
372 {
373         GHOST_TSuccess success;
374         switch (type) {
375         case GHOST_kDrawingContextTypeOpenGL:
376                 {
377                 if(m_stereoVisual)
378                         sPreferredFormat.dwFlags |= PFD_STEREO;
379
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;
384                         break;
385                 }
386                 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
387                         success = GHOST_kFailure;
388                         break;
389                 }
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);
395                 if (m_hGlRc) {
396                         if (s_firsthGLRc) {
397                                 wglShareLists(s_firsthGLRc, m_hGlRc);
398                         } else {
399                                 s_firsthGLRc = m_hGlRc;
400                         }
401
402                         success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
403                 }
404                 else {
405                         success = GHOST_kFailure;
406                 }
407                 }
408                 break;
409
410         case GHOST_kDrawingContextTypeNone:
411                 success = GHOST_kSuccess;
412                 break;
413
414         default:
415                 success = GHOST_kFailure;
416         }
417         return success;
418 }
419
420
421 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
422 {
423         GHOST_TSuccess success;
424         switch (m_drawingContextType) {
425         case GHOST_kDrawingContextTypeOpenGL:
426                 if (m_hGlRc) {
427                         success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
428                         if (m_hGlRc == s_firsthGLRc) {
429                                 s_firsthGLRc = NULL;
430                         }
431                         m_hGlRc = 0;
432                 }
433                 else {
434                         success = GHOST_kFailure;
435                 }
436                 break;
437         case GHOST_kDrawingContextTypeNone:
438                 success = GHOST_kSuccess;
439                 break;
440         default:
441                 success = GHOST_kFailure;
442         }
443         return success;
444 }
445
446 void GHOST_WindowWin32::lostMouseCapture()
447 {
448         if (m_hasMouseCaptured) {
449                 m_hasMouseCaptured = false;
450                 m_nPressedButtons = 0;
451         }
452 }
453
454 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
455 {
456         if (press) {
457                 if (!m_hasMouseCaptured) {
458                         ::SetCapture(m_hWnd);
459                         m_hasMouseCaptured = true;
460                 }
461                 m_nPressedButtons++;
462         } else {
463                 if (m_nPressedButtons) {
464                         m_nPressedButtons--;
465                         if (!m_nPressedButtons) {
466                                 ::ReleaseCapture();
467                                 m_hasMouseCaptured = false;
468                         }
469                 }
470         }
471 }
472
473
474 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
475 {
476         if (!visible) {
477                 while (::ShowCursor(FALSE) >= 0);
478         }
479         else {
480                 while (::ShowCursor(TRUE) < 0);
481         }
482
483         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
484                 ::SetCursor( m_customCursor );
485         } else {
486                 // Convert GHOST cursor to Windows OEM cursor
487                 bool success = true;
488                 LPCSTR id;
489                 switch (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;
510                 default:
511                         success = false;
512                 }
513                 
514                 if (success) {
515                         HCURSOR hCursor = ::SetCursor(::LoadCursor(0, id));
516                 }
517         }
518 }
519
520 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
521 {
522         if (::GetForegroundWindow() == m_hWnd) {
523                 loadCursor(visible, getCursorShape());
524         }
525
526         return GHOST_kSuccess;
527 }
528
529 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
530 {
531         if (m_customCursor) {
532                 DestroyCursor(m_customCursor);
533                 m_customCursor = NULL;
534         }
535
536         if (::GetForegroundWindow() == m_hWnd) {
537                 loadCursor(getCursorVisibility(), cursorShape);
538         }
539
540         return GHOST_kSuccess;
541 }
542
543 /** Reverse the bits in a GHOST_TUns16 */
544 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
545 {
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);
550         return shrt;
551 }
552
553 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY)
554 {
555         GHOST_TUns32 andData[32];
556         GHOST_TUns32 xorData[32];
557         int y;
558
559         if (m_customCursor) {
560                 DestroyCursor(m_customCursor);
561                 m_customCursor = NULL;
562         }
563
564         memset(&andData, 0xFF, sizeof(andData));
565         memset(&xorData, 0, sizeof(xorData));
566
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));
570
571                 xorData[y]= fullBitRow & fullMaskRow;
572                 andData[y]= ~fullMaskRow;
573         }
574
575         m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
576         if (!m_customCursor) {
577                 return GHOST_kFailure;
578         }
579
580         if (::GetForegroundWindow() == m_hWnd) {
581                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
582         }
583
584         return GHOST_kSuccess;
585 }
586