Install
$ agentstack add skill-kyu-n-gdx-claude-skills-libgdx-lwjgl3-desktop ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
libGDX LWJGL3 Desktop Backend
Reference for the LWJGL3 desktop launcher, window configuration, fullscreen/windowed management, desktop lifecycle behavior, file access, and common gotchas.
LWJGL3 is the current desktop backend. The old LwjglApplication / LwjglApplicationConfiguration classes are the legacy LWJGL2 backend — do NOT use them. Always use Lwjgl3Application and Lwjgl3ApplicationConfiguration.
Launcher Class
The desktop launcher lives in the desktop module, not in core. It contains the main() method.
package com.example.game.lwjgl3;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import com.example.game.MyGame;
public class DesktopLauncher {
public static void main(String[] args) {
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setTitle("My Game");
config.setWindowedMode(1280, 720);
config.useVsync(true);
config.setForegroundFPS(60);
config.setWindowIcon("icons/icon128.png", "icons/icon32.png", "icons/icon16.png"); // JPEG, PNG, or BMP
config.setIdleFPS(15); // reduce CPU when unfocused/iconified
new Lwjgl3Application(new MyGame(), config);
}
}
Never put main() in the core module. Core is cross-platform; the launcher is platform-specific.
Configuration Quick Reference
| Method | Purpose | |---|---| | setTitle(String) | Window title bar text | | setWindowedMode(w, h) | Initial window size in pixels | | useVsync(boolean) | Sync frame rate to monitor refresh rate | | setForegroundFPS(int) | Software cap on render loop FPS | | setWindowIcon(String...) | Window/taskbar icons — JPEG, PNG, or BMP | | setIdleFPS(int) | Render rate when unfocused/iconified (default 60) | | setResizable(boolean) | Allow user to resize window | | setDecorated(boolean) | Show/hide title bar and borders | | setFullscreenMode(DisplayMode) | Start in fullscreen | | setBackBufferConfig(r,g,b,a,depth,stencil,samples) | Color/depth/stencil bits and MSAA | | pauseWhenLostFocus(boolean) | When true, fires pause()/resume() on focus loss/gain (not just iconify). Default: false | | setWindowListener(Lwjgl3WindowListener) | Window event callbacks (focus, iconify, file drop) |
setForegroundFPS() vs useVsync()
These are independent controls:
useVsync(true)— Syncs buffer swaps to the monitor's refresh rate (e.g., 60Hz). Eliminates screen tearing. Hardware-driven.setForegroundFPS(n)— Software cap: sleeps between frames to limit render() calls to n per second. Works independently of vsync.
Use both together, or either alone:
useVsync(true)+setForegroundFPS(60)— Vsync for smoothness, FPS cap as a safety netuseVsync(false)+setForegroundFPS(120)— No vsync, software-limited to 120fpsuseVsync(true)+setForegroundFPS(0)— Vsync only, no software cap
setForegroundFPS(0) means unlimited — the render loop spins as fast as possible, using 100% CPU. Always set a reasonable cap unless you have a specific reason not to.
setIdleFPS(n) — Controls the render rate when the window is unfocused or iconified. Default is 60. Since render() continues running even when the window is minimized, use setIdleFPS(15) or similar to reduce background CPU usage without stopping the loop entirely.
Back Buffer Configuration
setBackBufferConfig(r, g, b, a, depth, stencil, samples) controls the color depth, depth buffer, stencil buffer, and MSAA samples for the OpenGL context. The default config has no stencil buffer. If you use FrameBuffer with stencil operations, you must request stencil bits here — otherwise stencil writes silently fail:
// Request 8-bit stencil buffer (default has 0)
config.setBackBufferConfig(8, 8, 8, 8, 16, 8, 0);
// R G B A depth stencil MSAA
Fullscreen and Windowed Mode
Start in fullscreen:
config.setFullscreenMode(Lwjgl3ApplicationConfiguration.getDisplayMode());
Toggle at runtime:
// To fullscreen
Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
// To windowed
Gdx.graphics.setWindowedMode(1280, 720);
Monitor enumeration:
Graphics.Monitor[] monitors = Lwjgl3ApplicationConfiguration.getMonitors();
Graphics.DisplayMode[] modes = Lwjgl3ApplicationConfiguration.getDisplayModes(monitors[0]);
Desktop Lifecycle: pause() / resume()
| Event | What happens | |---|---| | Window minimized/iconified | pause() fires | | Window restored from taskbar | resume() fires | | Window loses focus (Alt+Tab) | Nothing — pause() does NOT fire | | Window closed | pause() → dispose() | | render() while iconified | Continues running |
Key difference from Android: On desktop, pause() fires ONLY on iconification (minimize), NOT on focus loss. The render loop keeps running even when the window is iconified or unfocused. To detect focus loss, use Lwjgl3WindowListener (see below). To receive pause()/resume() on focus loss (matching Android behavior), call config.pauseWhenLostFocus(true). This is false by default.
Window Listener
Lwjgl3WindowListener provides callbacks for window events that ApplicationListener doesn't cover. Register via config.setWindowListener(). Use Lwjgl3WindowAdapter to override only what you need:
config.setWindowListener(new Lwjgl3WindowAdapter() {
@Override public void focusLost() { paused = true; }
@Override public void focusGained() { paused = false; }
});
Available callbacks: focusLost(), focusGained(), iconified(boolean), maximized(boolean), filesDropped(String[]), closeRequested() (return false to cancel close).
filesDropped() enables drag-and-drop file support — a desktop-only feature useful for editors and tools. Do NOT use raw GLFW calls (e.g., glfwSetWindowFocusCallback) — use Lwjgl3WindowListener instead.
Multi-Window Support
LWJGL3 supports multiple windows — each gets its own ApplicationListener:
Lwjgl3Application app = (Lwjgl3Application) Gdx.app;
Lwjgl3WindowConfiguration windowConfig = new Lwjgl3WindowConfiguration();
windowConfig.setTitle("Debug Window");
windowConfig.setWindowedMode(400, 300);
app.newWindow(new MyDebugListener(), windowConfig);
All windows update on the same thread, sequentially. Gdx.graphics / Gdx.input point to whichever window is currently being updated. This is desktop-only — do not use in cross-platform core code.
File Access on Desktop
| Method | Reads from | Writable | Notes | |---|---|---|---| | Gdx.files.internal("x") | assets/ on classpath | No | Bundled game assets | | Gdx.files.local("x") | Working directory | Yes | Depends on where JVM launched from | | Gdx.files.external("x") | User home directory | Yes | ~/ on Linux/macOS, C:\Users\\ on Windows |
Working directory gotcha: Gdx.files.local() resolves relative to the JVM working directory. In IntelliJ or Gradle run configurations, you must explicitly set the working directory to the assets/ folder (or core project root) — otherwise local file paths will resolve to unexpected locations.
Clipboard
// Read
String text = Gdx.app.getClipboard().getContents();
// Write
Gdx.app.getClipboard().setContents("copied text");
Custom Cursor
// System cursor
Gdx.graphics.setSystemCursor(Graphics.Cursor.SystemCursor.Crosshair);
// Custom cursor from Pixmap
Pixmap pm = new Pixmap(Gdx.files.internal("cursor.png"));
Cursor cursor = Gdx.graphics.newCursor(pm, hotspotX, hotspotY);
Gdx.graphics.setCursor(cursor);
pm.dispose(); // Pixmap can be disposed after creating cursor
Common Mistakes
- Using
LwjglApplication/LwjglApplicationConfiguration— Those are the legacy LWJGL2 backend. Always useLwjgl3Application/Lwjgl3ApplicationConfiguration. - Putting
main()in the core module — The launcher class withmain()belongs in the desktop module. Core is cross-platform. - Assuming focus loss triggers
pause()— On LWJGL3 desktop, only iconification (minimize) triggerspause(). Alt+Tab / focus loss does nothing. - Setting
setForegroundFPS(0)without knowing the effect — Zero means unlimited. The CPU will spin at 100%. Always set a cap. - Expecting
Gdx.files.local()to find files in the assets folder —local()uses the working directory, which may not beassets/. Set the working directory in your IDE/Gradle run config, or useinternal()for read-only assets. - Assuming
render()stops when the window is minimized — On desktop,render()continues running even when iconified. Check pause state explicitly if you need to stop updates. - Using LWJGL2-era
DisplayorMonitorAPIs — UseLwjgl3ApplicationConfiguration.getMonitors()andgetDisplayModes()for monitor enumeration. - Not setting
setIdleFPS()and wondering why the game uses 100% CPU when minimized —render()keeps running atsetForegroundFPSrate even when iconified. UsesetIdleFPS(15)or similar to reduce background CPU usage. - Using raw GLFW calls or polling to detect focus loss instead of
Lwjgl3WindowListener— libGDX providesfocusLost()andfocusGained()callbacks viaLwjgl3WindowAdapter. Don't go behind the framework withGLFW.glfwSetWindowFocusCallback()or direct LWJGL3 imports.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kyu-n
- Source: kyu-n/gdx-claude-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.