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