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