Don't use CS_CLASSDC after all. Makes drawing multiple windows funky.
[blender-staging.git] / intern / ghost / intern / GHOST_SystemWin32.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL 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.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): none yet.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /**
30
31  * $Id$
32  * Copyright (C) 2001 NaN Technologies B.V.
33  * @author      Maarten Gribnau
34  * @date        May 7, 2001
35  */
36
37 #include <iostream>
38
39 #include "GHOST_SystemWin32.h"
40 #include "GHOST_EventDragnDrop.h"
41
42 #define WIN32_LEAN_AND_MEAN
43 #ifdef _WIN32_IE
44 #undef _WIN32_IE
45 #endif
46 #define _WIN32_IE 0x0501
47 #include <windows.h>
48 #include <shlobj.h>
49
50 // win64 doesn't define GWL_USERDATA
51 #ifdef WIN32
52 #ifndef GWL_USERDATA
53 #define GWL_USERDATA GWLP_USERDATA
54 #define GWL_WNDPROC GWLP_WNDPROC
55 #endif
56 #endif
57
58 /*
59  * According to the docs the mouse wheel message is supported from windows 98 
60  * upwards. Leaving WINVER at default value, the WM_MOUSEWHEEL message and the 
61  * wheel detent value are undefined.
62  */
63 #ifndef WM_MOUSEWHEEL
64 #define WM_MOUSEWHEEL 0x020A
65 #endif // WM_MOUSEWHEEL
66 #ifndef WHEEL_DELTA
67 #define WHEEL_DELTA 120 /* Value for rolling one detent, (old convention! MS changed it) */
68 #endif // WHEEL_DELTA
69
70 /* 
71  * Defines for mouse buttons 4 and 5 aka xbutton1 and xbutton2.
72  * MSDN: Declared in Winuser.h, include Windows.h 
73  * This does not seem to work with MinGW so we define our own here.
74  */
75 #ifndef XBUTTON1
76 #define XBUTTON1 0x0001
77 #endif // XBUTTON1
78 #ifndef XBUTTON2
79 #define XBUTTON2 0x0002
80 #endif // XBUTTON2
81 #ifndef WM_XBUTTONUP
82 #define WM_XBUTTONUP 524
83 #endif // WM_XBUTTONUP
84 #ifndef WM_XBUTTONDOWN
85 #define WM_XBUTTONDOWN 523
86 #endif // WM_XBUTTONDOWN
87
88 #include "GHOST_Debug.h"
89 #include "GHOST_DisplayManagerWin32.h"
90 #include "GHOST_EventButton.h"
91 #include "GHOST_EventCursor.h"
92 #include "GHOST_EventKey.h"
93 #include "GHOST_EventWheel.h"
94 #include "GHOST_EventNDOF.h"
95 #include "GHOST_TimerTask.h"
96 #include "GHOST_TimerManager.h"
97 #include "GHOST_WindowManager.h"
98 #include "GHOST_WindowWin32.h"
99 #include "GHOST_NDOFManager.h"
100
101 // Key code values not found in winuser.h
102 #ifndef VK_MINUS
103 #define VK_MINUS 0xBD
104 #endif // VK_MINUS
105 #ifndef VK_SEMICOLON
106 #define VK_SEMICOLON 0xBA
107 #endif // VK_SEMICOLON
108 #ifndef VK_PERIOD
109 #define VK_PERIOD 0xBE
110 #endif // VK_PERIOD
111 #ifndef VK_COMMA
112 #define VK_COMMA 0xBC
113 #endif // VK_COMMA
114 #ifndef VK_QUOTE
115 #define VK_QUOTE 0xDE
116 #endif // VK_QUOTE
117 #ifndef VK_BACK_QUOTE
118 #define VK_BACK_QUOTE 0xC0
119 #endif // VK_BACK_QUOTE
120 #ifndef VK_SLASH
121 #define VK_SLASH 0xBF
122 #endif // VK_SLASH
123 #ifndef VK_BACK_SLASH
124 #define VK_BACK_SLASH 0xDC
125 #endif // VK_BACK_SLASH
126 #ifndef VK_EQUALS
127 #define VK_EQUALS 0xBB
128 #endif // VK_EQUALS
129 #ifndef VK_OPEN_BRACKET
130 #define VK_OPEN_BRACKET 0xDB
131 #endif // VK_OPEN_BRACKET
132 #ifndef VK_CLOSE_BRACKET
133 #define VK_CLOSE_BRACKET 0xDD
134 #endif // VK_CLOSE_BRACKET
135 #ifndef VK_GR_LESS
136 #define VK_GR_LESS 0xE2
137 #endif // VK_GR_LESS
138
139
140 GHOST_SystemWin32::GHOST_SystemWin32()
141 : m_hasPerformanceCounter(false), m_freq(0), m_start(0)
142 {
143         m_displayManager = new GHOST_DisplayManagerWin32 ();
144         GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
145         m_displayManager->initialize();
146         
147         // Check if current keyboard layout uses AltGr
148         this->keyboardAltGr();
149         
150         // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
151         OleInitialize(0);
152 }
153
154 GHOST_SystemWin32::~GHOST_SystemWin32()
155 {
156         // Shutdown COM
157         OleUninitialize();
158 }
159
160
161 GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const
162 {
163         // Hardware does not support high resolution timers. We will use GetTickCount instead then.
164         if (!m_hasPerformanceCounter) {
165                 return ::GetTickCount();
166         }
167
168         // Retrieve current count
169         __int64 count = 0;
170         ::QueryPerformanceCounter((LARGE_INTEGER*)&count);
171
172         // Calculate the time passed since system initialization.
173         __int64 delta = 1000*(count-m_start);
174
175         GHOST_TUns64 t = (GHOST_TUns64)(delta/m_freq);
176         return t; 
177 }
178
179
180 GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const
181 {
182         GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n");
183         GHOST_TUns8 numDisplays;
184         m_displayManager->getNumDisplays(numDisplays);
185         return numDisplays;
186 }
187
188
189 void GHOST_SystemWin32::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
190 {
191         width = ::GetSystemMetrics(SM_CXSCREEN);
192         height= ::GetSystemMetrics(SM_CYSCREEN);
193 }
194
195
196 GHOST_IWindow* GHOST_SystemWin32::createWindow(
197         const STR_String& title, 
198         GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height,
199         GHOST_TWindowState state, GHOST_TDrawingContextType type,
200         bool stereoVisual, const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow )
201 {
202         GHOST_Window* window = 0;
203         window = new GHOST_WindowWin32 (this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples);
204         if (window) {
205                 if (window->getValid()) {
206                         // Store the pointer to the window
207 //                      if (state != GHOST_kWindowStateFullScreen) {
208                                 m_windowManager->addWindow(window);
209 //                      }
210                 }
211                 else {
212                         // An invalid window could be one that was used to test for AA
213                         window = ((GHOST_WindowWin32*)window)->getNextWindow();
214                         
215                         // If another window is found, let the wm know about that one, but not the old one
216                         if (window->getValid()) {
217                                 m_windowManager->addWindow(window);
218                         }
219                         else {
220                                 delete window;
221                                 window = 0;
222                         }
223
224                 }
225         }
226         return window;
227 }
228
229
230 bool GHOST_SystemWin32::processEvents(bool waitForEvent)
231 {
232         MSG msg;
233         bool anyProcessed = false;
234
235         do {
236                 GHOST_TimerManager* timerMgr = getTimerManager();
237
238                 if (waitForEvent && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
239 #if 1
240                         ::Sleep(1);
241 #else
242                         GHOST_TUns64 next = timerMgr->nextFireTime();
243                         GHOST_TInt64 maxSleep = next - getMilliSeconds();
244                         
245                         if (next == GHOST_kFireTimeNever) {
246                                 ::WaitMessage();
247                         } else if(maxSleep >= 0.0) {
248                                 ::SetTimer(NULL, 0, maxSleep, NULL);
249                                 ::WaitMessage();
250                                 ::KillTimer(NULL, 0);
251                         }
252 #endif
253                 }
254
255                 if (timerMgr->fireTimers(getMilliSeconds())) {
256                         anyProcessed = true;
257                 }
258
259                 // Process all the events waiting for us
260                 while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0) {
261                         ::TranslateMessage(&msg);
262                         ::DispatchMessage(&msg);
263                         anyProcessed = true;
264                 }
265         } while (waitForEvent && !anyProcessed);
266
267         return anyProcessed;
268 }
269
270
271 GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
272 {
273         POINT point;
274         if(::GetCursorPos(&point)){
275                 x = point.x;
276                 y = point.y;
277                 return GHOST_kSuccess;
278         }
279         return GHOST_kFailure;
280 }
281
282
283 GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
284 {
285         if (!GetActiveWindow())
286                 return GHOST_kFailure;
287         return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
288 }
289
290
291 GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys& keys) const
292 {
293         bool down = HIBYTE(::GetKeyState(VK_SHIFT)) != 0;
294         keys.set(GHOST_kModifierKeyLeftShift, down);
295         keys.set(GHOST_kModifierKeyRightShift, down);
296         
297         down = HIBYTE(::GetKeyState(VK_MENU)) != 0;
298         keys.set(GHOST_kModifierKeyLeftAlt, down);
299         keys.set(GHOST_kModifierKeyRightAlt, down);
300         
301         down = HIBYTE(::GetKeyState(VK_CONTROL)) != 0;
302         keys.set(GHOST_kModifierKeyLeftControl, down);
303         keys.set(GHOST_kModifierKeyRightControl, down);
304         
305         bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
306         bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
307         if(lwindown || rwindown)
308                 keys.set(GHOST_kModifierKeyOS, true);
309         else
310                 keys.set(GHOST_kModifierKeyOS, false);
311         return GHOST_kSuccess;
312 }
313
314
315 GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons& buttons) const
316 {
317         /* Check for swapped buttons (left-handed mouse buttons)
318          * GetAsyncKeyState() will give back the state of the physical mouse buttons.
319          */
320         bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;
321
322         bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0;
323         buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);
324
325         down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0;
326         buttons.set(GHOST_kButtonMaskMiddle, down);
327
328         down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0;
329         buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
330         return GHOST_kSuccess;
331 }
332
333
334 GHOST_TSuccess GHOST_SystemWin32::init()
335 {
336         GHOST_TSuccess success = GHOST_System::init();
337
338         /* Disable scaling on high DPI displays on Vista */
339         HMODULE user32 = ::LoadLibraryA("user32.dll");
340         typedef BOOL (WINAPI * LPFNSETPROCESSDPIAWARE)();
341         LPFNSETPROCESSDPIAWARE SetProcessDPIAware =
342                 (LPFNSETPROCESSDPIAWARE)GetProcAddress(user32, "SetProcessDPIAware");
343         if (SetProcessDPIAware)
344                 SetProcessDPIAware();
345         FreeLibrary(user32);
346
347         // Determine whether this system has a high frequency performance counter. */
348         m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq) == TRUE;
349         if (m_hasPerformanceCounter) {
350                 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n")
351                 ::QueryPerformanceCounter((LARGE_INTEGER*)&m_start);
352         }
353         else {
354                 GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n")
355         }
356
357         if (success) {
358                 WNDCLASS wc;
359                 wc.style= CS_HREDRAW | CS_VREDRAW;
360                 wc.lpfnWndProc= s_wndProc;
361                 wc.cbClsExtra= 0;
362                 wc.cbWndExtra= 0;
363                 wc.hInstance= ::GetModuleHandle(0);
364                 wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
365                 
366                 if (!wc.hIcon) {
367                         ::LoadIcon(NULL, IDI_APPLICATION);
368                 }
369                 wc.hCursor = ::LoadCursor(0, IDC_ARROW);
370                 wc.hbrBackground= (HBRUSH)::GetStockObject(BLACK_BRUSH);
371                 wc.lpszMenuName = 0;
372                 wc.lpszClassName= GHOST_WindowWin32::getWindowClassName();
373
374                 // Use RegisterClassEx for setting small icon
375                 if (::RegisterClass(&wc) == 0) {
376                         success = GHOST_kFailure;
377                 }
378         }
379         return success;
380 }
381
382
383 GHOST_TSuccess GHOST_SystemWin32::exit()
384 {
385         return GHOST_System::exit();
386 }
387
388
389 GHOST_TKey GHOST_SystemWin32::convertKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam) const
390 {
391         bool isExtended = (lParam&(1<<24))?true:false;
392         
393         GHOST_TKey key;
394         GHOST_ModifierKeys oldModifiers, newModifiers;
395         ((GHOST_SystemWin32*)getSystem())->retrieveModifierKeys(oldModifiers);
396         ((GHOST_SystemWin32*)getSystem())->getModifierKeys(newModifiers);
397
398         if ((wParam >= '0') && (wParam <= '9')) {
399                 // VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
400                 key = (GHOST_TKey)(wParam - '0' + GHOST_kKey0);
401         }
402         else if ((wParam >= 'A') && (wParam <= 'Z')) {
403                 // VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
404                 key = (GHOST_TKey)(wParam - 'A' + GHOST_kKeyA);
405         }
406         else if ((wParam >= VK_F1) && (wParam <= VK_F24)) {
407                 key = (GHOST_TKey)(wParam - VK_F1 + GHOST_kKeyF1);
408         }
409         else {
410                 switch (wParam) {
411                 case VK_RETURN:
412                         key = isExtended?GHOST_kKeyNumpadEnter:GHOST_kKeyEnter;
413                         break;
414
415                 case VK_BACK:     key = GHOST_kKeyBackSpace;            break;
416                 case VK_TAB:      key = GHOST_kKeyTab;                          break;
417                 case VK_ESCAPE:   key = GHOST_kKeyEsc;                          break;
418                 case VK_SPACE:    key = GHOST_kKeySpace;                        break;
419                 case VK_PRIOR:    key = GHOST_kKeyUpPage;                       break;
420                 case VK_NEXT:     key = GHOST_kKeyDownPage;                     break;
421                 case VK_END:      key = GHOST_kKeyEnd;                          break;
422                 case VK_HOME:     key = GHOST_kKeyHome;                         break;
423                 case VK_INSERT:   key = GHOST_kKeyInsert;                       break;
424                 case VK_DELETE:   key = GHOST_kKeyDelete;                       break;
425                 case VK_LEFT:     key = GHOST_kKeyLeftArrow;            break;
426                 case VK_RIGHT:    key = GHOST_kKeyRightArrow;           break;
427                 case VK_UP:       key = GHOST_kKeyUpArrow;                      break;
428                 case VK_DOWN:     key = GHOST_kKeyDownArrow;            break;
429                 case VK_NUMPAD0:  key = GHOST_kKeyNumpad0;                      break;
430                 case VK_NUMPAD1:  key = GHOST_kKeyNumpad1;                      break;
431                 case VK_NUMPAD2:  key = GHOST_kKeyNumpad2;                      break;
432                 case VK_NUMPAD3:  key = GHOST_kKeyNumpad3;                      break;
433                 case VK_NUMPAD4:  key = GHOST_kKeyNumpad4;                      break;
434                 case VK_NUMPAD5:  key = GHOST_kKeyNumpad5;                      break;
435                 case VK_NUMPAD6:  key = GHOST_kKeyNumpad6;                      break;
436                 case VK_NUMPAD7:  key = GHOST_kKeyNumpad7;                      break;
437                 case VK_NUMPAD8:  key = GHOST_kKeyNumpad8;                      break;
438                 case VK_NUMPAD9:  key = GHOST_kKeyNumpad9;                      break;
439                 case VK_SNAPSHOT: key = GHOST_kKeyPrintScreen;          break;
440                 case VK_PAUSE:    key = GHOST_kKeyPause;                        break;
441                 case VK_MULTIPLY: key = GHOST_kKeyNumpadAsterisk;        break;
442                 case VK_SUBTRACT: key = GHOST_kKeyNumpadMinus;          break;
443                 case VK_DECIMAL:  key = GHOST_kKeyNumpadPeriod;         break;
444                 case VK_DIVIDE:   key = GHOST_kKeyNumpadSlash;          break;
445                 case VK_ADD:      key = GHOST_kKeyNumpadPlus;           break;
446
447                 case VK_SEMICOLON:              key = GHOST_kKeySemicolon;              break;
448                 case VK_EQUALS:                 key = GHOST_kKeyEqual;                  break;
449                 case VK_COMMA:                  key = GHOST_kKeyComma;                  break;
450                 case VK_MINUS:                  key = GHOST_kKeyMinus;                  break;
451                 case VK_PERIOD:                 key = GHOST_kKeyPeriod;                 break;
452                 case VK_SLASH:                  key = GHOST_kKeySlash;                  break;
453                 case VK_BACK_QUOTE:             key = GHOST_kKeyAccentGrave;    break;
454                 case VK_OPEN_BRACKET:   key = GHOST_kKeyLeftBracket;    break;
455                 case VK_BACK_SLASH:             key = GHOST_kKeyBackslash;              break;
456                 case VK_CLOSE_BRACKET:  key = GHOST_kKeyRightBracket;   break;
457                 case VK_QUOTE:                  key = GHOST_kKeyQuote;                  break;
458                 case VK_GR_LESS:                key = GHOST_kKeyGrLess;                 break;
459
460                 case VK_SHIFT:
461                         {
462                                 bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftShift) != newModifiers.get(GHOST_kModifierKeyLeftShift);
463                                 if(lchanged) {
464                                         key = GHOST_kKeyLeftShift;
465                                 } else {
466                                         key = GHOST_kKeyRightShift;
467                                 }
468                         }
469                         break;
470                 case VK_CONTROL:
471                         {
472                                 bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftControl) != newModifiers.get(GHOST_kModifierKeyLeftControl);
473                                 if(lchanged) {
474                                         key = GHOST_kKeyLeftControl;
475                                 } else {
476                                         key = GHOST_kKeyRightControl;
477                                 }
478                         }
479                         break;
480                 case VK_MENU:
481                         {
482                                 if(m_hasAltGr && isExtended) {
483                                         // We have here an extended RAlt, which is AltGr. The keyboard driver on Windows sends before this a LControl, so
484                                         // to be able to input characters created with AltGr (normal on German, French, Finnish and other keyboards) we
485                                         // push an extra LControl up event. This ensures we don't have a 'hanging' ctrl event in Blender windowmanager
486                                         // when typing in Text editor or Console.
487                                         GHOST_Event *extra = new GHOST_EventKey(getSystem()->getMilliSeconds(), GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl, '\0');
488                                         ((GHOST_SystemWin32*)getSystem())->pushEvent(extra);
489                                         newModifiers.set(GHOST_kModifierKeyRightControl, false);
490                                         newModifiers.set(GHOST_kModifierKeyLeftControl, false);
491                                 }
492                                 bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftAlt) != newModifiers.get(GHOST_kModifierKeyLeftAlt);
493                                 if(lchanged) {
494                                         key = GHOST_kKeyLeftAlt;
495                                 } else {
496                                         key = GHOST_kKeyRightAlt;
497                                 }
498                         }
499                         break;
500                 case VK_LWIN:
501                 case VK_RWIN:
502                         key = GHOST_kKeyOS;
503                         break;
504                 case VK_NUMLOCK: key = GHOST_kKeyNumLock; break;
505                 case VK_SCROLL: key = GHOST_kKeyScrollLock; break;
506                 case VK_CAPITAL: key = GHOST_kKeyCapsLock; break;
507                 default:
508                         key = GHOST_kKeyUnknown;
509                         break;
510                 }
511         }
512         ((GHOST_SystemWin32*)getSystem())->storeModifierKeys(newModifiers);
513         return key;
514 }
515
516 GHOST_EventButton* GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TButtonMask mask)
517 {
518         return new GHOST_EventButton (getSystem()->getMilliSeconds(), type, window, mask);
519 }
520
521
522 GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow)
523 {
524         GHOST_TInt32 x_screen, y_screen;
525         GHOST_SystemWin32 * system = ((GHOST_SystemWin32 * ) getSystem());
526         GHOST_WindowWin32 * window = ( GHOST_WindowWin32 * ) Iwindow;
527         
528         system->getCursorPosition(x_screen, y_screen);
529
530         if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
531         {
532                 GHOST_TInt32 x_new= x_screen;
533                 GHOST_TInt32 y_new= y_screen;
534                 GHOST_TInt32 x_accum, y_accum;
535                 GHOST_Rect bounds;
536
537                 /* fallback to window bounds */
538                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure){
539                         window->getClientBounds(bounds);
540                 }
541
542                 /* could also clamp to screen bounds
543                  * wrap with a window outside the view will fail atm  */
544
545                 bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */
546
547                 window->getCursorGrabAccum(x_accum, y_accum);
548                 if(x_new != x_screen|| y_new != y_screen) {
549                         /* when wrapping we don't need to add an event because the
550                          * setCursorPosition call will cause a new event after */
551                         system->setCursorPosition(x_new, y_new); /* wrap */
552                         window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
553                 }else{
554                         return new GHOST_EventCursor(system->getMilliSeconds(),
555                                                                                  GHOST_kEventCursorMove,
556                                                                                  window,
557                                                                                  x_screen + x_accum,
558                                                                                  y_screen + y_accum
559                         );
560                 }
561
562         }
563         else {
564                 return new GHOST_EventCursor(system->getMilliSeconds(),
565                                                                          GHOST_kEventCursorMove,
566                                                                          window,
567                                                                          x_screen,
568                                                                          y_screen
569                 );
570         }
571         return NULL;
572 }
573
574
575 GHOST_EventWheel* GHOST_SystemWin32::processWheelEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam)
576 {
577         // short fwKeys = LOWORD(wParam);                       // key flags
578         int zDelta = (short) HIWORD(wParam);    // wheel rotation
579         
580         // zDelta /= WHEEL_DELTA;
581         // temporary fix below: microsoft now has added more precision, making the above division not work
582         if (zDelta <= 0 ) zDelta= -1; else zDelta= 1;   
583         
584         // short xPos = (short) LOWORD(lParam); // horizontal position of pointer
585         // short yPos = (short) HIWORD(lParam); // vertical position of pointer
586         return new GHOST_EventWheel (getSystem()->getMilliSeconds(), window, zDelta);
587 }
588
589
590 GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, bool keyDown, WPARAM wParam, LPARAM lParam)
591 {
592         GHOST_TKey key = ((GHOST_SystemWin32*)getSystem())->convertKey(window, wParam, lParam);
593         GHOST_EventKey* event;
594         if (key != GHOST_kKeyUnknown) {
595                 MSG keyMsg;
596                 char ascii = '\0';
597
598                         /* Eat any character related messages */
599                 if (::PeekMessage(&keyMsg, NULL, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) {
600                         ascii = (char) keyMsg.wParam;
601                         
602                 }
603
604                 event = new GHOST_EventKey(getSystem()->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, ascii);
605         }
606         else {
607                 event = 0;
608         }
609         return event;
610 }
611
612
613 GHOST_Event* GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window)
614 {
615         return new GHOST_Event(getSystem()->getMilliSeconds(), type, window);
616 }
617
618 GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType, 
619                                                                                                         GHOST_TDragnDropTypes draggedObjectType,
620                                                                                                         GHOST_IWindow* window,
621                                                                                                         int mouseX, int mouseY,
622                                                                                                         void* data)
623 {
624         GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
625         return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
626                                                                                                           eventType,
627                                                                                                           draggedObjectType,
628                                                                                                           window,mouseX,mouseY,data)
629                         );
630 }
631
632 void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
633 {
634         minmax->ptMinTrackSize.x=320;
635         minmax->ptMinTrackSize.y=240;
636 }
637
638
639 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
640 {
641         GHOST_Event* event = 0;
642         LRESULT lResult = 0;
643         GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
644         GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized")
645
646         if (hwnd) {
647                 GHOST_WindowWin32* window = (GHOST_WindowWin32*)::GetWindowLong(hwnd, GWL_USERDATA);
648                 if (window) {
649                         switch (msg) {
650                                 // we need to check if new key layout has AltGr
651                                 case WM_INPUTLANGCHANGE:
652                                         system->keyboardAltGr();
653                                         break;
654                                 ////////////////////////////////////////////////////////////////////////
655                                 // Keyboard events, processed
656                                 ////////////////////////////////////////////////////////////////////////
657                                 case WM_KEYDOWN:
658                                 case WM_SYSKEYDOWN:
659                                         event = processKeyEvent(window, true, wParam, lParam);
660                                         if (!event) {
661                                                 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
662                                                 GHOST_PRINT(msg)
663                                                 GHOST_PRINT(" key ignored\n")
664                                         }
665                                         break;
666
667                                 case WM_KEYUP:
668                                 case WM_SYSKEYUP:
669                                         event = processKeyEvent(window, false, wParam, lParam);
670                                         if (!event) {
671                                                 GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
672                                                 GHOST_PRINT(msg)
673                                                 GHOST_PRINT(" key ignored\n")
674                                         }
675                                         break;
676
677                                 ////////////////////////////////////////////////////////////////////////
678                                 // Keyboard events, ignored
679                                 ////////////////////////////////////////////////////////////////////////
680                                 case WM_CHAR:
681                                         /* The WM_CHAR message is posted to the window with the keyboard focus when 
682                                          * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR 
683                                          * contains the character code of the key that was pressed. 
684                                          */
685                                 case WM_DEADCHAR:
686                                         /* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
687                                          * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR 
688                                          * specifies a character code generated by a dead key. A dead key is a key that 
689                                          * generates a character, such as the umlaut (double-dot), that is combined with 
690                                          * another character to form a composite character. For example, the umlaut-O 
691                                          * character (Ù) is generated by typing the dead key for the umlaut character, and 
692                                          * then typing the O key.
693                                          */
694                                 case WM_SYSDEADCHAR:
695                                         /* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when 
696                                          * a WM_SYSKEYDOWN message is translated by the TranslateMessage function. 
697                                          * WM_SYSDEADCHAR specifies the character code of a system dead key - that is, 
698                                          * a dead key that is pressed while holding down the alt key. 
699                                          */
700                                         break;
701                                 ////////////////////////////////////////////////////////////////////////
702                                 // Tablet events, processed
703                                 ////////////////////////////////////////////////////////////////////////
704                                 case WT_PACKET:
705                                         ((GHOST_WindowWin32*)window)->processWin32TabletEvent(wParam, lParam);
706                                         break;
707                                 case WT_CSRCHANGE:
708                                 case WT_PROXIMITY:
709                                         ((GHOST_WindowWin32*)window)->processWin32TabletInitEvent();
710                                         break;
711                                 ////////////////////////////////////////////////////////////////////////
712                                 // Mouse events, processed
713                                 ////////////////////////////////////////////////////////////////////////
714                                 case WM_LBUTTONDOWN:
715                                         window->registerMouseClickEvent(true);
716                                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
717                                         break;
718                                 case WM_MBUTTONDOWN:
719                                         window->registerMouseClickEvent(true);
720                                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
721                                         break;
722                                 case WM_RBUTTONDOWN:
723                                         window->registerMouseClickEvent(true);
724                                         event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
725                                         break;
726                                 case WM_XBUTTONDOWN:
727                                         window->registerMouseClickEvent(true);
728                                         if ((short) HIWORD(wParam) == XBUTTON1){
729                                                 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
730                                         }else if((short) HIWORD(wParam) == XBUTTON2){
731                                                 event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
732                                         }
733                                         break;
734                                 case WM_LBUTTONUP:
735                                         window->registerMouseClickEvent(false);
736                                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
737                                         break;
738                                 case WM_MBUTTONUP:
739                                         window->registerMouseClickEvent(false);
740                                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
741                                         break;
742                                 case WM_RBUTTONUP:
743                                         window->registerMouseClickEvent(false);
744                                         event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
745                                         break;
746                                 case WM_XBUTTONUP:
747                                         window->registerMouseClickEvent(false);
748                                         if ((short) HIWORD(wParam) == XBUTTON1){
749                                                 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
750                                         }else if((short) HIWORD(wParam) == XBUTTON2){
751                                                 event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
752                                         }
753                                         break;
754                                 case WM_MOUSEMOVE:
755                                         event = processCursorEvent(GHOST_kEventCursorMove, window);
756                                         break;
757                                 case WM_MOUSEWHEEL:
758                                         /* The WM_MOUSEWHEEL message is sent to the focus window 
759                                          * when the mouse wheel is rotated. The DefWindowProc 
760                                          * function propagates the message to the window's parent.
761                                          * There should be no internal forwarding of the message, 
762                                          * since DefWindowProc propagates it up the parent chain 
763                                          * until it finds a window that processes it.
764                                          */
765                                         event = processWheelEvent(window, wParam, lParam);
766                                         break;
767                                 case WM_SETCURSOR:
768                                         /* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
769                                          * to move within a window and mouse input is not captured.
770                                          * This means we have to set the cursor shape every time the mouse moves!
771                                          * The DefWindowProc function uses this message to set the cursor to an 
772                                          * arrow if it is not in the client area.
773                                          */
774                                         if (LOWORD(lParam) == HTCLIENT) {
775                                                 // Load the current cursor
776                                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
777                                                 // Bypass call to DefWindowProc
778                                                 return 0;
779                                         } 
780                                         else {
781                                                 // Outside of client area show standard cursor
782                                                 window->loadCursor(true, GHOST_kStandardCursorDefault);
783                                         }
784                                         break;
785
786                                 ////////////////////////////////////////////////////////////////////////
787                                 // Mouse events, ignored
788                                 ////////////////////////////////////////////////////////////////////////
789                                 case WM_NCMOUSEMOVE:
790                                         /* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved 
791                                          * within the nonclient area of the window. This message is posted to the window 
792                                          * that contains the cursor. If a window has captured the mouse, this message is not posted.
793                                          */
794                                 case WM_NCHITTEST:
795                                         /* The WM_NCHITTEST message is sent to a window when the cursor moves, or 
796                                          * when a mouse button is pressed or released. If the mouse is not captured, 
797                                          * the message is sent to the window beneath the cursor. Otherwise, the message 
798                                          * is sent to the window that has captured the mouse. 
799                                          */
800                                         break;
801
802                                 ////////////////////////////////////////////////////////////////////////
803                                 // Window events, processed
804                                 ////////////////////////////////////////////////////////////////////////
805                                 case WM_CLOSE:
806                                         /* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
807                                         event = processWindowEvent(GHOST_kEventWindowClose, window);
808                                         break;
809                                 case WM_ACTIVATE:
810                                         /* The WM_ACTIVATE message is sent to both the window being activated and the window being 
811                                          * deactivated. If the windows use the same input queue, the message is sent synchronously, 
812                                          * first to the window procedure of the top-level window being deactivated, then to the window
813                                          * procedure of the top-level window being activated. If the windows use different input queues,
814                                          * the message is sent asynchronously, so the window is activated immediately. 
815                                          */
816                                         event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
817                                         /* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
818                                         will not be dispatched to OUR active window if we minimize one of OUR windows. */
819                                         lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
820                                         break;
821                                 case WM_PAINT:
822                                         /* An application sends the WM_PAINT message when the system or another application 
823                                          * makes a request to paint a portion of an application's window. The message is sent
824                                          * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage 
825                                          * function when the application obtains a WM_PAINT message by using the GetMessage or 
826                                          * PeekMessage function. 
827                                          */
828                                         event = processWindowEvent(GHOST_kEventWindowUpdate, window);
829                                         ::ValidateRect(hwnd, NULL);
830                                         break;
831                                 case WM_GETMINMAXINFO:
832                                         /* The WM_GETMINMAXINFO message is sent to a window when the size or 
833                                          * position of the window is about to change. An application can use 
834                                          * this message to override the window's default maximized size and 
835                                          * position, or its default minimum or maximum tracking size. 
836                                          */
837                                         processMinMaxInfo((MINMAXINFO *) lParam);
838                                         /* Let DefWindowProc handle it. */
839                                         break;
840                                 case WM_SIZE:
841                                         /* The WM_SIZE message is sent to a window after its size has changed.
842                                          * The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
843                                          * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
844                                          * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
845                                          * message without calling DefWindowProc.
846                                          */
847                                         event = processWindowEvent(GHOST_kEventWindowSize, window);
848                                         break;
849                                 case WM_CAPTURECHANGED:
850                                         window->lostMouseCapture();
851                                         break;
852                                 case WM_MOVING:
853                                         /* The WM_MOVING message is sent to a window that the user is moving. By processing 
854                                          * this message, an application can monitor the size and position of the drag rectangle
855                                          * and, if needed, change its size or position.
856                                          */
857                                 case WM_MOVE:
858                                         /* The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
859                                          * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
860                                          * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
861                                          * message without calling DefWindowProc. 
862                                          */
863                                         event = processWindowEvent(GHOST_kEventWindowMove, window);
864                                         break;
865                                 ////////////////////////////////////////////////////////////////////////
866                                 // Window events, ignored
867                                 ////////////////////////////////////////////////////////////////////////
868                                 case WM_WINDOWPOSCHANGED:
869                                         /* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
870                                          * in the Z order has changed as a result of a call to the SetWindowPos function or 
871                                          * another window-management function.
872                                          * The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
873                                          * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
874                                          * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
875                                          * message without calling DefWindowProc.
876                                          */
877                                 case WM_ERASEBKGND:
878                                         /* An application sends the WM_ERASEBKGND message when the window background must be 
879                                          * erased (for example, when a window is resized). The message is sent to prepare an 
880                                          * invalidated portion of a window for painting. 
881                                          */
882                                 case WM_NCPAINT:
883                                         /* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
884                                 case WM_NCACTIVATE:
885                                         /* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed 
886                                          * to indicate an active or inactive state. 
887                                          */
888                                 case WM_DESTROY:
889                                         /* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window 
890                                          * procedure of the window being destroyed after the window is removed from the screen. 
891                                          * This message is sent first to the window being destroyed and then to the child windows 
892                                          * (if any) as they are destroyed. During the processing of the message, it can be assumed 
893                                          * that all child windows still exist. 
894                                          */
895                                 case WM_NCDESTROY:
896                                         /* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The 
897                                          * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
898                                          * message. WM_DESTROY is used to free the allocated memory object associated with the window. 
899                                          */
900                                 case WM_KILLFOCUS:
901                                         /* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. */
902                                 case WM_SHOWWINDOW:
903                                         /* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
904                                 case WM_WINDOWPOSCHANGING:
905                                         /* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in 
906                                          * the Z order is about to change as a result of a call to the SetWindowPos function or 
907                                          * another window-management function. 
908                                          */
909                                 case WM_SETFOCUS:
910                                         /* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
911                                 case WM_ENTERSIZEMOVE:
912                                         /* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving 
913                                          * or sizing modal loop. The window enters the moving or sizing modal loop when the user 
914                                          * clicks the window's title bar or sizing border, or when the window passes the 
915                                          * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the 
916                                          * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when 
917                                          * DefWindowProc returns. 
918                                          */
919                                         break;
920                                         
921                                 ////////////////////////////////////////////////////////////////////////
922                                 // Other events
923                                 ////////////////////////////////////////////////////////////////////////
924                                 case WM_GETTEXT:
925                                         /* An application sends a WM_GETTEXT message to copy the text that 
926                                          * corresponds to a window into a buffer provided by the caller. 
927                                          */
928                                 case WM_ACTIVATEAPP:
929                                         /* The WM_ACTIVATEAPP message is sent when a window belonging to a 
930                                          * different application than the active window is about to be activated.
931                                          * The message is sent to the application whose window is being activated
932                                          * and to the application whose window is being deactivated. 
933                                          */
934                                 case WM_TIMER:
935                                         /* The WIN32 docs say:
936                                          * The WM_TIMER message is posted to the installing thread's message queue
937                                          * when a timer expires. You can process the message by providing a WM_TIMER
938                                          * case in the window procedure. Otherwise, the default window procedure will
939                                          * call the TimerProc callback function specified in the call to the SetTimer
940                                          * function used to install the timer. 
941                                          *
942                                          * In GHOST, we let DefWindowProc call the timer callback.
943                                          */
944                                         break;
945                                 case WM_BLND_NDOF_AXIS:
946                                         {
947                                                 GHOST_TEventNDOFData ndofdata;
948                                                 system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
949                                                 system->m_eventManager->
950                                                         pushEvent(new GHOST_EventNDOF(
951                                                                 system->getMilliSeconds(), 
952                                                                 GHOST_kEventNDOFMotion, 
953                                                                 window, ndofdata));
954                                         }
955                                         break;
956                                 case WM_BLND_NDOF_BTN:
957                                         {
958                                                 GHOST_TEventNDOFData ndofdata;
959                                                 system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
960                                                 system->m_eventManager->
961                                                         pushEvent(new GHOST_EventNDOF(
962                                                                 system->getMilliSeconds(), 
963                                                                 GHOST_kEventNDOFButton, 
964                                                                 window, ndofdata));
965                                         }
966                                         break;
967                         }
968                 }
969                 else {
970                         // Event found for a window before the pointer to the class has been set.
971                         GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n")
972                         /* These are events we typically miss at this point:
973                            WM_GETMINMAXINFO     0x24
974                            WM_NCCREATE                  0x81
975                            WM_NCCALCSIZE                0x83
976                            WM_CREATE                    0x01
977                            We let DefWindowProc do the work.
978                         */
979                 }
980         }
981         else {
982                 // Events without valid hwnd
983                 GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n")
984         }
985
986         if (event) {
987                 system->pushEvent(event);
988         }
989         else {
990                 lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
991         }
992         return lResult;
993 }
994
995 GHOST_TUns8* GHOST_SystemWin32::getClipboard(bool selection) const 
996 {
997         char *buffer;
998         char *temp_buff;
999         
1000         if ( IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) {
1001                 HANDLE hData = GetClipboardData( CF_TEXT );
1002                 if (hData == NULL) {
1003                         CloseClipboard();
1004                         return NULL;
1005                 }
1006                 buffer = (char*)GlobalLock( hData );
1007                 
1008                 temp_buff = (char*) malloc(strlen(buffer)+1);
1009                 strcpy(temp_buff, buffer);
1010                 
1011                 GlobalUnlock( hData );
1012                 CloseClipboard();
1013                 
1014                 temp_buff[strlen(buffer)] = '\0';
1015                 if (buffer) {
1016                         return (GHOST_TUns8*)temp_buff;
1017                 } else {
1018                         return NULL;
1019                 }
1020         } else {
1021                 return NULL;
1022         }
1023 }
1024
1025 void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1026 {
1027         if(selection) {return;} // for copying the selection, used on X11
1028
1029         if (OpenClipboard(NULL)) {
1030                 HLOCAL clipbuffer;
1031                 char *data;
1032                 
1033                 if (buffer) {
1034                         EmptyClipboard();
1035                         
1036                         clipbuffer = LocalAlloc(LMEM_FIXED,((strlen(buffer)+1)));
1037                         data = (char*)GlobalLock(clipbuffer);
1038
1039                         strcpy(data, (char*)buffer);
1040                         data[strlen(buffer)] = '\0';
1041                         LocalUnlock(clipbuffer);
1042                         SetClipboardData(CF_TEXT,clipbuffer);
1043                 }
1044                 CloseClipboard();
1045         } else {
1046                 return;
1047         }
1048 }
1049
1050 const GHOST_TUns8* GHOST_SystemWin32::getSystemDir() const
1051 {
1052         static char knownpath[MAX_PATH];
1053         HRESULT hResult = SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath);
1054
1055         if (hResult == S_OK)
1056         {
1057                 return (GHOST_TUns8*)knownpath;
1058         }
1059
1060         return NULL;
1061 }
1062
1063 const GHOST_TUns8* GHOST_SystemWin32::getUserDir() const
1064 {
1065         static char knownpath[MAX_PATH];
1066         HRESULT hResult = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath);
1067
1068         if (hResult == S_OK)
1069         {
1070                 return (GHOST_TUns8*)knownpath;
1071         }
1072
1073         return NULL;
1074 }
1075
1076 const GHOST_TUns8* GHOST_SystemWin32::getBinaryDir() const
1077 {
1078         static char fullname[MAX_PATH];
1079         if(GetModuleFileName(0, fullname, MAX_PATH)) {
1080                 return (GHOST_TUns8*)fullname;
1081         }
1082
1083         return NULL;
1084 }