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