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