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