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