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