🧠 “My Window Won't Center (and Other Fullscreen Mode Traumas)”
A real-life story about how graphics engines can mess up your desktop without asking permission.
🧩 Technical Context
You're developing your game in Leadwerks 5, you create a windowed mode with beautiful dimensions like 1280 x 720
, and you decide to center it stylishly using:
local displaySize = display:GetSize() local posX = displaySize.x / 2 - width / 2 local posY = displaySize.y / 2 - height / 2
So far, so good...
BUT...
🧨 When you decide to switch to fullscreen mode, everything goes haywire.
💥 What Happened Here?
When you activate fullscreen, you're not just removing the window border and maximizing the visible area.
No, sir.
In many engines (and Leadwerks is no exception), the operating system completely changes the monitor's resolution to match that of the game window.
Yes, you read that right:
“The entire system lowers its resolution to accommodate your game.”
For example:
If your desktop was 1920x1080
and your window is 1280x720
, then upon going fullscreen, your entire operating system is now at 1280x720.
🕳️ What Does This Imply?
Upon returning to windowed mode, if you call display:GetSize()
again, what you get is no longer the real desktop resolution, but the last forced monitor size.
Result:
🔄 Your window is created mispositioned, miscentered, everything's wrong.
🧪 Is This a Bug?
No, it's “expected behavior with dark side effects.”
Engines like SDL, Unity, or Unreal Engine also have to deal with this.
-
SDL recommends using
SDL_GetDisplayBounds()
to get the real resolution. -
Unity has separate APIs for “render resolution” and “display resolution”.
-
Unreal uses
GetDesktopResolution()
only at startup, never after fullscreen.
✅ The Solution: Cache It Like It's Gold!
We created a system that:
-
Detects the real desktop resolution the first time the game starts in windowed mode.
-
Saves it in the configuration file as:
desktop_resolution=1920x1080
3. Uses that value whenever we need to center the window, and never touches it again.
Additionally, we made a small local cache in Lua to avoid reading the file all the time.
💾 BONUS: Temporary Backup in Case Everything Explodes
We also created a system to save the current resolution in a temporary file before applying any screen changes.
So, if the player presses "CANCEL", we simply restore from the backup, and the universe is back in balance.
💙 Conclusion
-
⚠️ Don't trust
display:GetSize()
after fullscreen. -
🛡️ Save the real desktop resolution once, and protect it like it's your HUD's lifeline.
-
🤓 Understanding how graphics engines work at a low level saves you hours of yelling at the monitor.
-
😂 And remember: sometimes, the real bug... is the implicit logic the system didn't tell you about
-- Last Update 5 de Abril 2025 Window = {} Window.__index = Window -- Variable local para almacenar la resolución de escritorio de forma persistente local cachedDesktopResolution = nil function Window:New(title, x, y, width, height, display, style) local this = setmetatable({}, Window) local title = title or "Ventana" local x = x or 0 local y = y or 0 local display = display or GetDisplays()[1] -- Cargar configuración local settingsManager = SettingsManager:New() local settings = settingsManager:Load() -- Guardar backup temporal de resolución y fullscreen antes de crear la nueva ventana local tempSettings = { resolution = tostring(width) .. " x " .. tostring(height), fullscreen = settings["fullscreen"] or "0" } settingsManager:SaveTemporary(tempSettings) Print("📝 Backup temporal guardado: " .. tempSettings.resolution) -- Inicializar la resolución de escritorio solo una vez y guardarla en un caché local if not cachedDesktopResolution then local storedDesktopRes = settings["desktop_resolution"] if storedDesktopRes and storedDesktopRes:match("^(%d+)x(%d+)$") then local w, h = storedDesktopRes:match("(%d+)x(%d+)") cachedDesktopResolution = { x = tonumber(w), y = tonumber(h) } Print("🖥️ Resolución de escritorio cargada desde config: " .. storedDesktopRes) else local size = display:GetSize() cachedDesktopResolution = { x = size.x, y = size.y } settings["desktop_resolution"] = tostring(size.x) .. "x" .. tostring(size.y) settingsManager:Save(settings) Print("🖥️ Resolución de escritorio guardada por primera vez: " .. settings["desktop_resolution"]) end else Print("🖥️ Usando resolución de escritorio en caché: " .. tostring(cachedDesktopResolution.x) .. "x" .. tostring(cachedDesktopResolution.y)) end local savedDesktopW = cachedDesktopResolution.x local savedDesktopH = cachedDesktopResolution.y -- Cargar o asignar resolución de ventana if settings["resolution"] then local w, h = settings["resolution"]:match("(%d+)%s*x%s*(%d+)") if w and h then width = tonumber(w) height = tonumber(h) Print("📐 Resolución de ventana cargada desde config: " .. width .. "x" .. height) else width = width or 800 height = height or 600 end else width = width or 800 height = height or 600 end -- Determinar modo ventana o fullscreen local fullscreen = false local fs = settings["fullscreen"] if fs then fs = fs:lower() if fs == "true" or fs == "1" or fs == "yes" then fullscreen = true end end if fullscreen then style = WINDOW_FULLSCREEN Print("🖥️ Modo pantalla completa activado.") else style = WINDOW_TITLEBAR Print("🪟 Modo ventana activado.") end -- Calcular posición centrada local posX = savedDesktopW / 2 - (width / 2) local posY = savedDesktopH / 2 - (height / 2) local window = CreateWindow(title, posX, posY, width, height, display, style) local framebuffer = CreateFramebuffer(window) Print("[Window] Ventana creada con éxito: " .. title) -- Métodos públicos function this:Closed() return window:Closed() end function this:GetWindow() return window end function this:GetFramebuffer() return framebuffer end function this:KeyDown(key) return window:KeyDown(key) end function this:GetWidth() return width end function this:GetHeight() return height end function this:GetTitle() return title end function this:SetTitle(newTitle) title = newTitle window:SetTitle(newTitle) end function this:Destroy() window = nil framebuffer = nil if self.settings then self.settings:Destroy() self.settings = nil end Print("[Window] 🧹 Recursos liberados correctamente.") end return this end -- 💾 Método estático para restaurar resolución desde el archivo temporal function Window.RestoreFromTemp() local sm = SettingsManager:New() local temp = sm:LoadTemporary() if temp and temp["resolution"] then local w, h = temp["resolution"]:match("(%d+)%s*x%s*(%d+)") if w and h then local fs = temp["fullscreen"] if fs == "1" or fs == "true" or fs == "yes" then fs = true else fs = false end Print("🔁 Restaurando resolución temporal: " .. w .. "x" .. h .. " | fullscreen: " .. tostring(fs)) return tonumber(w), tonumber(h), fs end end Print("⚠️ No se pudo restaurar resolución: archivo temporal no encontrado o malformado.") return nil end return Window
-
2
0 Comments
Recommended Comments
There are no comments to display.