diff options
| author | Mitchell Hashimoto <m@mitchellh.com> | 2025-08-21 10:13:38 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-21 10:13:38 -0700 |
| commit | d20376c1ff7e9490e34499fa59c54bbdd68f4ebf (patch) | |
| tree | a831a94498e6adc295601c2533726b4622f3c4c4 | |
| parent | 66e5081721e4e2158343a4b239b7ffc09dc8cbf2 (diff) | |
| parent | f178f4419e1815d514b267c7bc890a4f2c91e9ec (diff) | |
macOS Custom Icon + Persistence (#8230)
This PR aims to improve custom icons on macOS in the following ways. (I
based this PR on the discussion #3631)
### Currently
- Current Icon customizations are not persistent *(when closing the
application the icon in dock reverts back to official icon)*
- There is no officially supported way to change icon to be something
completely custom.
### After this PR
- Current icon customizations are persistent (closing the application no
longer reverts back to official icon)
- Ghostty config `macos-icon` has a new option `custom` which by default
looks for icon `~/.config/ghostty/Ghostty.icns`. It has an accompanying
new configuration `macos-custom-icon` which allows for a different path
to be specified, it does support more than just `.icns` as well.
Both changes are based on the thread with @sfsam in
https://github.com/ghostty-org/ghostty/discussions/3631#discussioncomment-12180647
Feedback is always welcome, if I have not done something up to par
please let me know and I will do my best to correct it.
NOTE: I did notice some newlines with indents which seems to be against
convention in those files so I removed the whitespace if this is not
preferred I can revert.
---
P.S. Thanks for all the work you put into making an awesome terminal!
| -rw-r--r-- | macos/Sources/App/macOS/AppDelegate.swift | 14 | ||||
| -rw-r--r-- | macos/Sources/Ghostty/Ghostty.Config.swift | 20 | ||||
| -rw-r--r-- | macos/Sources/Ghostty/Package.swift | 1 | ||||
| -rw-r--r-- | src/config/Config.zig | 12 |
4 files changed, 44 insertions, 3 deletions
diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 5940547b5..cbd3668c5 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -119,6 +119,9 @@ class AppDelegate: NSObject, @Published private(set) var appIcon: NSImage? = nil { didSet { NSApplication.shared.applicationIconImage = appIcon + let appPath = Bundle.main.bundlePath + NSWorkspace.shared.setIcon(appIcon, forFile: appPath, options: []) + NSWorkspace.shared.noteFileSystemChanged(appPath) } } @@ -255,13 +258,13 @@ class AppDelegate: NSObject, // Setup signal handlers setupSignals() - + // If we launched via zig run then we need to force foreground. if Ghostty.launchSource == .zig_run { // This never gets called until we click the dock icon. This forces it // activate immediately. applicationDidBecomeActive(.init(name: NSApplication.didBecomeActiveNotification)) - + // We run in the background, this forces us to the front. DispatchQueue.main.async { NSApp.setActivationPolicy(.regular) @@ -834,6 +837,13 @@ class AppDelegate: NSObject, case .xray: self.appIcon = NSImage(named: "XrayImage")! + case .custom: + if let userIcon = NSImage(contentsOfFile: config.macosCustomIcon) { + self.appIcon = userIcon + } else { + self.appIcon = nil // Revert back to official icon if invalid location + } + case .customStyle: guard let ghostColor = config.macosIconGhostColor else { break } guard let screenColors = config.macosIconScreenColor else { break } diff --git a/macos/Sources/Ghostty/Ghostty.Config.swift b/macos/Sources/Ghostty/Ghostty.Config.swift index 241c10632..a223b2a0a 100644 --- a/macos/Sources/Ghostty/Ghostty.Config.swift +++ b/macos/Sources/Ghostty/Ghostty.Config.swift @@ -164,7 +164,7 @@ extension Ghostty { let key = "window-position-x" return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil } - + var windowPositionY: Int16? { guard let config = self.config else { return nil } var v: Int16 = 0 @@ -301,6 +301,24 @@ extension Ghostty { return MacOSIcon(rawValue: str) ?? defaultValue } + var macosCustomIcon: String { + #if os(macOS) + let homeDirURL = FileManager.default.homeDirectoryForCurrentUser + let ghosttyConfigIconPath = homeDirURL.appendingPathComponent( + ".config/ghostty/Ghostty.icns", + conformingTo: .fileURL).path() + let defaultValue = ghosttyConfigIconPath + guard let config = self.config else { return defaultValue } + var v: UnsafePointer<Int8>? = nil + let key = "macos-custom-icon" + guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue } + guard let ptr = v else { return defaultValue } + return String(cString: ptr) + #else + return "" + #endif + } + var macosIconFrame: MacOSIconFrame { let defaultValue = MacOSIconFrame.aluminum guard let config = self.config else { return defaultValue } diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/Package.swift index 9b05934df..73487f1bd 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/Package.swift @@ -280,6 +280,7 @@ extension Ghostty { case paper case retro case xray + case custom case customStyle = "custom-style" } diff --git a/src/config/Config.zig b/src/config/Config.zig index 5ac2d6617..178b9f851 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -2735,6 +2735,8 @@ keybind: Keybinds = .{}, /// * `blueprint`, `chalkboard`, `microchip`, `glass`, `holographic`, /// `paper`, `retro`, `xray` - Official variants of the Ghostty icon /// hand-created by artists (no AI). +/// * `custom` - Use a completely custom icon. The location must be specified +/// using the additional `macos-custom-icon` configuration /// * `custom-style` - Use the official Ghostty icon but with custom /// styles applied to various layers. The custom styles must be /// specified using the additional `macos-icon`-prefixed configurations. @@ -2753,6 +2755,15 @@ keybind: Keybinds = .{}, /// effort. @"macos-icon": MacAppIcon = .official, +/// The absolute path to the custom icon file. +/// Supported formats include PNG, JPEG, and ICNS. +/// +/// Defaults to `~/.config/ghostty/Ghostty.icns` +/// +/// Note: This configuration is required when `macos-icon` is set to +/// `custom` +@"macos-custom-icon": ?[]const u8 = null, + /// The material to use for the frame of the macOS app icon. /// /// Valid values: @@ -6975,6 +6986,7 @@ pub const MacAppIcon = enum { paper, retro, xray, + custom, @"custom-style", }; |
