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