namespace gui; #if defined(__unix__) || defined(__APPLE__) #define property _property #define new _new #define class _class #define uint _uint #define Window X11Window #define Cursor X11Cursor #define Font X11Font #define Display X11Display #define Time X11Time #define KeyCode X11KeyCode #define Picture X11Picture #include #undef Window #undef Cursor #undef Font #undef Display #undef Time #undef KeyCode #undef Picture #undef uint #undef new #undef property #undef class #endif #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET) #if defined(__WIN32__) #define WIN32_LEAN_AND_MEAN #include static WSADATA wsaData; #elif defined(__unix__) || defined(__APPLE__) default: #define uint _uint #include #include #include #include #include #include #include #include #include private: #undef uint typedef int SOCKET; typedef struct hostent HOSTENT; typedef struct sockaddr SOCKADDR; typedef struct sockaddr_in SOCKADDR_IN; typedef struct in_addr IN_ADDR; #define closesocket(s) close(s) #endif import "network" #endif #if defined(__APPLE__) import "CocoaInterface" #elif defined(__unix__) import "XInterface" #endif import "Window" GuiApplication guiApp; int terminateX; enum GuiErrorCode : ErrorCode { driverNotSupported = ErrorCode { VeryFatal, 1 }, windowCreationFailed = ErrorCode { VeryFatal, 2 }, graphicsLoadingFailed = ErrorCode { VeryFatal, 3 }, modeSwitchFailed = ErrorCode { VeryFatal, 4 } }; static Array 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; char ** driverNames; int numSkins; char ** skinNames; bool textMode; subclass(Interface) interfaceDriver; subclass(Skin) currentSkin; // Desktop window Window desktop; // Screen mode flags bool modeSwitching; bool fullScreenMode; // Needs to start at true for the desktop to resize bool fullScreen; Resolution resolution; PixelFormat pixelFormat; int refreshRate; char * defaultDisplayDriver; Cursor systemCursors[SystemCursor]; bool cursorUpdate; OldList customCursors; // Window Timers OldList windowTimers; // Mouse events Window prevWindow; // Used for OnMouseLeave Window windowCaptured; // Mouse based moving & resizing Window windowMoving; Point windowMovingStart; Point windowMovingBefore; Size windowResizingBefore; Point movingLast; bool windowIsResizing; bool resizeX, resizeEndX; bool resizeY, resizeEndY; // Mouse based scrolling Window windowScrolling; Point windowScrollingBefore, windowScrollingStart; // Mouse cursors Bitmap cursorBackground { }; int cursorBackgroundX, cursorBackgroundY; int cursorBackgroundW, cursorBackgroundH; // Carets Window caretOwner; // State based input Window acquiredWindow; int acquiredMouseX, acquiredMouseY; Cursor currentCursor; uint errorLevel, lastErrorCode; bool processAll; Mutex waitMutex {}; bool waiting; Mutex lockMutex {}; Window interimWindow; bool caretEnabled; char appName[1024]; uint timerResolution; Size virtualScreen; Point virtualScreenPos; int mainThread; GuiApplication() { SystemCursor c; mainThread = GetCurrentThreadID(); if(!guiApp) guiApp = this; strcpy(appName, $"ECERE Application"); processAll = true; // TODO: // customCursors.offset = OFFSET(Cursor, prev); windowTimers.offset = (uint)&((Timer)0).prev; for(c = 0; cbitmap) { int mouseX, mouseY; Surface surface; Box against = {0,0, desktop.w-1,desktop.h-1}; Box box = {0, 0, currentCursor->bitmap->width,currentCursor->bitmap->height}; interfaceDriver->GetMousePosition(&mouseX, &mouseY); mouseX -= currentCursor->hotSpotX; mouseY -= currentCursor->hotSpotY; // Preserve Background if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING)) { cursorBackgroundX = mouseX; cursorBackgroundY = mouseY; cursorBackgroundW = currentCursor->bitmap->width; cursorBackgroundH = currentCursor->bitmap->height; eDisplay_Grab(desktop.display, cursorBackground, mouseX, mouseY, cursorBackgroundW, cursorBackgroundH); } eBox_ClipOffset(&box, &against, mouseX, mouseY); if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING)) eDisplay_StartUpdate(desktop.display); // Display Cursor surface = eDisplay_GetSurface(desktop.display, mouseX, mouseY, &box); if(surface) { eSurface_SetForeground(surface, WHITE); eSurface_Blit(surface, currentCursor->bitmap, 0,0, 0,0, currentCursor->bitmap->width,currentCursor->bitmap->height); eInstance_Delete(surface); if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING)) { box.left += mouseX; box.right += mouseX; box.top += mouseY; box.bottom += mouseY; eDisplay_Update(desktop.display, &box); } } if(!(eDisplay_GetFlags(desktop.display) & DISPLAY_FLIPPING)) eDisplay_EndUpdate(desktop.display); } */ } void RestoreCursorBackground() { /* // Restore Cursor Background if(cursorBackground && desktop.active) { Box box = {0, 0, cursorBackgroundW-1,cursorBackgroundH-1}; Box against = {0,0, desktop.w-1,desktop.h-1}; Surface surface; eBox_ClipOffset(&box, &against, cursorBackgroundX, cursorBackgroundY); if((surface = eDisplay_GetSurface(desktop.display, cursorBackgroundX,cursorBackgroundY, &box))) { eSurface_Blit(surface, cursorBackground, 0, 0, 0,0, cursorBackgroundW,cursorBackgroundH); eInstance_Delete(surface); } } */ } bool IsModeSwitching() { return modeSwitching; } public bool SetDesktopPosition(int x, int y, int w, int h, bool moveChildren) { bool result = true; bool windowResized = desktop.size.w != w || desktop.size.h != h; bool windowMoved = desktop.clientStart.x != x || desktop.clientStart.y != y; if((windowResized || windowMoved) && moveChildren) { Window child; desktop.Position(x, y, w, h, true, true, true, true, false, false); // Maximized native decorations windows suffer when we drag the dock around, so remaximize them // It's a little jumpy, but oh well. for(child = desktop.children.first; child; child = child.next) { if(child.nativeDecorations && child.rootWindow == child && child.state == maximized) { child.state = normal; child.state = maximized; } } /*for(child = desktop.children.first; child; child = child.next) { if(!child.systemParent) { if(child.anchored) { int x, y, w, h; child.ComputeAnchors( child.ax, child.ay, child.aw, child.ah, &x, &y, &w, &h); child.Position(x, y, w, h, true, true, true, true, false); } if(child.state == Maximized) { child.x = desktop.x; child.y = desktop.y; 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) { desktop.display.Lock(true); if(windowResized) { if(!desktop.display.Resize(desktop.size.w, desktop.size.h)) result = false; desktop.dirty = true; if(!desktop.display.flags.flipping) desktop.Update(null); } // When does the desktop have a display in not fullscreen mode? if(!fullScreenMode && !modeSwitching) desktop.UpdateDisplay(); desktop.display.Unlock(); } } else desktop.SetPosition(x, y, w, h, false, false, false); return result; } void SetAppFocus(bool state) { // Shouldn't be property here desktop.active = state; } bool SelectSkin(char * skinName) { bool result = false; subclass(Skin) skin; OldLink link; for(link = class(Skin).derivatives.first; link; link = link.next) { skin = link.data; if(skin.name && !strcmp(skin.name, skinName)) break; } if(!link) skin = null; if(skin) { if(skin != currentSkin || !currentSkin) { // Try finding a driver to support this mode if(skin.textMode != textMode) { return false; } else { bool needReload = false; if(!modeSwitching && currentSkin) { modeSwitching = true; desktop.UnloadGraphics(true); needReload = true; } UnapplySkin(class(Window)); currentSkin = skin; ApplySkin(class(Window), skin.name, null); if(needReload) { if(desktop.SetupDisplay()) if(desktop.LoadGraphics(false, true)) result = true; modeSwitching = false; } else result = true; } } else result = true; } return result; } void Initialize(bool switchMode) { static bool initialized = false; // TODO: // if(!initialized && eClass_IsDerived(__ecereModule->app->module.inst.class, guiApplicationClass)) if(!initialized) { char * defaultDriver = null; #if defined(ECERE_VANILLA) char * driver = null; #else // char * driver = getenv("ECERE_DRIVER"); char * driver = null; static char driverStorage[1024]; GetEnvironment("ECERE_DRIVER", driverStorage, sizeof(driverStorage)); if(driverStorage[0]) driver = driverStorage; #endif initialized = true; fullScreenMode = true; // Needs to start at true for the desktop to resize // Set this to true earlier so we can override it! //processAll = true; errorLevel = 2; lockMutex.Wait(); #if defined(__unix__) if(xGlobalDisplay) XLockDisplay(xGlobalDisplay); #endif // Setup Desktop if(!desktop) { desktop = Window { }; incref desktop; incref desktop; desktop.childrenOrder.circ = true; desktop.childrenCycle.circ = true; desktop.background = blue; desktop.rootWindow = desktop; desktop.cursor = GetCursor(arrow); desktop.caption = new char[strlen(appName) + 1]; strcpy(desktop.caption, appName); *&desktop.visible = true; desktop.position = Point { }; desktop.mutex = Mutex { }; desktop.created = true; } #if defined(__WIN32__) { if(driver) defaultDriver = driver; else if(this.isGUIApp && !textMode) defaultDriver = "GDI"; else defaultDriver = "Win32Console"; } #elif defined(__APPLE__) { if (driver) { defaultDriver = driver; } else { defaultDriver = "CocoaOpenGL"; } } #else if(this.isGUIApp && !textMode) { char * display = getenv("DISPLAY"); if(!display || !display[0] || !SwitchMode(false, "X", 0, 0, 0, null, true)) defaultDriver = "NCurses"; // SwitchMode(true, "NCurses", 0, PixelFormatText, 0, null, true); else if(!driver) defaultDriver = "X"; else defaultDriver = driver; } else defaultDriver = "NCurses"; #endif if(switchMode) { if(defaultDriver) SwitchMode(false, defaultDriver, 0, 0, 0, null, true); else { /* #if defined(__WIN32__) SwitchMode(true, "Win32Console", 0, PixelFormatText, 0, null, true); #endif */ #if defined(__DOS__) SwitchMode(true, "SVGA", Res640x480, PixelFormat8, 0, null, true); #endif #if defined(__APPLE__) SwitchMode(true, "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; } else defaultDisplayDriver = defaultDriver; } } public: virtual bool Init(void); virtual bool Cycle(bool idle); virtual void Terminate(void); void Main(void) { Window window; if(Init()) { if(desktop) { // better solution when designing tab order/activated window etc, why do windows move in the list? while(true) { for(window = desktop.children.first; window; window = window.next) { if(window.autoCreate && !window.created) { if(window.Create()) break; } } if(!window) break; } } if(desktop) { int terminated = 0; incref desktop; while(desktop && interfaceDriver) { bool wait; Window child; if(terminateX != terminated) { terminated = terminateX; desktop.Destroy(0); if(desktop.created) { terminated = 0; terminateX = 0; //printf("Resetting terminate X to 0\n"); } } for(child = desktop.children.first; child; child = child.next) if(child.created && child.visible) break; if(!child) break; for(window = desktop.children.first; window; window = window.next) if(window.mutex) window.mutex.Wait(); UpdateDisplay(); for(window = desktop.children.first; window; window = window.next) if(window.mutex) window.mutex.Release(); wait = !ProcessInput(true); #ifdef _DEBUG if(lockMutex.owningThread != GetCurrentThreadID()) PrintLn("WARNING: ProcessInput returned unlocked GUI!"); #endif if(!Cycle(wait)) wait = false; if(wait) Wait(); else { #if defined(__unix__) if(xGlobalDisplay) XUnlockDisplay(xGlobalDisplay); #endif lockMutex.Release(); lockMutex.Wait(); #if defined(__unix__) if(xGlobalDisplay) XLockDisplay(xGlobalDisplay); #endif } } eInstance_DecRef(desktop); } } Terminate(); } void Wait(void) { #if defined(__unix__) if(xGlobalDisplay) XUnlockDisplay(xGlobalDisplay); #endif lockMutex.Release(); waitMutex.Wait(); waiting = true; if(interfaceDriver) interfaceDriver.Wait(); waiting = false; waitMutex.Release(); lockMutex.Wait(); #if defined(__unix__) if(xGlobalDisplay) XLockDisplay(xGlobalDisplay); #endif } bool ProcessInput(bool useProcessAll) { if(interfaceDriver) { bool result = 0; #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET) ProcessNetworkEvents(); #endif /* result = interfaceDriver.ProcessInput(useProcessAll && processAll); if(!desktop || !interfaceDriver) return; { bool wait; Window child; for(child = app.desktop.children.first; child; child = child.next) if(child.created && child.visible) break; if(!child) return result; } result |= UpdateTimers(); result |= ProcessFileNotifications(); */ result |= ProcessFileNotifications(); result |= UpdateTimers(); result |= interfaceDriver.ProcessInput(useProcessAll && processAll); return result; } return false; } void UpdateDisplay(void) { #if !defined(ECERE_VANILLA) && !defined(ECERE_NO3D) if(Desktop3DUpdateDisplay()) return; #endif if(interfaceDriver) { if(fullScreenMode && desktop.display) { desktop.mutex.Wait(); if(desktop.active) { desktop.display.Lock(true); if(desktop.dirty || cursorUpdate) { if(desktop.display.flags.flipping) desktop.Update(null); desktop.UpdateDisplay(); cursorUpdate = true; } if(cursorUpdate || desktop.dirty) { PreserveAndDrawCursor(); cursorUpdate = false; desktop.dirty = false; RestoreCursorBackground(); } desktop.display.Unlock(); } desktop.mutex.Release(); } else { Window window; for(window = desktop.children.first; window; window = window.next) { if(window.mutex) window.mutex.Wait(); if(window.visible && window.dirty) { // Logf("Updating %s\n", window.name); interfaceDriver.Lock(window); if(window.display) { if(window.display.current) { printf("bug"); } window.display.Lock(true); window.UpdateDisplay(); window.display.Unlock(); } window.dirty = false; interfaceDriver.Unlock(window); /* Log("--------------\n"); usleep(1000000); */ } if(window.mutex) window.mutex.Release(); } } } } void WaitEvent(void) { globalSystem.eventSemaphore.Wait(); } #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET) bool ProcessNetworkEvents() { bool gotEvent = false; #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__) if(network.networkInitialized) { Service service; Socket socket, next; struct timeval tv = {0, 0}; fd_set rs, ws, es; Service nextService; OldLink semPtr; PauseNetworkEvents(); network.mutex.Wait(); #ifdef DEBUG_SOCKETS if(network.connectEvent || network.networkEvent) Log("[P] [NProcess]\n"); #endif rs = network.readSet; ws = network.writeSet; es = network.exceptSet; if((network.ns && select(network.ns, &rs, &ws, &es, &tv)) || network.leftOverBytes) { network.leftOverBytes = false; // Danger here? Why looping with a next and not unlocking anything? for(socket = network.connectSockets.first; socket; socket = next) { next = socket.next; if(!socket.processAlone && FD_ISSET(socket.s, &ws)) { network.mutex.Release(); socket.connectThread.Wait(); network.mutex.Wait(); } } for(socket = network.sockets.first; socket; socket = next) { next = socket.next; if(!socket.processAlone) { network.mutex.Release(); gotEvent |= socket.ProcessSocket(&rs, &ws, &es); network.mutex.Wait(); } } for(service = network.services.first; service; service = nextService) { nextService = service.next; if(!service.processAlone) { if(FD_ISSET(service.s, &rs)) { #ifdef DEBUG_SOCKETS Logf("[P] Accepting connection (%x)\n", service.s); #endif service.accepted = false; service.OnAccept(); if(!service.accepted) { SOCKET s; SOCKADDR_IN a; int addrLen = sizeof(a); s = accept(service.s,(SOCKADDR *)&a,&addrLen); closesocket(s); } gotEvent |= true; #ifdef DEBUG_SOCKETS Log("[P] Connection accepted\n"); #endif } } for(socket = service.sockets.first; socket; socket = next) { next = socket.next; if(!socket.processAlone) { network.mutex.Release(); gotEvent |= socket.ProcessSocket(&rs, &ws, &es); network.mutex.Wait(); } } } } if(network.connectEvent) { bool goOn = true; while(goOn) { goOn = false; for(socket = network.connectSockets.first; socket; socket = next) { next = socket.next; if(socket._connected && socket._connected != -2) { network.connectSockets.Remove(socket); delete socket.connectThread; // printf("%s is connected = %d\n", socket._class.name, socket._connected); if(socket._connected == -1) { #ifdef DEBUG_SOCKETS Logf("[P] Processing disconnected connect (%x)\n", socket.s); #endif #if 0 if(socket.disconnectCode == ResolveFailed) Logf("Error resolving address %s\n", socket.address); #endif if(socket.s == network.ns - 1) Network_DetermineMaxSocket(); socket.Free(false); delete socket; } else if(socket._connected == 1) { #ifdef DEBUG_SOCKETS Log("[P] Processing connected connect\n"); #endif FD_CLR(socket.s, &network.writeSet); 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); } gotEvent |= true; goOn = true; break; } } } network.connectEvent = false; } if(network.networkEvent) { network.networkEvent = false; network.selectSemaphore.Release(); } if(gotEvent) { for(semPtr = network.mtSemaphores.first; semPtr; semPtr = semPtr.next) { ((Semaphore)semPtr.data).Release(); } } network.mutex.Release(); ResumeNetworkEvents(); } #endif return gotEvent; } void WaitNetworkEvent() { if(network.networkInitialized) { if(GetCurrentThreadID() == network.mainThreadID) { WaitEvent(); } else { Semaphore semaphore { }; OldLink semPtr { data = semaphore }; network.mutex.Wait(); network.mtSemaphores.Add(semPtr); network.mutex.Release(); ResumeNetworkEvents(); semaphore.Wait(); PauseNetworkEvents(); network.mutex.Wait(); network.mtSemaphores.Delete(semPtr); network.mutex.Release(); delete semaphore; } } } void PauseNetworkEvents() { if(network.networkInitialized) { network.processMutex.Wait(); } } void ResumeNetworkEvents() { if(network.networkInitialized) { network.processMutex.Release(); } } #endif void SignalEvent(void) { globalSystem.eventSemaphore.Release(); } // 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 result = false; OldLink link; char * fbDriver; bool fbFullScreen = 0; Resolution fbResolution = 0; PixelFormat fbColorDepth = 0; int fbRefreshRate = 0; subclass(Interface) inter; subclass(Skin) skin = null; if(skinName) { OldLink link; for(link = class(Skin).derivatives.first; link; link = link.next) { skin = link.data; if(skin.name && !strcmp(skin.name, skinName)) break; } if(!link) skin = null; } Initialize(false); fbDriver = defaultDisplayDriver; inter = interfaceDriver; if(interfaceDriver) interfaceDriver.GetCurrentMode(&fbFullScreen, &fbResolution, &fbColorDepth, &fbRefreshRate); if(!driverName && !interfaceDriver) driverName = defaultDisplayDriver; if(driverName || (skin && skin.textMode != textMode)) { for(link = class(Interface).derivatives.first; link; link = link.next) { bool foundDriver = false; int c, numDrivers; char ** graphicsDrivers; inter = link.data; graphicsDrivers = inter.GraphicsDrivers(&numDrivers); for(c=0; c