ecere/GuiApplication: Fixed fallback driver for OS X to be NCurses
[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 = "NCurses"; //"CocoaOpenGL";
609          }
610    #elif defined(__ANDROID__)
611          {
612             if(driver)
613                defaultDriver = driver;
614             else
615                defaultDriver = "OpenGL";
616          }
617    #else
618          if((this.isGUIApp & 1) && !textMode)
619          {
620             char * display = getenv("DISPLAY");
621
622             if(!display || !display[0] || !SwitchMode(false, "X", 0, 0, 0, null, true))
623                defaultDriver = "NCurses";
624                // SwitchMode(true, "NCurses", 0, PixelFormatText, 0, null, true);
625             else if(!driver)
626                defaultDriver = "X";
627             else
628                defaultDriver = driver;
629          }
630          else
631             defaultDriver = "NCurses";
632    #endif
633          if(switchMode)
634          {
635             if(defaultDriver)
636                SwitchMode(false, defaultDriver, 0, 0, 0, null, true);
637             else
638             {
639             /*
640          #if defined(__WIN32__)
641                SwitchMode(true, "Win32Console", 0, PixelFormatText, 0, null, true);
642          #endif
643             */
644
645          #if defined(__DOS__)
646                SwitchMode(true, "SVGA", Res640x480, PixelFormat8, 0, null, true);
647          #endif
648
649          #if defined(__APPLE__)
650                SwitchMode(true, "X" /*"CocoaOpenGL"*/, 0, 0, 0, null, true);
651          #endif
652
653          #if defined(__unix__)
654          #if defined(ECERE_MINIGLX)
655                SwitchMode(true, "OpenGL", 0, 0, 0, null, true);
656          #endif
657          #endif
658             }
659             if(!interfaceDriver)
660                guiApplicationInitialized = false;
661          }
662          else
663             defaultDisplayDriver = defaultDriver;
664       }
665    }
666
667 public:
668    virtual bool Init(void);
669    virtual bool Cycle(bool idle);
670    virtual void Terminate(void);
671
672    void Main(void)
673    {
674       Window window;
675
676       if(Init())
677       {
678          if(desktop)
679          {
680             // better solution when designing tab order/activated window etc, why do windows move in the list?
681             while(true)
682             {
683                for(window = desktop.children.first; window; window = window.next)
684                {
685                   if(window.autoCreate && !window.created)
686                   {
687                      if(window.Create())
688                         break;
689                   }
690                }
691                if(!window) break;
692             }
693          }
694
695          if(desktop)
696          {
697             int terminated = 0;
698             incref desktop;
699             ProcessInput(true);
700             while(desktop && interfaceDriver)
701             {
702                bool wait;
703                Window child;
704                if(terminateX != terminated)
705                {
706                   terminated = terminateX;
707                   desktop.Destroy(0);
708                   if(desktop.created)
709                   {
710                      terminated = 0;
711                      terminateX = 0;
712                      //printf("Resetting terminate X to 0\n");
713                   }
714                }
715
716                for(child = desktop.children.first; child; child = child.next)
717                   if(child.created && child.visible)
718                      break;
719                if(!child) break;
720
721                for(window = desktop.children.first; window; window = window.next)
722                   if(window.mutex) window.mutex.Wait();
723                UpdateDisplay();
724                for(window = desktop.children.first; window; window = window.next)
725                   if(window.mutex) window.mutex.Release();
726                wait = !ProcessInput(true);
727 #ifdef _DEBUG
728                if(lockMutex.owningThread != GetCurrentThreadID())
729                   PrintLn("WARNING: ProcessInput returned unlocked GUI!");
730 #endif
731                if(!Cycle(wait))
732                   wait = false;
733
734                if(wait)
735                   Wait();
736                else
737                {
738 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
739                   if(xGlobalDisplay)
740                      XUnlockDisplay(xGlobalDisplay);
741 #endif
742
743                   lockMutex.Release();
744                   lockMutex.Wait();
745
746 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
747                   if(xGlobalDisplay)
748                      XLockDisplay(xGlobalDisplay);
749 #endif
750                }
751             }
752             eInstance_DecRef(desktop);
753          }
754       }
755       Terminate();
756
757 #if defined(__ANDROID__)
758       // Because destruction of GuiApp won't be from main thread
759       lockMutex.Release();
760 #endif
761    }
762
763    void Wait(void)
764    {
765 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
766       if(xGlobalDisplay)
767          XUnlockDisplay(xGlobalDisplay);
768 #endif
769
770       lockMutex.Release();
771
772       waitMutex.Wait();
773       waiting = true;
774       if(interfaceDriver)
775          interfaceDriver.Wait();
776       waiting = false;
777       waitMutex.Release();
778
779       lockMutex.Wait();
780
781 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
782       if(xGlobalDisplay)
783          XLockDisplay(xGlobalDisplay);
784 #endif
785    }
786
787    bool ProcessInput(bool useProcessAll)
788    {
789       if(interfaceDriver)
790       {
791          bool result = 0;
792
793 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
794          ProcessNetworkEvents();
795 #endif
796
797          /*
798          result = interfaceDriver.ProcessInput(useProcessAll && processAll);
799          if(!desktop || !interfaceDriver) return;
800          {
801             bool wait;
802             Window child;
803             for(child = app.desktop.children.first; child; child = child.next)
804                if(child.created && child.visible)
805                   break;
806             if(!child) return result;
807          }
808
809          result |= UpdateTimers();
810          result |= ProcessFileNotifications();
811          */
812
813          result |= ProcessFileNotifications();
814          result |= UpdateTimers();
815          result |= interfaceDriver.ProcessInput(useProcessAll && processAll);
816
817          return result;
818       }
819       return false;
820    }
821
822    void UpdateDisplay(void)
823    {
824 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
825       if(Desktop3DUpdateDisplay()) return;
826 #endif
827
828       if(interfaceDriver)
829       {
830          if(fullScreenMode && desktop.display)
831          {
832             desktop.mutex.Wait();
833             if(desktop.active)
834             {
835                desktop.display.Lock(true);
836
837                if(desktop.dirty || cursorUpdate)
838                {
839                   if(desktop.display.flags.flipping)
840                      desktop.Update(null);
841                   desktop.UpdateDisplay();
842                   cursorUpdate = true;
843                }
844                if(cursorUpdate || desktop.dirty)
845                {
846                   PreserveAndDrawCursor();
847                   cursorUpdate = false;
848                   desktop.dirty = false;
849                   RestoreCursorBackground();
850                }
851
852                desktop.display.Unlock();
853             }
854             desktop.mutex.Release();
855          }
856          else
857          {
858             Window window;
859
860             for(window = desktop.children.first; window; window = window.next)
861             {
862                if(window.mutex) window.mutex.Wait();
863                if(window.visible && window.dirty)
864                {
865                   // Logf("Updating %s\n", window.name);
866                   interfaceDriver.Lock(window);
867                   if(window.display)
868                   {
869                      if(window.display.current)
870                      {
871                         printf("bug");
872                      }
873                      window.display.Lock(true);
874                      window.UpdateDisplay();
875                      window.display.Unlock();
876                   }
877
878                   window.dirty = false;
879                   interfaceDriver.Unlock(window);
880                   /*
881                   Log("--------------\n");
882                   usleep(1000000);
883                   */
884                }
885                if(window.mutex) window.mutex.Release();
886             }
887          }
888       }
889    }
890
891    void WaitEvent(void)
892    {
893       globalSystem.eventSemaphore.Wait();
894    }
895
896 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
897    bool ProcessNetworkEvents()
898    {
899       bool gotEvent = false;
900    #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
901       if(network.networkInitialized)
902       {
903          Service service;
904          Socket socket, next;
905          struct timeval tv = {0, 0};
906          fd_set rs, ws, es;
907          Service nextService;
908          OldLink semPtr;
909
910          PauseNetworkEvents();
911          network.mutex.Wait();
912
913    #ifdef DEBUG_SOCKETS
914          if(network.connectEvent || network.networkEvent)
915             Log("[P] [NProcess]\n");
916    #endif
917          rs = network.readSet;
918          ws = network.writeSet;
919          es = network.exceptSet;
920
921          if((network.ns && select(network.ns, &rs, &ws, &es, &tv)) || network.leftOverBytes)
922          {
923             network.leftOverBytes = false;
924
925             // Danger here? Why looping with a next and not unlocking anything?
926             for(socket = network.connectSockets.first; socket; socket = next)
927             {
928                next = socket.next;
929                if(!socket.processAlone && FD_ISSET(socket.s, &ws))
930                {
931                   network.mutex.Release();
932                   socket.connectThread.Wait();
933                   network.mutex.Wait();
934                }
935             }
936             for(socket = network.sockets.first; socket; socket = next)
937             {
938                next = socket.next;
939                if(!socket.processAlone)
940                {
941                   network.mutex.Release();
942                   gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
943                   network.mutex.Wait();
944                }
945             }
946
947             for(service = network.services.first; service; service = nextService)
948             {
949                nextService = service.next;
950                if(!service.processAlone)
951                {
952                   if(FD_ISSET(service.s, &rs))
953                   {
954       #ifdef DEBUG_SOCKETS
955                      Logf("[P] Accepting connection (%x)\n", service.s);
956       #endif
957                      service.accepted = false;
958                      service.OnAccept();
959                      if(!service.accepted)
960                      {
961                         SOCKET s;
962                         SOCKADDR_IN a;
963                         int addrLen = sizeof(a);
964                         s = accept(service.s,(SOCKADDR *)&a,&addrLen);
965                         closesocket(s);
966                      }
967                      gotEvent |= true;
968
969       #ifdef DEBUG_SOCKETS
970                      Log("[P] Connection accepted\n");
971       #endif
972                   }
973                }
974                for(socket = service.sockets.first; socket; socket = next)
975                {
976                   next = socket.next;
977                   if(!socket.processAlone)
978                   {
979                      network.mutex.Release();
980                      gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
981                      network.mutex.Wait();
982                   }
983                }
984             }
985          }
986          if(network.connectEvent)
987          {
988             bool goOn = true;
989             while(goOn)
990             {
991                goOn = false;
992                for(socket = network.connectSockets.first; socket; socket = next)
993                {
994                   next = socket.next;
995                   if(socket._connected  && socket._connected != -2)
996                   {
997                      network.connectSockets.Remove(socket);
998                      delete socket.connectThread;
999
1000                      // printf("%s is connected = %d\n", socket._class.name, socket._connected);
1001                      if(socket._connected == -1)
1002                      {
1003       #ifdef DEBUG_SOCKETS
1004                         Logf("[P] Processing disconnected connect (%x)\n", socket.s);
1005       #endif
1006       #if 0
1007                         if(socket.disconnectCode == ResolveFailed)
1008                            Logf("Error resolving address %s\n", socket.address);
1009       #endif
1010                         if(socket.s == network.ns - 1)
1011                            Network_DetermineMaxSocket();
1012
1013                         socket.Free(false);
1014                         delete socket;
1015                      }
1016                      else if(socket._connected == 1)
1017                      {
1018       #ifdef DEBUG_SOCKETS
1019                         Log("[P] Processing connected connect\n");
1020       #endif
1021                         FD_CLR(socket.s, &network.writeSet);
1022                         FD_SET(socket.s, &network.readSet);
1023                         FD_SET(socket.s, &network.exceptSet);
1024                         network.mutex.Release();
1025
1026                         // printf("Calling OnConnect on %s\n", socket._class.name);
1027                         socket.OnConnect();
1028                         network.mutex.Wait();
1029                         if(socket._connected)
1030                            network.sockets.Add(socket);
1031                      }
1032                      gotEvent |= true;
1033                      goOn = true;
1034                      break;
1035                   }
1036                }
1037             }
1038             network.connectEvent = false;
1039          }
1040          if(network.networkEvent)
1041          {
1042             network.networkEvent = false;
1043             network.selectSemaphore.Release();
1044          }
1045
1046          if(gotEvent)
1047          {
1048             for(semPtr = network.mtSemaphores.first; semPtr; semPtr = semPtr.next)
1049             {
1050                ((Semaphore)semPtr.data).Release();
1051             }
1052          }
1053
1054          network.mutex.Release();
1055          ResumeNetworkEvents();
1056       }
1057    #endif
1058       return gotEvent;
1059    }
1060
1061    void WaitNetworkEvent()
1062    {
1063       if(network.networkInitialized)
1064       {
1065          if(GetCurrentThreadID() == network.mainThreadID)
1066          {
1067             WaitEvent();
1068          }
1069          else
1070          {
1071             Semaphore semaphore { };
1072             OldLink semPtr { data = semaphore };
1073             network.mutex.Wait();
1074             network.mtSemaphores.Add(semPtr);
1075             network.mutex.Release();
1076
1077             ResumeNetworkEvents();
1078             semaphore.Wait();
1079             PauseNetworkEvents();
1080             network.mutex.Wait();
1081             network.mtSemaphores.Delete(semPtr);
1082             network.mutex.Release();
1083             delete semaphore;
1084          }
1085       }
1086    }
1087
1088    void PauseNetworkEvents()
1089    {
1090       if(network.networkInitialized)
1091       {
1092          network.processMutex.Wait();
1093       }
1094    }
1095
1096    void ResumeNetworkEvents()
1097    {
1098       if(network.networkInitialized)
1099       {
1100          network.processMutex.Release();
1101       }
1102    }
1103 #endif
1104
1105    void SignalEvent(void)
1106    {
1107       globalSystem.eventSemaphore.Release();
1108    }
1109
1110    // TODO: Might want to make this private with simpler public version?
1111    bool SwitchMode(bool fullScreen, char * driverName, Resolution resolution, PixelFormat colorDepth, int refreshRate, char * skinName, bool fallBack)
1112    {
1113       bool result = false;
1114       OldLink link;
1115       char * fbDriver;
1116       bool fbFullScreen = 0;
1117       Resolution fbResolution = 0;
1118       PixelFormat fbColorDepth = 0;
1119       int fbRefreshRate = 0;
1120       subclass(Interface) inter;
1121       subclass(Skin) skin = null;
1122
1123       if(skinName)
1124       {
1125          OldLink link;
1126
1127          for(link = class(Skin).derivatives.first; link; link = link.next)
1128          {
1129             skin = link.data;
1130             if(skin.name && !strcmp(skin.name, skinName))
1131                break;
1132          }
1133          if(!link) skin = null;
1134       }
1135
1136       Initialize(false);
1137
1138       fbDriver = defaultDisplayDriver;
1139       inter = interfaceDriver;
1140
1141       if(interfaceDriver)
1142          interfaceDriver.GetCurrentMode(&fbFullScreen, &fbResolution, &fbColorDepth, &fbRefreshRate);
1143
1144       if(!driverName && !interfaceDriver)
1145          driverName = defaultDisplayDriver;
1146
1147       if(driverName || (skin && skin.textMode != textMode))
1148       {
1149          for(link = class(Interface).derivatives.first; link; link = link.next)
1150          {
1151             bool foundDriver = false;
1152             int c, numDrivers;
1153             char ** graphicsDrivers;
1154             inter = link.data;
1155
1156             graphicsDrivers = inter.GraphicsDrivers(&numDrivers);
1157
1158             for(c=0; c<numDrivers; c++)
1159                if(!driverName || !strcmp(driverName, graphicsDrivers[c]))
1160                {
1161                   if(!skin || skin.textMode == IsDriverTextMode(graphicsDrivers[c]))
1162                   {
1163                      driverName = graphicsDrivers[c];
1164                      foundDriver = true;
1165                      break;
1166                   }
1167                }
1168             if(foundDriver)
1169                break;
1170          }
1171          if(!link)
1172             inter = null;
1173       }
1174
1175       /*
1176       if(driverName)
1177       {
1178 #if defined(__WIN32__)
1179 #if !defined(ECERE_VANILLA)
1180          if(!strcmp(driverName, "Win32Console")) inter = (subclass(Interface))class(Win32ConsoleInterface); else
1181 #endif
1182          inter = (subclass(Interface))class(Win32Interface);
1183 #else
1184          if(!strcmp(driverName, "X")) inter = (subclass(Interface))class(XInterface);
1185          else inter = (subclass(Interface))class(NCursesInterface);
1186 #endif
1187       }
1188       */
1189
1190       if(interfaceDriver && (!driverName || (fbDriver && !strcmp(fbDriver, driverName))) &&
1191          fullScreen == fbFullScreen &&
1192          (!resolution || resolution == fbResolution) &&
1193          (!colorDepth || colorDepth == fbColorDepth) &&
1194          (!refreshRate || refreshRate == fbRefreshRate) &&
1195          (currentSkin && (!skinName || !strcmp(currentSkin.name, skinName))))
1196          result = true;
1197       else if(inter)
1198       {
1199          bool wasFullScreen = fullScreenMode;
1200          subclass(Skin) oldSkin = currentSkin;
1201
1202          textMode = false;
1203          modeSwitching = true;
1204
1205          if(interfaceDriver)
1206             desktop.UnloadGraphics(true);
1207
1208          if(inter != interfaceDriver)
1209          {
1210             if(interfaceDriver)
1211             {
1212                interfaceDriver.Terminate();
1213             }
1214             result = inter.Initialize();
1215          }
1216          else
1217             result = true;
1218          if(result)
1219          {
1220             result = false;
1221
1222             interfaceDriver = inter;
1223             interfaceDriver.SetTimerResolution(timerResolution);
1224             inter.EnsureFullScreen(&fullScreen);
1225             fullScreenMode = fullScreen;
1226
1227             if((!wasFullScreen && !fullScreen) ||
1228                inter.ScreenMode(fullScreen, resolution, colorDepth, refreshRate, &textMode))
1229             {
1230                if(!fbDriver || (driverName && strcmp(fbDriver, driverName)))
1231                   defaultDisplayDriver = driverName;
1232
1233                if(!skinName || !SelectSkin(skinName))
1234                {
1235                   if(!currentSkin || currentSkin.textMode != textMode ||
1236                      !SelectSkin(currentSkin.name))
1237                   {
1238                      OldLink link;
1239                      subclass(Skin) skin = null;
1240
1241                      for(link = class(Skin).derivatives.first; link; link = link.next)
1242                      {
1243                         skin = link.data;
1244                         if(skin.textMode == textMode)
1245                            break;
1246                      }
1247                      if(!link) skin = null;
1248
1249                      if(skin)
1250 #if !defined(__ANDROID__)
1251                         SelectSkin(skin.name);
1252 #else
1253                         currentSkin = skin;
1254 #endif
1255                   }
1256                }
1257
1258                if(currentSkin && desktop.SetupDisplay())
1259                {
1260                   desktop.active = true;
1261
1262                   if(fullScreen)
1263                   {
1264                      desktop.display.Lock(false);
1265                      desktop.display.Position(0,0);
1266                      desktop.display.Unlock();
1267                   }
1268
1269                   if(desktop.LoadGraphics(false, oldSkin != currentSkin))
1270                   {
1271                      if(fbDriver)
1272                      {
1273                         desktop.UpdateDisplay();
1274                      }
1275
1276                      this.fullScreen = fullScreen;
1277                      result = true;
1278                   }
1279                }
1280             }
1281          }
1282          modeSwitching = false;
1283          if(!result)
1284             LogErrorCode(modeSwitchFailed, driverName ? driverName : defaultDisplayDriver);
1285       }
1286       else
1287          LogErrorCode(driverNotSupported, driverName ? driverName : defaultDisplayDriver);
1288
1289       if(!result && fallBack && fbDriver)
1290       {
1291          if(!SwitchMode(fbFullScreen, fbDriver, fbResolution, fbColorDepth, fbRefreshRate, null, false))
1292             Log($"Error falling back to previous video mode.\n");
1293       }
1294       return result;
1295    }
1296
1297    bool ProcessFileNotifications()
1298    {
1299       bool activity = false;
1300       FileMonitor monitor, next;
1301       static int reentrant = 0;
1302
1303       // Reentrant FileNotification is asking for trouble since each monitor is spawning a Modal() MessageBox
1304       if(reentrant) return false;
1305       // printf("[%d] Waiting in ProcessFileNotifications for fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1306       globalSystem.fileMonitorMutex.Wait();
1307       reentrant++;
1308       for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1309       {
1310          FileNotify notify;
1311
1312          next = monitor.next;
1313          incref monitor;
1314          if(next)
1315             incref next;
1316
1317          if(!monitor.reentrant && !monitor.toBeFreed)
1318          {
1319             monitor.reentrant = true;
1320             while((notify = monitor.fileNotifies.first))
1321             {
1322                monitor.fileNotifies.Remove(notify);
1323
1324                if(monitor.active)
1325                {
1326                   if(monitor.directory)
1327                   {
1328                      if(!monitor.OnDirNotify(monitor.data, notify.action, notify.fileName, notify.param))
1329                         monitor.StopMonitoring();
1330                   }
1331                   else
1332                   {
1333                      if(!monitor.OnFileNotify(monitor.data, notify.action, notify.param))
1334                         monitor.StopMonitoring();
1335                   }
1336                }
1337                monitor.reentrant = false;
1338
1339                notify.Free();
1340                delete notify;
1341                activity = true;
1342             }
1343             monitor.reentrant = false;
1344          }
1345          delete monitor;
1346          if(next && next._refCount > 1)
1347             next._refCount--;
1348          else
1349             delete next;
1350       }
1351       reentrant--;
1352       if(!reentrant)
1353       {
1354          for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1355          {
1356             next = monitor.next;
1357             if(monitor.toBeFreed && !monitor.reentrant)
1358                monitor.FreeMonitor();
1359          }
1360       }
1361       // printf("[%d] Releasing in ProcessFileNotifications fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1362       globalSystem.fileMonitorMutex.Release();
1363       return activity;
1364    }
1365
1366    void Lock(void)
1367    {
1368       lockMutex.Wait();
1369 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1370       if(xGlobalDisplay)
1371          XLockDisplay(xGlobalDisplay);
1372 #endif
1373    }
1374
1375    void Unlock(void)
1376    {
1377 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1378       if(xGlobalDisplay)
1379          XUnlockDisplay(xGlobalDisplay);
1380 #endif
1381       lockMutex.Release();
1382    }
1383
1384    Cursor GetCursor(SystemCursor cursor)
1385    {
1386       return systemCursors[cursor];
1387    }
1388
1389    bool GetKeyState(Key key)
1390    {
1391       return interfaceDriver.GetKeyState(key);
1392    }
1393
1394    bool GetMouseState(MouseButtons * buttons, int * x, int * y)
1395    {
1396       return interfaceDriver.GetMouseState(buttons, x, y);
1397    }
1398
1399    // Properties
1400    property char * appName
1401    {
1402       set
1403       {
1404          strcpy(appName, value);
1405          if(desktop) desktop.text = appName;
1406       }
1407       get
1408       {
1409          return (char *)(this ? appName : null);
1410       }
1411    };
1412    property Semaphore semaphore { get { return globalSystem.eventSemaphore; } };
1413    property bool alwaysEmptyInput{ set { processAll = value; } get { return processAll; } };
1414    property bool fullScreen
1415    {
1416       set
1417       {
1418          SwitchMode(value, defaultDisplayDriver, resolution,
1419             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1420       }
1421       get { return this ? fullScreen : false; }
1422    };
1423    property char * driver
1424    {
1425       set
1426       {
1427          SwitchMode( fullScreen, value, resolution, pixelFormat, refreshRate,
1428             currentSkin ? currentSkin.name : null, true);
1429        }
1430        get { return this ? defaultDisplayDriver : null; }
1431    };
1432    property Resolution resolution
1433    {
1434       set
1435       {
1436          SwitchMode(fullScreen, defaultDisplayDriver, value, pixelFormat, refreshRate,
1437             currentSkin ? currentSkin.name : null, true);
1438       }
1439       get { return this ? resolution : 0; }
1440    };
1441    property PixelFormat pixelFormat
1442    {
1443       set
1444       {
1445          SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1446             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1447       }
1448       get { return this ? pixelFormat : 0; }
1449    };
1450    property int refreshRate
1451    {
1452       set
1453       {
1454          SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1455             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1456       }
1457       get { return this ? refreshRate : 0; }
1458    };
1459    property char * skin
1460    {
1461       set { SelectSkin(value); }
1462       get { return (this && currentSkin) ? currentSkin.name : null; }
1463    };
1464    property bool textMode
1465    {
1466       set { textMode = value; }     // TODO: Implement switching
1467       get { return this ? textMode : false; }
1468    };
1469    property Window desktop { get { return this ? desktop : null; } };
1470    property char ** drivers { get { return null; } };
1471    property char ** skins { get { return null; } };
1472    property subclass(Skin) currentSkin { get { return this ? currentSkin : null; } };
1473    property int numDrivers { get { return 0; } };
1474    property int numSkins { get { return 0; } };
1475    property uint timerResolution
1476    {
1477       set { timerResolution = value; if(interfaceDriver) interfaceDriver.SetTimerResolution(value); }
1478    };
1479 };