4 #include <emscripten.h>
8 #include <emscripten.h>
11 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
12 #define property _property
17 #define Window X11Window
18 #define Cursor X11Cursor
20 #define Display X11Display
22 #define KeyCode X11KeyCode
23 #define Picture X11Picture
25 #include <X11/Xutil.h>
42 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
43 #if defined(__WIN32__)
44 #define SOCKLEN_TYPE int
45 #define WIN32_LEAN_AND_MEAN
46 #define String _String
50 #elif defined(__unix__) || defined(__APPLE__)
53 #define SOCKLEN_TYPE socklen_t
58 #include <sys/select.h>
60 #include <netinet/in.h>
62 #include <sys/socket.h>
64 #include <sys/types.h>
66 #include <arpa/inet.h>
73 typedef struct hostent HOSTENT;
74 typedef struct sockaddr SOCKADDR;
75 typedef struct sockaddr_in SOCKADDR_IN;
76 typedef struct in_addr IN_ADDR;
77 #define closesocket(s) close(s)
84 #if defined(__APPLE__) && !defined(ECERE_VANILLA)
85 import "CocoaInterface"
88 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
95 /*static */bool guiApplicationInitialized = false;
96 GuiApplication guiApp;
99 public class GuiApplication : Application
108 subclass(Interface) interfaceDriver;
109 subclass(Skin) currentSkin;
116 bool fullScreenMode; // Needs to start at true for the desktop to resize
119 Resolution resolution;
120 PixelFormat pixelFormat;
123 const char * defaultDisplayDriver;
125 Cursor systemCursors[SystemCursor];
129 OldList customCursors;
132 OldList windowTimers;
135 Window prevWindow; // Used for OnMouseLeave
136 List<Window> overWindows { }; // Used for OnMouseLeave
137 Window windowCaptured;
139 // Mouse based moving & resizing
141 Point windowMovingStart;
142 Point windowMovingBefore;
143 Size windowResizingBefore;
145 bool windowIsResizing;
146 bool resizeX, resizeEndX;
147 bool resizeY, resizeEndY;
149 // Mouse based scrolling
150 Window windowScrolling;
151 Point windowScrollingBefore, windowScrollingStart;
154 Bitmap cursorBackground { };
155 int cursorBackgroundX, cursorBackgroundY;
156 int cursorBackgroundW, cursorBackgroundH;
162 Window acquiredWindow;
163 int acquiredMouseX, acquiredMouseY;
165 Cursor currentCursor;
167 uint errorLevel, lastErrorCode;
171 #if !defined(__EMSCRIPTEN__)
175 #if !defined(__EMSCRIPTEN__)
179 Window interimWindow;
183 uint timerResolution;
186 Point virtualScreenPos;
194 #if !defined(__EMSCRIPTEN__)
195 mainThread = GetCurrentThreadID();
200 strcpy(appName, $"ECERE Application");
205 // customCursors.offset = OFFSET(Cursor, prev);
206 windowTimers.offset = (uint)(uintptr)&((Timer)0).prev;
208 for(c = 0; c<SystemCursor::enumSize; c++)
209 systemCursors[c] = Cursor { systemCursor = c; };
211 #if !defined(__EMSCRIPTEN__)
212 globalSystem.eventSemaphore = Semaphore { };
213 globalSystem.fileMonitorMutex = Mutex { };
214 globalSystem.fileMonitors.offset = (uint)(uintptr)&((FileMonitor)0).prev;
226 customCursors.Clear();
228 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
230 XUnlockDisplay(xGlobalDisplay);
233 #if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
234 // Because destruction of app won't be from main thread
235 if(guiApplicationInitialized)
241 interfaceDriver.Terminate();
244 // interfaceDrivers.Free(null);
250 for(c = 0; c<SystemCursor::enumSize; c++)
251 delete systemCursors[c];
253 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
257 #if !defined(__EMSCRIPTEN__)
258 delete globalSystem.eventSemaphore;
259 delete globalSystem.fileMonitorMutex;
260 delete globalSystem.fileMonitorThread;
263 UnapplySkin(class(Window));
267 Timer timer, nextTimer;
268 for(timer = windowTimers.first; timer; timer = nextTimer)
270 nextTimer = timer.next;
279 Time time = GetTime();
282 for(timer = windowTimers.first; timer; timer = timer.next)
283 timer.dispatched = false;
286 for(timer = windowTimers.first; timer; timer = timer.next)
288 if(!timer.dispatched)
290 if((timer.delay - (Seconds)(time - timer.lastTime)) < Seconds { 0.00001 })
293 timer.lastTime = time;
294 if(timer.DelayExpired(timer.window))
296 timer.dispatched = true;
298 eInstance_DecRef(timer);
308 // --- Mouse-based window movement ---
309 void SetCurrentCursor(Window window, Cursor cursor)
311 currentCursor = cursor;
314 if(fullScreenMode && cursor.bitmap)
315 interfaceDriver.SetMouseCursor(window ? window : desktop, (SystemCursor)-1);
318 interfaceDriver.SetMouseCursor(window ? window : desktop, cursor.systemCursor);
319 cursorBackground.Free();
325 void PreserveAndDrawCursor()
328 if(!acquiredWindow && cursorUpdate && currentCursor && currentCursor->bitmap)
332 Box against = {0,0, desktop.w-1,desktop.h-1};
333 Box box = {0, 0, currentCursor->bitmap->width,currentCursor->bitmap->height};
335 interfaceDriver->GetMousePosition(&mouseX, &mouseY);
337 mouseX -= currentCursor->hotSpotX;
338 mouseY -= currentCursor->hotSpotY;
340 // Preserve Background
341 if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
343 cursorBackgroundX = mouseX;
344 cursorBackgroundY = mouseY;
345 cursorBackgroundW = currentCursor->bitmap->width;
346 cursorBackgroundH = currentCursor->bitmap->height;
347 eDisplay_Grab(desktop.display, cursorBackground,
348 mouseX, mouseY, cursorBackgroundW, cursorBackgroundH);
351 eBox_ClipOffset(&box, &against, mouseX, mouseY);
353 if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
354 eDisplay_StartUpdate(desktop.display);
356 surface = eDisplay_GetSurface(desktop.display, mouseX, mouseY, &box);
359 eSurface_SetForeground(surface, WHITE);
360 eSurface_Blit(surface, currentCursor->bitmap, 0,0, 0,0,
361 currentCursor->bitmap->width,currentCursor->bitmap->height);
362 eInstance_Delete(surface);
364 if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
369 box.bottom += mouseY;
370 eDisplay_Update(desktop.display, &box);
373 if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
374 eDisplay_EndUpdate(desktop.display);
379 void RestoreCursorBackground()
382 // Restore Cursor Background
383 if(cursorBackground && desktop.active)
385 Box box = {0, 0, cursorBackgroundW-1,cursorBackgroundH-1};
386 Box against = {0,0, desktop.w-1,desktop.h-1};
389 eBox_ClipOffset(&box, &against, cursorBackgroundX, cursorBackgroundY);
390 if((surface = eDisplay_GetSurface(desktop.display, cursorBackgroundX,cursorBackgroundY, &box)))
392 eSurface_Blit(surface, cursorBackground, 0, 0, 0,0, cursorBackgroundW,cursorBackgroundH);
393 eInstance_Delete(surface);
399 bool IsModeSwitching()
401 return modeSwitching;
404 public bool SetDesktopPosition(int x, int y, int w, int h, bool moveChildren)
407 bool windowResized = desktop.size.w != w || desktop.size.h != h;
408 bool windowMoved = desktop.clientStart.x != x || desktop.clientStart.y != y;
410 if((windowResized || windowMoved) && moveChildren)
413 desktop.Position(x, y, w, h, true, true, true, true, false, false);
415 // Maximized native decorations windows suffer when we drag the dock around, so remaximize them
416 // It's a little jumpy, but oh well.
418 // Made this Windows only as it was causing occasional wrong stacking of windows in X11/Cinnamon
419 // when switching debugged app from full-screen
421 for(child = desktop.children.first; child; child = child.next)
423 if(child.nativeDecorations && child.rootWindow == child && child.state == maximized)
425 #if defined(__WIN32__)
426 child.state = normal;
427 child.state = maximized;
431 child.state = normal;
432 child.state = maximized;
435 child.requireRemaximize = true;
439 /*for(child = desktop.children.first; child; child = child.next)
441 if(!child.systemParent)
446 child.ComputeAnchors(
447 child.ax, child.ay, child.aw, child.ah,
449 child.Position(x, y, w, h, true, true, true, true, false);
451 if(child.state == Maximized)
455 child.ComputeAnchors(,
456 A_LEFT,A_LEFT,A_OFFSET,A_OFFSET,
458 child.Position(, x, y, w, h, false, true, true, true, false);
464 desktop.display.Lock(true);
467 if(!desktop.display.Resize(desktop.size.w, desktop.size.h))
470 desktop.dirty = true;
471 if(!desktop.display.flags.flipping)
472 desktop.Update(null);
474 // When does the desktop have a display in not fullscreen mode?
475 if(!fullScreenMode && !modeSwitching)
476 desktop.UpdateDisplay();
477 desktop.display.Unlock();
481 desktop.SetPosition(x, y, w, h, false, false, false);
485 void SetAppFocus(bool state)
487 // Shouldn't be property here
488 desktop.active = state;
491 bool SelectSkin(const char * skinName)
497 for(link = class(Skin).derivatives.first; link; link = link.next)
500 if(skin.name && !strcmp(skin.name, skinName))
503 if(!link) skin = null;
507 if(skin != currentSkin || !currentSkin)
509 // Try finding a driver to support this mode
510 if(skin.textMode != textMode)
516 bool needReload = false;
518 if(!modeSwitching && currentSkin)
520 modeSwitching = true;
521 desktop.UnloadGraphics(true);
525 UnapplySkin(class(Window));
529 ApplySkin(class(Window), skin.name, null);
533 if(desktop.SetupDisplay())
534 if(desktop.LoadGraphics(false, true))
536 modeSwitching = false;
548 void Initialize(bool switchMode)
551 // if(!initialized && eClass_IsDerived(__ecereModule->app->module.inst.class, guiApplicationClass))
552 if(!guiApplicationInitialized)
554 const char * defaultDriver = null;
555 #if defined(ECERE_VANILLA) || defined(ECERE_ONEDRIVER)
556 char * driver = null;
559 // char * driver = getenv("ECERE_DRIVER");
560 char * driver = null;
561 static char driverStorage[1024];
562 GetEnvironment("ECERE_DRIVER", driverStorage, sizeof(driverStorage));
563 if(driverStorage[0]) driver = driverStorage;
565 guiApplicationInitialized = true;
567 fullScreenMode = true; // Needs to start at true for the desktop to resize
568 // Set this to true earlier so we can override it!
573 #if !defined(__EMSCRIPTEN__)
576 /*#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
578 XLockDisplay(xGlobalDisplay);
584 desktop = Window { nativeDecorations = false };
587 desktop.childrenOrder.circ = true;
588 desktop.childrenCycle.circ = true;
589 desktop.background = blue;
590 desktop.rootWindow = desktop;
591 desktop.cursor = GetCursor(arrow);
592 desktop.caption = appName;
593 *&desktop.visible = true;
594 desktop.position = Point { };
595 #if !defined(__EMSCRIPTEN__)
596 desktop.mutex = Mutex { };
598 desktop.created = true;
601 #if defined(__WIN32__)
604 defaultDriver = driver;
605 else if((this.isGUIApp & 1) && !textMode)
606 defaultDriver = "GDI";
608 defaultDriver = "Win32Console";
610 #elif defined(__APPLE__)
613 defaultDriver = driver;
615 defaultDriver = "X"; //"CocoaOpenGL";
617 #elif defined(__ANDROID__)
620 defaultDriver = driver;
622 defaultDriver = "OpenGL";
624 #elif defined(__EMSCRIPTEN__)
627 defaultDriver = driver;
629 defaultDriver = "OpenGL";
632 if((this.isGUIApp & 1) && !textMode)
634 char * display = getenv("DISPLAY");
636 if(!display || !display[0] || !SwitchMode(false, "X", 0, 0, 0, null, true))
637 defaultDriver = "NCurses";
638 // SwitchMode(true, "NCurses", 0, PixelFormatText, 0, null, true);
642 defaultDriver = driver;
645 defaultDriver = "NCurses";
650 SwitchMode(false, defaultDriver, 0, 0, 0, null, true);
654 #if defined(__WIN32__)
655 SwitchMode(true, "Win32Console", 0, PixelFormatText, 0, null, true);
660 SwitchMode(true, "SVGA", Res640x480, PixelFormat8, 0, null, true);
663 #if defined(__APPLE__)
664 // SwitchMode(true, "X" /*"CocoaOpenGL"*/, 0, 0, 0, null, true);
667 #if defined(__unix__)
668 #if defined(ECERE_MINIGLX)
669 SwitchMode(true, "OpenGL", 0, 0, 0, null, true);
673 /*if(!interfaceDriver)
674 guiApplicationInitialized = false;*/
677 defaultDisplayDriver = defaultDriver;
682 virtual bool Init(void);
683 virtual bool Cycle(bool idle);
684 virtual void Terminate(void);
694 // better solution when designing tab order/activated window etc, why do windows move in the list?
697 for(window = desktop.children.first; window; window = window.next)
699 if(window.autoCreate && !window.created)
709 #ifdef __EMSCRIPTEN__
710 emscripten_set_main_loop(emscripten_main_loop_callback, 1/*60*/, 1);
718 while(desktop && interfaceDriver)
722 if(terminateX != terminated)
724 terminated = terminateX;
730 //printf("Resetting terminate X to 0\n");
734 for(child = desktop.children.first; child; child = child.next)
735 if(child.created && child.visible)
739 #if !defined(__EMSCRIPTEN__)
740 for(window = desktop.children.first; window; window = window.next)
741 if(window.mutex) window.mutex.Wait();
744 #if !defined(__EMSCRIPTEN__)
745 for(window = desktop.children.first; window; window = window.next)
746 if(window.mutex) window.mutex.Release();
748 wait = !ProcessInput(true);
749 #if !defined(__EMSCRIPTEN__)
750 #if defined(_DEBUG) && !defined(MEMINFO)
751 if(lockMutex.owningThread != GetCurrentThreadID())
752 PrintLn("WARNING: ProcessInput returned unlocked GUI!");
762 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
764 XUnlockDisplay(xGlobalDisplay);
767 #if !defined(__EMSCRIPTEN__)
772 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
774 XLockDisplay(xGlobalDisplay);
778 eInstance_DecRef(desktop);
783 #if defined(__ANDROID__)
784 // Because destruction of GuiApp won't be from main thread
791 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
793 XUnlockDisplay(xGlobalDisplay);
796 #if !defined(__EMSCRIPTEN__)
803 interfaceDriver.Wait();
805 #if !defined(__EMSCRIPTEN__)
811 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
813 XLockDisplay(xGlobalDisplay);
817 bool ProcessInput(bool useProcessAll)
823 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
824 ProcessNetworkEvents();
828 result = interfaceDriver.ProcessInput(useProcessAll && processAll);
829 if(!desktop || !interfaceDriver) return;
833 for(child = app.desktop.children.first; child; child = child.next)
834 if(child.created && child.visible)
836 if(!child) return result;
839 result |= UpdateTimers();
840 result |= ProcessFileNotifications();
843 result |= ProcessFileNotifications();
844 result |= UpdateTimers();
845 result |= interfaceDriver.ProcessInput(useProcessAll && processAll);
852 void UpdateDisplay(void)
854 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
855 if(Desktop3DUpdateDisplay()) return;
860 if(fullScreenMode && desktop.display)
862 #if !defined(__EMSCRIPTEN__)
863 desktop.mutex.Wait();
867 desktop.display.Lock(true);
869 if(desktop.dirty || cursorUpdate)
871 if(desktop.display.flags.flipping)
872 desktop.Update(null);
873 desktop.UpdateDisplay();
876 if(cursorUpdate || desktop.dirty)
878 PreserveAndDrawCursor();
879 cursorUpdate = false;
880 desktop.dirty = false;
881 RestoreCursorBackground();
884 desktop.display.Unlock();
886 #if !defined(__EMSCRIPTEN__)
887 desktop.mutex.Release();
894 for(window = desktop.children.first; window; window = window.next)
896 #if !defined(__EMSCRIPTEN__)
897 if(window.mutex) window.mutex.Wait();
899 if(window.visible && window.dirty)
901 // Logf("Updating %s\n", window.name);
902 interfaceDriver.Lock(window);
905 if(window.display.current)
909 window.display.Lock(true);
910 window.UpdateDisplay();
911 window.display.Unlock();
914 window.dirty = false;
915 interfaceDriver.Unlock(window);
917 Log("--------------\n");
921 #if !defined(__EMSCRIPTEN__)
922 if(window.mutex) window.mutex.Release();
931 #if !defined(__EMSCRIPTEN__)
932 globalSystem.eventSemaphore.Wait();
936 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
937 bool ProcessNetworkEvents()
939 bool gotEvent = false;
940 #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
941 if(network.networkInitialized)
945 struct timeval tv = {0, 0};
950 PauseNetworkEvents();
951 network.mutex.Wait();
954 if(network.connectEvent || network.networkEvent)
955 Log("[P] [NProcess]\n");
957 rs = network.readSet;
958 ws = network.writeSet;
959 es = network.exceptSet;
961 if((network.ns && select(network.ns, &rs, &ws, &es, &tv)) || network.leftOverBytes)
963 network.leftOverBytes = false;
965 // Danger here? Why looping with a next and not unlocking anything?
966 for(socket = network.connectSockets.first; socket; socket = next)
969 if(!socket.processAlone && FD_ISSET(socket.s, &ws))
971 network.mutex.Release();
972 socket.connectThread.Wait();
973 network.mutex.Wait();
976 for(socket = network.sockets.first; socket; socket = next)
979 if(!socket.processAlone)
981 network.mutex.Release();
982 gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
983 network.mutex.Wait();
987 for(service = network.services.first; service; service = nextService)
989 nextService = service.next;
990 if(!service.processAlone)
992 if(FD_ISSET(service.s, &rs))
995 Logf("[P] Accepting connection (%x)\n", service.s);
997 service.accepted = false;
999 if(!service.accepted)
1003 SOCKLEN_TYPE addrLen = sizeof(a);
1004 s = accept(service.s,(SOCKADDR *)&a,&addrLen);
1009 #ifdef DEBUG_SOCKETS
1010 Log("[P] Connection accepted\n");
1014 for(socket = service.sockets.first; socket; socket = next)
1017 if(!socket.processAlone)
1019 network.mutex.Release();
1020 gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
1021 network.mutex.Wait();
1026 if(network.connectEvent)
1032 for(socket = network.connectSockets.first; socket; socket = next)
1035 if(socket._connected && socket._connected != -2)
1037 network.connectSockets.Remove(socket);
1038 delete socket.connectThread;
1040 // printf("%s is connected = %d\n", socket._class.name, socket._connected);
1041 if(socket._connected == -1)
1043 #ifdef DEBUG_SOCKETS
1044 Logf("[P] Processing disconnected connect (%x)\n", socket.s);
1047 if(socket.disconnectCode == ResolveFailed)
1048 Logf("Error resolving address %s\n", socket.address);
1050 if(socket.s == network.ns - 1)
1051 Network_DetermineMaxSocket();
1053 socket._connected = 0;
1057 else if(socket._connected == 1)
1059 #ifdef DEBUG_SOCKETS
1060 Log("[P] Processing connected connect\n");
1062 FD_CLR(socket.s, &network.writeSet);
1063 FD_SET(socket.s, &network.readSet);
1064 FD_SET(socket.s, &network.exceptSet);
1065 network.mutex.Release();
1067 // printf("Calling OnConnect on %s\n", socket._class.name);
1069 network.mutex.Wait();
1070 if(socket._connected)
1071 network.sockets.Add(socket);
1079 network.connectEvent = false;
1081 if(network.networkEvent)
1083 network.networkEvent = false;
1084 network.selectSemaphore.Release();
1089 for(semPtr = network.mtSemaphores.first; semPtr; semPtr = semPtr.next)
1091 ((Semaphore)semPtr.data).Release();
1095 network.mutex.Release();
1096 ResumeNetworkEvents();
1102 void WaitNetworkEvent()
1104 if(network.networkInitialized)
1106 if(GetCurrentThreadID() == network.mainThreadID)
1112 Semaphore semaphore { };
1113 OldLink semPtr { data = semaphore };
1114 network.mutex.Wait();
1115 network.mtSemaphores.Add(semPtr);
1116 network.mutex.Release();
1118 ResumeNetworkEvents();
1120 PauseNetworkEvents();
1121 network.mutex.Wait();
1122 network.mtSemaphores.Delete(semPtr);
1123 network.mutex.Release();
1129 void PauseNetworkEvents()
1131 if(network.networkInitialized)
1133 network.processMutex.Wait();
1137 void ResumeNetworkEvents()
1139 if(network.networkInitialized)
1141 network.processMutex.Release();
1146 void SignalEvent(void)
1148 #if !defined(__EMSCRIPTEN__)
1149 globalSystem.eventSemaphore.Release();
1153 // TODO: Might want to make this private with simpler public version?
1154 bool SwitchMode(bool fullScreen, const char * driverName, Resolution resolution, PixelFormat colorDepth, int refreshRate, const char * skinName, bool fallBack)
1156 bool result = false;
1158 const char * fbDriver;
1159 bool fbFullScreen = 0;
1160 Resolution fbResolution = 0;
1161 PixelFormat fbColorDepth = 0;
1162 int fbRefreshRate = 0;
1163 subclass(Interface) inter;
1164 subclass(Skin) skin = null;
1170 for(link = class(Skin).derivatives.first; link; link = link.next)
1173 if(skin.name && !strcmp(skin.name, skinName))
1176 if(!link) skin = null;
1181 fbDriver = defaultDisplayDriver;
1182 inter = interfaceDriver;
1185 interfaceDriver.GetCurrentMode(&fbFullScreen, &fbResolution, &fbColorDepth, &fbRefreshRate);
1187 if(!driverName && !interfaceDriver)
1188 driverName = defaultDisplayDriver;
1190 if(driverName || (skin && skin.textMode != textMode))
1192 for(link = class(Interface).derivatives.first; link; link = link.next)
1194 bool foundDriver = false;
1196 const char ** graphicsDrivers;
1199 graphicsDrivers = inter.GraphicsDrivers(&numDrivers);
1201 for(c=0; c<numDrivers; c++)
1202 if(!driverName || !strcmp(driverName, graphicsDrivers[c]))
1204 if(!skin || skin.textMode == IsDriverTextMode(graphicsDrivers[c]))
1206 driverName = graphicsDrivers[c];
1221 #if defined(__WIN32__)
1222 #if !defined(ECERE_VANILLA)
1223 if(!strcmp(driverName, "Win32Console")) inter = (subclass(Interface))class(Win32ConsoleInterface); else
1225 inter = (subclass(Interface))class(Win32Interface);
1227 if(!strcmp(driverName, "X")) inter = (subclass(Interface))class(XInterface);
1228 else inter = (subclass(Interface))class(NCursesInterface);
1233 if(interfaceDriver && (!driverName || (fbDriver && !strcmp(fbDriver, driverName))) &&
1234 fullScreen == fbFullScreen &&
1235 (!resolution || resolution == fbResolution) &&
1236 (!colorDepth || colorDepth == fbColorDepth) &&
1237 (!refreshRate || refreshRate == fbRefreshRate) &&
1238 (currentSkin && (!skinName || !strcmp(currentSkin.name, skinName))))
1242 bool wasFullScreen = fullScreenMode;
1243 subclass(Skin) oldSkin = currentSkin;
1246 modeSwitching = true;
1249 desktop.UnloadGraphics(true);
1251 if(inter != interfaceDriver)
1255 interfaceDriver.Terminate();
1257 result = inter.Initialize();
1265 interfaceDriver = inter;
1266 interfaceDriver.SetTimerResolution(timerResolution);
1267 inter.EnsureFullScreen(&fullScreen);
1268 fullScreenMode = fullScreen;
1270 if((!wasFullScreen && !fullScreen) ||
1271 inter.ScreenMode(fullScreen, resolution, colorDepth, refreshRate, &textMode))
1273 if(!fbDriver || (driverName && strcmp(fbDriver, driverName)))
1274 defaultDisplayDriver = driverName;
1276 if(!skinName || !SelectSkin(skinName))
1278 if(!currentSkin || currentSkin.textMode != textMode ||
1279 !SelectSkin(currentSkin.name))
1282 subclass(Skin) skin = null;
1284 for(link = class(Skin).derivatives.first; link; link = link.next)
1287 if(skin.textMode == textMode)
1290 if(!link) skin = null;
1293 #if !defined(__ANDROID__)
1294 SelectSkin(skin.name);
1301 if(currentSkin && desktop.SetupDisplay())
1303 desktop.active = true;
1307 desktop.display.Lock(false);
1308 desktop.display.Position(0,0);
1309 desktop.display.Unlock();
1312 if(desktop.LoadGraphics(false, oldSkin != currentSkin))
1316 desktop.UpdateDisplay();
1319 this.fullScreen = fullScreen;
1325 modeSwitching = false;
1327 LogErrorCode(modeSwitchFailed, driverName ? driverName : defaultDisplayDriver);
1330 LogErrorCode(driverNotSupported, driverName ? driverName : defaultDisplayDriver);
1332 if(!result && fallBack && fbDriver)
1334 if(!SwitchMode(fbFullScreen, fbDriver, fbResolution, fbColorDepth, fbRefreshRate, null, false))
1335 Log($"Error falling back to previous video mode.\n");
1340 bool ProcessFileNotifications()
1342 #if !defined(__EMSCRIPTEN__)
1343 bool activity = false;
1344 FileMonitor monitor, next;
1345 static int reentrant = 0;
1347 // Reentrant FileNotification is asking for trouble since each monitor is spawning a Modal() MessageBox
1348 if(reentrant) return false;
1349 // printf("[%d] Waiting in ProcessFileNotifications for fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1350 globalSystem.fileMonitorMutex.Wait();
1352 for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1356 next = monitor.next;
1361 if(!monitor.reentrant && !monitor.toBeFreed)
1363 monitor.reentrant = true;
1364 while((notify = monitor.fileNotifies.first))
1366 monitor.fileNotifies.Remove(notify);
1370 if(monitor.directory)
1372 if(!monitor.OnDirNotify(monitor.data, notify.action, notify.fileName, notify.param))
1373 monitor.StopMonitoring();
1377 if(!monitor.OnFileNotify(monitor.data, notify.action, notify.param))
1378 monitor.StopMonitoring();
1381 monitor.reentrant = false;
1387 monitor.reentrant = false;
1390 if(next && next._refCount > 1)
1398 for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1400 next = monitor.next;
1401 if(monitor.toBeFreed && !monitor.reentrant)
1402 monitor.FreeMonitor();
1405 // printf("[%d] Releasing in ProcessFileNotifications fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1406 globalSystem.fileMonitorMutex.Release();
1415 #if !defined(__EMSCRIPTEN__)
1417 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
1419 XLockDisplay(xGlobalDisplay);
1426 #if !defined(__EMSCRIPTEN__)
1427 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
1429 XUnlockDisplay(xGlobalDisplay);
1431 lockMutex.Release();
1435 Cursor GetCursor(SystemCursor cursor)
1437 return systemCursors[cursor];
1440 bool GetKeyState(Key key)
1442 return interfaceDriver.GetKeyState(key);
1445 bool GetMouseState(MouseButtons * buttons, int * x, int * y)
1447 return interfaceDriver.GetMouseState(buttons, x, y);
1451 property const char * appName
1455 strcpy(appName, value);
1456 if(desktop) desktop.text = appName;
1460 return (const char *)(this ? appName : null);
1463 #if !defined(__EMSCRIPTEN__)
1464 property Semaphore semaphore { get { return globalSystem.eventSemaphore; } };
1466 property bool alwaysEmptyInput{ set { processAll = value; } get { return processAll; } };
1467 property bool fullScreen
1471 SwitchMode(value, defaultDisplayDriver, resolution,
1472 pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1474 get { return this ? fullScreen : false; }
1476 property const char * driver
1480 SwitchMode( fullScreen, value, resolution, pixelFormat, refreshRate,
1481 currentSkin ? currentSkin.name : null, true);
1483 get { return this ? defaultDisplayDriver : null; }
1485 property Resolution resolution
1489 SwitchMode(fullScreen, defaultDisplayDriver, value, pixelFormat, refreshRate,
1490 currentSkin ? currentSkin.name : null, true);
1492 get { return this ? resolution : 0; }
1494 property PixelFormat pixelFormat
1498 SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1499 pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1501 get { return this ? pixelFormat : 0; }
1503 property int refreshRate
1507 SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1508 pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1510 get { return this ? refreshRate : 0; }
1512 property const char * skin
1514 set { SelectSkin(value); }
1515 get { return (this && currentSkin) ? currentSkin.name : null; }
1517 property bool textMode
1519 set { textMode = value; } // TODO: Implement switching
1520 get { return this ? textMode : false; }
1522 property Window desktop { get { return this ? desktop : null; } };
1523 property const char ** drivers { get { return null; } };
1524 property const char * const * skins { get { return null; } };
1525 property subclass(Skin) currentSkin { get { return this ? currentSkin : null; } };
1526 property int numDrivers { get { return 0; } };
1527 property int numSkins { get { return 0; } };
1528 property uint timerResolution
1530 set { timerResolution = value; if(interfaceDriver) interfaceDriver.SetTimerResolution(value); }
1534 #ifdef __EMSCRIPTEN__
1535 private void emscripten_main_loop_callback()
1537 guiApp.ProcessInput(false);
1538 guiApp.Cycle(false);
1539 guiApp.UpdateDisplay();