a68e6e87558c8a52d1a8ab102088e9b4f4cd6ea7
[sdk] / ecere / src / gui / drivers / Win32Interface.ec
1 namespace gui::drivers;
2
3 import "instance"
4
5 #define UNICODE
6
7 #if defined(__WIN32__)
8
9 #undef WINVER
10 #define WINVER 0x0500
11 #undef _WIN32_WINNT
12 #define _WIN32_WINNT 0x0502
13
14 #undef JOY_BUTTON1
15 #undef JOY_BUTTON2
16 #undef JOY_BUTTON3
17 #undef JOY_BUTTON4
18
19 #define WIN32_LEAN_AND_MEAN
20 #define Method _Method
21 #define byte _byte
22 #define int64 _int64
23 #define String _String
24 #define Mutex _Mutex
25 #define Platform _Platform
26 #include <windows.h>
27 #include <wincon.h>
28 #include <shellapi.h>
29
30
31 #if defined(ECERE_VANILLA)
32 #define ECERE_NOJOYSTICK
33 #endif
34
35 #ifndef ECERE_NODINPUT
36 #ifndef ECERE_NOJOYSTICK
37 #define DIRECTINPUT_VERSION 0x700
38 #include <dinput.h>
39 #else
40 #define DIRECTINPUT_VERSION 0x300
41 #endif
42
43 #endif
44
45 #include <mmsystem.h>
46
47 #include <imm.h>
48
49 #define VK_SEMI       0xBA
50 #define VK_EQUALS     0xBB
51 #define VK_COMMA      0xBC
52 #define VK_MINUS      0xBD
53 #define VK_PERIOD     0xBE
54 #define VK_SLASH      0xBF
55 #define VK_TILDE      0xC0
56 #define VK_LBRACKET   0xDB
57 #define VK_BACK_SLASH 0xDC
58 #define VK_RBRACKET   0xDD
59 #define VK_QUOTE      0xDE
60
61 #ifndef WM_MOUSEWHEEL
62 #define WM_MOUSEWHEEL 0x20A
63 #endif
64
65 #ifndef IDC_HAND
66 #define IDC_HAND (char *)0x7F89
67 #endif
68
69 #ifndef WS_EX_LAYERED
70
71 #define LWA_COLORKEY            0x00000001
72 #define LWA_ALPHA               0x00000002
73
74 #define WS_EX_LAYERED           0x00080000
75
76 WINUSERAPI
77 BOOL
78 WINAPI
79 SetLayeredWindowAttributes(
80     HWND hwnd,
81     COLORREF crKey,
82     BYTE bAlpha,
83     DWORD dwFlags);
84
85 #endif
86
87 #undef Method
88 #undef byte
89 #undef int64
90 #undef String
91 #undef Mutex
92 #undef Platform
93
94 import "Window"
95
96 #ifdef ECERE_VANILLA
97 #define ECERE_NODINPUT
98 #define ECERE_NOJOYSTICK
99 #define ECERE_NOBLENDING
100 #endif
101
102 static byte key2VK[256] =
103 {
104    0,VK_ESCAPE,'1','2','3','4','5','6','7','8','9','0',VK_MINUS,VK_EQUALS,VK_BACK,VK_TAB,
105    'Q','W','E','R','T','Y','U','I','O','P',VK_LBRACKET,VK_RBRACKET,VK_RETURN,VK_LCONTROL,'A','S',
106    'D','F','G','H','J','K','L',VK_SEMI,VK_QUOTE,VK_TILDE,VK_LSHIFT,VK_BACK_SLASH,'Z','X','C','V',
107    'B','N','M',VK_COMMA,VK_PERIOD,VK_DIVIDE,VK_RSHIFT,VK_MULTIPLY,VK_LMENU,VK_SPACE,VK_CAPITAL,VK_F1,VK_F2,VK_F3,VK_F4,VK_F5,
108    VK_F6,VK_F7,VK_F8,VK_F9,VK_F10,VK_NUMLOCK,VK_SCROLL,VK_NUMPAD7,VK_NUMPAD8,VK_NUMPAD9,VK_SUBTRACT,VK_NUMPAD4,VK_NUMPAD5,VK_NUMPAD6,VK_ADD,VK_NUMPAD1,
109    VK_NUMPAD2,VK_NUMPAD3,VK_NUMPAD0,VK_DELETE,0,0,0,VK_F11,VK_F12,0,0,0,0,0,0,0,
110    0,VK_RCONTROL,0,0,VK_RMENU,0,VK_HOME,VK_UP,VK_PRIOR,VK_LEFT,VK_RIGHT,VK_END,VK_DOWN,VK_NEXT,VK_INSERT,VK_DELETE
111 };
112 static const uint16 className[] = L"Ecere Application";
113 static HINSTANCE hInstance;
114
115 static WPARAM lastBits;
116 static LPARAM lastRes;
117
118 static DEVMODE devMode;
119 #ifndef ECERE_NODINPUT
120 static HWND acquiredWindow = null;
121 #endif
122 static HCURSOR systemCursors[SystemCursor];
123 static bool fullScreenMode;
124 static int desktopX = 0, desktopY = 0, desktopW = 0, desktopH = 0;
125 static DWORD hiResTimer;
126 // static HWND topWindow;
127
128 static HWND startBar;
129
130 #ifndef ECERE_NODINPUT
131 #define NUMJOY    4
132 static HMODULE dInputDll = null;
133 static BOOL * (WINAPI * directInputCreate)(HINSTANCE, DWORD, IDirectInput **, void *);
134 static IDirectInput * dInput = null;
135 static IDirectInputDevice * directMouse = null;
136 #ifndef ECERE_NOJOYSTICK
137 static int numJoysticks = 0;
138 static IDirectInputDevice2 * directJoysticks[NUMJOY] = {null,null,null,null};
139 #endif
140 #endif
141
142 default:
143 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
144 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp;
145 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown;
146 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit;
147 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove;
148 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick;
149 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown;
150 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp;
151 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick;
152 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown;
153 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp;
154 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick;
155 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown;
156 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp;
157
158 private:
159
160 static Box lastMonitorAreas[32];
161 static Box monitorAreas[32];
162 static int monitor;
163
164 static bool EnumerateMonitors(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
165 {
166    MONITORINFO info = { 0 };
167    info.cbSize = sizeof(MONITORINFO);
168    GetMonitorInfo(hMonitor, &info);
169    monitorAreas[monitor] = { info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom };
170    monitor++;
171    return monitor < 32;
172 }
173
174 static bool externalDisplayChange;
175
176 static int taskBarState;
177 static WINDOWPLACEMENT taskBarPlacement;
178 static bool activateApp;
179
180 static SystemCursor lastCursor = (SystemCursor)-1;
181
182 static void SmartShowWindow(HWND windowHandle, WindowState state, bool doActivate)
183 {
184    int showCmd;
185    if(state == maximized)
186       showCmd = doActivate ? SW_SHOWMAXIMIZED : SW_MAXIMIZE;
187    else if(state == minimized)
188       showCmd = SW_MINIMIZE;
189    else
190       showCmd = doActivate ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE;
191    ShowWindow(windowHandle, showCmd);
192 }
193
194 void AeroSnapPosition(Window window, int x, int y, int w, int h)
195 {
196    int oldX = window.absPosition.x;
197
198    // To prevent having a window < minClientSize with Aero Snap, because we don't receive a WM_SIZING!
199    // The sensible implementation of this is in WM_SIZING
200    {
201       int gw = w, gh = h;
202       MinMaxValue ew, eh;
203       window.GetDecorationsSize(&ew, &eh);
204
205       gw -= ew;
206       gh -= eh;
207
208       gw = Max(gw, 1);
209       gh = Max(gh, 1);
210
211       gw = Max(gw, window.minSize.w);
212       gh = Max(gh, window.minSize.h);
213       gw = Min(gw, window.maxSize.w);
214       gh = Min(gh, window.maxSize.h);
215
216       if(!window.OnResizing(&gw, &gh))
217       {
218          gw = window.clientSize.w;
219          gh = window.clientSize.h;
220       }
221
222       gw = Max(gw, window.skinMinSize.w);
223       gh = Max(gh, window.skinMinSize.h);
224
225       gw += ew;
226       gh += eh;
227
228       if(w != gw || h != gh)
229       {
230          bool move = false;
231          // Adjust x position if we resized from top or bottom left corner
232          if(x != oldX)
233          {
234             x += w - gw;
235             move = true;
236          }
237          w = gw;
238          h = gh;
239          if(window.state == minimized && !window.nativeDecorations && !window.hasMinimize)
240             y -= 26000;
241           guiApp.interfaceDriver.PositionRootWindow(window, x, y, w, h, move, true);
242       }
243    }
244    window.ExternalPosition(x, y, w, h);
245 }
246
247 void Win32UpdateStyle(Window window)
248 {
249    DWORD style = GetWindowLong(window.windowHandle, GWL_EXSTYLE);
250    if(window.showInTaskBar)
251       style |= WS_EX_APPWINDOW;
252    else
253       style &= ~WS_EX_APPWINDOW;
254
255    SetWindowLong(window.windowHandle, GWL_EXSTYLE, style);
256 }
257
258 class Win32Interface : Interface
259 {
260    class_property(name) = "Win32";
261
262    void ::RepositionDesktop(bool updateChildren)
263    {
264       int c;
265       static double lastTime = 0, time;
266       int x = 0, y = 0;
267       int w = 0, h = 0;
268       //static Size lastScreen;
269       //static Point lastScreenPos;
270       static double lastAutoHideCheck = 0;
271       int newTaskBarState = taskBarState;
272       HMONITOR primaryMonitor = 0;
273
274       time = GetTime();
275       if(time - lastTime < 0.1) return;
276       lastTime = time;
277
278       // Every sec, check for the auto hide property
279       if(time - lastAutoHideCheck > 1)
280       {
281          APPBARDATA appBarData = { 0 };
282          newTaskBarState = (int)SHAppBarMessage(ABM_GETSTATE, &appBarData);
283          lastAutoHideCheck = time;
284       }
285
286       monitor = 0;
287       EnumDisplayMonitors(null, null, EnumerateMonitors, 0);
288       for(c = 0; c<monitor; c++)
289       {
290          if(monitorAreas[c].left != lastMonitorAreas[c].left ||
291             monitorAreas[c].top != lastMonitorAreas[c].top ||
292             monitorAreas[c].right != lastMonitorAreas[c].right ||
293             monitorAreas[c].bottom != lastMonitorAreas[c].bottom)
294          {
295             break;
296          }
297       }
298
299       memcpy(lastMonitorAreas, monitorAreas, sizeof(monitorAreas));
300
301       guiApp.virtualScreen =
302       {
303          GetSystemMetrics(SM_CXVIRTUALSCREEN),
304          GetSystemMetrics(SM_CYVIRTUALSCREEN)
305       };
306       guiApp.virtualScreenPos =
307       {
308          x = GetSystemMetrics(SM_XVIRTUALSCREEN),
309          y = GetSystemMetrics(SM_YVIRTUALSCREEN)
310       };
311
312       {
313          HMONITOR monitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
314          if(monitor)
315          {
316             MONITORINFO info = { 0 };
317             info.cbSize = sizeof(MONITORINFO);
318             primaryMonitor = monitor;
319             GetMonitorInfo(monitor, &info);
320             x = info.rcMonitor.left;
321             y = info.rcMonitor.top;
322             w = info.rcMonitor.right - info.rcWork.left;
323             h = info.rcMonitor.bottom - info.rcWork.top;
324          }
325       }
326
327      {
328          WINDOWPLACEMENT placement = { 0 };
329          placement.length = sizeof(WINDOWPLACEMENT);
330
331          if(!GetWindowPlacement(startBar, &placement))
332          {
333             startBar = FindWindowA("Shell_TrayWnd", null);
334             GetWindowPlacement(startBar, &placement);
335          }
336          {
337             HMONITOR taskBarMonitor = MonitorFromWindow(startBar, MONITOR_DEFAULTTONEAREST);
338             if(primaryMonitor == taskBarMonitor)
339             {
340                if(placement.rcNormalPosition.top <= 0 && placement.rcNormalPosition.bottom >= h - 1)
341                {
342                   if(placement.rcNormalPosition.left <= 0)
343                   {
344                      x = (taskBarState & ABS_AUTOHIDE) ? 1 : placement.rcNormalPosition.right;
345                      w -= x;
346                   }
347                   else
348                      w = (taskBarState & ABS_AUTOHIDE) ? (w - 1) : Min(placement.rcNormalPosition.left, w);
349                }
350                else if(placement.rcNormalPosition.left <= 0 && placement.rcNormalPosition.right >= w - 1)
351                {
352                   if(placement.rcNormalPosition.top <= 0)
353                   {
354                      y = (taskBarState & ABS_AUTOHIDE) ? 1 : placement.rcNormalPosition.bottom;
355                      h -= y;
356                   }
357                   else
358                      h = (taskBarState & ABS_AUTOHIDE) ? (h - 1) : Min(placement.rcNormalPosition.top, h);
359                }
360             }
361          }
362
363         if(c < monitor ||
364          placement.rcNormalPosition.left != taskBarPlacement.rcNormalPosition.left ||
365          placement.rcNormalPosition.top != taskBarPlacement.rcNormalPosition.top ||
366          placement.rcNormalPosition.right != taskBarPlacement.rcNormalPosition.right ||
367          placement.rcNormalPosition.bottom != taskBarPlacement.rcNormalPosition.bottom  ||
368          newTaskBarState != taskBarState)
369          {
370             taskBarState = newTaskBarState;
371             guiApp.desktop.size.w = 0;
372             desktopX = x;
373             desktopY = y;
374             desktopW = w;
375             desktopH = h;
376             taskBarPlacement = placement;
377             guiApp.SetDesktopPosition(x, y, w, h, updateChildren);
378          }
379
380          //lastScreen = guiApp.virtualScreen;
381          //lastScreenPos = guiApp.virtualScreenPos;
382       }
383    }
384
385    /****************************************************************************
386       /// PRIVATE UTILITY FUNCTIONS /////////////
387    ****************************************************************************/
388    // --- Keyboard Input ---
389    bool ::ProcessKeyMessage(Window window, DWORD msg, WPARAM wParam, LPARAM lParam, unichar ch)
390    {
391       bool result = true;
392       Key code = 0;
393       Key key;
394       // UNICODE FIX
395       bool frenchShift = (ch < 0x10000) ? (((VkKeyScan((uint16)ch) >> 8) & 6) == 6) : false;
396
397       if(msg == WM_CHAR || msg == WM_DEADCHAR)
398       {
399          wParam = 0;
400          lParam = 0;
401       }
402       if(msg == WM_MOUSEWHEEL)
403          code = (((short) HIWORD(wParam)) < 0) ? wheelDown : wheelUp;
404       else
405       {
406          key = (byte)((lParam & 0xFF0000)>>16);
407          if(lParam & 0x1000000)
408          {
409             key = Interface::GetExtendedKey(key);
410             if(!key)
411             {
412                if(wParam == 0xb3)
413                   key = KeyCode::play;
414             }
415          }
416          code = key;
417       }
418
419       if(msg == WM_MOUSEWHEEL)
420       {
421          if(::GetAsyncKeyState(VK_SHIFT) & 0x80000)
422             code.shift = true;
423          if(::GetAsyncKeyState(VK_CONTROL) & 0x80000)
424             code.ctrl = true;
425          if(::GetAsyncKeyState(VK_MENU) & 0x80000)
426             code.alt = true;
427
428          result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, code, 0);
429       }
430       else
431       {
432          if(key != leftShift && key != rightShift && ::GetKeyState(VK_SHIFT) & 0x80000)
433             code.shift = true;
434          if(key != leftControl && key != rightControl && ::GetKeyState(VK_CONTROL) & 0x80000 && !frenchShift)
435             code.ctrl = true;
436          if(key != leftAlt && key != rightAlt && ::GetKeyState(VK_MENU) & 0x80000 && !frenchShift)
437             code.alt = true;
438          /*
439          byte ch = Interface::TranslateKeykey, code.shift);
440          if(::GetKeyState(VK_CAPITAL))
441             ch = toupper(ch);
442          */
443
444          if(msg == WM_KEYUP || msg == WM_SYSKEYUP)
445          {
446             result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyUp, code, ch);
447          }
448          else
449          {
450             if(!(lParam & 0x40000000))
451             {
452                /*
453                {
454                   char string[256];
455                   Class * keyClass = eSystem_FindClass(__ecereModule, "Key");
456                   code.OnGetString(string, null, null);
457                   eSystem_Logf("%s\n", string);
458                }
459                */
460
461                result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyDown, code, ch);
462             }
463             else if(key<128)
464                result = window.KeyMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnKeyHit, code,ch);
465          }
466       }
467
468       return result;
469    }
470
471    // --- Window procedure ---
472    DWORD CALLBACK ::ApplicationWindow(HWND windowHandle, UINT msg, WPARAM wParam, LPARAM lParam)
473    {
474 #ifdef _WIN64
475       Window window = (Window)GetWindowLongPtr(windowHandle, GWLP_USERDATA);
476 #else
477       Window window = (Window)GetWindowLong(windowHandle, GWL_USERDATA);
478 #endif
479       static Point lastPos;
480       if(window)
481       {
482          int x = 0,y = 0;
483          unichar ch = 0;
484          switch(msg)
485          {
486             case WM_QUERYNEWPALETTE:
487             case WM_PALETTECHANGED:
488                if(window.display)
489                   window.display.RestorePalette();
490                UpdateWindow(windowHandle);
491                break;
492             case WM_NCACTIVATE:
493             {
494                if(!fullScreenMode && !guiApp.IsModeSwitching())
495                {
496                   if(wParam)
497                   {
498                      Window modalRoot = window.FindModal();
499
500                      HWND modalWindow = modalRoot ? modalRoot.windowHandle : null;
501
502                      if(window.creationActivation == flash || window.hasMinimize || window.borderStyle != sizableThin)
503                      {
504                         FLASHWINFO flashInfo = { 0 };
505                         flashInfo.cbSize = sizeof(FLASHWINFO);
506                         flashInfo.hwnd = window.windowHandle;
507                         flashInfo.uCount = 0;
508                         flashInfo.dwFlags = FLASHW_STOP;
509                         FlashWindowEx((void *)&flashInfo);
510                      }
511
512                      if(modalWindow && modalWindow != windowHandle)
513                         modalRoot.ExternalActivate(true, true, window, null);
514                      else
515                         window.ExternalActivate(true, true, window, null);
516                   }
517                   else
518                   {
519                      HWND foreground;
520                      DWORD id;
521                      void * windowLong;
522                      foreground = GetForegroundWindow();
523                      if(foreground == windowHandle && lParam)
524                         foreground = (HWND)lParam;
525
526                      GetWindowThreadProcessId(foreground, &id);
527
528 #ifdef _WIN64
529                      windowLong = (void*)GetWindowLongPtr(foreground, GWLP_WNDPROC);
530 #else
531                      windowLong = (void*)GetWindowLong(foreground, GWL_WNDPROC);
532 #endif
533 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
534                      // The != ApplicationWindow check is for not recognizing the Console window as an Ecere Window
535                      // That check causes a problem with the OpenGL driver which seems to popup a window of a different class
536                      if(window.displaySystem && window.displaySystem.driver == class(OpenGLDisplayDriver))
537                         windowLong = (void *)ApplicationWindow;
538                      else
539                      {
540                         Window master = window.master;
541                         Window rootWindow = master.rootWindow;
542                         if(rootWindow.displaySystem && rootWindow.displaySystem.driver == class(OpenGLDisplayDriver))
543                            windowLong = (void *)ApplicationWindow;
544                      }
545 #endif
546                      if(id != GetCurrentProcessId() || windowLong != (void *)ApplicationWindow)
547                         window.ExternalActivate(false, true, window, null);
548                      // DefWindowProc for WM_NCACTIVATE draws the decorations, make sure it's drawn in the right state
549                      return (uint)DefWindowProc(windowHandle, msg, window.active, lParam);
550                   }
551                }
552                if(activateApp)
553                {
554                   for(window = guiApp.desktop.firstChild; window; window = window.next)
555                      SetWindowPos(window.windowHandle, window.style.stayOnTop ? HWND_TOPMOST : HWND_TOP, 0,0,0,0,
556                         SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOSIZE); //|SWP_NOREDRAW);
557                   activateApp = false;
558                }
559
560                return (uint)DefWindowProc(windowHandle, msg, window ? window.active : wParam, lParam);
561             }
562             case WM_ACTIVATEAPP:
563                if(!guiApp.IsModeSwitching())
564                {
565                   if(fullScreenMode)
566                   {
567                      HDC hdc = GetDC(windowHandle);
568                      if(wParam != TRUE)
569                      {
570                         guiApp.SetAppFocus(false);
571                         ShowWindow(windowHandle, SW_MINIMIZE);
572                         ChangeDisplaySettings(null,0);
573                         SetSystemPaletteUse(hdc, SYSPAL_STATIC);
574                      }
575                      else
576                      {
577                         ChangeDisplaySettings(&devMode, CDS_FULLSCREEN);
578                         SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC);
579                         if(window.display)
580                            window.display.RestorePalette();
581                         ShowWindow(windowHandle, SW_SHOWNORMAL);
582                         guiApp.SetAppFocus(true);
583                         if(window.display)
584                            window.display.Resize(window.clientSize.w, window.clientSize.h);
585                         window.Update(null);
586                      }
587                      ReleaseDC(windowHandle, hdc);
588                   }
589                   else
590                   {
591                      //Window window;
592
593                      if(wParam && !guiApp.desktop.active /*&& lParam != GetCurrentThreadID()*/)
594                      {
595                         activateApp = true;
596                         /*
597                         for(window = guiApp.desktop.firstChild; window; window = window.next)
598                            SetWindowPos(window.windowHandle, window.style.stayOnTop ? HWND_TOPMOST : HWND_TOP, 0,0,0,0,
599                               SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOSIZE); //|SWP_NOREDRAW);
600                         */
601                      }
602                      guiApp.SetAppFocus((bool)wParam);
603                   }
604                }
605                else
606                   guiApp.SetAppFocus((bool)wParam);
607                break;
608             case WM_PAINT:
609             {
610                PAINTSTRUCT ps;
611                if(!window.alphaBlend || window.display.pixelFormat != pixelFormat888)
612                {
613
614 /*
615 #define ACCESS_ITEM(l, id) \
616       ((FastItem)(((id) == -1) ? null : (((byte *)((l).items)) + (id) * (l).itemSize)))
617 */
618
619 #define ACCESS_ITEM(l, id) \
620    id
621
622                   BoxItem item = window.dirtyArea.count ? (BoxItem)ACCESS_ITEM(window.dirtyArea, window.dirtyArea.first) : null;
623
624                   BeginPaint(windowHandle, &ps);
625
626                   // Prevent flickering if we're going to update anyways
627                   /*
628                   printf("   Paint message (%d, %d)-(%d, %d)\n",
629                      item ? item.box.left : 0,
630                      item ? item.box.top : 0,
631                      item ? item.box.right : 0,
632                      item ? item.box.bottom : 0);
633                   */
634                   // Causes redraw bug...
635                   if(!window.manageDisplay || !item ||
636                      item.box.left > 0 ||
637                      item.box.top > 0 ||
638                      item.box.right < window.size.w - 1 ||
639                      item.box.bottom < window.size.h - 1)
640                   {
641                      Box box { ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right-1, ps.rcPaint.bottom-1 };
642                      window.UpdateDirty(box);
643                   }
644                      // TODO: Fix precompiler with
645                      // window.UpdateDirty((Box *)&ps.rcPaint);
646                   EndPaint(windowHandle, &ps);
647                }
648                else
649                {
650                   BeginPaint(windowHandle, &ps);
651                   EndPaint(windowHandle, &ps);
652                }
653                break;
654             }
655             case WM_DISPLAYCHANGE:
656             {
657                if(!guiApp.fullScreenMode && (lastBits != wParam || lastRes != lParam))
658                {
659                   RECT rect;
660                   HWND foregroundWindow = GetForegroundWindow();
661                   int w = LOWORD(lParam);
662                   int h = HIWORD(lParam);
663
664                   GetWindowRect(foregroundWindow, &rect);
665                   if(rect.right == w && rect.bottom == h)
666                      break;
667
668                   lastBits = wParam;
669                   lastRes = lParam;
670
671                   externalDisplayChange = true;
672                   if(guiApp.desktop.DisplayModeChanged())
673                   {
674                      char caption[2048];
675                      if(!window.style.hidden)
676                         SmartShowWindow(window.windowHandle, (window.nativeDecorations && window.state == maximized) ? maximized : normal, false);
677                      window.FigureCaption(caption);
678                      SetRootWindowCaption(window, caption);
679                   }
680                   externalDisplayChange = false;
681                }
682                break;
683             }
684             // Keyboard Messages
685             case WM_SYSKEYDOWN:
686             case WM_KEYDOWN:
687             case WM_SYSKEYUP:
688             case WM_KEYUP:
689             case WM_CHAR:
690             //case WM_DEADCHAR:
691             {
692                MSG charMsg;
693                DWORD min = 0, max = 0;
694
695                if(msg != WM_CHAR && window.composing)
696                   break;
697
698                switch(msg)
699                {
700                   case WM_SYSKEYDOWN: min = max = WM_SYSCHAR; break;
701                   case WM_KEYDOWN: min = max = WM_CHAR; break;
702                   case WM_SYSKEYUP: min = WM_SYSCHAR; max = WM_SYSDEADCHAR; break;
703                   case WM_KEYUP: min = WM_CHAR; max = WM_DEADCHAR; break;
704                }
705
706                // This is very annoying.
707                // PeekMessage somehow invokes this function directly... don't know why.
708                // Only crashes when running through debug mode in visual studio
709                incref window;
710                if(msg == WM_CHAR || msg == WM_DEADCHAR || PeekMessage(&charMsg, windowHandle, min, max, PM_REMOVE))
711                {
712                   ch = (msg == WM_CHAR || msg == WM_DEADCHAR) ? (unichar)wParam : (unichar)charMsg.wParam;
713                   // TOCHECK: What is this for again? Fixing some obscure activation status?
714                   // -- I believe this was somehow allowing 'unmaximizing', but was causing problems
715                   // as there was no way to prevent AltEnter from doing so (e.g. when it is used for a node property)
716                   // Worked around by fixing ProcessHotKeys to properly check for sysButtons in parent.parent when sys buttons
717                   // are placed inside a menu bar for a document
718                   /*
719                   if(msg == WM_SYSKEYDOWN && ch == 13)
720                   {
721                      ShowWindow(window.windowHandle, window.state == maximized ? SW_MAXIMIZE : SW_SHOWNORMAL);
722                      // This last line been commented out for a long time:
723                      // window.ExternalActivate(true, true, window, null);
724                   }
725                   */
726                   if(msg == WM_SYSKEYUP || msg == WM_KEYUP)
727                   {
728                      if(!ProcessKeyMessage(window, WM_KEYDOWN, 0x40000000, 0, ch))
729                         break;
730                   }
731                }
732             }
733             case WM_MOUSEWHEEL:
734                ProcessKeyMessage(window, msg, wParam, lParam, ch);
735                if(window.composing && msg == WM_CHAR)
736                {
737                   COMPOSITIONFORM form;
738                   Window caretOwner = guiApp.caretOwner;
739                   if(caretOwner)
740                   {
741                      HIMC ctx = ImmGetContext(windowHandle);
742                      form.dwStyle = CFS_POINT;
743                      form.ptCurrentPos.x = caretOwner.caretPos.x - caretOwner.scroll.x + caretOwner.absPosition.x - window.absPosition.x + 4;
744                      form.ptCurrentPos.y = caretOwner.caretPos.y - caretOwner.scroll.x + caretOwner.absPosition.y - window.absPosition.y + 2;
745                      window.composing = true;
746                      ImmSetCompositionWindow(ctx, &form);
747                      ImmReleaseContext(windowHandle, ctx);
748                   }
749                }
750                if(msg != WM_MOUSEWHEEL)
751                   delete window;
752                break;
753
754             case WM_IME_STARTCOMPOSITION:
755             {
756                COMPOSITIONFORM form;
757                Window caretOwner = guiApp.caretOwner;
758                if(caretOwner)
759                {
760                   HIMC ctx = ImmGetContext(windowHandle);
761                   form.dwStyle = CFS_POINT;
762                   form.ptCurrentPos.x = caretOwner.caretPos.x - caretOwner.scroll.x + caretOwner.absPosition.x - window.absPosition.x + 4;
763                   form.ptCurrentPos.y = caretOwner.caretPos.y - caretOwner.scroll.y + caretOwner.absPosition.y - window.absPosition.y + 2;
764                   /*
765                   form.rcArea.left = window.caretPos.x;
766                   form.rcArea.top = 250;
767                   form.rcArea.right = 500;
768                   form.rcArea.bottom = 300;
769                   */
770                   window.composing = true;
771                   DefWindowProc(windowHandle, msg, wParam, lParam);
772                   ImmSetCompositionWindow(ctx, &form);
773                   ImmSetStatusWindowPos(ctx, &form.ptCurrentPos);
774                   {
775                      FontResource res = caretOwner.font;
776                      LOGFONT font = { 0 };
777                      HDC hdc = GetDC(0);
778                      int pixels = GetDeviceCaps(hdc, LOGPIXELSY);
779                      ReleaseDC(0, hdc);
780                      font.lfHeight = -(int)((float)res.size * pixels / 72 + 0.5);
781                      font.lfWeight = res.flags.bold ? FW_BOLD : FW_NORMAL;
782                      font.lfItalic = res.flags.italic ? TRUE : FALSE,
783                      font.lfUnderline = res.flags.underline ? TRUE : FALSE;
784                      font.lfCharSet = DEFAULT_CHARSET;
785                      font.lfOutPrecision = OUT_DEFAULT_PRECIS;
786                      font.lfClipPrecision = CLIP_DEFAULT_PRECIS;
787                      font.lfQuality = DEFAULT_QUALITY;
788                      font.lfPitchAndFamily = (byte)(DEFAULT_PITCH|FF_DONTCARE); // TODO: Fix compiler 0 | 0 to produce byte, not int
789                      UTF8toUTF16Buffer(res.faceName, font.lfFaceName, LF_FACESIZE);
790
791                      ImmSetCompositionFont(ctx, &font);
792                      ImmReleaseContext(windowHandle, ctx);
793                   }
794                   return 1;
795                }
796                break;
797             }
798             case WM_IME_ENDCOMPOSITION:
799                window.composing = false;
800                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
801             /*case WM_IME_COMPOSITION:
802                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
803             case WM_IME_REQUEST:
804                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
805             case WM_IME_NOTIFY:
806                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
807             case WM_IME_KEYDOWN:
808                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
809             case WM_IME_KEYUP:
810                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
811             case WM_IME_SELECT:
812                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
813             case WM_IME_SETCONTEXT:
814                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
815             case WM_IME_CONTROL:
816                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
817             case WM_IME_CHAR:
818                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
819             case WM_IME_COMPOSITIONFULL:
820                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
821             */
822             case WM_NCHITTEST:
823                if(window.nativeDecorations)
824                   return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
825                else
826                // return HTCAPTION;
827                   return HTCLIENT;
828
829             // Mouse Messages
830             case WM_LBUTTONUP:
831             case WM_RBUTTONUP:
832             case WM_MBUTTONUP:
833             case WM_LBUTTONDOWN:
834             case WM_LBUTTONDBLCLK:
835             case WM_RBUTTONDOWN:
836             case WM_RBUTTONDBLCLK:
837             case WM_MBUTTONDOWN:
838             case WM_MBUTTONDBLCLK:
839             case WM_MOUSEMOVE:
840                x = window.absPosition.x;
841                y = window.absPosition.y;
842             /*case WM_NCLBUTTONUP:
843             case WM_NCRBUTTONUP:
844             case WM_NCMBUTTONUP:
845             case WM_NCLBUTTONDOWN:
846             case WM_NCLBUTTONDBLCLK:
847             case WM_NCRBUTTONDOWN:
848             case WM_NCRBUTTONDBLCLK:
849             case WM_NCMBUTTONDOWN:
850             case WM_NCMBUTTONDBLCLK:*/
851             case WM_NCMOUSEMOVE:
852             {
853                Modifiers code = 0;
854                bool consequential = false;
855
856                x += (short)LOWORD(lParam);
857                y += (short)HIWORD(lParam);
858
859                if(window.nativeDecorations && msg != WM_NCMOUSEMOVE)
860                {
861                   x += window.clientStart.x;
862                   y += window.clientStart.y - (window.hasMenuBar ? skinMenuHeight : 0);
863                }
864
865                if(::GetKeyState(VK_SHIFT) & 0x80000)   code.shift = true;
866                if(::GetKeyState(VK_CONTROL) & 0x80000) code.ctrl = true;
867                if(::GetKeyState(VK_MENU) & 0x80000)    code.alt = true;
868                if(wParam & MK_LBUTTON) code.left = true;
869                if(wParam & MK_MBUTTON) code.middle = true;
870                if(wParam & MK_RBUTTON) code.right = true;
871
872                if(msg == WM_MOUSEMOVE)
873                {
874                   if(lastPos.x == x && lastPos.y == y)
875                      consequential = true;
876                   lastPos.x = x;
877                   lastPos.y = y;
878                }
879
880                incref window;
881                switch(msg)
882                {
883                   case WM_NCMOUSEMOVE:
884                   case WM_MOUSEMOVE:
885                      window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMouseMove, x,y,&code, consequential, false);
886                      break;
887                   //case WM_NCLBUTTONDBLCLK:
888                   case WM_LBUTTONDBLCLK:
889                      if(!window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftDoubleClick, x,y,&code, false, true))
890                         break;
891                   //case WM_NCLBUTTONDOWN:
892                   case WM_LBUTTONDOWN:
893                      window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonDown,x,y,&code, false,
894                                              /*(msg == WM_LBUTTONDBLCLK) ? false: */true);
895                      break;
896                   //case WM_NCLBUTTONUP:
897                   case WM_LBUTTONUP:      window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnLeftButtonUp, x,y,&code, false, false);break;
898                   //case WM_NCMBUTTONDBLCLK:
899                   case WM_MBUTTONDBLCLK:
900                      if(!window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleDoubleClick, x,y,&code, false, false))
901                         break;
902                   //case WM_NCMBUTTONDOWN:
903                   case WM_MBUTTONDOWN:
904                      window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonDown, x,y,&code, false,
905                                              (msg == WM_LBUTTONDBLCLK) ? false: true);
906                      break;
907                   //case WM_NCMBUTTONUP:
908                   case WM_MBUTTONUP:
909                      window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnMiddleButtonUp, x,y,&code, false, false);
910                      break;
911                   //case WM_NCRBUTTONDBLCLK:
912                   case WM_RBUTTONDBLCLK:
913                      if(!window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightDoubleClick, x,y,&code, false, true))
914                         break;
915                   //case WM_NCRBUTTONDOWN:
916                   case WM_RBUTTONDOWN:
917                      window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonDown, x,y,&code, false,
918                                              (msg == WM_LBUTTONDBLCLK) ? false: true);
919                      break;
920                   //case WM_NCRBUTTONUP:
921                   case WM_RBUTTONUP:
922                      window.MouseMessage(__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRightButtonUp, x,y,&code, false, false);
923                      break;
924                }
925                delete window;
926                if(msg == WM_NCMOUSEMOVE)
927                   return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
928                break;
929             }
930             case WM_SETCURSOR:
931             {
932                if(window.nativeDecorations)
933                {
934                   if(DefWindowProc(windowHandle, msg, wParam, lParam))
935                      return 1;
936                }
937                // SetCursor(systemCursors[0]);
938                SetCursor((lastCursor == (SystemCursor)-1) ? null : systemCursors[lastCursor]);
939                return 0;
940             }
941             case WM_EXITMENULOOP:
942             case WM_EXITSIZEMOVE:
943                // We had some DirectInput stuff in here
944                break;
945             case WM_ENTERMENULOOP:
946             case WM_ENTERSIZEMOVE:
947                // We had some DirectInput stuff in here
948                break;
949             case WM_CLOSE:
950             {
951                window.Destroy(0);
952                return 0;
953             }
954             case WM_MOVE:
955             {
956                int x, y, w, h;
957                RECT rcWindow;
958                GetWindowRect(windowHandle, &rcWindow);
959
960                x = rcWindow.left - desktopX;
961                y = rcWindow.top  - desktopY;
962                w = rcWindow.right - rcWindow.left;
963                h = rcWindow.bottom - rcWindow.top;
964
965                AeroSnapPosition(window, x, y, w, h);
966                break;
967             }
968             /*case WM_MOVING:
969                break;*/
970             case WM_SIZING:
971             {
972                RECT * rect = (RECT *)lParam;
973                MinMaxValue ew, eh;
974                int w, h;
975
976                window.GetDecorationsSize(&ew, &eh);
977
978                w = rect->right - rect->left;
979                h = rect->bottom - rect->top;
980
981                w -= ew;
982                h -= eh;
983
984                w = Max(w, 1);
985                h = Max(h, 1);
986
987                w = Max(w, window.minSize.w);
988                h = Max(h, window.minSize.h);
989                w = Min(w, window.maxSize.w);
990                h = Min(h, window.maxSize.h);
991
992                if(!window.OnResizing(&w, &h))
993                {
994                   w = window.clientSize.w;
995                   h = window.clientSize.h;
996                }
997
998                w = Max(w, window.skinMinSize.w);
999                h = Max(h, window.skinMinSize.h);
1000
1001                w += ew;
1002                h += eh;
1003
1004                if(wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT)
1005                   rect->left = rect->right - w;
1006                else
1007                   rect->right = rect->left + w;
1008
1009                if(wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT)
1010                   rect->top = rect->bottom - h;
1011                else
1012                   rect->bottom = rect->top + h;
1013
1014                return 1;
1015             }
1016             case WM_SIZE:
1017             {
1018                if(window.nativeDecorations)
1019                {
1020                   int x, y, w, h;
1021                   RECT rcWindow;
1022                   GetWindowRect(windowHandle, &rcWindow);
1023
1024                   if(wParam == SIZE_MAXIMIZED && window.state != maximized)
1025                      window.state = maximized;
1026                   else if(wParam == SIZE_MINIMIZED && window.state != minimized)
1027                      window.state = minimized;
1028                   else if(wParam == SIZE_RESTORED && window.state != normal && window.visible)
1029                      window.state = normal;
1030
1031                   x = rcWindow.left - desktopX;
1032                   y = rcWindow.top  - desktopY;
1033                   w = rcWindow.right - rcWindow.left;
1034                   h = rcWindow.bottom - rcWindow.top;
1035
1036                   AeroSnapPosition(window, x, y, w, h);
1037                   if(!guiApp.modeSwitching)
1038                      window.UpdateVisual(null);
1039                }
1040                else
1041                   return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
1042                break;
1043             }
1044             default:
1045                return (uint)DefWindowProc(windowHandle, msg, wParam, lParam);
1046          }
1047       }
1048       else
1049          return (uint)DefMDIChildProc(windowHandle, msg, wParam, lParam);
1050          // return DefWindowProc(windowHandle, msg, wParam, lParam);
1051       return 0;
1052    }
1053
1054    // --- DirectInput ---
1055
1056    #ifndef ECERE_NODINPUT
1057
1058    #ifndef ECERE_NOJOYSTICK
1059    bool CALLBACK ::JoystickCallback( const DIDEVICEINSTANCE* pdidInstance, void * context )
1060    {
1061       if(!dInput->lpVtbl->CreateDevice(dInput, &GUID_Joystick,
1062          (IDirectInputDevice **)&directJoysticks[numJoysticks], null ))
1063          numJoysticks++;
1064       return DIENUM_CONTINUE;
1065    }
1066
1067    bool CALLBACK ::JoystickAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, void * context )
1068    {
1069       DIPROPRANGE diprg;
1070       IDirectInputDevice2 * curJoy = (IDirectInputDevice2 *)context;
1071
1072       diprg.diph.dwSize       = sizeof(DIPROPRANGE);
1073       diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
1074       diprg.diph.dwHow        = DIPH_BYOFFSET;
1075       diprg.diph.dwObj        = pdidoi->dwOfs;
1076       diprg.lMin              = -128;
1077       diprg.lMax              = 127;
1078       if(curJoy->lpVtbl->SetProperty(curJoy, DIPROP_RANGE, &diprg.diph))
1079          return DIENUM_STOP;
1080       return DIENUM_CONTINUE;
1081    }
1082    #endif
1083
1084    void ::TerminateDirectInput()
1085    {
1086       int j;
1087       if (directMouse)
1088       {
1089          directMouse->lpVtbl->Unacquire(directMouse);
1090          directMouse->lpVtbl->Release(directMouse);
1091          directMouse = null;
1092       }
1093    #ifndef ECERE_NOJOYSTICK
1094       for(j=0; j<numJoysticks; j++)
1095       {
1096          if (directJoysticks[j])
1097          {
1098             directJoysticks[j]->lpVtbl->Unacquire(directJoysticks[j]);
1099             directJoysticks[j]->lpVtbl->Release(directJoysticks[j]);
1100             directJoysticks[j] = null;
1101          }
1102       }
1103       numJoysticks = 0;
1104    #endif
1105       if(dInput)
1106       {
1107          dInput->lpVtbl->Release(dInput);
1108          dInput = null;
1109       }
1110
1111       directInputCreate = null;
1112
1113       if(dInputDll)
1114       {
1115          FreeLibrary(dInputDll);
1116          dInputDll = null;
1117       }
1118    }
1119
1120    bool ::InitDirectInput()
1121    {
1122       bool result = false;
1123
1124       dInputDll = LoadLibraryA("dinput.dll");
1125       if(dInputDll)
1126       {
1127          if((directInputCreate = (void *)GetProcAddress(dInputDll, "DirectInputCreateA")))
1128          {
1129             if(!directInputCreate( hInstance, DIRECTINPUT_VERSION, &dInput, null ))
1130             {
1131                int j;
1132
1133                // Mouse
1134                if(!dInput->lpVtbl->CreateDevice(dInput, &GUID_SysMouse, &directMouse, null ))
1135                   directMouse->lpVtbl->SetDataFormat(directMouse, &c_dfDIMouse );
1136
1137    #ifndef ECERE_NOJOYSTICK
1138                // Joystick
1139                dInput->lpVtbl->EnumDevices(dInput, DIDEVTYPE_JOYSTICK, JoystickCallback, null, DIEDFL_ATTACHEDONLY );
1140                for(j=0; j<NUMJOY; j++)
1141                   if(directJoysticks[j])
1142                      if(!directJoysticks[j]->lpVtbl->SetDataFormat(directJoysticks[j], &c_dfDIJoystick ))
1143                         directJoysticks[j]->lpVtbl->EnumObjects(directJoysticks[j], JoystickAxesCallback, directJoysticks[j], DIDFT_AXIS );
1144    #endif
1145                result = true;
1146             }
1147          }
1148       }
1149       return result;
1150    }
1151
1152    void ::AcquireDirectInput(HWND windowHandle, bool state)
1153    {
1154       if((state && !acquiredWindow) || (!state && acquiredWindow == windowHandle))
1155       {
1156          int j;
1157          if(directMouse)
1158          {
1159             if(state)
1160             {
1161                directMouse->lpVtbl->SetCooperativeLevel(directMouse, /*fullScreenMode ? */windowHandle /*: HWND_DESKTOP*/, DISCL_EXCLUSIVE|DISCL_FOREGROUND);
1162                directMouse->lpVtbl->Acquire(directMouse);
1163             }
1164             else
1165                directMouse->lpVtbl->Unacquire(directMouse);
1166          }
1167    #ifndef ECERE_NOJOYSTICK
1168          for(j = 0; j<NUMJOY; j++)
1169             if(directJoysticks[j])
1170             {
1171                if(state)
1172                {
1173                   directJoysticks[j]->lpVtbl->SetCooperativeLevel(directJoysticks[j], /*fullScreenMode ? */windowHandle /*: HWND_DESKTOP*/, DISCL_EXCLUSIVE|DISCL_FOREGROUND);
1174                   directJoysticks[j]->lpVtbl->Acquire(directJoysticks[j]);
1175                }
1176                else
1177                   directJoysticks[j]->lpVtbl->Unacquire(directJoysticks[j]);
1178             }
1179    #endif
1180          acquiredWindow = state ? windowHandle : null;
1181       }
1182    }
1183    #endif
1184
1185    /****************************************************************************
1186       /// INTEGRATION FUNCTIONALITY /////////////
1187    ****************************************************************************/
1188    bool eisWIN32ProcessKey(Window window, DWORD msg, DWORD wParam, DWORD lParam, byte ch)
1189    {
1190       return ProcessKeyMessage(window, msg, wParam, lParam, ch);
1191    }
1192
1193    HWND eisWIN32GetWindowHandle(Window window)
1194    {
1195       return window.windowHandle ? window.windowHandle : window.rootWindow.windowHandle;
1196    }
1197
1198    /****************************************************************************
1199       /// DRIVER IMPLEMENTATION /////////////
1200    ****************************************************************************/
1201
1202 #ifdef _WIN64
1203    void CALLBACK ::TimerProc(UINT uTimerID, UINT uMsg, uint64 dwUser, uint64 dw1, uint64 dw2)
1204 #else
1205    void CALLBACK ::TimerProc(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
1206 #endif
1207    {
1208       guiApp.SignalEvent();
1209    }
1210
1211    // --- User Interface System ---
1212    bool Initialize()
1213    {
1214       WNDCLASS wcl =
1215       {
1216          CS_DBLCLKS, ApplicationWindow, 0L, 0L, 0,
1217          LoadIcon(null, IDI_APPLICATION),
1218          null,
1219          null,
1220          null,
1221          className
1222       };
1223       HDC hdc = GetDC(0);
1224       lastRes = MAKELPARAM(GetSystemMetrics(SM_CYSCREEN), GetSystemMetrics(SM_CXSCREEN));
1225       lastBits = (WPARAM)GetDeviceCaps(hdc, BITSPIXEL);
1226       ReleaseDC(0, hdc);
1227
1228       AttachConsole(-1);
1229       wcl.hInstance = hInstance = GetModuleHandle(null);
1230       RegisterClass(&wcl);
1231
1232       // Move that in reposition desktop
1233       startBar = FindWindowA("Shell_TrayWnd", null);
1234
1235       systemCursors[arrow] = LoadCursor(null, IDC_ARROW);
1236       systemCursors[iBeam] = LoadCursor(null, IDC_IBEAM);
1237       systemCursors[cross] = LoadCursor(null, IDC_CROSS);
1238       systemCursors[moving] = LoadCursor(null, IDC_SIZEALL);
1239       systemCursors[sizeNESW] = LoadCursor(null, IDC_SIZENESW);
1240       systemCursors[sizeNS  ] = LoadCursor(null, IDC_SIZENS);
1241       systemCursors[sizeNWSE] = LoadCursor(null, IDC_SIZENWSE);
1242       systemCursors[sizeWE  ] = LoadCursor(null, IDC_SIZEWE);
1243       systemCursors[hand    ] = LoadCursor(null, IDC_HAND);
1244
1245       SetTimer(null, 0, (DWORD)(1000.0 / 18.2), null);
1246       //SetTimer(null, 0, 1, null);
1247       /*
1248       timeBeginPeriod(1);
1249
1250       hiResTimer = timeSetEvent(
1251          1000.0 / 250.0,
1252          1000.0 / 250.0,
1253          TimerProc,0,TIME_PERIODIC);
1254       */
1255       /*
1256       hiResTimer = timeSetEvent(
1257          1000.0 / 500.0,
1258          1000.0 / 500.0,
1259          TimerProc,0,TIME_PERIODIC);
1260          */
1261
1262    /*
1263       topWindow = CreateWindowEx(0, className, "",WS_POPUP,0,0,1,1,HWND_DESKTOP,
1264          null, hInstance, null);
1265    */
1266       return true;
1267    }
1268
1269    void Terminate()
1270    {
1271       if(hiResTimer) timeKillEvent(hiResTimer);
1272    #ifndef ECERE_NODINPUT
1273       TerminateDirectInput();
1274    #endif
1275    }
1276
1277    void SetTimerResolution(uint hertz)
1278    {
1279       if(hiResTimer) timeKillEvent(hiResTimer);
1280       if(hertz)
1281          hiResTimer = timeSetEvent(1000 / hertz, 1000 / hertz, TimerProc, 0, TIME_PERIODIC);
1282    }
1283
1284    bool ProcessInput(bool processAll)
1285    {
1286       MSG msg;
1287       if(!fullScreenMode)
1288          RepositionDesktop(true);
1289       if(PeekMessage(&msg,0,0,0,PM_NOREMOVE))
1290       {
1291          while(PeekMessage(&msg,0,0,0,PM_REMOVE))
1292          {
1293             TranslateMessage(&msg);
1294             DispatchMessage(&msg);
1295             // printf("%d\n", msg.message);
1296             if(!processAll || msg.message == WM_TIMER) break;
1297          }
1298          return true;
1299       }
1300       return false;
1301    }
1302
1303    void Wait()
1304    {
1305       MsgWaitForMultipleObjects(1, (void **)guiApp.semaphore, FALSE, (uint)(1000 / 18.2) /*INFINITE*/, QS_ALLINPUT);
1306    }
1307
1308    void Lock(Window window)
1309    {
1310
1311    }
1312
1313    void Unlock(Window window)
1314    {
1315
1316    }
1317
1318    const char ** GraphicsDrivers(int * numDrivers)
1319    {
1320       static const char *graphicsDrivers[] = { "GDI", "DirectDraw", "OpenGL", "Direct3D", "Direct3D8", "Direct3D9" };
1321       *numDrivers = sizeof(graphicsDrivers) / sizeof(char *);
1322       return (const char **)graphicsDrivers;
1323    }
1324
1325    void GetCurrentMode(bool * fullScreen, Resolution * resolution, PixelFormat * colorDepth, int * refreshRate)
1326    {
1327       *fullScreen = fullScreenMode;
1328       if(fullScreenMode)
1329       {
1330          Resolution c;
1331          for(c = 0; c<Resolution::enumSize; c++)
1332             if(GetResolutionWidth(c) == devMode.dmPelsWidth && GetResolutionHeight(c) == devMode.dmPelsHeight)
1333             {
1334                *resolution = c;
1335                break;
1336             }
1337          switch(devMode.dmBitsPerPel)
1338          {
1339             case 8: *colorDepth = pixelFormat8; break;
1340             case 16: *colorDepth = pixelFormat555; break;
1341             default: *colorDepth = pixelFormat888; break;
1342          }
1343          * refreshRate = devMode.dmDisplayFrequency;
1344       }
1345    }
1346
1347    void EnsureFullScreen(bool *fullScreen)
1348    {
1349
1350    }
1351
1352    bool ScreenMode(bool fullScreen, Resolution resolution, PixelFormat colorDepth, int refreshRate, bool * textMode)
1353    {
1354       bool result = true;
1355       HDC hdc = GetDC(0);
1356
1357       fullScreenMode = fullScreen;
1358
1359       if(fullScreen)
1360       {
1361          FillBytes(&devMode, 0, sizeof(DEVMODE));
1362            devMode.dmSize = (uint16)sizeof(devMode);
1363          devMode.dmFields |=DM_BITSPERPEL;
1364          devMode.dmFields |=DM_PELSWIDTH|DM_PELSHEIGHT;
1365          devMode.dmFields |= DM_DISPLAYFREQUENCY;
1366          devMode.dmBitsPerPel = colorDepth ? GetDepthBits(colorDepth) : GetDeviceCaps(hdc, BITSPIXEL);
1367          devMode.dmPelsWidth = resolution ? GetResolutionWidth(resolution) : GetSystemMetrics(SM_CXSCREEN);
1368          devMode.dmPelsHeight = resolution ? GetResolutionHeight(resolution) : GetSystemMetrics(SM_CYSCREEN);
1369          devMode.dmDisplayFrequency = refreshRate ? refreshRate : GetDeviceCaps(hdc, VREFRESH);
1370          if(ChangeDisplaySettings(&devMode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
1371             result = false;
1372          else
1373          {
1374             SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC);
1375             guiApp.SetDesktopPosition(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), false);
1376          }
1377       }
1378       else
1379       {
1380          static bool firstTime = true;
1381          if(!firstTime)
1382             ChangeDisplaySettings(null, 0);
1383          firstTime = false;
1384          SetSystemPaletteUse(hdc, SYSPAL_STATIC);
1385          desktopX = desktopY = desktopW = desktopH = 0;
1386
1387          RepositionDesktop(false);
1388       }
1389       ReleaseDC(0,hdc);
1390
1391       return result;
1392    }
1393
1394    // --- Window Creation ---
1395    void * CreateRootWindow(Window window)
1396    {
1397       HWND windowHandle;
1398       uint16 * text = UTF8toUTF16(window.text, null);
1399       if(fullScreenMode)
1400       {
1401          windowHandle = CreateWindowEx(0, className, text,
1402                         WS_POPUP,
1403                         0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN),
1404                         HWND_DESKTOP,
1405                         null, hInstance, null);
1406          ShowWindow(windowHandle, SW_SHOWNORMAL);
1407       }
1408       else if(window.systemParent)
1409          windowHandle = CreateWindowEx(0, className, text,
1410          WS_CHILD,0,0,1,1, window.systemParent, null, hInstance, null);
1411       else
1412       {
1413          DWORD style = 0;
1414          DWORD exStyle = 0;
1415          HWND parentWindow = null; //HWND_DESKTOP; // we get different behaviors with desktop...
1416          Window master = window.master, rootWindow = (master && master != guiApp.desktop) ? master.rootWindow : null;
1417          if(window.style.stayOnTop)
1418             exStyle |= WS_EX_TOPMOST;
1419
1420          if(rootWindow && (window._isModal || window.style.interim || (window.style.thin && !window.style.showInTaskBar)))
1421             parentWindow = rootWindow.is3D ? rootWindow.parent.windowHandle : rootWindow.windowHandle;
1422
1423          if(window.alphaBlend)
1424          // if(window.background.a < 255) //&& window.style & ES_REDRAW) Not needed anymore?
1425             exStyle |= WS_EX_LAYERED; // | WS_EX_TRANSPARENT;
1426
1427          // Toolwindow will disappear if they don't have AppWindow set
1428          if(window.style.showInTaskBar) // Took this out as a way to prevent sticky notes from showing in task bar... ( added flag to Communicator ) || (!parentWindow && window.style.thin))
1429          {
1430             exStyle |= WS_EX_APPWINDOW;
1431             parentWindow = null;
1432          }
1433
1434          if(window.style.thin)
1435             exStyle |= WS_EX_TOOLWINDOW;
1436
1437          if(window.windowHandle)
1438             windowHandle = window.windowHandle;
1439          else
1440          {
1441             if(window.nativeDecorations)
1442             {
1443                BorderBits borderStyle = window.borderStyle; // FIXME!
1444                style = WS_OVERLAPPED;
1445                if(borderStyle.fixed)
1446                   style |= WS_CAPTION;
1447                if(window.hasClose)
1448                   style |= WS_SYSMENU;
1449                if(borderStyle.sizable)
1450                   style |= WS_THICKFRAME;
1451                if(window.hasMinimize)
1452                   style |= WS_MINIMIZEBOX;
1453                if(window.hasMaximize)
1454                   style |= WS_MAXIMIZEBOX;
1455             }
1456             windowHandle = CreateWindowEx(
1457                exStyle,
1458                className, text,
1459                style | (window.systemParent ? WS_CHILD :
1460                (WS_POPUP | (window.style.hasMinimize ? WS_MINIMIZEBOX : 0))),
1461                   0,0,1,1, parentWindow, null, hInstance, null);
1462    #if 0
1463             if(exStyle & WS_EX_LAYERED)
1464                SetLayeredWindowAttributes(windowHandle, 0, 255 /*A(window.background)*/, LWA_ALPHA);
1465    #endif
1466          }
1467       }
1468       delete text;
1469 #ifdef _WIN64
1470       SetWindowLongPtr(windowHandle, GWLP_USERDATA, (int64)window);
1471 #else
1472       SetWindowLong(windowHandle, GWL_USERDATA, (DWORD)window);
1473 #endif
1474
1475       return windowHandle;
1476    }
1477
1478    void DestroyRootWindow(Window window)
1479    {
1480       HICON oldIcon;
1481       int c, lockCount = guiApp.lockMutex.lockCount;
1482       for(c = 0; c < lockCount; c++)
1483          guiApp.lockMutex.Release();
1484
1485       oldIcon = (HICON)SendMessage(window.windowHandle, WM_GETICON, ICON_BIG, 0);
1486 #ifdef _WIN64
1487       if(oldIcon && oldIcon != (HICON)GetClassLongPtr(window.windowHandle, GCLP_HICON))
1488 #else
1489       if(oldIcon && oldIcon != (HICON)GetClassLong(window.windowHandle, GCL_HICON))
1490 #endif
1491          DestroyIcon(oldIcon);
1492
1493         ShowWindow(window.windowHandle, SW_HIDE);
1494
1495 #ifdef _WIN64
1496       SetWindowLongPtr(window.windowHandle, GWLP_USERDATA, (int64)null);
1497 #else
1498       SetWindowLong(window.windowHandle, GWL_USERDATA, 0);
1499 #endif
1500       DestroyWindow(window.windowHandle);
1501
1502       for(c = 0; c < lockCount; c++)
1503          guiApp.lockMutex.Wait();
1504
1505       window.windowHandle = null;
1506    }
1507
1508    // -- Window manipulation ---
1509
1510    void SetRootWindowCaption(Window window, const char * name)
1511    {
1512       uint16 * text = UTF8toUTF16(name, null);
1513       guiApp.Unlock();
1514       SetWindowText(window.windowHandle, text);
1515       guiApp.Lock();
1516       delete text;
1517    }
1518
1519    void PositionRootWindow(Window window, int x, int y, int w, int h, bool move, bool resize)
1520    {
1521       int flags = SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCOPYBITS;
1522
1523       if(!window.systemParent && !fullScreenMode)
1524       {
1525          x += desktopX;
1526          y += desktopY;
1527       }
1528
1529       if(!move) flags |= SWP_NOMOVE;
1530       if(!resize) flags |= SWP_NOSIZE;
1531
1532       /*if(move && resize)
1533       {
1534          flags |= SWP_NOMOVE;
1535          SetWindowPos(window.windowHandle, null, x, y, w, h, flags);
1536          flags &=~SWP_NOMOVE;
1537          flags |= SWP_NOSIZE;
1538       }*/
1539       if(!window.nativeDecorations || window.state != maximized || !window.visible || guiApp.modeSwitching)
1540          SetWindowPos(window.windowHandle, null, x, y, w, h, flags);
1541    }
1542
1543    void OrderRootWindow(Window window, bool topMost)
1544    {
1545       SetWindowPos(window.windowHandle, topMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0,0,0,0,
1546          SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOMOVE|SWP_NOSIZE|SWP_NOREDRAW);
1547    }
1548
1549    void SetRootWindowColor(Window window)
1550    {
1551       if(window.alphaBlend && window.display.pixelFormat == pixelFormat888)
1552       {
1553          /*if(A(window.background) == 255)
1554          {
1555             if((style & WS_EX_LAYERED) == WS_EX_LAYERED)
1556                SetWindowLong(window.windowHandle, GWL_EXSTYLE, style &~WS_EX_LAYERED);
1557          }
1558          else*/
1559          {
1560 #ifndef ECERE_NOBLENDING
1561             DWORD style = GetWindowLong(window.windowHandle, GWL_EXSTYLE);
1562             if((style & WS_EX_LAYERED) != WS_EX_LAYERED)
1563                SetWindowLong(window.windowHandle, GWL_EXSTYLE, style | WS_EX_LAYERED);
1564 #endif
1565             // SetLayeredWindowAttributes(window.windowHandle, 0, 255 /*Max(A(window.background),1)*/, LWA_ALPHA);
1566          }
1567       }
1568    }
1569
1570    void OffsetWindow(Window window, int * x, int * y)
1571    {
1572       if(window.systemParent)
1573       {
1574          POINT point = {*x,*y};
1575          ClientToScreen(GetParent(window.windowHandle), &point);
1576          *x = point.x;
1577          *y = point.y;
1578       }
1579    }
1580
1581    void UpdateRootWindow(Window window)
1582    {
1583       UpdateWindow(window.windowHandle);
1584    }
1585
1586    void SetRootWindowState(Window window, WindowState state, bool visible)
1587    {
1588       if(visible)
1589       {
1590          WindowState curState = window.state;
1591          *&window.state = state;
1592          switch(state)
1593          {
1594             case maximized:
1595             case normal:
1596                SmartShowWindow(window.windowHandle, window.nativeDecorations ? state : normal, (window.active || window.creationActivation == activate) && !externalDisplayChange);
1597                break;
1598             case minimized:
1599                ShowWindow(window.windowHandle, SW_MINIMIZE);
1600                break;
1601          }
1602          *&window.state = curState;
1603       }
1604       else
1605       {
1606          ShowWindow(window.windowHandle, SW_HIDE);
1607       }
1608    }
1609
1610    void ActivateRootWindow(Window window)
1611    {
1612       if(!externalDisplayChange)
1613          SetForegroundWindow(window.windowHandle);
1614    }
1615
1616    void FlashRootWindow(Window window)
1617    {
1618       HWND hwnd = window.windowHandle;
1619       FLASHWINFO flashInfo = { 0 };
1620       Window master = window.master, rootWindow = (master && master != guiApp.desktop) ? master.rootWindow : null;
1621       if(!window.style.showInTaskBar && rootWindow && (window._isModal || window.style.interim))
1622          hwnd = rootWindow.windowHandle;
1623
1624       flashInfo.cbSize = sizeof(FLASHWINFO);
1625       flashInfo.hwnd = hwnd;
1626       flashInfo.uCount = 1;
1627       flashInfo.dwFlags = FLASHW_TRAY; // FLASHW_ALL;
1628       guiApp.Unlock();
1629       FlashWindowEx((void *)&flashInfo);
1630       guiApp.Lock();
1631    }
1632
1633    // --- Mouse-based window movement ---
1634    void StartMoving(Window window, int x, int y, bool fromKeyBoard)
1635    {
1636       if(!fullScreenMode && !window.systemParent)
1637          // Commented out for Chess game because AI thread takes over the moving of main window... Still required?
1638          //SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
1639          ; //SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
1640       if(fromKeyBoard)
1641       {
1642          SetWindowPos(window.windowHandle, HWND_TOPMOST, 0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
1643          mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0);
1644          if(!window.stayOnTop)
1645             SetWindowPos(window.windowHandle, HWND_NOTOPMOST, 0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
1646       }
1647    }
1648
1649    void StopMoving(Window window)
1650    {
1651       if(!fullScreenMode && !window.systemParent)
1652          SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
1653    }
1654
1655    // -- Mouse manipulation ---
1656
1657    void GetMousePosition(int *x, int *y)
1658    {
1659       POINT point;
1660       GetCursorPos(&point);
1661       *x = point.x;
1662       *y = point.y;
1663    }
1664
1665    void SetMousePosition(int x, int y)
1666    {
1667       SetCursorPos(x, y);
1668    }
1669
1670    void SetMouseRange(Window window, Box box)
1671    {
1672       ClipCursor((RECT *) box);
1673    }
1674
1675    void SetMouseCapture(Window window)
1676    {
1677       if(window)
1678          SetCapture(window.windowHandle);
1679       else
1680          ReleaseCapture();
1681    }
1682
1683    // -- Mouse cursor ---
1684
1685    void SetMouseCursor(Window window, SystemCursor cursor)
1686    {
1687       if(lastCursor != cursor)
1688       {
1689          lastCursor = cursor;
1690          SetCursor((cursor == (SystemCursor)-1) ? null : systemCursors[cursor]);
1691       }
1692    }
1693
1694    // --- Caret ---
1695
1696    void SetCaret(int x, int y, int size)
1697    {
1698
1699    }
1700
1701    // --- Clipboard manipulation ---
1702
1703    void ClearClipboard()
1704    {
1705         if(OpenClipboard(null))
1706         {
1707          EmptyClipboard();
1708            CloseClipboard();
1709       }
1710    }
1711
1712    bool AllocateClipboard(ClipBoard clipBoard, uint size)
1713    {
1714       bool result = false;
1715       clipBoard.text = new byte[size];
1716       result = true;
1717       return result;
1718    }
1719
1720    bool SaveClipboard(ClipBoard clipBoard)
1721    {
1722       bool result = false;
1723       if(clipBoard.text)
1724       {
1725          int wordCount;
1726          uint16 * u16text = UTF8toUTF16(clipBoard.text, &wordCount);
1727          wordCount++;
1728          clipBoard.handle = GlobalAlloc(GHND | GMEM_DDESHARE, wordCount * 2);
1729          if(clipBoard.handle)
1730          {
1731             uint16 * text = GlobalLock(clipBoard.handle);
1732             if(text)
1733             {
1734                memcpy(text, u16text, wordCount * 2);
1735                GlobalUnlock(clipBoard.handle);
1736                    if(OpenClipboard(null))
1737                    {
1738                   EmptyClipboard();
1739                       if(SetClipboardData(CF_UNICODETEXT, clipBoard.handle))
1740                   {
1741                          CloseClipboard();
1742                      result= true;
1743                   }
1744                }
1745             }
1746             if(!result)
1747                GlobalFree(clipBoard.handle);
1748          }
1749          delete u16text;
1750       }
1751       return result;
1752    }
1753
1754    bool LoadClipboard(ClipBoard clipBoard)
1755    {
1756       bool result = false;
1757         if(OpenClipboard(null))
1758         {
1759          if((clipBoard.handle = GetClipboardData(CF_UNICODETEXT)))
1760          {
1761             uint16 * u16text = GlobalLock(clipBoard.handle);
1762             if(u16text)
1763             {
1764                clipBoard.text = UTF16toUTF8(u16text);
1765                result = true;
1766                GlobalUnlock(clipBoard.handle);
1767             }
1768          }
1769          CloseClipboard();
1770       }
1771       return result;
1772    }
1773
1774    void UnloadClipboard(ClipBoard clipBoard)
1775    {
1776       delete clipBoard.text;
1777    }
1778
1779    // --- State based input ---
1780
1781    bool AcquireInput(Window window, bool state)
1782    {
1783    #ifndef ECERE_NODINPUT
1784       if(dInput || InitDirectInput())
1785       {
1786          AcquireDirectInput(window.windowHandle, state);
1787          return true;
1788       }
1789    #endif
1790       return false;
1791    }
1792
1793    bool GetMouseState(MouseButtons * buttons, int * x, int * y)
1794    {
1795       bool result = false;
1796    #ifndef ECERE_NODINPUT
1797       DIMOUSESTATE dims = {0};
1798
1799       if(acquiredWindow && directMouse)
1800       {
1801          if(directMouse->lpVtbl->GetDeviceState(directMouse, sizeof(DIMOUSESTATE), &dims ))
1802          {
1803             FillBytes(&dims, 0, sizeof(dims));
1804             directMouse->lpVtbl->Acquire(directMouse);
1805          }
1806          result = true;
1807       }
1808       /*  We don't really want this...
1809       else
1810       {
1811          if(GetAsyncKeyState(VK_LBUTTON))
1812             dims.rgbButtons[0] |= 0x80;
1813          if(GetAsyncKeyState(VK_RBUTTON))
1814             dims.rgbButtons[1] |= 0x80;
1815          if(GetAsyncKeyState(VK_MBUTTON))
1816             dims.rgbButtons[2] |= 0x80;
1817       }
1818       */
1819
1820       if(x)*x = dims.lX;
1821       if(y)*y = dims.lY;
1822       if(dims.lZ)
1823       {
1824 #ifdef _WIN64
1825          Window window = (Window)GetWindowLongPtr(acquiredWindow, GWLP_USERDATA);
1826 #else
1827          Window window = (Window)GetWindowLong(acquiredWindow, GWL_USERDATA);
1828 #endif
1829          ProcessKeyMessage(window, WM_MOUSEWHEEL, ((uint16)(short)dims.lZ) << 16, 0, 0);
1830       }
1831       if(buttons)
1832       {
1833          *buttons = MouseButtons {
1834              left = (dims.rgbButtons[0] & 0x80) ? true : false,
1835              right = (dims.rgbButtons[1] & 0x80) ? true : false,
1836              middle = (dims.rgbButtons[2] & 0x80) ? true : false };
1837       }
1838    #endif
1839
1840       return result;
1841    }
1842
1843    bool GetJoystickState(int device, Joystick joystick)
1844    {
1845       bool result = false;
1846    #ifndef ECERE_NODINPUT
1847       if(joystick != null)
1848       {
1849          DIJOYSTATE dijs = {0};
1850    #ifndef ECERE_NOJOYSTICK
1851          if(acquiredWindow && device < numJoysticks)
1852          {
1853             if(directJoysticks[device])
1854             {
1855                directJoysticks[device]->lpVtbl->Poll(directJoysticks[device]);
1856                if(directJoysticks[device]->lpVtbl->GetDeviceState(directJoysticks[device], sizeof(DIJOYSTATE), &dijs ))
1857                   directJoysticks[device]->lpVtbl->Acquire(directJoysticks[device]);
1858                result = true;
1859             }
1860          }
1861    #endif
1862          joystick.x = dijs.lX;
1863          joystick.y = dijs.lY;
1864          joystick.z = dijs.lZ;
1865          joystick.rx = dijs.lRx;
1866          joystick.ry = dijs.lRy;
1867          joystick.rz = dijs.lRz;
1868          joystick.buttons =
1869               ((dijs.rgbButtons[0] & 0x80) ? JOY_BUTTON1 : 0)
1870             | ((dijs.rgbButtons[1] & 0x80) ? JOY_BUTTON2 : 0)
1871             | ((dijs.rgbButtons[2] & 0x80) ? JOY_BUTTON3 : 0)
1872             | ((dijs.rgbButtons[3] & 0x80) ? JOY_BUTTON4 : 0);
1873       }
1874    #endif
1875       return result;
1876    }
1877
1878    bool GetKeyState(Key key)
1879    {
1880       bool keyState = false;
1881       if(key < 256 || key == alt || key == shift || key == control)
1882       {
1883          uint ks = 0;
1884          if(key == alt)
1885             ks = GetAsyncKeyState(VK_MENU);
1886          else if(key == control)
1887             ks = GetAsyncKeyState(VK_CONTROL);
1888          else if(key == shift)
1889             ks = GetAsyncKeyState(VK_SHIFT);
1890          else if(key2VK[key])
1891             ks = GetAsyncKeyState(key2VK[key]);
1892          keyState = (ks & 0x80000) ? true : false;
1893       }
1894       else if(key == capsState)
1895          keyState = (::GetKeyState(VK_CAPITAL) & 0x00000001) != 0;
1896       else if(key == numState)
1897          keyState = (::GetKeyState(VK_NUMLOCK) & 0x00000001) != 0;
1898       else if(key == scrollState)
1899          keyState = (::GetKeyState(VK_SCROLL) & 0x00000001) != 0;
1900       return keyState;
1901    }
1902
1903    bool SetIcon(Window window, BitmapResource resource)
1904    {
1905       HICON icon = null;
1906       HICON oldIcon = (HICON)SendMessage(window.windowHandle, WM_GETICON, ICON_BIG, 0);
1907
1908       // Dialogs Inherit master's icon if none set
1909       if(!window.style.showInTaskBar && window.hasClose)
1910       {
1911          Window master = window.master;
1912          while(master && !resource)
1913          {
1914             Window rootWindow = (master && master != guiApp.desktop) ? master.rootWindow : null;
1915             if(rootWindow && rootWindow.icon)
1916                resource = rootWindow.icon;
1917             else
1918                master = master.master;
1919          }
1920       }
1921
1922       // WARNING -- putting this here as it is right after CreateRootWindow
1923       // Take out Layered flag if we're not in 24 bit
1924       {
1925          if(window.alphaBlend && window.display.pixelFormat != pixelFormat888)
1926          {
1927 #ifndef ECERE_NOBLENDING
1928             DWORD style = GetWindowLong(window.windowHandle, GWL_EXSTYLE);
1929             style &= ~WS_EX_LAYERED;
1930             SetWindowLong(window.windowHandle, GWL_EXSTYLE, style);
1931 #endif
1932          }
1933       }
1934
1935 #ifdef _WIN64
1936       if(oldIcon && oldIcon != (HICON)GetClassLongPtr(window.windowHandle, GCLP_HICON))
1937 #else
1938       if(oldIcon && oldIcon != (HICON)GetClassLong(window.windowHandle, GCL_HICON))
1939 #endif
1940       {
1941          DestroyIcon(oldIcon);
1942       }
1943
1944       if(resource)
1945       {
1946          Bitmap bitmap { };
1947          if(bitmap.Load(resource.fileName, null, null))
1948          {
1949             Bitmap and { };
1950             PixelFormat format = window.display.pixelFormat;
1951             int bits = GetDepthBits(format);
1952             bool blend;
1953
1954             bitmap.Convert(null, pixelFormat888, null);
1955             and.Allocate(null, (bitmap.width+7/8), bitmap.height, 0, pixelFormat8, false);
1956
1957             blend = bits == 32 || bitmap.pixelFormat != pixelFormat888;
1958
1959             {
1960                byte * picture = and.picture;
1961                int c = 0;
1962                int b = 0;
1963                uint size = bitmap.height * bitmap.width;
1964                while(c < size)
1965                {
1966                   int m = 0;
1967                   byte mask = 0;
1968                   while(m < 8 && c < size)
1969                   {
1970                      mask <<= 1;
1971                      mask |= blend ? (!((ColorAlpha *)bitmap.picture)[c].a) : (((ColorAlpha *)bitmap.picture)[c].a <= 192);
1972                      c++;
1973                      m++;
1974                   }
1975                   picture[b++] = mask;
1976                }
1977                c = 0;
1978                while(c < size)
1979                {
1980                   ColorAlpha color = ((ColorAlpha *)bitmap.picture)[c];
1981                   if(blend ? (!color.a) : (color.a <= 192))
1982                   {
1983                      color.color = { 0, 0, 0 };
1984                      ((ColorAlpha *)bitmap.picture)[c] = color;
1985                   }
1986                   c++;
1987                }
1988             }
1989             if(bits == 15) { bits = 16; format = pixelFormat565; };
1990             bitmap.Convert(null, format, null);
1991
1992             icon = CreateIcon(hInstance, bitmap.width, bitmap.height, 1, (byte)bits, and.picture, bitmap.picture);
1993             delete and;
1994          }
1995          delete bitmap;
1996       }
1997       SendMessage(window.windowHandle, WM_SETICON, ICON_BIG, (LPARAM)icon);
1998       SendMessage(window.windowHandle, WM_SETICON, ICON_SMALL, (LPARAM)icon);
1999       return true;
2000    }
2001
2002    void ::GetScreenArea(Window window, Box box)
2003    {
2004       HMONITOR monitor = MonitorFromWindow(window.windowHandle, MONITOR_DEFAULTTONEAREST);
2005       HMONITOR taskBarMonitor = MonitorFromWindow(startBar, MONITOR_DEFAULTTONEAREST);
2006       if(monitor)
2007       {
2008          MONITORINFO info = { 0 };
2009          info.cbSize = sizeof(MONITORINFO);
2010          GetMonitorInfo(monitor, &info);
2011          // box = { info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom };
2012          box = { info.rcMonitor.left, info.rcMonitor.top, info.rcMonitor.right-1, info.rcMonitor.bottom-1 };
2013
2014          if(taskBarMonitor == monitor)
2015          {
2016             if(taskBarPlacement.rcNormalPosition.top <= box.top && taskBarPlacement.rcNormalPosition.bottom >= box.bottom)
2017             {
2018                if(taskBarPlacement.rcNormalPosition.left <= box.left)
2019                {
2020                   if(taskBarState & ABS_AUTOHIDE)
2021                      box.left++;
2022                   else
2023                      box.left = taskBarPlacement.rcNormalPosition.right;
2024                }
2025                else if(taskBarState & ABS_AUTOHIDE)
2026                   box.right -= 1;
2027                else
2028                   box.right = taskBarPlacement.rcNormalPosition.left;
2029             }
2030             else if(taskBarPlacement.rcNormalPosition.left <= box.left && taskBarPlacement.rcNormalPosition.right >= box.right)
2031             {
2032                if(taskBarPlacement.rcNormalPosition.top <= box.top)
2033                {
2034                   if(taskBarState & ABS_AUTOHIDE)
2035                      box.top += 1;
2036                   else
2037                      box.top = taskBarPlacement.rcNormalPosition.bottom;
2038                }
2039                else if(taskBarState & ABS_AUTOHIDE)
2040                   box.bottom -= 1;
2041                else
2042                   box.bottom = taskBarPlacement.rcNormalPosition.top;
2043             }
2044          }
2045
2046          box.left -= desktopX;
2047          box.top -= desktopY;
2048          box.right -= desktopX;
2049          box.bottom -= desktopY;
2050       }
2051    }
2052 }
2053
2054 #endif