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