ludic/docs/language/app/app-set_icon.md
Orkuncakilkaya 38b6b81cd7 feat(app): App.set_icon, so a game without a bundle still has an icon
`ludic bundle` builds an AppIcon.icns and macOS reads it out of the .app, so a
shipped game has an icon. `ludic build` produces a bare executable: no bundle, no
CFBundleIconFile, and so no icon at all - macOS draws the generic green "exec"
tile. That is the build a developer runs every day, which is why "the game has no
icon" can be true for months while the bundle is perfect. It was here: the .app's
icns validated against iconutil, the plist was right, the signature was right,
and NSWorkspace rendered the artwork - and the binary beside it still had the
exec tile.

App.set_icon(path) takes the bytes through lp_pak_open, so a packed path and a
loose one both work and this does not repeat Audio.load's trick of taking a
filesystem path only. NSData copies them, so the buffer goes straight back. A
missing or undecodable image leaves the existing icon alone rather than clearing
it; a bundled app is unaffected; headless links no AppKit and compiles it away.

Verified the Cocoa sequence against an ObjC twin doing the same message sends:
before, a bare binary's applicationIconImage is the generic 128x128 tile; after,
it is the 1024x1024 artwork.

Backend change, so selfhost/ludicc.seed.ll is reseeded: the bootstrap fixpoint
and the C-free rebuild from the seed both pass.
2026-09-12 12:55:12 +03:00

1.3 KiB

id name category kind tokens sig tip order ns member
app-set_icon App.set_icon app namespace-method App.set_icon App.set_icon(path) -> void Set the Dock icon from an image the game ships. 2 App set_icon

Sets the application's icon — the Dock tile, the Cmd-Tab switcher, the About box — from an image the game ships.

A bundled app already has one: ludic bundle builds AppIcon.icns from the manifest's app icon and macOS reads it from the bundle, so calling this there is harmless and changes nothing you can see. A plain binary is the case this exists for. ludic build produces an executable with no bundle around it, so macOS has nowhere to find an icon and draws the generic one — which is what a developer looks at every time they run the game.

The path is resolved the same way every other asset is, through the pack first and the filesystem second, so the same call works in a packed build and in a project directory. An image that cannot be found or decoded leaves the current icon alone rather than clearing it.

Parameters:

  • path — an image the game ships (PNG, JPEG or TIFF); 1024x1024 is the size macOS wants
program Titled {
  entry {
    App.set_icon("assets/app/icon.png")
  }
}

Headless builds have no AppKit, so this compiles to nothing there.