ecere/gui/Window: (#1083) Fixed stuck tooltips, stuck/crashing IDE
[sdk] / ecere / src / gui / GuiApplication.ec
1 namespace gui;
2
3 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
4 #define property _property
5 #define new _new
6 #define class _class
7 #define uint _uint
8
9 #define Window    X11Window
10 #define Cursor    X11Cursor
11 #define Font      X11Font
12 #define Display   X11Display
13 #define Time      X11Time
14 #define KeyCode   X11KeyCode
15 #define Picture   X11Picture
16
17 #include <X11/Xutil.h>
18
19 #undef Window
20 #undef Cursor
21 #undef Font
22 #undef Display
23 #undef Time
24 #undef KeyCode
25 #undef Picture
26
27 #undef uint
28 #undef new
29 #undef property
30 #undef class
31
32 #endif
33
34 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
35 #if defined(__WIN32__)
36 #define SOCKLEN_TYPE int
37 #define WIN32_LEAN_AND_MEAN
38 #define String _String
39 #include <winsock.h>
40 #undef String
41
42 #elif defined(__unix__) || defined(__APPLE__)
43
44 default:
45 #define SOCKLEN_TYPE socklen_t
46 #define uint _uint
47 #define set _set
48 #include <sys/time.h>
49 #include <unistd.h>
50 #include <sys/select.h>
51
52 #include <netinet/in.h>
53 #include <netdb.h>
54 #include <sys/socket.h>
55 #include <sys/wait.h>
56 #include <sys/types.h>
57 #include <sys/time.h>
58 #include <arpa/inet.h>
59
60 #undef set
61 #undef uint
62
63 private:
64 typedef int SOCKET;
65 typedef struct hostent HOSTENT;
66 typedef struct sockaddr SOCKADDR;
67 typedef struct sockaddr_in SOCKADDR_IN;
68 typedef struct in_addr IN_ADDR;
69 #define closesocket(s) close(s)
70
71 #endif
72
73 import "network"
74 #endif
75
76 #if defined(__APPLE__) && !defined(ECERE_VANILLA)
77 import "CocoaInterface"
78 #endif
79
80 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
81 import "XInterface"
82 #endif
83
84 import "Window"
85 import "List"
86
87 /*static */bool guiApplicationInitialized = false;
88 GuiApplication guiApp;
89 int terminateX;
90
91 public class GuiApplication : Application
92 {
93    int numDrivers;
94    char ** driverNames;
95    int numSkins;
96    char ** skinNames;
97
98    bool textMode;
99
100    subclass(Interface) interfaceDriver;
101    subclass(Skin) currentSkin;
102
103    // Desktop window
104    Window desktop;
105
106    // Screen mode flags
107    bool modeSwitching;
108    bool fullScreenMode; // Needs to start at true for the desktop to resize
109
110    bool fullScreen;
111    Resolution resolution;
112    PixelFormat pixelFormat;
113    int refreshRate;
114
115    const char * defaultDisplayDriver;
116
117    Cursor systemCursors[SystemCursor];
118
119    bool cursorUpdate;
120
121    OldList customCursors;
122
123    // Window Timers
124    OldList windowTimers;
125
126    // Mouse events
127    Window prevWindow;            // Used for OnMouseLeave
128    List<Window> overWindows { }; // Used for OnMouseLeave
129    Window windowCaptured;
130
131    // Mouse based moving & resizing
132    Window windowMoving;
133    Point windowMovingStart;
134    Point windowMovingBefore;
135    Size windowResizingBefore;
136    Point movingLast;
137    bool windowIsResizing;
138    bool resizeX, resizeEndX;
139    bool resizeY, resizeEndY;
140
141    // Mouse based scrolling
142    Window windowScrolling;
143    Point windowScrollingBefore, windowScrollingStart;
144
145    // Mouse cursors
146    Bitmap cursorBackground { };
147    int cursorBackgroundX, cursorBackgroundY;
148    int cursorBackgroundW, cursorBackgroundH;
149
150    // Carets
151    Window caretOwner;
152
153    // State based input
154    Window acquiredWindow;
155    int acquiredMouseX, acquiredMouseY;
156
157    Cursor currentCursor;
158
159    uint errorLevel, lastErrorCode;
160
161    bool processAll;
162
163    Mutex waitMutex {};
164    bool waiting;
165    Mutex lockMutex {};
166
167    Window interimWindow;
168    bool caretEnabled;
169
170    char appName[1024];
171    uint timerResolution;
172
173    Size virtualScreen;
174    Point virtualScreenPos;
175
176    int64 mainThread;
177
178    GuiApplication()
179    {
180       SystemCursor c;
181
182       mainThread = GetCurrentThreadID();
183       if(!guiApp)
184          guiApp = this;
185
186       strcpy(appName, $"ECERE Application");
187
188       processAll = true;
189
190       // TODO:
191       // customCursors.offset = OFFSET(Cursor, prev);
192       windowTimers.offset = (uint)(uintptr)&((Timer)0).prev;
193
194       for(c = 0; c<SystemCursor::enumSize; c++)
195          systemCursors[c] = Cursor { systemCursor = c; };
196
197       globalSystem.eventSemaphore = Semaphore { };
198       globalSystem.fileMonitorMutex = Mutex { };
199       globalSystem.fileMonitors.offset = (uint)(uintptr)&((FileMonitor)0).prev;
200       return true;
201    }
202
203    ~GuiApplication()
204    {
205       SystemCursor c;
206
207       if(desktop)
208          desktop.Destroy(0);
209       delete desktop;
210       customCursors.Clear();
211
212 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
213       if(xGlobalDisplay)
214          XUnlockDisplay(xGlobalDisplay);
215 #endif
216
217 #if !defined(__ANDROID__)
218       // Because destruction of app won't be from main thread
219       if(guiApplicationInitialized)
220          lockMutex.Release();
221 #endif
222
223       if(interfaceDriver)
224       {
225          interfaceDriver.Terminate();
226       }
227
228       // interfaceDrivers.Free(null);
229       delete driverNames;
230
231       // skins.Free(null);
232       delete skinNames;
233
234       for(c = 0; c<SystemCursor::enumSize; c++)
235          delete systemCursors[c];
236
237 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
238       Network_Terminate();
239 #endif
240
241       delete globalSystem.eventSemaphore;
242       delete globalSystem.fileMonitorMutex;
243       delete globalSystem.fileMonitorThread;
244
245       UnapplySkin(class(Window));
246
247       // Stop all timers
248       {
249          Timer timer, nextTimer;
250          for(timer = windowTimers.first; timer; timer = nextTimer)
251          {
252             nextTimer = timer.next;
253             timer.Stop();
254          }
255       }
256    }
257
258    bool UpdateTimers()
259    {
260       bool result = false;
261       Time time = GetTime();
262       Timer timer;
263
264       for(timer = windowTimers.first; timer; timer = timer.next)
265          timer.dispatched = false;
266       for(;;)
267       {
268          for(timer = windowTimers.first; timer; timer = timer.next)
269          {
270             if(!timer.dispatched)
271             {
272                if((timer.delay - (Seconds)(time - timer.lastTime)) < Seconds { 0.00001 })
273                {
274                   incref timer;
275                   timer.lastTime = time;
276                   if(timer.DelayExpired(timer.window))
277                      result = true;
278                   timer.dispatched = true;
279                   //delete timer;
280                   eInstance_DecRef(timer);
281                   break;
282                }
283             }
284          }
285          if(!timer) break;
286       }
287       return result;
288    }
289
290    // --- Mouse-based window movement ---
291    void SetCurrentCursor(Window window, Cursor cursor)
292    {
293       currentCursor = cursor;
294       if(cursor)
295       {
296          if(fullScreenMode && cursor.bitmap)
297             interfaceDriver.SetMouseCursor(window ? window : desktop, (SystemCursor)-1);
298          else
299          {
300             interfaceDriver.SetMouseCursor(window ? window : desktop, cursor.systemCursor);
301             cursorBackground.Free();
302          }
303       }
304       cursorUpdate = true;
305    }
306
307    void PreserveAndDrawCursor()
308    {
309       /*
310       if(!acquiredWindow && cursorUpdate && currentCursor && currentCursor->bitmap)
311       {
312          int mouseX, mouseY;
313          Surface surface;
314          Box against = {0,0, desktop.w-1,desktop.h-1};
315          Box box = {0, 0, currentCursor->bitmap->width,currentCursor->bitmap->height};
316
317          interfaceDriver->GetMousePosition(&mouseX, &mouseY);
318
319          mouseX -= currentCursor->hotSpotX;
320          mouseY -= currentCursor->hotSpotY;
321
322          // Preserve Background
323          if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
324          {
325             cursorBackgroundX = mouseX;
326             cursorBackgroundY = mouseY;
327             cursorBackgroundW = currentCursor->bitmap->width;
328             cursorBackgroundH = currentCursor->bitmap->height;
329             eDisplay_Grab(desktop.display, cursorBackground,
330                mouseX, mouseY, cursorBackgroundW, cursorBackgroundH);
331          }
332
333          eBox_ClipOffset(&box, &against, mouseX, mouseY);
334
335          if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
336             eDisplay_StartUpdate(desktop.display);
337          // Display Cursor
338          surface = eDisplay_GetSurface(desktop.display, mouseX, mouseY, &box);
339          if(surface)
340          {
341             eSurface_SetForeground(surface, WHITE);
342             eSurface_Blit(surface, currentCursor->bitmap, 0,0, 0,0,
343                currentCursor->bitmap->width,currentCursor->bitmap->height);
344             eInstance_Delete(surface);
345
346             if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
347             {
348                box.left += mouseX;
349                box.right += mouseX;
350                box.top += mouseY;
351                box.bottom += mouseY;
352                eDisplay_Update(desktop.display, &box);
353             }
354          }
355          if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
356             eDisplay_EndUpdate(desktop.display);
357       }
358       */
359    }
360
361    void RestoreCursorBackground()
362    {
363       /*
364       // Restore Cursor Background
365       if(cursorBackground && desktop.active)
366       {
367          Box box = {0, 0, cursorBackgroundW-1,cursorBackgroundH-1};
368          Box against = {0,0, desktop.w-1,desktop.h-1};
369          Surface surface;
370
371          eBox_ClipOffset(&box, &against, cursorBackgroundX, cursorBackgroundY);
372          if((surface = eDisplay_GetSurface(desktop.display, cursorBackgroundX,cursorBackgroundY, &box)))
373          {
374             eSurface_Blit(surface, cursorBackground, 0, 0, 0,0, cursorBackgroundW,cursorBackgroundH);
375             eInstance_Delete(surface);
376          }
377       }
378       */
379    }
380
381    bool IsModeSwitching()
382    {
383       return modeSwitching;
384    }
385
386    public bool SetDesktopPosition(int x, int y, int w, int h, bool moveChildren)
387    {
388       bool result = true;
389       bool windowResized = desktop.size.w != w || desktop.size.h != h;
390       bool windowMoved = desktop.clientStart.x != x || desktop.clientStart.y != y;
391
392       if((windowResized || windowMoved) && moveChildren)
393       {
394          Window child;
395          desktop.Position(x, y, w, h, true, true, true, true, false, false);
396
397          // Maximized native decorations windows suffer when we drag the dock around, so remaximize them
398          // It's a little jumpy, but oh well.
399
400          // Made this Windows only as it was causing occasional wrong stacking of windows in X11/Cinnamon
401          // when switching debugged app from full-screen
402
403          for(child = desktop.children.first; child; child = child.next)
404          {
405             if(child.nativeDecorations && child.rootWindow == child && child.state == maximized)
406             {
407 #if defined(__WIN32__)
408                child.state = normal;
409                child.state = maximized;
410 #else
411                if(child.active)
412                {
413                   child.state = normal;
414                   child.state = maximized;
415                }
416                else
417                   child.requireRemaximize = true;
418 #endif
419             }
420          }
421          /*for(child = desktop.children.first; child; child = child.next)
422          {
423             if(!child.systemParent)
424             {
425                if(child.anchored)
426                {
427                   int x, y, w, h;
428                   child.ComputeAnchors(
429                      child.ax, child.ay, child.aw, child.ah,
430                      &x, &y, &w, &h);
431                   child.Position(x, y, w, h, true, true, true, true, false);
432                }
433                if(child.state == Maximized)
434                {
435                   child.x = desktop.x;
436                   child.y = desktop.y;
437                   child.ComputeAnchors(,
438                      A_LEFT,A_LEFT,A_OFFSET,A_OFFSET,
439                      &x, &y, &w, &h);
440                   child.Position(, x, y, w, h, false, true, true, true, false);
441                }
442             }
443          }*/
444          if(desktop.display)
445          {
446             desktop.display.Lock(true);
447             if(windowResized)
448             {
449                if(!desktop.display.Resize(desktop.size.w, desktop.size.h))
450                   result = false;
451
452                desktop.dirty = true;
453                if(!desktop.display.flags.flipping)
454                   desktop.Update(null);
455             }
456             // When does the desktop have a display in not fullscreen mode?
457             if(!fullScreenMode && !modeSwitching)
458                desktop.UpdateDisplay();
459             desktop.display.Unlock();
460          }
461       }
462       else
463          desktop.SetPosition(x, y, w, h, false, false, false);
464       return result;
465    }
466
467    void SetAppFocus(bool state)
468    {
469       // Shouldn't be property here
470       desktop.active = state;
471    }
472
473    bool SelectSkin(const char * skinName)
474    {
475       bool result = false;
476       subclass(Skin) skin;
477       OldLink link;
478
479       for(link = class(Skin).derivatives.first; link; link = link.next)
480       {
481          skin = link.data;
482          if(skin.name && !strcmp(skin.name, skinName))
483             break;
484       }
485       if(!link) skin = null;
486
487       if(skin)
488       {
489          if(skin != currentSkin || !currentSkin)
490          {
491             // Try finding a driver to support this mode
492             if(skin.textMode != textMode)
493             {
494                return false;
495             }
496             else
497             {
498                bool needReload = false;
499
500                if(!modeSwitching && currentSkin)
501                {
502                   modeSwitching = true;
503                   desktop.UnloadGraphics(true);
504                   needReload = true;
505                }
506
507                UnapplySkin(class(Window));
508
509                currentSkin = skin;
510
511                ApplySkin(class(Window), skin.name, null);
512
513                if(needReload)
514                {
515                   if(desktop.SetupDisplay())
516                      if(desktop.LoadGraphics(false, true))
517                         result = true;
518                   modeSwitching = false;
519                }
520                else
521                   result = true;
522             }
523          }
524          else
525             result = true;
526       }
527       return result;
528    }
529
530    void Initialize(bool switchMode)
531    {
532       // TODO:
533       // if(!initialized && eClass_IsDerived(__ecereModule->app->module.inst.class, guiApplicationClass))
534       if(!guiApplicationInitialized)
535       {
536          const char * defaultDriver = null;
537 #if defined(ECERE_VANILLA) || defined(ECERE_ONEDRIVER)
538          char * driver = null;
539 #else
540
541          // char * driver = getenv("ECERE_DRIVER");
542          char * driver = null;
543          static char driverStorage[1024];
544          GetEnvironment("ECERE_DRIVER", driverStorage, sizeof(driverStorage));
545          if(driverStorage[0]) driver = driverStorage;
546 #endif
547          guiApplicationInitialized = true;
548
549          fullScreenMode = true; // Needs to start at true for the desktop to resize
550          // Set this to true earlier so we can override it!
551          //processAll = true;
552
553          errorLevel = 2;
554
555          lockMutex.Wait();
556 /*#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
557          if(xGlobalDisplay)
558             XLockDisplay(xGlobalDisplay);
559 #endif*/
560
561          // Setup Desktop
562          if(!desktop)
563          {
564             desktop = Window { nativeDecorations = false };
565             incref desktop;
566             incref desktop;
567             desktop.childrenOrder.circ = true;
568             desktop.childrenCycle.circ = true;
569             desktop.background = blue;
570             desktop.rootWindow = desktop;
571             desktop.cursor = GetCursor(arrow);
572             desktop.caption = appName;
573             *&desktop.visible = true;
574             desktop.position = Point { };
575             desktop.mutex = Mutex { };
576             desktop.created = true;
577          }
578
579    #if defined(__WIN32__)
580          {
581             if(driver)
582                defaultDriver = driver;
583             else if((this.isGUIApp & 1) && !textMode)
584                defaultDriver = "GDI";
585             else
586                defaultDriver = "Win32Console";
587          }
588    #elif defined(__APPLE__)
589          {
590             if (driver)
591                defaultDriver = driver;
592             else
593                defaultDriver = "X"; //"CocoaOpenGL";
594          }
595    #elif defined(__ANDROID__)
596          {
597             if(driver)
598                defaultDriver = driver;
599             else
600                defaultDriver = "OpenGL";
601          }
602    #else
603          if((this.isGUIApp & 1) && !textMode)
604          {
605             char * display = getenv("DISPLAY");
606
607             if(!display || !display[0] || !SwitchMode(false, "X", 0, 0, 0, null, true))
608                defaultDriver = "NCurses";
609                // SwitchMode(true, "NCurses", 0, PixelFormatText, 0, null, true);
610             else if(!driver)
611                defaultDriver = "X";
612             else
613                defaultDriver = driver;
614          }
615          else
616             defaultDriver = "NCurses";
617    #endif
618          if(switchMode)
619          {
620             if(defaultDriver)
621                SwitchMode(false, defaultDriver, 0, 0, 0, null, true);
622             else
623             {
624             /*
625          #if defined(__WIN32__)
626                SwitchMode(true, "Win32Console", 0, PixelFormatText, 0, null, true);
627          #endif
628             */
629
630          #if defined(__DOS__)
631                SwitchMode(true, "SVGA", Res640x480, PixelFormat8, 0, null, true);
632          #endif
633
634          #if defined(__APPLE__)
635                // SwitchMode(true, "X" /*"CocoaOpenGL"*/, 0, 0, 0, null, true);
636          #endif
637
638          #if defined(__unix__)
639          #if defined(ECERE_MINIGLX)
640                SwitchMode(true, "OpenGL", 0, 0, 0, null, true);
641          #endif
642          #endif
643             }
644             /*if(!interfaceDriver)
645                guiApplicationInitialized = false;*/
646          }
647          else
648             defaultDisplayDriver = defaultDriver;
649       }
650    }
651
652 public:
653    virtual bool Init(void);
654    virtual bool Cycle(bool idle);
655    virtual void Terminate(void);
656
657    void Main(void)
658    {
659       Window window;
660
661       if(Init())
662       {
663          if(desktop)
664          {
665             // better solution when designing tab order/activated window etc, why do windows move in the list?
666             while(true)
667             {
668                for(window = desktop.children.first; window; window = window.next)
669                {
670                   if(window.autoCreate && !window.created)
671                   {
672                      if(window.Create())
673                         break;
674                   }
675                }
676                if(!window) break;
677             }
678          }
679
680          if(desktop)
681          {
682             int terminated = 0;
683             incref desktop;
684             ProcessInput(true);
685             while(desktop && interfaceDriver)
686             {
687                bool wait;
688                Window child;
689                if(terminateX != terminated)
690                {
691                   terminated = terminateX;
692                   desktop.Destroy(0);
693                   if(desktop.created)
694                   {
695                      terminated = 0;
696                      terminateX = 0;
697                      //printf("Resetting terminate X to 0\n");
698                   }
699                }
700
701                for(child = desktop.children.first; child; child = child.next)
702                   if(child.created && child.visible)
703                      break;
704                if(!child) break;
705
706                for(window = desktop.children.first; window; window = window.next)
707                   if(window.mutex) window.mutex.Wait();
708                UpdateDisplay();
709                for(window = desktop.children.first; window; window = window.next)
710                   if(window.mutex) window.mutex.Release();
711                wait = !ProcessInput(true);
712 #ifdef _DEBUG
713                if(lockMutex.owningThread != GetCurrentThreadID())
714                   PrintLn("WARNING: ProcessInput returned unlocked GUI!");
715 #endif
716                if(!Cycle(wait))
717                   wait = false;
718
719                if(wait)
720                   Wait();
721                else
722                {
723 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
724                   if(xGlobalDisplay)
725                      XUnlockDisplay(xGlobalDisplay);
726 #endif
727
728                   lockMutex.Release();
729                   lockMutex.Wait();
730
731 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
732                   if(xGlobalDisplay)
733                      XLockDisplay(xGlobalDisplay);
734 #endif
735                }
736             }
737             eInstance_DecRef(desktop);
738          }
739       }
740       Terminate();
741
742 #if defined(__ANDROID__)
743       // Because destruction of GuiApp won't be from main thread
744       lockMutex.Release();
745 #endif
746    }
747
748    void Wait(void)
749    {
750 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
751       if(xGlobalDisplay)
752          XUnlockDisplay(xGlobalDisplay);
753 #endif
754
755       lockMutex.Release();
756
757       waitMutex.Wait();
758       waiting = true;
759       if(interfaceDriver)
760          interfaceDriver.Wait();
761       waiting = false;
762       waitMutex.Release();
763
764       lockMutex.Wait();
765
766 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
767       if(xGlobalDisplay)
768          XLockDisplay(xGlobalDisplay);
769 #endif
770    }
771
772    bool ProcessInput(bool useProcessAll)
773    {
774       if(interfaceDriver)
775       {
776          bool result = 0;
777
778 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
779          ProcessNetworkEvents();
780 #endif
781
782          /*
783          result = interfaceDriver.ProcessInput(useProcessAll && processAll);
784          if(!desktop || !interfaceDriver) return;
785          {
786             bool wait;
787             Window child;
788             for(child = app.desktop.children.first; child; child = child.next)
789                if(child.created && child.visible)
790                   break;
791             if(!child) return result;
792          }
793
794          result |= UpdateTimers();
795          result |= ProcessFileNotifications();
796          */
797
798          result |= ProcessFileNotifications();
799          result |= UpdateTimers();
800          result |= interfaceDriver.ProcessInput(useProcessAll && processAll);
801
802          return result;
803       }
804       return false;
805    }
806
807    void UpdateDisplay(void)
808    {
809 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
810       if(Desktop3DUpdateDisplay()) return;
811 #endif
812
813       if(interfaceDriver)
814       {
815          if(fullScreenMode && desktop.display)
816          {
817             desktop.mutex.Wait();
818             if(desktop.active)
819             {
820                desktop.display.Lock(true);
821
822                if(desktop.dirty || cursorUpdate)
823                {
824                   if(desktop.display.flags.flipping)
825                      desktop.Update(null);
826                   desktop.UpdateDisplay();
827                   cursorUpdate = true;
828                }
829                if(cursorUpdate || desktop.dirty)
830                {
831                   PreserveAndDrawCursor();
832                   cursorUpdate = false;
833                   desktop.dirty = false;
834                   RestoreCursorBackground();
835                }
836
837                desktop.display.Unlock();
838             }
839             desktop.mutex.Release();
840          }
841          else
842          {
843             Window window;
844
845             for(window = desktop.children.first; window; window = window.next)
846             {
847                if(window.mutex) window.mutex.Wait();
848                if(window.visible && window.dirty)
849                {
850                   // Logf("Updating %s\n", window.name);
851                   interfaceDriver.Lock(window);
852                   if(window.display)
853                   {
854                      if(window.display.current)
855                      {
856                         printf("bug");
857                      }
858                      window.display.Lock(true);
859                      window.UpdateDisplay();
860                      window.display.Unlock();
861                   }
862
863                   window.dirty = false;
864                   interfaceDriver.Unlock(window);
865                   /*
866                   Log("--------------\n");
867                   usleep(1000000);
868                   */
869                }
870                if(window.mutex) window.mutex.Release();
871             }
872          }
873       }
874    }
875
876    void WaitEvent(void)
877    {
878       globalSystem.eventSemaphore.Wait();
879    }
880
881 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
882    bool ProcessNetworkEvents()
883    {
884       bool gotEvent = false;
885    #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
886       if(network.networkInitialized)
887       {
888          Service service;
889          Socket socket, next;
890          struct timeval tv = {0, 0};
891          fd_set rs, ws, es;
892          Service nextService;
893          OldLink semPtr;
894
895          PauseNetworkEvents();
896          network.mutex.Wait();
897
898    #ifdef DEBUG_SOCKETS
899          if(network.connectEvent || network.networkEvent)
900             Log("[P] [NProcess]\n");
901    #endif
902          rs = network.readSet;
903          ws = network.writeSet;
904          es = network.exceptSet;
905
906          if((network.ns && select(network.ns, &rs, &ws, &es, &tv)) || network.leftOverBytes)
907          {
908             network.leftOverBytes = false;
909
910             // Danger here? Why looping with a next and not unlocking anything?
911             for(socket = network.connectSockets.first; socket; socket = next)
912             {
913                next = socket.next;
914                if(!socket.processAlone && FD_ISSET(socket.s, &ws))
915                {
916                   network.mutex.Release();
917                   socket.connectThread.Wait();
918                   network.mutex.Wait();
919                }
920             }
921             for(socket = network.sockets.first; socket; socket = next)
922             {
923                next = socket.next;
924                if(!socket.processAlone)
925                {
926                   network.mutex.Release();
927                   gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
928                   network.mutex.Wait();
929                }
930             }
931
932             for(service = network.services.first; service; service = nextService)
933             {
934                nextService = service.next;
935                if(!service.processAlone)
936                {
937                   if(FD_ISSET(service.s, &rs))
938                   {
939       #ifdef DEBUG_SOCKETS
940                      Logf("[P] Accepting connection (%x)\n", service.s);
941       #endif
942                      service.accepted = false;
943                      service.OnAccept();
944                      if(!service.accepted)
945                      {
946                         SOCKET s;
947                         SOCKADDR_IN a;
948                         SOCKLEN_TYPE addrLen = sizeof(a);
949                         s = accept(service.s,(SOCKADDR *)&a,&addrLen);
950                         closesocket(s);
951                      }
952                      gotEvent |= true;
953
954       #ifdef DEBUG_SOCKETS
955                      Log("[P] Connection accepted\n");
956       #endif
957                   }
958                }
959                for(socket = service.sockets.first; socket; socket = next)
960                {
961                   next = socket.next;
962                   if(!socket.processAlone)
963                   {
964                      network.mutex.Release();
965                      gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
966                      network.mutex.Wait();
967                   }
968                }
969             }
970          }
971          if(network.connectEvent)
972          {
973             bool goOn = true;
974             while(goOn)
975             {
976                goOn = false;
977                for(socket = network.connectSockets.first; socket; socket = next)
978                {
979                   next = socket.next;
980                   if(socket._connected && socket._connected != -2)
981                   {
982                      network.connectSockets.Remove(socket);
983                      delete socket.connectThread;
984
985                      // printf("%s is connected = %d\n", socket._class.name, socket._connected);
986                      if(socket._connected == -1)
987                      {
988       #ifdef DEBUG_SOCKETS
989                         Logf("[P] Processing disconnected connect (%x)\n", socket.s);
990       #endif
991       #if 0
992                         if(socket.disconnectCode == ResolveFailed)
993                            Logf("Error resolving address %s\n", socket.address);
994       #endif
995                         if(socket.s == network.ns - 1)
996                            Network_DetermineMaxSocket();
997
998                         socket._connected = 0;
999                         socket.Free(false);
1000                         delete socket;
1001                      }
1002                      else if(socket._connected == 1)
1003                      {
1004       #ifdef DEBUG_SOCKETS
1005                         Log("[P] Processing connected connect\n");
1006       #endif
1007                         FD_CLR(socket.s, &network.writeSet);
1008                         FD_SET(socket.s, &network.readSet);
1009                         FD_SET(socket.s, &network.exceptSet);
1010                         network.mutex.Release();
1011
1012                         // printf("Calling OnConnect on %s\n", socket._class.name);
1013                         socket.OnConnect();
1014                         network.mutex.Wait();
1015                         if(socket._connected)
1016                            network.sockets.Add(socket);
1017                      }
1018                      gotEvent |= true;
1019                      goOn = true;
1020                      break;
1021                   }
1022                }
1023             }
1024             network.connectEvent = false;
1025          }
1026          if(network.networkEvent)
1027          {
1028             network.networkEvent = false;
1029             network.selectSemaphore.Release();
1030          }
1031
1032          if(gotEvent)
1033          {
1034             for(semPtr = network.mtSemaphores.first; semPtr; semPtr = semPtr.next)
1035             {
1036                ((Semaphore)semPtr.data).Release();
1037             }
1038          }
1039
1040          network.mutex.Release();
1041          ResumeNetworkEvents();
1042       }
1043    #endif
1044       return gotEvent;
1045    }
1046
1047    void WaitNetworkEvent()
1048    {
1049       if(network.networkInitialized)
1050       {
1051          if(GetCurrentThreadID() == network.mainThreadID)
1052          {
1053             WaitEvent();
1054          }
1055          else
1056          {
1057             Semaphore semaphore { };
1058             OldLink semPtr { data = semaphore };
1059             network.mutex.Wait();
1060             network.mtSemaphores.Add(semPtr);
1061             network.mutex.Release();
1062
1063             ResumeNetworkEvents();
1064             semaphore.Wait();
1065             PauseNetworkEvents();
1066             network.mutex.Wait();
1067             network.mtSemaphores.Delete(semPtr);
1068             network.mutex.Release();
1069             delete semaphore;
1070          }
1071       }
1072    }
1073
1074    void PauseNetworkEvents()
1075    {
1076       if(network.networkInitialized)
1077       {
1078          network.processMutex.Wait();
1079       }
1080    }
1081
1082    void ResumeNetworkEvents()
1083    {
1084       if(network.networkInitialized)
1085       {
1086          network.processMutex.Release();
1087       }
1088    }
1089 #endif
1090
1091    void SignalEvent(void)
1092    {
1093       globalSystem.eventSemaphore.Release();
1094    }
1095
1096    // TODO: Might want to make this private with simpler public version?
1097    bool SwitchMode(bool fullScreen, const char * driverName, Resolution resolution, PixelFormat colorDepth, int refreshRate, const char * skinName, bool fallBack)
1098    {
1099       bool result = false;
1100       OldLink link;
1101       const char * fbDriver;
1102       bool fbFullScreen = 0;
1103       Resolution fbResolution = 0;
1104       PixelFormat fbColorDepth = 0;
1105       int fbRefreshRate = 0;
1106       subclass(Interface) inter;
1107       subclass(Skin) skin = null;
1108
1109       if(skinName)
1110       {
1111          OldLink link;
1112
1113          for(link = class(Skin).derivatives.first; link; link = link.next)
1114          {
1115             skin = link.data;
1116             if(skin.name && !strcmp(skin.name, skinName))
1117                break;
1118          }
1119          if(!link) skin = null;
1120       }
1121
1122       Initialize(false);
1123
1124       fbDriver = defaultDisplayDriver;
1125       inter = interfaceDriver;
1126
1127       if(interfaceDriver)
1128          interfaceDriver.GetCurrentMode(&fbFullScreen, &fbResolution, &fbColorDepth, &fbRefreshRate);
1129
1130       if(!driverName && !interfaceDriver)
1131          driverName = defaultDisplayDriver;
1132
1133       if(driverName || (skin && skin.textMode != textMode))
1134       {
1135          for(link = class(Interface).derivatives.first; link; link = link.next)
1136          {
1137             bool foundDriver = false;
1138             int c, numDrivers;
1139             const char ** graphicsDrivers;
1140             inter = link.data;
1141
1142             graphicsDrivers = inter.GraphicsDrivers(&numDrivers);
1143
1144             for(c=0; c<numDrivers; c++)
1145                if(!driverName || !strcmp(driverName, graphicsDrivers[c]))
1146                {
1147                   if(!skin || skin.textMode == IsDriverTextMode(graphicsDrivers[c]))
1148                   {
1149                      driverName = graphicsDrivers[c];
1150                      foundDriver = true;
1151                      break;
1152                   }
1153                }
1154             if(foundDriver)
1155                break;
1156          }
1157          if(!link)
1158             inter = null;
1159       }
1160
1161       /*
1162       if(driverName)
1163       {
1164 #if defined(__WIN32__)
1165 #if !defined(ECERE_VANILLA)
1166          if(!strcmp(driverName, "Win32Console")) inter = (subclass(Interface))class(Win32ConsoleInterface); else
1167 #endif
1168          inter = (subclass(Interface))class(Win32Interface);
1169 #else
1170          if(!strcmp(driverName, "X")) inter = (subclass(Interface))class(XInterface);
1171          else inter = (subclass(Interface))class(NCursesInterface);
1172 #endif
1173       }
1174       */
1175
1176       if(interfaceDriver && (!driverName || (fbDriver && !strcmp(fbDriver, driverName))) &&
1177          fullScreen == fbFullScreen &&
1178          (!resolution || resolution == fbResolution) &&
1179          (!colorDepth || colorDepth == fbColorDepth) &&
1180          (!refreshRate || refreshRate == fbRefreshRate) &&
1181          (currentSkin && (!skinName || !strcmp(currentSkin.name, skinName))))
1182          result = true;
1183       else if(inter)
1184       {
1185          bool wasFullScreen = fullScreenMode;
1186          subclass(Skin) oldSkin = currentSkin;
1187
1188          textMode = false;
1189          modeSwitching = true;
1190
1191          if(interfaceDriver)
1192             desktop.UnloadGraphics(true);
1193
1194          if(inter != interfaceDriver)
1195          {
1196             if(interfaceDriver)
1197             {
1198                interfaceDriver.Terminate();
1199             }
1200             result = inter.Initialize();
1201          }
1202          else
1203             result = true;
1204          if(result)
1205          {
1206             result = false;
1207
1208             interfaceDriver = inter;
1209             interfaceDriver.SetTimerResolution(timerResolution);
1210             inter.EnsureFullScreen(&fullScreen);
1211             fullScreenMode = fullScreen;
1212
1213             if((!wasFullScreen && !fullScreen) ||
1214                inter.ScreenMode(fullScreen, resolution, colorDepth, refreshRate, &textMode))
1215             {
1216                if(!fbDriver || (driverName && strcmp(fbDriver, driverName)))
1217                   defaultDisplayDriver = driverName;
1218
1219                if(!skinName || !SelectSkin(skinName))
1220                {
1221                   if(!currentSkin || currentSkin.textMode != textMode ||
1222                      !SelectSkin(currentSkin.name))
1223                   {
1224                      OldLink link;
1225                      subclass(Skin) skin = null;
1226
1227                      for(link = class(Skin).derivatives.first; link; link = link.next)
1228                      {
1229                         skin = link.data;
1230                         if(skin.textMode == textMode)
1231                            break;
1232                      }
1233                      if(!link) skin = null;
1234
1235                      if(skin)
1236 #if !defined(__ANDROID__)
1237                         SelectSkin(skin.name);
1238 #else
1239                         currentSkin = skin;
1240 #endif
1241                   }
1242                }
1243
1244                if(currentSkin && desktop.SetupDisplay())
1245                {
1246                   desktop.active = true;
1247
1248                   if(fullScreen)
1249                   {
1250                      desktop.display.Lock(false);
1251                      desktop.display.Position(0,0);
1252                      desktop.display.Unlock();
1253                   }
1254
1255                   if(desktop.LoadGraphics(false, oldSkin != currentSkin))
1256                   {
1257                      if(fbDriver)
1258                      {
1259                         desktop.UpdateDisplay();
1260                      }
1261
1262                      this.fullScreen = fullScreen;
1263                      result = true;
1264                   }
1265                }
1266             }
1267          }
1268          modeSwitching = false;
1269          if(!result)
1270             LogErrorCode(modeSwitchFailed, driverName ? driverName : defaultDisplayDriver);
1271       }
1272       else
1273          LogErrorCode(driverNotSupported, driverName ? driverName : defaultDisplayDriver);
1274
1275       if(!result && fallBack && fbDriver)
1276       {
1277          if(!SwitchMode(fbFullScreen, fbDriver, fbResolution, fbColorDepth, fbRefreshRate, null, false))
1278             Log($"Error falling back to previous video mode.\n");
1279       }
1280       return result;
1281    }
1282
1283    bool ProcessFileNotifications()
1284    {
1285       bool activity = false;
1286       FileMonitor monitor, next;
1287       static int reentrant = 0;
1288
1289       // Reentrant FileNotification is asking for trouble since each monitor is spawning a Modal() MessageBox
1290       if(reentrant) return false;
1291       // printf("[%d] Waiting in ProcessFileNotifications for fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1292       globalSystem.fileMonitorMutex.Wait();
1293       reentrant++;
1294       for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1295       {
1296          FileNotify notify;
1297
1298          next = monitor.next;
1299          incref monitor;
1300          if(next)
1301             incref next;
1302
1303          if(!monitor.reentrant && !monitor.toBeFreed)
1304          {
1305             monitor.reentrant = true;
1306             while((notify = monitor.fileNotifies.first))
1307             {
1308                monitor.fileNotifies.Remove(notify);
1309
1310                if(monitor.active)
1311                {
1312                   if(monitor.directory)
1313                   {
1314                      if(!monitor.OnDirNotify(monitor.data, notify.action, notify.fileName, notify.param))
1315                         monitor.StopMonitoring();
1316                   }
1317                   else
1318                   {
1319                      if(!monitor.OnFileNotify(monitor.data, notify.action, notify.param))
1320                         monitor.StopMonitoring();
1321                   }
1322                }
1323                monitor.reentrant = false;
1324
1325                notify.Free();
1326                delete notify;
1327                activity = true;
1328             }
1329             monitor.reentrant = false;
1330          }
1331          delete monitor;
1332          if(next && next._refCount > 1)
1333             next._refCount--;
1334          else
1335             delete next;
1336       }
1337       reentrant--;
1338       if(!reentrant)
1339       {
1340          for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1341          {
1342             next = monitor.next;
1343             if(monitor.toBeFreed && !monitor.reentrant)
1344                monitor.FreeMonitor();
1345          }
1346       }
1347       // printf("[%d] Releasing in ProcessFileNotifications fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1348       globalSystem.fileMonitorMutex.Release();
1349       return activity;
1350    }
1351
1352    void Lock(void)
1353    {
1354       lockMutex.Wait();
1355 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1356       if(xGlobalDisplay)
1357          XLockDisplay(xGlobalDisplay);
1358 #endif
1359    }
1360
1361    void Unlock(void)
1362    {
1363 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1364       if(xGlobalDisplay)
1365          XUnlockDisplay(xGlobalDisplay);
1366 #endif
1367       lockMutex.Release();
1368    }
1369
1370    Cursor GetCursor(SystemCursor cursor)
1371    {
1372       return systemCursors[cursor];
1373    }
1374
1375    bool GetKeyState(Key key)
1376    {
1377       return interfaceDriver.GetKeyState(key);
1378    }
1379
1380    bool GetMouseState(MouseButtons * buttons, int * x, int * y)
1381    {
1382       return interfaceDriver.GetMouseState(buttons, x, y);
1383    }
1384
1385    // Properties
1386    property const char * appName
1387    {
1388       set
1389       {
1390          strcpy(appName, value);
1391          if(desktop) desktop.text = appName;
1392       }
1393       get
1394       {
1395          return (const char *)(this ? appName : null);
1396       }
1397    };
1398    property Semaphore semaphore { get { return globalSystem.eventSemaphore; } };
1399    property bool alwaysEmptyInput{ set { processAll = value; } get { return processAll; } };
1400    property bool fullScreen
1401    {
1402       set
1403       {
1404          SwitchMode(value, defaultDisplayDriver, resolution,
1405             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1406       }
1407       get { return this ? fullScreen : false; }
1408    };
1409    property const char * driver
1410    {
1411       set
1412       {
1413          SwitchMode( fullScreen, value, resolution, pixelFormat, refreshRate,
1414             currentSkin ? currentSkin.name : null, true);
1415        }
1416        get { return this ? defaultDisplayDriver : null; }
1417    };
1418    property Resolution resolution
1419    {
1420       set
1421       {
1422          SwitchMode(fullScreen, defaultDisplayDriver, value, pixelFormat, refreshRate,
1423             currentSkin ? currentSkin.name : null, true);
1424       }
1425       get { return this ? resolution : 0; }
1426    };
1427    property PixelFormat pixelFormat
1428    {
1429       set
1430       {
1431          SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1432             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1433       }
1434       get { return this ? pixelFormat : 0; }
1435    };
1436    property int refreshRate
1437    {
1438       set
1439       {
1440          SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1441             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1442       }
1443       get { return this ? refreshRate : 0; }
1444    };
1445    property const char * skin
1446    {
1447       set { SelectSkin(value); }
1448       get { return (this && currentSkin) ? currentSkin.name : null; }
1449    };
1450    property bool textMode
1451    {
1452       set { textMode = value; }     // TODO: Implement switching
1453       get { return this ? textMode : false; }
1454    };
1455    property Window desktop { get { return this ? desktop : null; } };
1456    property const char ** drivers { get { return null; } };
1457    property const char * const * skins { get { return null; } };
1458    property subclass(Skin) currentSkin { get { return this ? currentSkin : null; } };
1459    property int numDrivers { get { return 0; } };
1460    property int numSkins { get { return 0; } };
1461    property uint timerResolution
1462    {
1463       set { timerResolution = value; if(interfaceDriver) interfaceDriver.SetTimerResolution(value); }
1464    };
1465 };