3 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
4 #define property _property
9 #define Window X11Window
10 #define Cursor X11Cursor
12 #define Display X11Display
14 #define KeyCode X11KeyCode
15 #define Picture X11Picture
17 #include <X11/Xutil.h>
34 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
35 #if defined(__WIN32__)
37 #define WIN32_LEAN_AND_MEAN
38 #define String _String
41 static WSADATA wsaData;
43 #elif defined(__unix__) || defined(__APPLE__)
50 #include <sys/select.h>
52 #include <netinet/in.h>
54 #include <sys/socket.h>
56 #include <sys/types.h>
58 #include <arpa/inet.h>
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)
76 #if defined(__APPLE__) && !defined(ECERE_VANILLA)
77 import "CocoaInterface"
80 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
86 /*static */bool guiApplicationInitialized = false;
87 GuiApplication guiApp;
90 enum GuiErrorCode : ErrorCode
92 driverNotSupported = ErrorCode { VeryFatal, 1 },
93 windowCreationFailed = ErrorCode { VeryFatal, 2 },
94 graphicsLoadingFailed = ErrorCode { VeryFatal, 3 },
95 modeSwitchFailed = ErrorCode { VeryFatal, 4 }
98 static Array<String> errorMessages
101 $"Graphics driver not supported by any user interface system",
102 $"Window creation failed",
103 $"Window graphics loading failed",
104 $"Driver/Mode switch failed"
107 public class GuiApplication : Application
116 subclass(Interface) interfaceDriver;
117 subclass(Skin) currentSkin;
124 bool fullScreenMode; // Needs to start at true for the desktop to resize
127 Resolution resolution;
128 PixelFormat pixelFormat;
131 char * defaultDisplayDriver;
133 Cursor systemCursors[SystemCursor];
137 OldList customCursors;
140 OldList windowTimers;
143 Window prevWindow; // Used for OnMouseLeave
144 Window windowCaptured;
146 // Mouse based moving & resizing
148 Point windowMovingStart;
149 Point windowMovingBefore;
150 Size windowResizingBefore;
152 bool windowIsResizing;
153 bool resizeX, resizeEndX;
154 bool resizeY, resizeEndY;
156 // Mouse based scrolling
157 Window windowScrolling;
158 Point windowScrollingBefore, windowScrollingStart;
161 Bitmap cursorBackground { };
162 int cursorBackgroundX, cursorBackgroundY;
163 int cursorBackgroundW, cursorBackgroundH;
169 Window acquiredWindow;
170 int acquiredMouseX, acquiredMouseY;
172 Cursor currentCursor;
174 uint errorLevel, lastErrorCode;
182 Window interimWindow;
186 uint timerResolution;
189 Point virtualScreenPos;
197 mainThread = GetCurrentThreadID();
201 strcpy(appName, $"ECERE Application");
206 // customCursors.offset = OFFSET(Cursor, prev);
207 windowTimers.offset = (uint)&((Timer)0).prev;
209 for(c = 0; c<SystemCursor::enumSize; c++)
210 systemCursors[c] = Cursor { systemCursor = c; };
212 globalSystem.eventSemaphore = Semaphore { };
213 globalSystem.fileMonitorMutex = Mutex { };
214 globalSystem.fileMonitors.offset = (uint)&((FileMonitor)0).prev;
225 customCursors.Clear();
227 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
229 XUnlockDisplay(xGlobalDisplay);
232 #if !defined(__ANDROID__)
233 // Because destruction of app won't be from main thread
234 if(guiApplicationInitialized)
240 interfaceDriver.Terminate();
243 // interfaceDrivers.Free(null);
249 for(c = 0; c<SystemCursor::enumSize; c++)
250 delete systemCursors[c];
252 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
256 delete globalSystem.eventSemaphore;
257 delete globalSystem.fileMonitorMutex;
258 delete globalSystem.fileMonitorThread;
260 UnapplySkin(class(Window));
264 Timer timer, nextTimer;
265 for(timer = windowTimers.first; timer; timer = nextTimer)
267 nextTimer = timer.next;
276 Time time = GetTime();
279 for(timer = windowTimers.first; timer; timer = timer.next)
280 timer.dispatched = false;
283 for(timer = windowTimers.first; timer; timer = timer.next)
285 if(!timer.dispatched)
287 if((timer.delay - (Seconds)(time - timer.lastTime)) < Seconds { 0.00001 })
290 timer.lastTime = time;
291 if(timer.DelayExpired(timer.window))
293 timer.dispatched = true;
295 eInstance_DecRef(timer);
305 // --- Mouse-based window movement ---
306 void SetCurrentCursor(Window window, Cursor cursor)
308 currentCursor = cursor;
311 if(fullScreenMode && cursor.bitmap)
312 interfaceDriver.SetMouseCursor(window ? window : desktop, (SystemCursor)-1);
315 interfaceDriver.SetMouseCursor(window ? window : desktop, cursor.systemCursor);
316 cursorBackground.Free();
322 void PreserveAndDrawCursor()
325 if(!acquiredWindow && cursorUpdate && currentCursor && currentCursor->bitmap)
329 Box against = {0,0, desktop.w-1,desktop.h-1};
330 Box box = {0, 0, currentCursor->bitmap->width,currentCursor->bitmap->height};
332 interfaceDriver->GetMousePosition(&mouseX, &mouseY);
334 mouseX -= currentCursor->hotSpotX;
335 mouseY -= currentCursor->hotSpotY;
337 // Preserve Background
338 if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
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);
348 eBox_ClipOffset(&box, &against, mouseX, mouseY);
350 if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
351 eDisplay_StartUpdate(desktop.display);
353 surface = eDisplay_GetSurface(desktop.display, mouseX, mouseY, &box);
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);
361 if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
366 box.bottom += mouseY;
367 eDisplay_Update(desktop.display, &box);
370 if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING))
371 eDisplay_EndUpdate(desktop.display);
376 void RestoreCursorBackground()
379 // Restore Cursor Background
380 if(cursorBackground && desktop.active)
382 Box box = {0, 0, cursorBackgroundW-1,cursorBackgroundH-1};
383 Box against = {0,0, desktop.w-1,desktop.h-1};
386 eBox_ClipOffset(&box, &against, cursorBackgroundX, cursorBackgroundY);
387 if((surface = eDisplay_GetSurface(desktop.display, cursorBackgroundX,cursorBackgroundY, &box)))
389 eSurface_Blit(surface, cursorBackground, 0, 0, 0,0, cursorBackgroundW,cursorBackgroundH);
390 eInstance_Delete(surface);
396 bool IsModeSwitching()
398 return modeSwitching;
401 public bool SetDesktopPosition(int x, int y, int w, int h, bool moveChildren)
404 bool windowResized = desktop.size.w != w || desktop.size.h != h;
405 bool windowMoved = desktop.clientStart.x != x || desktop.clientStart.y != y;
407 if((windowResized || windowMoved) && moveChildren)
410 desktop.Position(x, y, w, h, true, true, true, true, false, false);
412 // Maximized native decorations windows suffer when we drag the dock around, so remaximize them
413 // It's a little jumpy, but oh well.
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
418 for(child = desktop.children.first; child; child = child.next)
420 if(child.nativeDecorations && child.rootWindow == child && child.state == maximized)
422 #if defined(__WIN32__)
423 child.state = normal;
424 child.state = maximized;
428 child.state = normal;
429 child.state = maximized;
432 child.requireRemaximize = true;
436 /*for(child = desktop.children.first; child; child = child.next)
438 if(!child.systemParent)
443 child.ComputeAnchors(
444 child.ax, child.ay, child.aw, child.ah,
446 child.Position(x, y, w, h, true, true, true, true, false);
448 if(child.state == Maximized)
452 child.ComputeAnchors(,
453 A_LEFT,A_LEFT,A_OFFSET,A_OFFSET,
455 child.Position(, x, y, w, h, false, true, true, true, false);
461 desktop.display.Lock(true);
464 if(!desktop.display.Resize(desktop.size.w, desktop.size.h))
467 desktop.dirty = true;
468 if(!desktop.display.flags.flipping)
469 desktop.Update(null);
471 // When does the desktop have a display in not fullscreen mode?
472 if(!fullScreenMode && !modeSwitching)
473 desktop.UpdateDisplay();
474 desktop.display.Unlock();
478 desktop.SetPosition(x, y, w, h, false, false, false);
482 void SetAppFocus(bool state)
484 // Shouldn't be property here
485 desktop.active = state;
488 bool SelectSkin(char * skinName)
494 for(link = class(Skin).derivatives.first; link; link = link.next)
497 if(skin.name && !strcmp(skin.name, skinName))
500 if(!link) skin = null;
504 if(skin != currentSkin || !currentSkin)
506 // Try finding a driver to support this mode
507 if(skin.textMode != textMode)
513 bool needReload = false;
515 if(!modeSwitching && currentSkin)
517 modeSwitching = true;
518 desktop.UnloadGraphics(true);
522 UnapplySkin(class(Window));
526 ApplySkin(class(Window), skin.name, null);
530 if(desktop.SetupDisplay())
531 if(desktop.LoadGraphics(false, true))
533 modeSwitching = false;
545 void Initialize(bool switchMode)
548 // if(!initialized && eClass_IsDerived(__ecereModule->app->module.inst.class, guiApplicationClass))
549 if(!guiApplicationInitialized)
551 char * defaultDriver = null;
552 #if defined(ECERE_VANILLA) || defined(ECERE_ONEDRIVER)
553 char * driver = null;
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;
562 guiApplicationInitialized = true;
564 fullScreenMode = true; // Needs to start at true for the desktop to resize
565 // Set this to true earlier so we can override it!
571 /*#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
573 XLockDisplay(xGlobalDisplay);
579 desktop = Window { nativeDecorations = false };
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;
594 #if defined(__WIN32__)
597 defaultDriver = driver;
598 else if((this.isGUIApp & 1) && !textMode)
599 defaultDriver = "GDI";
601 defaultDriver = "Win32Console";
603 #elif defined(__APPLE__)
606 defaultDriver = driver;
608 defaultDriver = "NCurses"; //"CocoaOpenGL";
610 #elif defined(__ANDROID__)
613 defaultDriver = driver;
615 defaultDriver = "OpenGL";
618 if((this.isGUIApp & 1) && !textMode)
620 char * display = getenv("DISPLAY");
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);
628 defaultDriver = driver;
631 defaultDriver = "NCurses";
636 SwitchMode(false, defaultDriver, 0, 0, 0, null, true);
640 #if defined(__WIN32__)
641 SwitchMode(true, "Win32Console", 0, PixelFormatText, 0, null, true);
646 SwitchMode(true, "SVGA", Res640x480, PixelFormat8, 0, null, true);
649 #if defined(__APPLE__)
650 SwitchMode(true, "X" /*"CocoaOpenGL"*/, 0, 0, 0, null, true);
653 #if defined(__unix__)
654 #if defined(ECERE_MINIGLX)
655 SwitchMode(true, "OpenGL", 0, 0, 0, null, true);
660 guiApplicationInitialized = false;
663 defaultDisplayDriver = defaultDriver;
668 virtual bool Init(void);
669 virtual bool Cycle(bool idle);
670 virtual void Terminate(void);
680 // better solution when designing tab order/activated window etc, why do windows move in the list?
683 for(window = desktop.children.first; window; window = window.next)
685 if(window.autoCreate && !window.created)
700 while(desktop && interfaceDriver)
704 if(terminateX != terminated)
706 terminated = terminateX;
712 //printf("Resetting terminate X to 0\n");
716 for(child = desktop.children.first; child; child = child.next)
717 if(child.created && child.visible)
721 for(window = desktop.children.first; window; window = window.next)
722 if(window.mutex) window.mutex.Wait();
724 for(window = desktop.children.first; window; window = window.next)
725 if(window.mutex) window.mutex.Release();
726 wait = !ProcessInput(true);
728 if(lockMutex.owningThread != GetCurrentThreadID())
729 PrintLn("WARNING: ProcessInput returned unlocked GUI!");
738 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
740 XUnlockDisplay(xGlobalDisplay);
746 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
748 XLockDisplay(xGlobalDisplay);
752 eInstance_DecRef(desktop);
757 #if defined(__ANDROID__)
758 // Because destruction of GuiApp won't be from main thread
765 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
767 XUnlockDisplay(xGlobalDisplay);
775 interfaceDriver.Wait();
781 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
783 XLockDisplay(xGlobalDisplay);
787 bool ProcessInput(bool useProcessAll)
793 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
794 ProcessNetworkEvents();
798 result = interfaceDriver.ProcessInput(useProcessAll && processAll);
799 if(!desktop || !interfaceDriver) return;
803 for(child = app.desktop.children.first; child; child = child.next)
804 if(child.created && child.visible)
806 if(!child) return result;
809 result |= UpdateTimers();
810 result |= ProcessFileNotifications();
813 result |= ProcessFileNotifications();
814 result |= UpdateTimers();
815 result |= interfaceDriver.ProcessInput(useProcessAll && processAll);
822 void UpdateDisplay(void)
824 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
825 if(Desktop3DUpdateDisplay()) return;
830 if(fullScreenMode && desktop.display)
832 desktop.mutex.Wait();
835 desktop.display.Lock(true);
837 if(desktop.dirty || cursorUpdate)
839 if(desktop.display.flags.flipping)
840 desktop.Update(null);
841 desktop.UpdateDisplay();
844 if(cursorUpdate || desktop.dirty)
846 PreserveAndDrawCursor();
847 cursorUpdate = false;
848 desktop.dirty = false;
849 RestoreCursorBackground();
852 desktop.display.Unlock();
854 desktop.mutex.Release();
860 for(window = desktop.children.first; window; window = window.next)
862 if(window.mutex) window.mutex.Wait();
863 if(window.visible && window.dirty)
865 // Logf("Updating %s\n", window.name);
866 interfaceDriver.Lock(window);
869 if(window.display.current)
873 window.display.Lock(true);
874 window.UpdateDisplay();
875 window.display.Unlock();
878 window.dirty = false;
879 interfaceDriver.Unlock(window);
881 Log("--------------\n");
885 if(window.mutex) window.mutex.Release();
893 globalSystem.eventSemaphore.Wait();
896 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
897 bool ProcessNetworkEvents()
899 bool gotEvent = false;
900 #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
901 if(network.networkInitialized)
905 struct timeval tv = {0, 0};
910 PauseNetworkEvents();
911 network.mutex.Wait();
914 if(network.connectEvent || network.networkEvent)
915 Log("[P] [NProcess]\n");
917 rs = network.readSet;
918 ws = network.writeSet;
919 es = network.exceptSet;
921 if((network.ns && select(network.ns, &rs, &ws, &es, &tv)) || network.leftOverBytes)
923 network.leftOverBytes = false;
925 // Danger here? Why looping with a next and not unlocking anything?
926 for(socket = network.connectSockets.first; socket; socket = next)
929 if(!socket.processAlone && FD_ISSET(socket.s, &ws))
931 network.mutex.Release();
932 socket.connectThread.Wait();
933 network.mutex.Wait();
936 for(socket = network.sockets.first; socket; socket = next)
939 if(!socket.processAlone)
941 network.mutex.Release();
942 gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
943 network.mutex.Wait();
947 for(service = network.services.first; service; service = nextService)
949 nextService = service.next;
950 if(!service.processAlone)
952 if(FD_ISSET(service.s, &rs))
955 Logf("[P] Accepting connection (%x)\n", service.s);
957 service.accepted = false;
959 if(!service.accepted)
963 int addrLen = sizeof(a);
964 s = accept(service.s,(SOCKADDR *)&a,&addrLen);
970 Log("[P] Connection accepted\n");
974 for(socket = service.sockets.first; socket; socket = next)
977 if(!socket.processAlone)
979 network.mutex.Release();
980 gotEvent |= socket.ProcessSocket(&rs, &ws, &es);
981 network.mutex.Wait();
986 if(network.connectEvent)
992 for(socket = network.connectSockets.first; socket; socket = next)
995 if(socket._connected && socket._connected != -2)
997 network.connectSockets.Remove(socket);
998 delete socket.connectThread;
1000 // printf("%s is connected = %d\n", socket._class.name, socket._connected);
1001 if(socket._connected == -1)
1003 #ifdef DEBUG_SOCKETS
1004 Logf("[P] Processing disconnected connect (%x)\n", socket.s);
1007 if(socket.disconnectCode == ResolveFailed)
1008 Logf("Error resolving address %s\n", socket.address);
1010 if(socket.s == network.ns - 1)
1011 Network_DetermineMaxSocket();
1016 else if(socket._connected == 1)
1018 #ifdef DEBUG_SOCKETS
1019 Log("[P] Processing connected connect\n");
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();
1026 // printf("Calling OnConnect on %s\n", socket._class.name);
1028 network.mutex.Wait();
1029 if(socket._connected)
1030 network.sockets.Add(socket);
1038 network.connectEvent = false;
1040 if(network.networkEvent)
1042 network.networkEvent = false;
1043 network.selectSemaphore.Release();
1048 for(semPtr = network.mtSemaphores.first; semPtr; semPtr = semPtr.next)
1050 ((Semaphore)semPtr.data).Release();
1054 network.mutex.Release();
1055 ResumeNetworkEvents();
1061 void WaitNetworkEvent()
1063 if(network.networkInitialized)
1065 if(GetCurrentThreadID() == network.mainThreadID)
1071 Semaphore semaphore { };
1072 OldLink semPtr { data = semaphore };
1073 network.mutex.Wait();
1074 network.mtSemaphores.Add(semPtr);
1075 network.mutex.Release();
1077 ResumeNetworkEvents();
1079 PauseNetworkEvents();
1080 network.mutex.Wait();
1081 network.mtSemaphores.Delete(semPtr);
1082 network.mutex.Release();
1088 void PauseNetworkEvents()
1090 if(network.networkInitialized)
1092 network.processMutex.Wait();
1096 void ResumeNetworkEvents()
1098 if(network.networkInitialized)
1100 network.processMutex.Release();
1105 void SignalEvent(void)
1107 globalSystem.eventSemaphore.Release();
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)
1113 bool result = false;
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;
1127 for(link = class(Skin).derivatives.first; link; link = link.next)
1130 if(skin.name && !strcmp(skin.name, skinName))
1133 if(!link) skin = null;
1138 fbDriver = defaultDisplayDriver;
1139 inter = interfaceDriver;
1142 interfaceDriver.GetCurrentMode(&fbFullScreen, &fbResolution, &fbColorDepth, &fbRefreshRate);
1144 if(!driverName && !interfaceDriver)
1145 driverName = defaultDisplayDriver;
1147 if(driverName || (skin && skin.textMode != textMode))
1149 for(link = class(Interface).derivatives.first; link; link = link.next)
1151 bool foundDriver = false;
1153 char ** graphicsDrivers;
1156 graphicsDrivers = inter.GraphicsDrivers(&numDrivers);
1158 for(c=0; c<numDrivers; c++)
1159 if(!driverName || !strcmp(driverName, graphicsDrivers[c]))
1161 if(!skin || skin.textMode == IsDriverTextMode(graphicsDrivers[c]))
1163 driverName = graphicsDrivers[c];
1178 #if defined(__WIN32__)
1179 #if !defined(ECERE_VANILLA)
1180 if(!strcmp(driverName, "Win32Console")) inter = (subclass(Interface))class(Win32ConsoleInterface); else
1182 inter = (subclass(Interface))class(Win32Interface);
1184 if(!strcmp(driverName, "X")) inter = (subclass(Interface))class(XInterface);
1185 else inter = (subclass(Interface))class(NCursesInterface);
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))))
1199 bool wasFullScreen = fullScreenMode;
1200 subclass(Skin) oldSkin = currentSkin;
1203 modeSwitching = true;
1206 desktop.UnloadGraphics(true);
1208 if(inter != interfaceDriver)
1212 interfaceDriver.Terminate();
1214 result = inter.Initialize();
1222 interfaceDriver = inter;
1223 interfaceDriver.SetTimerResolution(timerResolution);
1224 inter.EnsureFullScreen(&fullScreen);
1225 fullScreenMode = fullScreen;
1227 if((!wasFullScreen && !fullScreen) ||
1228 inter.ScreenMode(fullScreen, resolution, colorDepth, refreshRate, &textMode))
1230 if(!fbDriver || (driverName && strcmp(fbDriver, driverName)))
1231 defaultDisplayDriver = driverName;
1233 if(!skinName || !SelectSkin(skinName))
1235 if(!currentSkin || currentSkin.textMode != textMode ||
1236 !SelectSkin(currentSkin.name))
1239 subclass(Skin) skin = null;
1241 for(link = class(Skin).derivatives.first; link; link = link.next)
1244 if(skin.textMode == textMode)
1247 if(!link) skin = null;
1250 #if !defined(__ANDROID__)
1251 SelectSkin(skin.name);
1258 if(currentSkin && desktop.SetupDisplay())
1260 desktop.active = true;
1264 desktop.display.Lock(false);
1265 desktop.display.Position(0,0);
1266 desktop.display.Unlock();
1269 if(desktop.LoadGraphics(false, oldSkin != currentSkin))
1273 desktop.UpdateDisplay();
1276 this.fullScreen = fullScreen;
1282 modeSwitching = false;
1284 LogErrorCode(modeSwitchFailed, driverName ? driverName : defaultDisplayDriver);
1287 LogErrorCode(driverNotSupported, driverName ? driverName : defaultDisplayDriver);
1289 if(!result && fallBack && fbDriver)
1291 if(!SwitchMode(fbFullScreen, fbDriver, fbResolution, fbColorDepth, fbRefreshRate, null, false))
1292 Log($"Error falling back to previous video mode.\n");
1297 bool ProcessFileNotifications()
1299 bool activity = false;
1300 FileMonitor monitor, next;
1301 static int reentrant = 0;
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();
1308 for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1312 next = monitor.next;
1317 if(!monitor.reentrant && !monitor.toBeFreed)
1319 monitor.reentrant = true;
1320 while((notify = monitor.fileNotifies.first))
1322 monitor.fileNotifies.Remove(notify);
1326 if(monitor.directory)
1328 if(!monitor.OnDirNotify(monitor.data, notify.action, notify.fileName, notify.param))
1329 monitor.StopMonitoring();
1333 if(!monitor.OnFileNotify(monitor.data, notify.action, notify.param))
1334 monitor.StopMonitoring();
1337 monitor.reentrant = false;
1343 monitor.reentrant = false;
1346 if(next && next._refCount > 1)
1354 for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
1356 next = monitor.next;
1357 if(monitor.toBeFreed && !monitor.reentrant)
1358 monitor.FreeMonitor();
1361 // printf("[%d] Releasing in ProcessFileNotifications fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
1362 globalSystem.fileMonitorMutex.Release();
1369 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1371 XLockDisplay(xGlobalDisplay);
1377 #if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
1379 XUnlockDisplay(xGlobalDisplay);
1381 lockMutex.Release();
1384 Cursor GetCursor(SystemCursor cursor)
1386 return systemCursors[cursor];
1389 bool GetKeyState(Key key)
1391 return interfaceDriver.GetKeyState(key);
1394 bool GetMouseState(MouseButtons * buttons, int * x, int * y)
1396 return interfaceDriver.GetMouseState(buttons, x, y);
1400 property char * appName
1404 strcpy(appName, value);
1405 if(desktop) desktop.text = appName;
1409 return (char *)(this ? appName : null);
1412 property Semaphore semaphore { get { return globalSystem.eventSemaphore; } };
1413 property bool alwaysEmptyInput{ set { processAll = value; } get { return processAll; } };
1414 property bool fullScreen
1418 SwitchMode(value, defaultDisplayDriver, resolution,
1419 pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1421 get { return this ? fullScreen : false; }
1423 property char * driver
1427 SwitchMode( fullScreen, value, resolution, pixelFormat, refreshRate,
1428 currentSkin ? currentSkin.name : null, true);
1430 get { return this ? defaultDisplayDriver : null; }
1432 property Resolution resolution
1436 SwitchMode(fullScreen, defaultDisplayDriver, value, pixelFormat, refreshRate,
1437 currentSkin ? currentSkin.name : null, true);
1439 get { return this ? resolution : 0; }
1441 property PixelFormat pixelFormat
1445 SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1446 pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1448 get { return this ? pixelFormat : 0; }
1450 property int refreshRate
1454 SwitchMode(fullScreen, defaultDisplayDriver, resolution,
1455 pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
1457 get { return this ? refreshRate : 0; }
1459 property char * skin
1461 set { SelectSkin(value); }
1462 get { return (this && currentSkin) ? currentSkin.name : null; }
1464 property bool textMode
1466 set { textMode = value; } // TODO: Implement switching
1467 get { return this ? textMode : false; }
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
1477 set { timerResolution = value; if(interfaceDriver) interfaceDriver.SetTimerResolution(value); }