Install to Claude Code
npx -y skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-platform-desktopDescription
godot platform desktop
SKILL.md
---
name: godot-platform-desktop
description: "Expert blueprint for desktop platforms (Windows/Linux/macOS) covering keyboard/mouse controls, settings menus, window management (fullscreen, resolution), keybind remapping, and Steam integration. Use when targeting PC platforms or implementing desktop-specific features. Keywords desktop, Windows, Linux, macOS, settings, keybinds, ConfigFile, DisplayServer, Steam, fullscreen."
---
# Platform: Desktop
Settings flexibility, window management, and kb/mouse precision define desktop gaming.
## NEVER Do (Expert Desktop Rules)
### Window & Display
- **NEVER hardcode resolution or fullscreen modes** — A 1920x1080 fullscreen on a 4K monitor is blurry. Always provide a settings menu with a resolution dropdown and a mode toggle.
- **NEVER ignore DPI scale factors** — Manually centering windows without `DisplayServer.screen_get_scale()` results in incorrect positioning on HiDPI displays.
- **NEVER skip a borderless window option** — Exclusive fullscreen can break multi-monitor focus. Offer `WINDOW_MODE_FULLSCREEN` (borderless).
### Input & Persistence
- **NEVER use `keycode` for movement rebinds** — Use `physical_keycode` to ensure WASD works correctly across international keyboard layouts (AZERTY/Dvorak).
- **NEVER save settings or user data to `res://`** — Filesystem is read-only in exported releases. Always use `user://`.
- **NEVER skip `NOTIFICATION_WM_CLOSE_REQUEST`** — Failing to handle quit signals causes data loss. Intercept and flush and ConfigFile data before `get_tree().quit()`.
### Performance & Integration
- **NEVER run utility tools at max framerate** — Enable `OS.low_processor_usage_mode` to prevent high GPU heat in static desktop apps.
- **NEVER call proprietary SDKs (Steam/Epic) directly** — Always wrap in `Engine.has_singleton()` to prevent crashes in non-store builds.
- **NEVER block the main thread with massive I/O** — Deserializing 100MB+ configs stalls the engine. Offload to `WorkerThreadPool`.
---
## Godot 4.7: Desktop
- **HDR output** on Windows, macOS, and Linux (Wayland) — enable in Rendering → Viewport settings.
## Available Scripts
> **MANDATORY**: Read the appropriate script before implementing the corresponding pattern.
### [desktop_window_manager.gd](scripts/desktop_window_manager.gd)
Expert DPI-aware multi-monitor window positioning using `DisplayServer`.
### [desktop_settings_persistent.gd](scripts/desktop_settings_persistent.gd)
Production settings persistence using `ConfigFile` for persistent INI data.
### [physical_input_rebinder.gd](scripts/physical_input_rebinder.gd)
Expert positional rebind system using `physical_keycode` for AZERTY/Dvorak.
### [platform_sdk_wrapper.gd](scripts/platform_sdk_wrapper.gd)
Safe PC SDK singleton wrapper (Steamworks/Epic) with crash guards.
### [native_dialog_helper.gd](scripts/native_dialog_helper.gd)
Expert native OS file dialogs and system alerts logic.
### [secondary_window_spawner.gd](scripts/secondary_window_spawner.gd)
True multi-window management for secondary Viewports/Windows.
### [graceful_shutdown_handler.gd](scripts/graceful_shutdown_handler.gd)
Safe close-request interceptor for data flushing and exit guards.
### [low_processor_eco_mode.gd](scripts/low_processor_eco_mode.gd)
Eco mode optimization for desktop tools and launchers.
### [desktop_performance_monitor.gd](scripts/desktop_performance_monitor.gd)
OS-level hardware detection for dynamic graphics presets.
### [native_shell_executor.gd](scripts/native_shell_executor.gd)
Expert native shell command execution and output capture.
---
```gdscript
# settings.gd
extends Control
func _ready() -> void:
load_settings()
apply_settings()
func load_settings() -> void:
var config := ConfigFile.new()
config.load("user://settings.cfg")
$Graphics/ResolutionDropdown.selected = config.get_value("graphics", "resolution", 0)
$Graphics/FullscreenCheck.button_pressed = config.get_value("graphics", "fullscreen", false)
$Audio/MasterSlider.value = config.get_value("audio", "master_volume", 1.0)
func save_settings() -> void:
var config := ConfigFile.new()
config.set_value("graphics", "resolution", $Graphics/ResolutionDropdown.selected)
config.set_value("graphics", "fullscreen", $Graphics/FullscreenCheck.button_pressed)
config.set_value("audio", "master_volume", $Audio/MasterSlider.value)
config.save("user://settings.cfg")
func apply_settings() -> void:
# Resolution
var resolutions := [Vector2i(1920, 1080), Vector2i(2560, 1440), Vector2i(3840, 2160)]
var resolution := resolutions[$Graphics/ResolutionDropdown.selected]
get_window().size = resolution
# Fullscreen
if $Graphics/FullscreenCheck.button_pressed:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
else:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
# Audio
AudioServer.set_bus_volume_db(0, linear_to_db($Audio/MasterSlider.value))
```
## Keyboard Remapping
```gdscript
# Allow players to rebind keys
func rebind_action(action: String, new_key: Key) -> void:
# Remove existing
InputMap.action_erase_events(action)
# Add new
var event := InputEventKey.new()
event.keycode = new_key
InputMap.action_add_event(action, event)
# Save
save_input_map()
```
## Window Management
```gdscript
# Toggle fullscreen
func toggle_fullscreen() -> void:
if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
else:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
```
## Steam Integration (if using)
```gdscript
# Using GodotSteam plugin
var steam_id: int
func _ready() -> void:
if Steam.isSteamRunning():
steam_id = Steam.getSteamID()
Steam.achievement_progress.connect(_on_achievement_progress)
func unlock_achievement(name: String) -> void:
Steam.setAchievement(name)
Steam.storeStats()
```
## Best Practices
1. **Settings** - Extensive graphics/audio options
2. **Keybinds** - Allow remapping
3. **Alt+F4** - Support quit shortcuts
4. **Save Location** - Use `user://` directory
## Expert Techniques & Optimizations
### 1. Alt-Tab-Pause (Stuck Input Guard)
When a game loses focus, the OS may intercept "key up" events, leaving Godot's `Input` state stuck. Use `NOTIFICATION_APPLICATION_FOCUS_OUT` to pause the game and reset input logic.
```gdscript
func _notification(what: int) -> void:
match what:
NOTIFICATION_APPLICATION_FOCUS_OUT:
get_tree().paused = true
# Optional: Manually release critical actions
Input.action_release("move_forward")
NOTIFICATION_APPLICATION_FOCUS_IN:
get_tree().paused = false
```
### 2. Social Integrations (GDExtension)
For Discord-RPC or Vapor network pipes, use GDExtension to bind native shared libraries (.dll, .so, .dylib) to Godot at runtime. This keeps the engine core small while enabling expert-level social features.
```gdscript
# Logic assuming a 'SocialIntegration' GDExtension is loaded
func _init_social() -> void:
if ClassDB.class_exists("SocialIntegration"):
var social = ClassDB.instantiate("SocialIntegration")
social.set_activity("In Menu", "Level 1")
```
### 3. Desktop-Launcher Template
Use a dedicated Godot project as a lightweight launcher. It reads/writes `user://settings.cfg` and spawns the main game using `OS.create_process()`.
```gdscript
func _on_play_pressed() -> void:
var exe_path := OS.get_executable_path()
var args := PackedStringArray(["--main-pack", "game_data.pck"])
var pid := OS.create_process(exe_path, args)
if pid > 0:
get_tree().quit()
```
## Reference
- Related: `godot-export-builds`, `godot-save-load-systems`
### Related
- Master Skill: [godot-master](../godot-master/SKILL.md)
