ecere/GuiApplication: Avoid extra SwitchMode when setting driver explicitly
[sdk] / ecere / src / gui / GuiApplication.ec
1 namespace gui;
2
3 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
4 #define property _property
5 #define new _new
6 #define class _class
7 #define uint _uint
8
9 #define Window    X11Window
10 #define Cursor    X11Cursor
11 #define Font      X11Font
12 #define Display   X11Display
13 #define Time      X11Time
14 #define KeyCode   X11KeyCode
15 #define Picture   X11Picture
16
17 #include <X11/Xutil.h>
18
19 #undef Window
20 #undef Cursor
21 #undef Font
22 #undef Display
23 #undef Time
24 #undef KeyCode
25 #undef Picture
26
27 #undef uint
28 #undef new
29 #undef property
30 #undef class
31
32 #endif
33
34 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
35 #if defined(__WIN32__)
36
37 #define WIN32_LEAN_AND_MEAN
38 #define String _String
39 #include <winsock.h>
40 #undef String
41 static WSADATA wsaData;
42
43 #elif defined(__unix__) || defined(__APPLE__)
44
45 default:
46 #define uint _uint
47 #define set _set
48 #include <sys/time.h>
49 #include <unistd.h>
50 #include <sys/select.h>
51
52 #include <netinet/in.h>
53 #include <netdb.h>
54 #include <sys/socket.h>
55 #include <sys/wait.h>
56 #include <sys/types.h>
57 #include <sys/time.h>
58 #include <arpa/inet.h>
59
60 #undef set
61 #undef uint
62
63 private:
64 typedef int SOCKET;
65 typedef struct hostent HOSTENT;
66 typedef struct sockaddr SOCKADDR;
67 typedef struct sockaddr_in SOCKADDR_IN;
68 typedef struct in_addr IN_ADDR;
69 #define closesocket(s) close(s)
70
71 #endif
72
73 import "network"
74 #endif
75
76 #if defined(__APPLE__) && !defined(ECERE_VANILLA)
77 import "CocoaInterface"
78 #endif
79
80 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
81 import "XInterface"
82 #endif
83
84 import "Window"
85
86 /*static */bool guiApplicationInitialized = false;
87 GuiApplication guiApp;
88 int terminateX;
89
90 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    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)&((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)&((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(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          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                         int 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.Free(false);
997                         delete socket;
998                      }
999                      else if(socket._connected == 1)
1000                      {
1001       #ifdef DEBUG_SOCKETS
1002                         Log("[P] Processing connected connect\n");
1003       #endif
1004                         FD_CLR(socket.s, &network.writeSet);
1005                         FD_SET(socket.s, &network.readSet);
1006                         FD_SET(socket.s, &network.exceptSet);
1007                         network.mutex.Release();
1008
1009                         // printf("Calling OnConnect on %s\n", socket._class.name);
1010                         socket.OnConnect();
1011                         network.mutex.Wait();
1012                         if(socket._connected)
1013                            network.sockets.Add(socket);
1014                      }
1015                      gotEvent |= true;
1016                      goOn = true;
1017                      break;
1018                   }
1019                }
1020             }
1021             network.connectEvent = false;
1022          }
1023          if(network.networkEvent)
1024          {
1025             network.networkEvent = false;
1026             network.selectSemaphore.Release();
1027          }
1028
1029          if(gotEvent)
1030          {
1031             for(semPtr = network.mtSemaphores.first; semPtr; semPtr = semPtr.next)
1032             {
1033                ((Semaphore)semPtr.data).Release();
1034             }
1035          }
1036
1037          network.mutex.Release();
1038          ResumeNetworkEvents();
1039       }
1040    #endif
1041       return gotEvent;
1042    }
1043
1044    void WaitNetworkEvent()
1045    {
1046       if(network.networkInitialized)
1047       {
1048          if(GetCurrentThreadID() == network.mainThreadID)
1049          {
1050             WaitEvent();
1051          }
1052          else
1053          {
1054             Semaphore semaphore { };
1055             OldLink semPtr { data = semaphore };
1056             network.mutex.Wait();
1057             network.mtSemaphores.Add(semPtr);
1058             network.mutex.Release();
1059
1060             ResumeNetworkEvents();
1061             semaphore.Wait();
1062             PauseNetworkEvents();
1063             network.mutex.Wait();
1064             network.mtSemaphores.Delete(semPtr);
1065             network.mutex.Release();
1066             delete semaphore;
1067          }
1068       }
1069    }
1070
1071    void PauseNetworkEvents()
1072    {
1073       if(network.networkInitialized)
1074       {
1075          network.processMutex.Wait();
1076       }
1077    }
1078
1079    void ResumeNetworkEvents()
1080    {
1081       if(network.networkInitialized)
1082       {
1083          network.processMutex.Release();
1084       }
1085    }
1086 #endif
1087
1088    void SignalEvent(void)
1089    {
1090       globalSystem.eventSemaphore.Release();
1091    }
1092
1093    // TODO: Might want to make this private with simpler public version?
1094    bool SwitchMode(bool fullScreen, char * driverName, Resolution resolution, PixelFormat colorDepth, int refreshRate, char * skinName, bool fallBack)
1095    {
1096       bool result = false;
1097       OldLink link;
1098       char * fbDriver;
1099       bool fbFullScreen = 0;
1100       Resolution fbResolution = 0;
1101       PixelFormat fbColorDepth = 0;
1102       int fbRefreshRate = 0;
1103       subclass(Interface) inter;
1104       subclass(Skin) skin = null;
1105
1106       if(skinName)
1107       {
1108          OldLink link;
1109
1110          for(link = class(Skin).derivatives.first; link; link = link.next)
1111          {
1112             skin = link.data;
1113             if(skin.name && !strcmp(skin.name, skinName))
1114                break;
1115          }
1116          if(!link) skin = null;
1117       }
1118
1119       Initialize(false);
1120
1121       fbDriver = defaultDisplayDriver;
1122       inter = interfaceDriver;
1123
1124       if(interfaceDriver)
1125          interfaceDriver.GetCurrentMode(&fbFullScreen, &fbResolution, &fbColorDepth, &fbRefreshRate);
1126
1127       if(!driverName && !interfaceDriver)
1128          driverName = defaultDisplayDriver;
1129
1130       if(driverName || (skin && skin.textMode != textMode))
1131       {
1132          for(link = class(Interface).derivatives.first; link; link = link.next)
1133          {
1134             bool foundDriver = false;
1135             int c, numDrivers;
1136             char ** graphicsDrivers;
1137             inter = link.data;
1138
1139             graphicsDrivers = inter.GraphicsDrivers(&numDrivers);
1140
1141             for(c=0; c<numDrivers; c++)
1142                if(!driverName || !strcmp(driverName, graphicsDrivers[c]))
1143                {
1144                   if(!skin || skin.textMode == IsDriverTextMode(graphicsDrivers[c]))
1145                   {
1146                      driverName = graphicsDrivers[c];
1147                      foundDriver = true;
1148                      break;
1149                   }
1150                }
1151             if(foundDriver)
1152                break;
1153          }
1154          if(!link)
1155             inter = null;
1156       }
1157
1158       /*
1159       if(driverName)
1160       {
1161 #if defined(__WIN32__)
1162 #if !defined(ECERE_VANILLA)
1163          if(!strcmp(driverName, "Win32Console")) inter = (subclass(Interface))class(Win32ConsoleInterface); else
1164 #endif
1165          inter = (subclass(Interface))class(Win32Interface);
1166 #else
1167          if(!strcmp(driverName, "X")) inter = (subclass(Interface))class(XInterface);
1168          else inter = (subclass(Interface))class(NCursesInterface);
1169 #endif
1170       }
1171       */
1172
1173       if(interfaceDriver && (!driverName || (fbDriver && !strcmp(fbDriver, driverName))) &&
1174          fullScreen == fbFullScreen &&
1175          (!resolution || resolution == fbResolution) &&
1176          (!colorDepth || colorDepth == fbColorDepth) &&
1177          (!refreshRate || refreshRate == fbRefreshRate) &&
1178          (currentSkin && (!skinName || !strcmp(currentSkin.name, skinName))))
1179          result = true;
1180       else if(inter)
1181       {
1182          bool wasFullScreen = fullScreenMode;
1183          subclass(Skin) oldSkin = currentSkin;
1184
1185          textMode = false;
1186          modeSwitching = true;
1187
1188          if(interfaceDriver)
1189             desktop.UnloadGraphics(true);
1190
1191          if(inter != interfaceDriver)
1192          {
1193             if(interfaceDriver)
1194             {
1195                interfaceDriver.Terminate();
1196             }
1197             result = inter.Initialize();
1198          }
1199          else
1200             result = true;
1201          if(result)
1202          {
1203             result = false;
1204
1205             interfaceDriver = inter;
1206             interfaceDriver.SetTimerResolution(timerResolution);
1207             inter.EnsureFullScreen(&fullScreen);
1208             fullScreenMode = fullScreen;
1209
1210             if((!wasFullScreen && !fullScreen) ||
1211                inter.ScreenMode(fullScreen, resolution, colorDepth, refreshRate, &textMode))
1212             {
1213                if(!fbDriver || (driverName && strcmp(fbDriver, driverName)))
1214                   defaultDisplayDriver = driverName;
1215
1216                if(!skinName || !SelectSkin(skinName))
1217                {
1218                   if(!currentSkin || currentSkin.textMode != textMode ||
1219                      !SelectSkin(currentSkin.name))
1220                   {
1221                      OldLink link;
1222                      subclass(Skin) skin = null;
1223
1224                      for(link = class(Skin).derivatives.first; link; link = link.next)
1225                      {
1226                         skin = link.data;
1227                         if(skin.textMode == textMode)
1228                            break;
1229                      }
1230                      if(!link) skin = null;
1231
1232                      if(skin)
1233 #if !defined(__ANDROID__)
1234                         SelectSkin(skin.name);
1235 #else
1236                         currentSkin = skin;
1237 #endif
1238                   }
1239                }
1240
1241                if(currentSkin && desktop.SetupDisplay())
1242                {
1243                   desktop.active = true;
1244
1245                   if(fullScreen)
1246                   {
1247                      desktop.display.Lock(false);
1248                      desktop.display.Position(0,0);
1249                      desktop.display.Unlock();
1250                   }
1251
1252                   if(desktop.LoadGraphics(false, oldSkin != currentSkin))
1253                   {
1254                      if(fbDriver)
1255                      {
1256                         desktop.UpdateDisplay();
1257                      }
1258
1259                      this.fullScreen = fullScreen;
1260                      result = true;
1261                   }
1262                }
1263             }
1264          }
1265          modeSwitching = false;
1266          if(!result)
1267             LogErrorCode(modeSwitchFailed, driverName ? driverName : defaultDisplayDriver);
1268       }
1269       else
1270          LogErrorCode(driverNotSupported, driverName ? driverName : defaultDisplayDriver);
1271
1272       if(!result && fallBack && fbDriver)
1273       {
1274          if(!SwitchMode(fbFullScreen, fbDriver, fbResolution, fbColorDepth, fbRefreshRate, null, false))
1275             Log($"Error falling back to previous video mode.\n");
1276       }
1277       return result;
1278    }
1279
1280    bool ProcessFileNotifications()
1281    {
1282       bool activity = false;
1283       FileMonitor monitor, next;
1284       static int reentrant = 0;
1285
1286       // Reentrant FileNotification is asking for trouble since each monitor is spawning a Modal() MessageBox
1287       if(reentrant) return false;
1288       // printf("[%d] Waiting in ProcessFileNotifications for fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1289       globalSystem.fileMonitorMutex.Wait();
1290       reentrant++;
1291       for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1292       {
1293          FileNotify notify;
1294
1295          next = monitor.next;
1296          incref monitor;
1297          if(next)
1298             incref next;
1299
1300          if(!monitor.reentrant && !monitor.toBeFreed)
1301          {
1302             monitor.reentrant = true;
1303             while((notify = monitor.fileNotifies.first))
1304             {
1305                monitor.fileNotifies.Remove(notify);
1306
1307                if(monitor.active)
1308                {
1309                   if(monitor.directory)
1310                   {
1311                      if(!monitor.OnDirNotify(monitor.data, notify.action, notify.fileName, notify.param))
1312                         monitor.StopMonitoring();
1313                   }
1314                   else
1315                   {
1316                      if(!monitor.OnFileNotify(monitor.data, notify.action, notify.param))
1317                         monitor.StopMonitoring();
1318                   }
1319                }
1320                monitor.reentrant = false;
1321
1322                notify.Free();
1323                delete notify;
1324                activity = true;
1325             }
1326             monitor.reentrant = false;
1327          }
1328          delete monitor;
1329          if(next && next._refCount > 1)
1330             next._refCount--;
1331          else
1332             delete next;
1333       }
1334       reentrant--;
1335       if(!reentrant)
1336       {
1337          for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1338          {
1339             next = monitor.next;
1340             if(monitor.toBeFreed && !monitor.reentrant)
1341                monitor.FreeMonitor();
1342          }
1343       }
1344       // printf("[%d] Releasing in ProcessFileNotifications fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1345       globalSystem.fileMonitorMutex.Release();
1346       return activity;
1347    }
1348
1349    void Lock(void)
1350    {
1351       lockMutex.Wait();
1352 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1353       if(xGlobalDisplay)
1354          XLockDisplay(xGlobalDisplay);
1355 #endif
1356    }
1357
1358    void Unlock(void)
1359    {
1360 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1361       if(xGlobalDisplay)
1362          XUnlockDisplay(xGlobalDisplay);
1363 #endif
1364       lockMutex.Release();
1365    }
1366
1367    Cursor GetCursor(SystemCursor cursor)
1368    {
1369       return systemCursors[cursor];
1370    }
1371
1372    bool GetKeyState(Key key)
1373    {
1374       return interfaceDriver.GetKeyState(key);
1375    }
1376
1377    bool GetMouseState(MouseButtons * buttons, int * x, int * y)
1378    {
1379       return interfaceDriver.GetMouseState(buttons, x, y);
1380    }
1381
1382    // Properties
1383    property char * appName
1384    {
1385       set
1386       {
1387          strcpy(appName, value);
1388          if(desktop) desktop.text = appName;
1389       }
1390       get
1391       {
1392          return (char *)(this ? appName : null);
1393       }
1394    };
1395    property Semaphore semaphore { get { return globalSystem.eventSemaphore; } };
1396    property bool alwaysEmptyInput{ set { processAll = value; } get { return processAll; } };
1397    property bool fullScreen
1398    {
1399       set
1400       {
1401          SwitchMode(value, defaultDisplayDriver, resolution,
1402             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1403       }
1404       get { return this ? fullScreen : false; }
1405    };
1406    property char * driver
1407    {
1408       set
1409       {
1410          if((value && !defaultDisplayDriver) || (!value && defaultDisplayDriver) || strcmpi(defaultDisplayDriver, value))
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 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 char ** drivers { get { return null; } };
1455    property char ** 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 };