ecere/gui/GuiApplication;X11Interface: Calling ProcessInput() once before first Displ...
[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(Window window, Cursor cursor)
307    {
308       currentCursor = cursor;
309       if(cursor)
310       {
311          if(fullScreenMode && cursor.bitmap)
312             interfaceDriver.SetMouseCursor(window ? window : desktop, (SystemCursor)-1);
313          else
314          {
315             interfaceDriver.SetMouseCursor(window ? window : desktop, 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             ProcessInput(true);
687             while(desktop && interfaceDriver)
688             {
689                bool wait;
690                Window child;
691                if(terminateX != terminated)
692                {
693                   terminated = terminateX;
694                   desktop.Destroy(0);
695                   if(desktop.created)
696                   {
697                      terminated = 0;
698                      terminateX = 0;
699                      //printf("Resetting terminate X to 0\n");
700                   }
701                }
702
703                for(child = desktop.children.first; child; child = child.next)
704                   if(child.created && child.visible)
705                      break;
706                if(!child) break;
707
708                for(window = desktop.children.first; window; window = window.next)
709                   if(window.mutex) window.mutex.Wait();
710                UpdateDisplay();
711                for(window = desktop.children.first; window; window = window.next)
712                   if(window.mutex) window.mutex.Release();
713                wait = !ProcessInput(true);
714 #ifdef _DEBUG
715                if(lockMutex.owningThread != GetCurrentThreadID())
716                   PrintLn("WARNING: ProcessInput returned unlocked GUI!");
717 #endif
718                if(!Cycle(wait))
719                   wait = false;
720
721                if(wait)
722                   Wait();
723                else
724                {
725 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
726                   if(xGlobalDisplay)
727                      XUnlockDisplay(xGlobalDisplay);
728 #endif
729
730                   lockMutex.Release();
731                   lockMutex.Wait();
732
733 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
734                   if(xGlobalDisplay)
735                      XLockDisplay(xGlobalDisplay);
736 #endif
737                }
738             }
739             eInstance_DecRef(desktop);
740          }
741       }
742       Terminate();
743
744 #if defined(__ANDROID__)
745       // Because destruction of GuiApp won't be from main thread
746       lockMutex.Release();
747 #endif
748    }
749
750    void Wait(void)
751    {
752 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
753       if(xGlobalDisplay)
754          XUnlockDisplay(xGlobalDisplay);
755 #endif
756
757       lockMutex.Release();
758
759       waitMutex.Wait();
760       waiting = true;
761       if(interfaceDriver)
762          interfaceDriver.Wait();
763       waiting = false;
764       waitMutex.Release();
765
766       lockMutex.Wait();
767
768 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
769       if(xGlobalDisplay)
770          XLockDisplay(xGlobalDisplay);
771 #endif
772    }
773
774    bool ProcessInput(bool useProcessAll)
775    {
776       if(interfaceDriver)
777       {
778          bool result = 0;
779
780 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
781          ProcessNetworkEvents();
782 #endif
783
784          /*
785          result = interfaceDriver.ProcessInput(useProcessAll && processAll);
786          if(!desktop || !interfaceDriver) return;
787          {
788             bool wait;
789             Window child;
790             for(child = app.desktop.children.first; child; child = child.next)
791                if(child.created && child.visible)
792                   break;
793             if(!child) return result;
794          }
795
796          result |= UpdateTimers();
797          result |= ProcessFileNotifications();
798          */
799
800          result |= ProcessFileNotifications();
801          result |= UpdateTimers();
802          result |= interfaceDriver.ProcessInput(useProcessAll && processAll);
803
804          return result;
805       }
806       return false;
807    }
808
809    void UpdateDisplay(void)
810    {
811 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
812       if(Desktop3DUpdateDisplay()) return;
813 #endif
814
815       if(interfaceDriver)
816       {
817          if(fullScreenMode && desktop.display)
818          {
819             desktop.mutex.Wait();
820             if(desktop.active)
821             {
822                desktop.display.Lock(true);
823
824                if(desktop.dirty || cursorUpdate)
825                {
826                   if(desktop.display.flags.flipping)
827                      desktop.Update(null);
828                   desktop.UpdateDisplay();
829                   cursorUpdate = true;
830                }
831                if(cursorUpdate || desktop.dirty)
832                {
833                   PreserveAndDrawCursor();
834                   cursorUpdate = false;
835                   desktop.dirty = false;
836                   RestoreCursorBackground();
837                }
838
839                desktop.display.Unlock();
840             }
841             desktop.mutex.Release();
842          }
843          else
844          {
845             Window window;
846
847             for(window = desktop.children.first; window; window = window.next)
848             {
849                if(window.mutex) window.mutex.Wait();
850                if(window.visible && window.dirty)
851                {
852                   // Logf("Updating %s\n", window.name);
853                   interfaceDriver.Lock(window);
854                   if(window.display)
855                   {
856                      if(window.display.current)
857                      {
858                         printf("bug");
859                      }
860                      window.display.Lock(true);
861                      window.UpdateDisplay();
862                      window.display.Unlock();
863                   }
864
865                   window.dirty = false;
866                   interfaceDriver.Unlock(window);
867                   /*
868                   Log("--------------\n");
869                   usleep(1000000);
870                   */
871                }
872                if(window.mutex) window.mutex.Release();
873             }
874          }
875       }
876    }
877
878    void WaitEvent(void)
879    {
880       globalSystem.eventSemaphore.Wait();
881    }
882
883 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
884    bool ProcessNetworkEvents()
885    {
886       bool gotEvent = false;
887    #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
888       if(network.networkInitialized)
889       {
890          Service service;
891          Socket socket, next;
892          struct timeval tv = {0, 0};
893          fd_set rs, ws, es;
894          Service nextService;
895          OldLink semPtr;
896
897          PauseNetworkEvents();
898          network.mutex.Wait();
899
900    #ifdef DEBUG_SOCKETS
901          if(network.connectEvent || network.networkEvent)
902             Log("[P] [NProcess]\n");
903    #endif
904          rs = network.readSet;
905          ws = network.writeSet;
906          es = network.exceptSet;
907
908          if((network.ns && select(network.ns, &rs, &ws, &es, &tv)) || network.leftOverBytes)
909          {
910             network.leftOverBytes = false;
911
912             // Danger here? Why looping with a next and not unlocking anything?
913             for(socket = network.connectSockets.first; socket; socket = next)
914             {
915                next = socket.next;
916                if(!socket.processAlone && FD_ISSET(socket.s, &ws))
917                {
918                   network.mutex.Release();
919                   socket.connectThread.Wait();
920                   network.mutex.Wait();
921                }
922             }
923             for(socket = network.sockets.first; socket; socket = next)
924             {
925                next = socket.next;
926                if(!socket.processAlone)
927                {
928                   network.mutex.Release();
929                   gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
930                   network.mutex.Wait();
931                }
932             }
933
934             for(service = network.services.first; service; service = nextService)
935             {
936                nextService = service.next;
937                if(!service.processAlone)
938                {
939                   if(FD_ISSET(service.s, &rs))
940                   {
941       #ifdef DEBUG_SOCKETS
942                      Logf("[P] Accepting connection (%x)\n", service.s);
943       #endif
944                      service.accepted = false;
945                      service.OnAccept();
946                      if(!service.accepted)
947                      {
948                         SOCKET s;
949                         SOCKADDR_IN a;
950                         int addrLen = sizeof(a);
951                         s = accept(service.s,(SOCKADDR *)&a,&addrLen);
952                         closesocket(s);
953                      }
954                      gotEvent |= true;
955
956       #ifdef DEBUG_SOCKETS
957                      Log("[P] Connection accepted\n");
958       #endif
959                   }
960                }
961                for(socket = service.sockets.first; socket; socket = next)
962                {
963                   next = socket.next;
964                   if(!socket.processAlone)
965                   {
966                      network.mutex.Release();
967                      gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
968                      network.mutex.Wait();
969                   }
970                }
971             }
972          }
973          if(network.connectEvent)
974          {
975             bool goOn = true;
976             while(goOn)
977             {
978                goOn = false;
979                for(socket = network.connectSockets.first; socket; socket = next)
980                {
981                   next = socket.next;
982                   if(socket._connected  && socket._connected != -2)
983                   {
984                      network.connectSockets.Remove(socket);
985                      delete socket.connectThread;
986
987                      // printf("%s is connected = %d\n", socket._class.name, socket._connected);
988                      if(socket._connected == -1)
989                      {
990       #ifdef DEBUG_SOCKETS
991                         Logf("[P] Processing disconnected connect (%x)\n", socket.s);
992       #endif
993       #if 0
994                         if(socket.disconnectCode == ResolveFailed)
995                            Logf("Error resolving address %s\n", socket.address);
996       #endif
997                         if(socket.s == network.ns - 1)
998                            Network_DetermineMaxSocket();
999
1000                         socket.Free(false);
1001                         delete socket;
1002                      }
1003                      else if(socket._connected == 1)
1004                      {
1005       #ifdef DEBUG_SOCKETS
1006                         Log("[P] Processing connected connect\n");
1007       #endif
1008                         FD_CLR(socket.s, &network.writeSet);
1009                         FD_SET(socket.s, &network.readSet);
1010                         FD_SET(socket.s, &network.exceptSet);
1011                         network.mutex.Release();
1012
1013                         // printf("Calling OnConnect on %s\n", socket._class.name);
1014                         socket.OnConnect();
1015                         network.mutex.Wait();
1016                         if(socket._connected)
1017                            network.sockets.Add(socket);
1018                      }
1019                      gotEvent |= true;
1020                      goOn = true;
1021                      break;
1022                   }
1023                }
1024             }
1025             network.connectEvent = false;
1026          }
1027          if(network.networkEvent)
1028          {
1029             network.networkEvent = false;
1030             network.selectSemaphore.Release();
1031          }
1032
1033          if(gotEvent)
1034          {
1035             for(semPtr = network.mtSemaphores.first; semPtr; semPtr = semPtr.next)
1036             {
1037                ((Semaphore)semPtr.data).Release();
1038             }
1039          }
1040
1041          network.mutex.Release();
1042          ResumeNetworkEvents();
1043       }
1044    #endif
1045       return gotEvent;
1046    }
1047
1048    void WaitNetworkEvent()
1049    {
1050       if(network.networkInitialized)
1051       {
1052          if(GetCurrentThreadID() == network.mainThreadID)
1053          {
1054             WaitEvent();
1055          }
1056          else
1057          {
1058             Semaphore semaphore { };
1059             OldLink semPtr { data = semaphore };
1060             network.mutex.Wait();
1061             network.mtSemaphores.Add(semPtr);
1062             network.mutex.Release();
1063
1064             ResumeNetworkEvents();
1065             semaphore.Wait();
1066             PauseNetworkEvents();
1067             network.mutex.Wait();
1068             network.mtSemaphores.Delete(semPtr);
1069             network.mutex.Release();
1070             delete semaphore;
1071          }
1072       }
1073    }
1074
1075    void PauseNetworkEvents()
1076    {
1077       if(network.networkInitialized)
1078       {
1079          network.processMutex.Wait();
1080       }
1081    }
1082
1083    void ResumeNetworkEvents()
1084    {
1085       if(network.networkInitialized)
1086       {
1087          network.processMutex.Release();
1088       }
1089    }
1090 #endif
1091
1092    void SignalEvent(void)
1093    {
1094       globalSystem.eventSemaphore.Release();
1095    }
1096
1097    // TODO: Might want to make this private with simpler public version?
1098    bool SwitchMode(bool fullScreen, char * driverName, Resolution resolution, PixelFormat colorDepth, int refreshRate, char * skinName, bool fallBack)
1099    {
1100       bool result = false;
1101       OldLink link;
1102       char * fbDriver;
1103       bool fbFullScreen = 0;
1104       Resolution fbResolution = 0;
1105       PixelFormat fbColorDepth = 0;
1106       int fbRefreshRate = 0;
1107       subclass(Interface) inter;
1108       subclass(Skin) skin = null;
1109
1110       if(skinName)
1111       {
1112          OldLink link;
1113
1114          for(link = class(Skin).derivatives.first; link; link = link.next)
1115          {
1116             skin = link.data;
1117             if(skin.name && !strcmp(skin.name, skinName))
1118                break;
1119          }
1120          if(!link) skin = null;
1121       }
1122
1123       Initialize(false);
1124
1125       fbDriver = defaultDisplayDriver;
1126       inter = interfaceDriver;
1127
1128       if(interfaceDriver)
1129          interfaceDriver.GetCurrentMode(&fbFullScreen, &fbResolution, &fbColorDepth, &fbRefreshRate);
1130
1131       if(!driverName && !interfaceDriver)
1132          driverName = defaultDisplayDriver;
1133
1134       if(driverName || (skin && skin.textMode != textMode))
1135       {
1136          for(link = class(Interface).derivatives.first; link; link = link.next)
1137          {
1138             bool foundDriver = false;
1139             int c, numDrivers;
1140             char ** graphicsDrivers;
1141             inter = link.data;
1142
1143             graphicsDrivers = inter.GraphicsDrivers(&numDrivers);
1144
1145             for(c=0; c<numDrivers; c++)
1146                if(!driverName || !strcmp(driverName, graphicsDrivers[c]))
1147                {
1148                   if(!skin || skin.textMode == IsDriverTextMode(graphicsDrivers[c]))
1149                   {
1150                      driverName = graphicsDrivers[c];
1151                      foundDriver = true;
1152                      break;
1153                   }
1154                }
1155             if(foundDriver)
1156                break;
1157          }
1158          if(!link)
1159             inter = null;
1160       }
1161
1162       /*
1163       if(driverName)
1164       {
1165 #if defined(__WIN32__)
1166 #if !defined(ECERE_VANILLA)
1167          if(!strcmp(driverName, "Win32Console")) inter = (subclass(Interface))class(Win32ConsoleInterface); else
1168 #endif
1169          inter = (subclass(Interface))class(Win32Interface);
1170 #else
1171          if(!strcmp(driverName, "X")) inter = (subclass(Interface))class(XInterface);
1172          else inter = (subclass(Interface))class(NCursesInterface);
1173 #endif
1174       }
1175       */
1176
1177       if(interfaceDriver && (!driverName || (fbDriver && !strcmp(fbDriver, driverName))) &&
1178          fullScreen == fbFullScreen &&
1179          (!resolution || resolution == fbResolution) &&
1180          (!colorDepth || colorDepth == fbColorDepth) &&
1181          (!refreshRate || refreshRate == fbRefreshRate) &&
1182          (currentSkin && (!skinName || !strcmp(currentSkin.name, skinName))))
1183          result = true;
1184       else if(inter)
1185       {
1186          bool wasFullScreen = fullScreenMode;
1187          subclass(Skin) oldSkin = currentSkin;
1188
1189          textMode = false;
1190          modeSwitching = true;
1191
1192          if(interfaceDriver)
1193             desktop.UnloadGraphics(true);
1194
1195          if(inter != interfaceDriver)
1196          {
1197             if(interfaceDriver)
1198             {
1199                interfaceDriver.Terminate();
1200             }
1201             result = inter.Initialize();
1202          }
1203          else
1204             result = true;
1205          if(result)
1206          {
1207             result = false;
1208
1209             interfaceDriver = inter;
1210             interfaceDriver.SetTimerResolution(timerResolution);
1211             inter.EnsureFullScreen(&fullScreen);
1212             fullScreenMode = fullScreen;
1213
1214             if((!wasFullScreen && !fullScreen) ||
1215                inter.ScreenMode(fullScreen, resolution, colorDepth, refreshRate, &textMode))
1216             {
1217                if(!fbDriver || (driverName && strcmp(fbDriver, driverName)))
1218                   defaultDisplayDriver = driverName;
1219
1220                if(!skinName || !SelectSkin(skinName))
1221                {
1222                   if(!currentSkin || currentSkin.textMode != textMode ||
1223                      !SelectSkin(currentSkin.name))
1224                   {
1225                      OldLink link;
1226                      subclass(Skin) skin = null;
1227
1228                      for(link = class(Skin).derivatives.first; link; link = link.next)
1229                      {
1230                         skin = link.data;
1231                         if(skin.textMode == textMode)
1232                            break;
1233                      }
1234                      if(!link) skin = null;
1235
1236                      if(skin)
1237 #if !defined(__ANDROID__)
1238                         SelectSkin(skin.name);
1239 #else
1240                         currentSkin = skin;
1241 #endif
1242                   }
1243                }
1244
1245                if(currentSkin && desktop.SetupDisplay())
1246                {
1247                   desktop.active = true;
1248
1249                   if(fullScreen)
1250                   {
1251                      desktop.display.Lock(false);
1252                      desktop.display.Position(0,0);
1253                      desktop.display.Unlock();
1254                   }
1255
1256                   if(desktop.LoadGraphics(false, oldSkin != currentSkin))
1257                   {
1258                      if(fbDriver)
1259                      {
1260                         desktop.UpdateDisplay();
1261                      }
1262
1263                      this.fullScreen = fullScreen;
1264                      result = true;
1265                   }
1266                }
1267             }
1268          }
1269          modeSwitching = false;
1270          if(!result)
1271             LogErrorCode(modeSwitchFailed, driverName ? driverName : defaultDisplayDriver);
1272       }
1273       else
1274          LogErrorCode(driverNotSupported, driverName ? driverName : defaultDisplayDriver);
1275
1276       if(!result && fallBack && fbDriver)
1277       {
1278          if(!SwitchMode(fbFullScreen, fbDriver, fbResolution, fbColorDepth, fbRefreshRate, null, false))
1279             Log($"Error falling back to previous video mode.\n");
1280       }
1281       return result;
1282    }
1283
1284    bool ProcessFileNotifications()
1285    {
1286       bool activity = false;
1287       FileMonitor monitor, next;
1288       static int reentrant = 0;
1289
1290       // Reentrant FileNotification is asking for trouble since each monitor is spawning a Modal() MessageBox
1291       if(reentrant) return false;
1292       // printf("[%d] Waiting in ProcessFileNotifications for fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1293       globalSystem.fileMonitorMutex.Wait();
1294       reentrant++;
1295       for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1296       {
1297          FileNotify notify;
1298
1299          next = monitor.next;
1300          incref monitor;
1301          if(next)
1302             incref next;
1303
1304          if(!monitor.reentrant && !monitor.toBeFreed)
1305          {
1306             monitor.reentrant = true;
1307             while((notify = monitor.fileNotifies.first))
1308             {
1309                monitor.fileNotifies.Remove(notify);
1310
1311                if(monitor.active)
1312                {
1313                   if(monitor.directory)
1314                   {
1315                      if(!monitor.OnDirNotify(monitor.data, notify.action, notify.fileName, notify.param))
1316                         monitor.StopMonitoring();
1317                   }
1318                   else
1319                   {
1320                      if(!monitor.OnFileNotify(monitor.data, notify.action, notify.param))
1321                         monitor.StopMonitoring();
1322                   }
1323                }
1324                monitor.reentrant = false;
1325
1326                notify.Free();
1327                delete notify;
1328                activity = true;
1329             }
1330             monitor.reentrant = false;
1331          }
1332          delete monitor;
1333          if(next && next._refCount > 1)
1334             next._refCount--;
1335          else
1336             delete next;
1337       }
1338       reentrant--;
1339       if(!reentrant)
1340       {
1341          for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1342          {
1343             next = monitor.next;
1344             if(monitor.toBeFreed && !monitor.reentrant)
1345                monitor.FreeMonitor();
1346          }
1347       }
1348       // printf("[%d] Releasing in ProcessFileNotifications fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1349       globalSystem.fileMonitorMutex.Release();
1350       return activity;
1351    }
1352
1353    void Lock(void)
1354    {
1355       lockMutex.Wait();
1356 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1357       if(xGlobalDisplay)
1358          XLockDisplay(xGlobalDisplay);
1359 #endif
1360    }
1361
1362    void Unlock(void)
1363    {
1364 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1365       if(xGlobalDisplay)
1366          XUnlockDisplay(xGlobalDisplay);
1367 #endif
1368       lockMutex.Release();
1369    }
1370
1371    Cursor GetCursor(SystemCursor cursor)
1372    {
1373       return systemCursors[cursor];
1374    }
1375
1376    bool GetKeyState(Key key)
1377    {
1378       return interfaceDriver.GetKeyState(key);
1379    }
1380
1381    bool GetMouseState(MouseButtons * buttons, int * x, int * y)
1382    {
1383       return interfaceDriver.GetMouseState(buttons, x, y);
1384    }
1385
1386    // Properties
1387    property char * appName
1388    {
1389       set
1390       {
1391          strcpy(appName, value);
1392          if(desktop) desktop.text = appName;
1393       }
1394       get
1395       {
1396          return (char *)(this ? appName : null);
1397       }
1398    };
1399    property Semaphore semaphore { get { return globalSystem.eventSemaphore; } };
1400    property bool alwaysEmptyInput{ set { processAll = value; } get { return processAll; } };
1401    property bool fullScreen
1402    {
1403       set
1404       {
1405          SwitchMode(value, defaultDisplayDriver, resolution,
1406             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1407       }
1408       get { return this ? fullScreen : false; }
1409    };
1410    property char * driver
1411    {
1412       set
1413       {
1414          SwitchMode( fullScreen, value, resolution, pixelFormat, refreshRate,
1415             currentSkin ? currentSkin.name : null, true);
1416        }
1417        get { return this ? defaultDisplayDriver : null; }
1418    };
1419    property Resolution resolution
1420    {
1421       set
1422       {
1423          SwitchMode(fullScreen, defaultDisplayDriver, value, pixelFormat, refreshRate,
1424             currentSkin ? currentSkin.name : null, true);
1425       }
1426       get { return this ? resolution : 0; }
1427    };
1428    property PixelFormat pixelFormat
1429    {
1430       set
1431       {
1432          SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1433             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1434       }
1435       get { return this ? pixelFormat : 0; }
1436    };
1437    property int refreshRate
1438    {
1439       set
1440       {
1441          SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1442             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1443       }
1444       get { return this ? refreshRate : 0; }
1445    };
1446    property char * skin
1447    {
1448       set { SelectSkin(value); }
1449       get { return (this && currentSkin) ? currentSkin.name : null; }
1450    };
1451    property bool textMode
1452    {
1453       set { textMode = value; }     // TODO: Implement switching
1454       get { return this ? textMode : false; }
1455    };
1456    property Window desktop { get { return this ? desktop : null; } };
1457    property char ** drivers { get { return null; } };
1458    property char ** skins { get { return null; } };
1459    property subclass(Skin) currentSkin { get { return this ? currentSkin : null; } };
1460    property int numDrivers { get { return 0; } };
1461    property int numSkins { get { return 0; } };
1462    property uint timerResolution
1463    {
1464       set { timerResolution = value; if(interfaceDriver) interfaceDriver.SetTimerResolution(value); }
1465    };
1466 };