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