Robert (DetectiveThorn) Wenzlaff's Knife subdivide tool. See previous
[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         // 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)
354         glFinish();
355         return ::SwapBuffers(m_hDC) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
356 }
357
358
359 GHOST_TSuccess GHOST_WindowWin32::activateDrawingContext()
360 {
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;
365                 }
366                 else {
367                         success = GHOST_kFailure;
368                 }
369         }
370         else {
371                 success = GHOST_kSuccess;
372         }
373         return success;
374 }
375
376
377 GHOST_TSuccess GHOST_WindowWin32::invalidate()
378 {
379         GHOST_TSuccess success;
380         if (m_hWnd) {
381                 success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
382         }
383         else {
384                 success = GHOST_kFailure;
385         }
386         return success;
387 }
388
389
390 GHOST_TSuccess GHOST_WindowWin32::installDrawingContext(GHOST_TDrawingContextType type)
391 {
392         GHOST_TSuccess success;
393         switch (type) {
394         case GHOST_kDrawingContextTypeOpenGL:
395                 {
396                 if(m_stereoVisual)
397                         sPreferredFormat.dwFlags |= PFD_STEREO;
398
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;
403                         break;
404                 }
405                 if (::SetPixelFormat(m_hDC, iPixelFormat, &sPreferredFormat) == FALSE) {
406                         success = GHOST_kFailure;
407                         break;
408                 }
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);
414                 if (m_hGlRc) {
415                         if (s_firsthGLRc) {
416                                 wglShareLists(s_firsthGLRc, m_hGlRc);
417                         } else {
418                                 s_firsthGLRc = m_hGlRc;
419                         }
420
421                         success = ::wglMakeCurrent(m_hDC, m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
422                 }
423                 else {
424                         success = GHOST_kFailure;
425                 }
426                 }
427                 break;
428
429         case GHOST_kDrawingContextTypeNone:
430                 success = GHOST_kSuccess;
431                 break;
432
433         default:
434                 success = GHOST_kFailure;
435         }
436         return success;
437 }
438
439
440 GHOST_TSuccess GHOST_WindowWin32::removeDrawingContext()
441 {
442         GHOST_TSuccess success;
443         switch (m_drawingContextType) {
444         case GHOST_kDrawingContextTypeOpenGL:
445                 if (m_hGlRc) {
446                         success = ::wglDeleteContext(m_hGlRc) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
447                         if (m_hGlRc == s_firsthGLRc) {
448                                 s_firsthGLRc = NULL;
449                         }
450                         m_hGlRc = 0;
451                 }
452                 else {
453                         success = GHOST_kFailure;
454                 }
455                 break;
456         case GHOST_kDrawingContextTypeNone:
457                 success = GHOST_kSuccess;
458                 break;
459         default:
460                 success = GHOST_kFailure;
461         }
462         return success;
463 }
464
465 void GHOST_WindowWin32::lostMouseCapture()
466 {
467         if (m_hasMouseCaptured) {
468                 m_hasMouseCaptured = false;
469                 m_nPressedButtons = 0;
470         }
471 }
472
473 void GHOST_WindowWin32::registerMouseClickEvent(bool press)
474 {
475         if (press) {
476                 if (!m_hasMouseCaptured) {
477                         ::SetCapture(m_hWnd);
478                         m_hasMouseCaptured = true;
479                 }
480                 m_nPressedButtons++;
481         } else {
482                 if (m_nPressedButtons) {
483                         m_nPressedButtons--;
484                         if (!m_nPressedButtons) {
485                                 ::ReleaseCapture();
486                                 m_hasMouseCaptured = false;
487                         }
488                 }
489         }
490 }
491
492
493 void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
494 {
495         if (!visible) {
496                 while (::ShowCursor(FALSE) >= 0);
497         }
498         else {
499                 while (::ShowCursor(TRUE) < 0);
500         }
501
502         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
503                 ::SetCursor( m_customCursor );
504         } else {
505                 // Convert GHOST cursor to Windows OEM cursor
506                 bool success = true;
507                 LPCSTR id;
508                 switch (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_CROSS;         break;
530                         default:
531                         success = false;
532                 }
533                 
534                 if (success) {
535                         HCURSOR hCursor = ::SetCursor(::LoadCursor(0, id));
536                 }
537         }
538 }
539
540 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
541 {
542         if (::GetForegroundWindow() == m_hWnd) {
543                 loadCursor(visible, getCursorShape());
544         }
545
546         return GHOST_kSuccess;
547 }
548
549 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
550 {
551         if (m_customCursor) {
552                 DestroyCursor(m_customCursor);
553                 m_customCursor = NULL;
554         }
555
556         if (::GetForegroundWindow() == m_hWnd) {
557                 loadCursor(getCursorVisibility(), cursorShape);
558         }
559
560         return GHOST_kSuccess;
561 }
562
563 /** Reverse the bits in a GHOST_TUns16 */
564 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
565 {
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);
570         return shrt;
571 }
572
573 GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY)
574 {
575         GHOST_TUns32 andData[32];
576         GHOST_TUns32 xorData[32];
577         int y;
578
579         if (m_customCursor) {
580                 DestroyCursor(m_customCursor);
581                 m_customCursor = NULL;
582         }
583
584         memset(&andData, 0xFF, sizeof(andData));
585         memset(&xorData, 0, sizeof(xorData));
586
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));
590
591                 xorData[y]= fullBitRow & fullMaskRow;
592                 andData[y]= ~fullMaskRow;
593         }
594
595         m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
596         if (!m_customCursor) {
597                 return GHOST_kFailure;
598         }
599
600         if (::GetForegroundWindow() == m_hWnd) {
601                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
602         }
603
604         return GHOST_kSuccess;
605 }
606