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