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