Initial revision
[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 #include <string.h>
41
42 #include "GHOST_WindowWin32.h"
43
44 #include <GL/gl.h>
45
46 LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
47 const int GHOST_WindowWin32::s_maxTitleLength = 128;
48 HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
49
50 /*
51  * Color and depth bit values are not to be trusted.
52  * For instance, on TNT2:
53  * When the screen color depth is set to 16 bit, we get 5 color bits
54  * and 16 depth bits.
55  * When the screen color depth is set to 32 bit, we get 8 color bits
56  * and 24 depth bits.
57  * Just to be safe, we request high waulity settings.
58  */
59 static PIXELFORMATDESCRIPTOR sPreferredFormat = {
60         sizeof(PIXELFORMATDESCRIPTOR),  /* size */
61         1,                              /* version */
62         PFD_SUPPORT_OPENGL |
63         PFD_DRAW_TO_WINDOW |
64         PFD_DOUBLEBUFFER,               /* support double-buffering */
65         PFD_TYPE_RGBA,                  /* color type */
66         32,                             /* prefered color depth */
67         0, 0, 0, 0, 0, 0,               /* color bits (ignored) */
68         0,                              /* no alpha buffer */
69         0,                              /* alpha bits (ignored) */
70         0,                              /* no accumulation buffer */
71         0, 0, 0, 0,                     /* accum bits (ignored) */
72         32,                             /* depth buffer */
73         0,                              /* no stencil buffer */
74         0,                              /* no auxiliary buffers */
75         PFD_MAIN_PLANE,                 /* main layer */
76         0,                              /* reserved */
77         0, 0, 0                         /* no layer, visible, damage masks */
78 };
79
80 GHOST_WindowWin32::GHOST_WindowWin32(
81         const STR_String& title,
82         GHOST_TInt32 left,
83         GHOST_TInt32 top,
84         GHOST_TUns32 width,
85         GHOST_TUns32 height,
86         GHOST_TWindowState state,
87         GHOST_TDrawingContextType type,
88         const bool stereoVisual)
89 :
90         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone,
91         stereoVisual),
92         m_hDC(0),
93         m_hGlRc(0),
94         m_hasMouseCaptured(false),
95         m_nPressedButtons(0),
96         m_customCursor(0)
97 {
98         if (state != GHOST_kWindowStateFullScreen) {
99                         /* Convert client size into window size */
100                 width += GetSystemMetrics(SM_CXSIZEFRAME)*2;
101                 height += GetSystemMetrics(SM_CYSIZEFRAME)*2 + GetSystemMetrics(SM_CYCAPTION);
102
103                 m_hWnd = ::CreateWindow(
104                         s_windowClassName,                      // pointer to registered class name
105                         title,                                          // pointer to window name
106                         WS_OVERLAPPEDWINDOW,            // window style
107                         left,                                           // horizontal position of window
108                         top,                                            // vertical position of window
109                         width,                                          // window width
110                         height,                                         // window height
111                         0,                                                      // handle to parent or owner window
112                         0,                                                      // handle to menu or child-window identifier
113                         ::GetModuleHandle(0),           // handle to application instance
114                         0);                                                     // pointer to window-creation data
115         }
116         else {
117                 m_hWnd = ::CreateWindow(
118                         s_windowClassName,                      // pointer to registered class name
119                         title,                                          // pointer to window name
120                         WS_POPUP | WS_MAXIMIZE,         // window style
121                         left,                                           // horizontal position of window
122                         top,                                            // vertical position of window
123                         width,                                          // window width
124                         height,                                         // window height
125                         0,                                                      // handle to parent or owner window
126                         0,                                                      // handle to menu or child-window identifier
127                         ::GetModuleHandle(0),           // handle to application instance
128                         0);                                                     // pointer to window-creation data
129         }
130         if (m_hWnd) {
131                 // Store a pointer to this class in the window structure
132                 LONG result = ::SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
133
134                 // Store the device context
135                 m_hDC = ::GetDC(m_hWnd);
136
137                 // Show the window
138                 int nCmdShow;
139                 switch (state) {
140                         case GHOST_kWindowStateMaximized:
141                                 nCmdShow = SW_SHOWMAXIMIZED;
142                                 break;
143                         case GHOST_kWindowStateMinimized:
144                                 nCmdShow = SW_SHOWMINIMIZED;
145                                 break;
146                         case GHOST_kWindowStateNormal:
147                         default:
148                                 nCmdShow = SW_SHOWNORMAL;
149                                 break;
150                 }
151                 setDrawingContextType(type);
152                 ::ShowWindow(m_hWnd, nCmdShow);
153                 // Force an initial paint of the window
154                 ::UpdateWindow(m_hWnd);
155         }
156 }
157
158
159 GHOST_WindowWin32::~GHOST_WindowWin32()
160 {
161         if (m_customCursor) {
162                 DestroyCursor(m_customCursor);
163                 m_customCursor = NULL;
164         }
165
166         setDrawingContextType(GHOST_kDrawingContextTypeNone);
167         if (m_hDC) {
168                 ::ReleaseDC(m_hWnd, m_hDC);
169                 m_hDC = 0;
170         }
171         if (m_hWnd) {
172                 ::DestroyWindow(m_hWnd);
173                 m_hWnd = 0;
174         }
175 }
176
177 bool GHOST_WindowWin32::getValid() const
178 {
179         return m_hWnd != 0;
180 }
181
182
183 void GHOST_WindowWin32::setTitle(const STR_String& title)
184 {
185         ::SetWindowText(m_hWnd, title);
186 }
187
188
189 void GHOST_WindowWin32::getTitle(STR_String& title) const
190 {
191         char buf[s_maxTitleLength];
192         ::GetWindowText(m_hWnd, buf, s_maxTitleLength);
193         STR_String temp (buf);
194         title = buf;
195 }
196
197
198 void GHOST_WindowWin32::getWindowBounds(GHOST_Rect& bounds) const
199 {
200         RECT rect;
201         ::GetWindowRect(m_hWnd, &rect);
202         bounds.m_b = rect.bottom;
203         bounds.m_l = rect.left;
204         bounds.m_r = rect.right;
205         bounds.m_t = rect.top;
206 }
207
208
209 void GHOST_WindowWin32::getClientBounds(GHOST_Rect& bounds) const
210 {
211         RECT rect;
212         ::GetClientRect(m_hWnd, &rect);
213         bounds.m_b = rect.bottom;
214         bounds.m_l = rect.left;
215         bounds.m_r = rect.right;
216         bounds.m_t = rect.top;
217 }
218
219
220 GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
221 {
222         GHOST_TSuccess success;
223         GHOST_Rect cBnds, wBnds;
224         getClientBounds(cBnds);
225         if (cBnds.getWidth() != width) {
226                 getWindowBounds(wBnds);
227                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
228                 int cy = wBnds.getHeight();
229                 success =  ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
230                         GHOST_kSuccess : GHOST_kFailure;
231         }
232         else {
233                 success = GHOST_kSuccess;
234         }
235         return success;
236 }
237
238
239 GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
240 {
241         GHOST_TSuccess success;
242         GHOST_Rect cBnds, wBnds;
243         getClientBounds(cBnds);
244         if (cBnds.getHeight() != height) {
245                 getWindowBounds(wBnds);
246                 int cx = wBnds.getWidth();
247                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
248                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
249                         GHOST_kSuccess : GHOST_kFailure;
250         }
251         else {
252                 success = GHOST_kSuccess;
253         }
254         return success;
255 }
256
257
258 GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
259 {
260         GHOST_TSuccess success;
261         GHOST_Rect cBnds, wBnds;
262         getClientBounds(cBnds);
263         if ((cBnds.getWidth() != width) || (cBnds.getHeight() != height)) {
264                 getWindowBounds(wBnds);
265                 int cx = wBnds.getWidth() + width - cBnds.getWidth();
266                 int cy = wBnds.getHeight() + height - cBnds.getHeight();
267                 success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
268                         GHOST_kSuccess : GHOST_kFailure;
269         }
270         else {
271                 success = GHOST_kSuccess;
272         }
273         return success;
274 }
275
276
277 GHOST_TWindowState GHOST_WindowWin32::getState() const
278 {
279         GHOST_TWindowState state;
280         if (::IsIconic(m_hWnd)) {
281                 state = GHOST_kWindowStateMinimized;
282         }
283         else if (::IsZoomed(m_hWnd)) {
284                 state = GHOST_kWindowStateMaximized;
285         }
286         else {
287                 state = GHOST_kWindowStateNormal;
288         }
289         return state;
290 }
291
292
293 void GHOST_WindowWin32::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
294 {
295         POINT point = { inX, inY };
296         ::ScreenToClient(m_hWnd, &point);
297         outX = point.x;
298         outY = point.y;
299 }
300
301
302 void GHOST_WindowWin32::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
303 {
304         POINT point = { inX, inY };
305         ::ClientToScreen(m_hWnd, &point);
306         outX = point.x;
307         outY = point.y;
308 }
309
310
311 GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
312 {
313         WINDOWPLACEMENT wp;
314         wp.length = sizeof(WINDOWPLACEMENT);
315         ::GetWindowPlacement(m_hWnd, &wp);
316         switch (state) {
317         case GHOST_kWindowStateMinimized: wp.showCmd = SW_SHOWMAXIMIZED; break;
318         case GHOST_kWindowStateMaximized: wp.showCmd = SW_SHOWMINIMIZED; break;
319         case GHOST_kWindowStateNormal: default: wp.showCmd = SW_SHOWNORMAL; break;
320         }
321         return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
322 }
323
324
325 GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
326 {
327         HWND hWndInsertAfter = order == GHOST_kWindowOrderTop ? HWND_TOP : HWND_BOTTOM;
328         return ::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
329 }
330
331
332 GHOST_TSuccess GHOST_WindowWin32::swapBuffers()
333 {
334         return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
335 }
336
337
338 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
339 {
340         GHOST_TSuccess success;
341         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
342                 if (m_hDC && m_hGlRc) {
343                         success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
344                 }
345                 else {
346                         success = GHOST_kFailure;
347                 }
348         }
349         else {
350                 success = GHOST_kSuccess;
351         }
352         return success;
353 }
354
355
356 GHOST_TSuccess GHOST_WindowWin32::invalidate()
357 {
358         GHOST_TSuccess success;
359         if (m_hWnd) {
360                 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
361         }
362         else {
363                 success = GHOST_kFailure;
364         }
365         return success;
366 }
367
368
369 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
370 {
371         GHOST_TSuccess success;
372         switch (type) {
373         case GHOST_kDrawingContextTypeOpenGL:
374                 {
375                 if(m_stereoVisual)
376                         sPreferredFormat.dwFlags |= PFD_STEREO;
377
378                 // Attempt to match device context pixel format to the preferred format
379                 int iPixelFormat = ::ChoosePixelFormat(m_hDC, &sPreferredFormat);
380                 if (iPixelFormat == 0) {
381                         success = GHOST_kFailure;
382                         break;
383                 }
384                 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
385                         success = GHOST_kFailure;
386                         break;
387                 }
388                 // For debugging only: retrieve the pixel format chosen
389                 PIXELFORMATDESCRIPTOR preferredFormat;
390                 ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &preferredFormat);
391                 // Create the context
392                 m_hGlRc = ::wglCreateContext(m_hDC);
393                 if (m_hGlRc) {
394                         if (s_firsthGLRc) {
395                                 wglShareLists(s_firsthGLRc, m_hGlRc);
396                         } else {
397                                 s_firsthGLRc = m_hGlRc;
398                         }
399
400                         success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
401                 }
402                 else {
403                         success = GHOST_kFailure;
404                 }
405                 }
406                 break;
407
408         case GHOST_kDrawingContextTypeNone:
409                 success = GHOST_kSuccess;
410                 break;
411
412         default:
413                 success = GHOST_kFailure;
414         }
415         return success;
416 }
417
418
419 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
420 {
421         GHOST_TSuccess success;
422         switch (m_drawingContextType) {
423         case GHOST_kDrawingContextTypeOpenGL:
424                 if (m_hGlRc) {
425                         success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
426                         if (m_hGlRc == s_firsthGLRc) {
427                                 s_firsthGLRc = NULL;
428                         }
429                         m_hGlRc = 0;
430                 }
431                 else {
432                         success = GHOST_kFailure;
433                 }
434                 break;
435         case GHOST_kDrawingContextTypeNone:
436                 success = GHOST_kSuccess;
437                 break;
438         default:
439                 success = GHOST_kFailure;
440         }
441         return success;
442 }
443
444 void GHOST_WindowWin32::lostMouseCapture()
445 {
446         if (m_hasMouseCaptured) {
447                 m_hasMouseCaptured = false;
448                 m_nPressedButtons = 0;
449         }
450 }
451
452 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
453 {
454         if (press) {
455                 if (!m_hasMouseCaptured) {
456                         ::SetCapture(m_hWnd);
457                         m_hasMouseCaptured = true;
458                 }
459                 m_nPressedButtons++;
460         } else {
461                 if (m_nPressedButtons) {
462                         m_nPressedButtons--;
463                         if (!m_nPressedButtons) {
464                                 ::ReleaseCapture();
465                                 m_hasMouseCaptured = false;
466                         }
467                 }
468         }
469 }
470
471
472 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
473 {
474         if (!visible) {
475                 while (::ShowCursor(FALSE) >= 0);
476         }
477         else {
478                 while (::ShowCursor(TRUE) < 0);
479         }
480
481         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
482                 ::SetCursor( m_customCursor );
483         } else {
484                 // Convert GHOST cursor to Windows OEM cursor
485                 bool success = true;
486                 LPCSTR id;
487                 switch (cursor) {
488                         case GHOST_kStandardCursorDefault:                              id = IDC_ARROW;
489                         case GHOST_kStandardCursorRightArrow:                   id = IDC_ARROW;         break;
490                         case GHOST_kStandardCursorLeftArrow:                    id = IDC_ARROW;         break;
491                         case GHOST_kStandardCursorInfo:                                 id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
492                         case GHOST_kStandardCursorDestroy:                              id = IDC_NO;            break;  // Slashed circle
493                         case GHOST_kStandardCursorHelp:                                 id = IDC_HELP;          break;  // Arrow and question mark
494                         case GHOST_kStandardCursorCycle:                                id = IDC_NO;            break;  // Slashed circle
495                         case GHOST_kStandardCursorSpray:                                id = IDC_SIZEALL;       break;  // Four-pointed arrow pointing north, south, east, and west
496                         case GHOST_kStandardCursorWait:                                 id = IDC_WAIT;          break;  // Hourglass
497                         case GHOST_kStandardCursorText:                                 id = IDC_IBEAM;         break;  // I-beam
498                         case GHOST_kStandardCursorCrosshair:                    id = IDC_CROSS;         break;  // Crosshair
499                         case GHOST_kStandardCursorUpDown:                               id = IDC_SIZENS;        break;  // Double-pointed arrow pointing north and south
500                         case GHOST_kStandardCursorLeftRight:                    id = IDC_SIZEWE;        break;  // Double-pointed arrow pointing west and east
501                         case GHOST_kStandardCursorTopSide:                              id = IDC_UPARROW;       break;  // Vertical arrow
502                         case GHOST_kStandardCursorBottomSide:                   id = IDC_SIZENS;        break;
503                         case GHOST_kStandardCursorLeftSide:                             id = IDC_SIZEWE;        break;
504                         case GHOST_kStandardCursorTopLeftCorner:                id = IDC_SIZENWSE;      break;
505                         case GHOST_kStandardCursorTopRightCorner:               id = IDC_SIZENESW;      break;
506                         case GHOST_kStandardCursorBottomRightCorner:    id = IDC_SIZENWSE;      break;
507                         case GHOST_kStandardCursorBottomLeftCorner:             id = IDC_SIZENESW;      break;
508                 default:
509                         success = false;
510                 }
511                 
512                 if (success) {
513                         HCURSOR hCursor = ::SetCursor(::LoadCursor(0, id));
514                 }
515         }
516 }
517
518 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
519 {
520         if (::GetForegroundWindow() == m_hWnd) {
521                 loadCursor(visible, getCursorShape());
522         }
523
524         return GHOST_kSuccess;
525 }
526
527 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
528 {
529         if (m_customCursor) {
530                 DestroyCursor(m_customCursor);
531                 m_customCursor = NULL;
532         }
533
534         if (::GetForegroundWindow() == m_hWnd) {
535                 loadCursor(getCursorVisibility(), cursorShape);
536         }
537
538         return GHOST_kSuccess;
539 }
540
541 /** Reverse the bits in a GHOST_TUns16 */
542 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
543 {
544         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
545         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
546         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
547         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
548         return shrt;
549 }
550
551 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY)
552 {
553         GHOST_TUns32 andData[32];
554         GHOST_TUns32 xorData[32];
555         int y;
556
557         if (m_customCursor) {
558                 DestroyCursor(m_customCursor);
559                 m_customCursor = NULL;
560         }
561
562         memset(&andData, 0xFF, sizeof(andData));
563         memset(&xorData, 0, sizeof(xorData));
564
565         for (y=0; y<16; y++) {
566                 GHOST_TUns32 fullBitRow = uns16ReverseBits((bitmap[y][0]<<8) | (bitmap[y][1]<<0));
567                 GHOST_TUns32 fullMaskRow = uns16ReverseBits((mask[y][0]<<8) | (mask[y][1]<<0));
568
569                 xorData[y]= fullBitRow & fullMaskRow;
570                 andData[y]= ~fullMaskRow;
571         }
572
573         m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
574         if (!m_customCursor) {
575                 return GHOST_kFailure;
576         }
577
578         if (::GetForegroundWindow() == m_hWnd) {
579                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
580         }
581
582         return GHOST_kSuccess;
583 }
584