Unstaged changes (WIP)
[sdk] / ecere / src / gui / GuiApplication.ec
index 7b3faea..fb4005b 100644 (file)
@@ -1,6 +1,14 @@
 namespace gui;
 
-#if defined(__unix__) || defined(__APPLE__)
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
+
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
+
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
 #define property _property
 #define new _new
 #define class _class
@@ -33,17 +41,21 @@ namespace gui;
 
 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
 #if defined(__WIN32__)
-
+#define SOCKLEN_TYPE int
 #define WIN32_LEAN_AND_MEAN
+#define String _String
 #include <winsock.h>
-static WSADATA wsaData;
+#undef String
 
 #elif defined(__unix__) || defined(__APPLE__)
 
 default:
+#define SOCKLEN_TYPE socklen_t
 #define uint _uint
+#define set _set
 #include <sys/time.h>
 #include <unistd.h>
+#include <sys/select.h>
 
 #include <netinet/in.h>
 #include <netdb.h>
@@ -53,8 +65,10 @@ default:
 #include <sys/time.h>
 #include <arpa/inet.h>
 
-private:
+#undef set
 #undef uint
+
+private:
 typedef int SOCKET;
 typedef struct hostent HOSTENT;
 typedef struct sockaddr SOCKADDR;
@@ -67,32 +81,21 @@ typedef struct in_addr IN_ADDR;
 import "network"
 #endif
 
-#if defined(__unix__) || defined(__APPLE__)
+#if defined(__APPLE__) && !defined(ECERE_VANILLA)
+import "CocoaInterface"
+#endif
+
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
 import "XInterface"
 #endif
 
 import "Window"
+import "List"
 
+/*static */bool guiApplicationInitialized = false;
 GuiApplication guiApp;
 int terminateX;
 
-enum GuiErrorCode : ErrorCode
-{
-   driverNotSupported      = ErrorCode { VeryFatal, 1 },
-   windowCreationFailed    = ErrorCode { VeryFatal, 2 },
-   graphicsLoadingFailed   = ErrorCode { VeryFatal, 3 },
-   modeSwitchFailed        = ErrorCode { VeryFatal, 4 }
-};
-
-static char * errorMessages[] =
-{
-   "No error",
-   "Graphics driver not supported by any user interface system",
-   "Window creation failed",
-   "Window graphics loading failed",
-   "Driver/Mode switch failed"
-};
-
 public class GuiApplication : Application
 {
    int numDrivers;
@@ -117,7 +120,7 @@ public class GuiApplication : Application
    PixelFormat pixelFormat;
    int refreshRate;
 
-   char * defaultDisplayDriver;
+   const char * defaultDisplayDriver;
 
    Cursor systemCursors[SystemCursor];
 
@@ -129,7 +132,8 @@ public class GuiApplication : Application
    OldList windowTimers;
 
    // Mouse events
-   Window prevWindow;     // Used for OnMouseLeave
+   Window prevWindow;            // Used for OnMouseLeave
+   List<Window> overWindows { }; // Used for OnMouseLeave
    Window windowCaptured;
 
    // Mouse based moving & resizing
@@ -164,9 +168,13 @@ public class GuiApplication : Application
 
    bool processAll;
 
+#if !defined(__EMSCRIPTEN__)
    Mutex waitMutex {};
+#endif
    bool waiting;
+#if !defined(__EMSCRIPTEN__)
    Mutex lockMutex {};
+#endif
 
    Window interimWindow;
    bool caretEnabled;
@@ -174,30 +182,37 @@ public class GuiApplication : Application
    char appName[1024];
    uint timerResolution;
 
-   Size virtualScreen;   
+   Size virtualScreen;
    Point virtualScreenPos;
 
+   int64 mainThread;
+
    GuiApplication()
    {
       SystemCursor c;
-      
+
+#if !defined(__EMSCRIPTEN__)
+      mainThread = GetCurrentThreadID();
+#endif
       if(!guiApp)
          guiApp = this;
 
-      strcpy(appName, "ECERE Application");
+      strcpy(appName, $"ECERE Application");
 
       processAll = true;
 
       // TODO:
       // customCursors.offset = OFFSET(Cursor, prev);
-      windowTimers.offset = (uint)&((Timer)0).prev;
+      windowTimers.offset = (uint)(uintptr)&((Timer)0).prev;
 
       for(c = 0; c<SystemCursor::enumSize; c++)
          systemCursors[c] = Cursor { systemCursor = c; };
 
+#if !defined(__EMSCRIPTEN__)
       globalSystem.eventSemaphore = Semaphore { };
       globalSystem.fileMonitorMutex = Mutex { };
-      globalSystem.fileMonitors.offset = (uint)&((FileMonitor)0).prev;
+      globalSystem.fileMonitors.offset = (uint)(uintptr)&((FileMonitor)0).prev;
+#endif
       return true;
    }
 
@@ -210,12 +225,16 @@ public class GuiApplication : Application
       delete desktop;
       customCursors.Clear();
 
-#if defined(__unix__)
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
       if(xGlobalDisplay)
          XUnlockDisplay(xGlobalDisplay);
 #endif
 
-      lockMutex.Release();
+#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
+      // Because destruction of app won't be from main thread
+      if(guiApplicationInitialized)
+         lockMutex.Release();
+#endif
 
       if(interfaceDriver)
       {
@@ -235,11 +254,23 @@ public class GuiApplication : Application
       Network_Terminate();
 #endif
 
+#if !defined(__EMSCRIPTEN__)
       delete globalSystem.eventSemaphore;
       delete globalSystem.fileMonitorMutex;
       delete globalSystem.fileMonitorThread;
+#endif
 
       UnapplySkin(class(Window));
+
+      // Stop all timers
+      {
+         Timer timer, nextTimer;
+         for(timer = windowTimers.first; timer; timer = nextTimer)
+         {
+            nextTimer = timer.next;
+            timer.Stop();
+         }
+      }
    }
 
    bool UpdateTimers()
@@ -275,16 +306,16 @@ public class GuiApplication : Application
    }
 
    // --- Mouse-based window movement ---
-   void SetCurrentCursor(Cursor cursor)
+   void SetCurrentCursor(Window window, Cursor cursor)
    {
       currentCursor = cursor;
       if(cursor)
       {
          if(fullScreenMode && cursor.bitmap)
-            interfaceDriver.SetMouseCursor((SystemCursor)-1);
+            interfaceDriver.SetMouseCursor(window ? window : desktop, (SystemCursor)-1);
          else
          {
-            interfaceDriver.SetMouseCursor(cursor.systemCursor);
+            interfaceDriver.SetMouseCursor(window ? window : desktop, cursor.systemCursor);
             cursorBackground.Free();
          }
       }
@@ -368,7 +399,7 @@ public class GuiApplication : Application
    bool IsModeSwitching()
    {
       return modeSwitching;
-   }  
+   }
 
    public bool SetDesktopPosition(int x, int y, int w, int h, bool moveChildren)
    {
@@ -383,12 +414,26 @@ public class GuiApplication : Application
 
          // Maximized native decorations windows suffer when we drag the dock around, so remaximize them
          // It's a little jumpy, but oh well.
+
+         // Made this Windows only as it was causing occasional wrong stacking of windows in X11/Cinnamon
+         // when switching debugged app from full-screen
+
          for(child = desktop.children.first; child; child = child.next)
          {
             if(child.nativeDecorations && child.rootWindow == child && child.state == maximized)
             {
+#if defined(__WIN32__)
                child.state = normal;
                child.state = maximized;
+#else
+               if(child.active)
+               {
+                  child.state = normal;
+                  child.state = maximized;
+               }
+               else
+                  child.requireRemaximize = true;
+#endif
             }
          }
          /*for(child = desktop.children.first; child; child = child.next)
@@ -407,11 +452,11 @@ public class GuiApplication : Application
                {
                   child.x = desktop.x;
                   child.y = desktop.y;
-                  child.ComputeAnchors(, 
-                     A_LEFT,A_LEFT,A_OFFSET,A_OFFSET, 
+                  child.ComputeAnchors(,
+                     A_LEFT,A_LEFT,A_OFFSET,A_OFFSET,
                      &x, &y, &w, &h);
                   child.Position(, x, y, w, h, false, true, true, true, false);
-               }           
+               }
             }
          }*/
          if(desktop.display)
@@ -438,17 +483,17 @@ public class GuiApplication : Application
    }
 
    void SetAppFocus(bool state)
-   {  
+   {
       // Shouldn't be property here
       desktop.active = state;
    }
 
-   bool SelectSkin(char * skinName)
+   bool SelectSkin(const char * skinName)
    {
       bool result = false;
       subclass(Skin) skin;
       OldLink link;
-      
+
       for(link = class(Skin).derivatives.first; link; link = link.next)
       {
          skin = link.data;
@@ -456,7 +501,7 @@ public class GuiApplication : Application
             break;
       }
       if(!link) skin = null;
-      
+
       if(skin)
       {
          if(skin != currentSkin || !currentSkin)
@@ -478,7 +523,7 @@ public class GuiApplication : Application
                }
 
                UnapplySkin(class(Window));
-               
+
                currentSkin = skin;
 
                ApplySkin(class(Window), skin.name, null);
@@ -502,14 +547,12 @@ public class GuiApplication : Application
 
    void Initialize(bool switchMode)
    {
-      static bool initialized = false;
-      
       // TODO:
       // if(!initialized && eClass_IsDerived(__ecereModule->app->module.inst.class, guiApplicationClass))
-      if(!initialized)
+      if(!guiApplicationInitialized)
       {
-         char * defaultDriver = null;
-#if defined(ECERE_VANILLA)
+         const char * defaultDriver = null;
+#if defined(ECERE_VANILLA) || defined(ECERE_ONEDRIVER)
          char * driver = null;
 #else
 
@@ -519,7 +562,7 @@ public class GuiApplication : Application
          GetEnvironment("ECERE_DRIVER", driverStorage, sizeof(driverStorage));
          if(driverStorage[0]) driver = driverStorage;
 #endif
-         initialized = true;
+         guiApplicationInitialized = true;
 
          fullScreenMode = true; // Needs to start at true for the desktop to resize
          // Set this to true earlier so we can override it!
@@ -527,16 +570,18 @@ public class GuiApplication : Application
 
          errorLevel = 2;
 
+#if !defined(__EMSCRIPTEN__)
          lockMutex.Wait();
-#if defined(__unix__)
+#endif
+/*#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
          if(xGlobalDisplay)
             XLockDisplay(xGlobalDisplay);
-#endif
+#endif*/
 
          // Setup Desktop
          if(!desktop)
          {
-            desktop = Window { };
+            desktop = Window { nativeDecorations = false };
             incref desktop;
             incref desktop;
             desktop.childrenOrder.circ = true;
@@ -544,11 +589,12 @@ public class GuiApplication : Application
             desktop.background = blue;
             desktop.rootWindow = desktop;
             desktop.cursor = GetCursor(arrow);
-            desktop.caption = new char[strlen(appName) + 1];
-            strcpy(desktop.caption, appName);
+            desktop.caption = appName;
             *&desktop.visible = true;
             desktop.position = Point { };
+#if !defined(__EMSCRIPTEN__)
             desktop.mutex = Mutex { };
+#endif
             desktop.created = true;
          }
 
@@ -556,13 +602,34 @@ public class GuiApplication : Application
          {
             if(driver)
                defaultDriver = driver;
-            else if(this.isGUIApp && !textMode)
+            else if((this.isGUIApp & 1) && !textMode)
                defaultDriver = "GDI";
             else
                defaultDriver = "Win32Console";
          }
+   #elif defined(__APPLE__)
+         {
+            if (driver)
+               defaultDriver = driver;
+            else
+               defaultDriver = "X"; //"CocoaOpenGL";
+         }
+   #elif defined(__ANDROID__)
+         {
+            if(driver)
+               defaultDriver = driver;
+            else
+               defaultDriver = "OpenGL";
+         }
+   #elif defined(__EMSCRIPTEN__)
+         {
+            if(driver)
+               defaultDriver = driver;
+            else
+               defaultDriver = "OpenGL";
+         }
    #else
-         if(this.isGUIApp && !textMode)
+         if((this.isGUIApp & 1) && !textMode)
          {
             char * display = getenv("DISPLAY");
 
@@ -593,14 +660,18 @@ public class GuiApplication : Application
                SwitchMode(true, "SVGA", Res640x480, PixelFormat8, 0, null, true);
          #endif
 
-         #if defined(__unix__) || defined(__APPLE__)
+         #if defined(__APPLE__)
+               // SwitchMode(true, "X" /*"CocoaOpenGL"*/, 0, 0, 0, null, true);
+         #endif
+
+         #if defined(__unix__)
          #if defined(ECERE_MINIGLX)
                SwitchMode(true, "OpenGL", 0, 0, 0, null, true);
          #endif
          #endif
             }
-            if(!interfaceDriver)
-               initialized = false;
+            /*if(!interfaceDriver)
+               guiApplicationInitialized = false;*/
          }
          else
             defaultDisplayDriver = defaultDriver;
@@ -635,10 +706,15 @@ public:
             }
          }
 
+#ifdef __EMSCRIPTEN__
+      emscripten_set_main_loop(emscripten_main_loop_callback, 1/*60*/, 1);
+#endif
+
          if(desktop)
          {
             int terminated = 0;
             incref desktop;
+            ProcessInput(true);
             while(desktop && interfaceDriver)
             {
                bool wait;
@@ -660,28 +736,40 @@ public:
                      break;
                if(!child) break;
 
+#if !defined(__EMSCRIPTEN__)
                for(window = desktop.children.first; window; window = window.next)
                   if(window.mutex) window.mutex.Wait();
+#endif
                UpdateDisplay();
+#if !defined(__EMSCRIPTEN__)
                for(window = desktop.children.first; window; window = window.next)
                   if(window.mutex) window.mutex.Release();
+#endif
                wait = !ProcessInput(true);
+#if !defined(__EMSCRIPTEN__)
+#if defined(_DEBUG) && !defined(MEMINFO)
+               if(lockMutex.owningThread != GetCurrentThreadID())
+                  PrintLn("WARNING: ProcessInput returned unlocked GUI!");
+#endif
+#endif
                if(!Cycle(wait))
                   wait = false;
 
-               if(wait) 
+               if(wait)
                   Wait();
                else
                {
-#if defined(__unix__)
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
                   if(xGlobalDisplay)
                      XUnlockDisplay(xGlobalDisplay);
 #endif
 
+#if !defined(__EMSCRIPTEN__)
                   lockMutex.Release();
                   lockMutex.Wait();
+#endif
 
-#if defined(__unix__)
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
                   if(xGlobalDisplay)
                      XLockDisplay(xGlobalDisplay);
 #endif
@@ -691,27 +779,36 @@ public:
          }
       }
       Terminate();
+
+#if defined(__ANDROID__)
+      // Because destruction of GuiApp won't be from main thread
+      lockMutex.Release();
+#endif
    }
 
    void Wait(void)
    {
-#if defined(__unix__)
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
       if(xGlobalDisplay)
          XUnlockDisplay(xGlobalDisplay);
 #endif
 
+#if !defined(__EMSCRIPTEN__)
       lockMutex.Release();
 
       waitMutex.Wait();
+#endif
       waiting = true;
       if(interfaceDriver)
          interfaceDriver.Wait();
       waiting = false;
+#if !defined(__EMSCRIPTEN__)
       waitMutex.Release();
 
       lockMutex.Wait();
+#endif
 
-#if defined(__unix__)
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
       if(xGlobalDisplay)
          XLockDisplay(xGlobalDisplay);
 #endif
@@ -742,7 +839,7 @@ public:
          result |= UpdateTimers();
          result |= ProcessFileNotifications();
          */
-         
+
          result |= ProcessFileNotifications();
          result |= UpdateTimers();
          result |= interfaceDriver.ProcessInput(useProcessAll && processAll);
@@ -757,12 +854,14 @@ public:
 #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D)
       if(Desktop3DUpdateDisplay()) return;
 #endif
-      
+
       if(interfaceDriver)
       {
          if(fullScreenMode && desktop.display)
          {
+#if !defined(__EMSCRIPTEN__)
             desktop.mutex.Wait();
+#endif
             if(desktop.active)
             {
                desktop.display.Lock(true);
@@ -784,7 +883,9 @@ public:
 
                desktop.display.Unlock();
             }
+#if !defined(__EMSCRIPTEN__)
             desktop.mutex.Release();
+#endif
          }
          else
          {
@@ -792,7 +893,9 @@ public:
 
             for(window = desktop.children.first; window; window = window.next)
             {
+#if !defined(__EMSCRIPTEN__)
                if(window.mutex) window.mutex.Wait();
+#endif
                if(window.visible && window.dirty)
                {
                   // Logf("Updating %s\n", window.name);
@@ -815,7 +918,9 @@ public:
                   usleep(1000000);
                   */
                }
+#if !defined(__EMSCRIPTEN__)
                if(window.mutex) window.mutex.Release();
+#endif
             }
          }
       }
@@ -823,7 +928,9 @@ public:
 
    void WaitEvent(void)
    {
+#if !defined(__EMSCRIPTEN__)
       globalSystem.eventSemaphore.Wait();
+#endif
    }
 
 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
@@ -842,7 +949,7 @@ public:
 
          PauseNetworkEvents();
          network.mutex.Wait();
-         
+
    #ifdef DEBUG_SOCKETS
          if(network.connectEvent || network.networkEvent)
             Log("[P] [NProcess]\n");
@@ -893,7 +1000,7 @@ public:
                      {
                         SOCKET s;
                         SOCKADDR_IN a;
-                        int addrLen = sizeof(a);
+                        SOCKLEN_TYPE addrLen = sizeof(a);
                         s = accept(service.s,(SOCKADDR *)&a,&addrLen);
                         closesocket(s);
                      }
@@ -925,7 +1032,7 @@ public:
                for(socket = network.connectSockets.first; socket; socket = next)
                {
                   next = socket.next;
-                  if(socket._connected  && socket._connected != -2)
+                  if(socket._connected && socket._connected != -2)
                   {
                      network.connectSockets.Remove(socket);
                      delete socket.connectThread;
@@ -943,7 +1050,8 @@ public:
                         if(socket.s == network.ns - 1)
                            Network_DetermineMaxSocket();
 
-                        socket.Free();
+                        socket._connected = 0;
+                        socket.Free(false);
                         delete socket;
                      }
                      else if(socket._connected == 1)
@@ -955,11 +1063,12 @@ public:
                         FD_SET(socket.s, &network.readSet);
                         FD_SET(socket.s, &network.exceptSet);
                         network.mutex.Release();
-                        
+
                         // printf("Calling OnConnect on %s\n", socket._class.name);
                         socket.OnConnect();
                         network.mutex.Wait();
-                        network.sockets.Add(socket);
+                        if(socket._connected)
+                           network.sockets.Add(socket);
                      }
                      gotEvent |= true;
                      goOn = true;
@@ -1036,15 +1145,17 @@ public:
 
    void SignalEvent(void)
    {
+#if !defined(__EMSCRIPTEN__)
       globalSystem.eventSemaphore.Release();
+#endif
    }
 
    // TODO: Might want to make this private with simpler public version?
-   bool SwitchMode(bool fullScreen, char * driverName, Resolution resolution, PixelFormat colorDepth, int refreshRate, char * skinName, bool fallBack)
+   bool SwitchMode(bool fullScreen, const char * driverName, Resolution resolution, PixelFormat colorDepth, int refreshRate, const char * skinName, bool fallBack)
    {
       bool result = false;
       OldLink link;
-      char * fbDriver;
+      const char * fbDriver;
       bool fbFullScreen = 0;
       Resolution fbResolution = 0;
       PixelFormat fbColorDepth = 0;
@@ -1055,7 +1166,7 @@ public:
       if(skinName)
       {
          OldLink link;
-         
+
          for(link = class(Skin).derivatives.first; link; link = link.next)
          {
             skin = link.data;
@@ -1082,7 +1193,7 @@ public:
          {
             bool foundDriver = false;
             int c, numDrivers;
-            char ** graphicsDrivers;
+            const char ** graphicsDrivers;
             inter = link.data;
 
             graphicsDrivers = inter.GraphicsDrivers(&numDrivers);
@@ -1101,12 +1212,12 @@ public:
                break;
          }
          if(!link)
-            inter = null;      
+            inter = null;
       }
-   
+
       /*
       if(driverName)
-      {   
+      {
 #if defined(__WIN32__)
 #if !defined(ECERE_VANILLA)
          if(!strcmp(driverName, "Win32Console")) inter = (subclass(Interface))class(Win32ConsoleInterface); else
@@ -1119,7 +1230,7 @@ public:
       }
       */
 
-      if(interfaceDriver && (!driverName || (fbDriver && !strcmp(fbDriver, driverName))) && 
+      if(interfaceDriver && (!driverName || (fbDriver && !strcmp(fbDriver, driverName))) &&
          fullScreen == fbFullScreen &&
          (!resolution || resolution == fbResolution) &&
          (!colorDepth || colorDepth == fbColorDepth) &&
@@ -1161,7 +1272,7 @@ public:
             {
                if(!fbDriver || (driverName && strcmp(fbDriver, driverName)))
                   defaultDisplayDriver = driverName;
-      
+
                if(!skinName || !SelectSkin(skinName))
                {
                   if(!currentSkin || currentSkin.textMode != textMode ||
@@ -1169,7 +1280,7 @@ public:
                   {
                      OldLink link;
                      subclass(Skin) skin = null;
-                     
+
                      for(link = class(Skin).derivatives.first; link; link = link.next)
                      {
                         skin = link.data;
@@ -1179,14 +1290,18 @@ public:
                      if(!link) skin = null;
 
                      if(skin)
+#if !defined(__ANDROID__)
                         SelectSkin(skin.name);
+#else
+                        currentSkin = skin;
+#endif
                   }
                }
 
                if(currentSkin && desktop.SetupDisplay())
                {
                   desktop.active = true;
-               
+
                   if(fullScreen)
                   {
                      desktop.display.Lock(false);
@@ -1217,18 +1332,21 @@ public:
       if(!result && fallBack && fbDriver)
       {
          if(!SwitchMode(fbFullScreen, fbDriver, fbResolution, fbColorDepth, fbRefreshRate, null, false))
-            Log("Error falling back to previous video mode.\n");
+            Log($"Error falling back to previous video mode.\n");
       }
       return result;
    }
 
    bool ProcessFileNotifications()
    {
+#if !defined(__EMSCRIPTEN__)
       bool activity = false;
       FileMonitor monitor, next;
       static int reentrant = 0;
-      
-      // printf("[%d] Waiting in ProcessFileNotifications for fileMonitor Mutex %x...\n", GetCurrentThreadID(), globalSystem.fileMonitorMutex);
+
+      // Reentrant FileNotification is asking for trouble since each monitor is spawning a Modal() MessageBox
+      if(reentrant) return false;
+      // printf("[%d] Waiting in ProcessFileNotifications for fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
       globalSystem.fileMonitorMutex.Wait();
       reentrant++;
       for(monitor = globalSystem.fileMonitors.first; monitor; monitor = next)
@@ -1237,7 +1355,9 @@ public:
 
          next = monitor.next;
          incref monitor;
-         
+         if(next)
+            incref next;
+
          if(!monitor.reentrant && !monitor.toBeFreed)
          {
             monitor.reentrant = true;
@@ -1267,6 +1387,10 @@ public:
             monitor.reentrant = false;
          }
          delete monitor;
+         if(next && next._refCount > 1)
+            next._refCount--;
+         else
+            delete next;
       }
       reentrant--;
       if(!reentrant)
@@ -1278,27 +1402,34 @@ public:
                monitor.FreeMonitor();
          }
       }
-      // printf("[%d] Releasing in ProcessFileNotifications fileMonitor Mutex %x...\n", GetCurrentThreadID(), globalSystem.fileMonitorMutex);
+      // printf("[%d] Releasing in ProcessFileNotifications fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
       globalSystem.fileMonitorMutex.Release();
       return activity;
+#else
+      return false;
+#endif
    }
 
    void Lock(void)
    {
+#if !defined(__EMSCRIPTEN__)
       lockMutex.Wait();
-#if defined(__unix__)
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
       if(xGlobalDisplay)
          XLockDisplay(xGlobalDisplay);
 #endif
+#endif
    }
 
    void Unlock(void)
    {
-#if defined(__unix__)
+#if !defined(__EMSCRIPTEN__)
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
       if(xGlobalDisplay)
          XUnlockDisplay(xGlobalDisplay);
 #endif
       lockMutex.Release();
+#endif
    }
 
    Cursor GetCursor(SystemCursor cursor)
@@ -1315,9 +1446,9 @@ public:
    {
       return interfaceDriver.GetMouseState(buttons, x, y);
    }
-   
+
    // Properties
-   property char * appName
+   property const char * appName
    {
       set
       {
@@ -1326,34 +1457,36 @@ public:
       }
       get
       {
-         return (char *)(this ? appName : null);
+         return (const char *)(this ? appName : null);
       }
    };
+#if !defined(__EMSCRIPTEN__)
    property Semaphore semaphore { get { return globalSystem.eventSemaphore; } };
+#endif
    property bool alwaysEmptyInput{ set { processAll = value; } get { return processAll; } };
    property bool fullScreen
    {
       set
       {
-         SwitchMode(value, defaultDisplayDriver, resolution, 
+         SwitchMode(value, defaultDisplayDriver, resolution,
             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
       }
       get { return this ? fullScreen : false; }
    };
-   property char * driver
+   property const char * driver
    {
       set
       {
-         SwitchMode( fullScreen, value, resolution, pixelFormat, refreshRate, 
+         SwitchMode( fullScreen, value, resolution, pixelFormat, refreshRate,
             currentSkin ? currentSkin.name : null, true);
-       } 
+       }
        get { return this ? defaultDisplayDriver : null; }
    };
    property Resolution resolution
    {
       set
       {
-         SwitchMode(fullScreen, defaultDisplayDriver, value, pixelFormat, refreshRate, 
+         SwitchMode(fullScreen, defaultDisplayDriver, value, pixelFormat, refreshRate,
             currentSkin ? currentSkin.name : null, true);
       }
       get { return this ? resolution : 0; }
@@ -1362,7 +1495,7 @@ public:
    {
       set
       {
-         SwitchMode(fullScreen, defaultDisplayDriver, resolution, 
+         SwitchMode(fullScreen, defaultDisplayDriver, resolution,
             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
       }
       get { return this ? pixelFormat : 0; }
@@ -1371,12 +1504,12 @@ public:
    {
       set
       {
-         SwitchMode(fullScreen, defaultDisplayDriver, resolution, 
+         SwitchMode(fullScreen, defaultDisplayDriver, resolution,
             pixelFormat, refreshRate, currentSkin ? currentSkin.name : null, true);
       }
       get { return this ? refreshRate : 0; }
    };
-   property char * skin
+   property const char * skin
    {
       set { SelectSkin(value); }
       get { return (this && currentSkin) ? currentSkin.name : null; }
@@ -1387,13 +1520,22 @@ public:
       get { return this ? textMode : false; }
    };
    property Window desktop { get { return this ? desktop : null; } };
-   property char ** drivers { get { return null; } };
-   property char ** skins { get { return null; } };
+   property const char ** drivers { get { return null; } };
+   property const char * const * skins { get { return null; } };
    property subclass(Skin) currentSkin { get { return this ? currentSkin : null; } };
    property int numDrivers { get { return 0; } };
    property int numSkins { get { return 0; } };
    property uint timerResolution
    {
-      set { timerResolution = value; if(interfaceDriver) interfaceDriver.SetTimerResolution(value); } 
+      set { timerResolution = value; if(interfaceDriver) interfaceDriver.SetTimerResolution(value); }
    };
 };
+
+#ifdef __EMSCRIPTEN__
+private void emscripten_main_loop_callback()
+{
+   guiApp.ProcessInput(false);
+   guiApp.Cycle(false);
+   guiApp.UpdateDisplay();
+}
+#endif