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