Documentation
¶
Overview ¶
Package launch provides application launching strategies for macOS app bundles.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Config ¶
type Config struct {
// AppName is the name of the application
AppName string
// BundleID is the bundle identifier
BundleID string
// Permissions are the requested macOS permissions (as strings)
Permissions []string
// Debug enables debug logging
Debug bool
// ForceLaunchServices forces use of LaunchServices
ForceLaunchServices bool
// ForceDirectExecution forces direct execution
ForceDirectExecution bool
// Background indicates the app should not steal focus (LSBackgroundOnly apps)
Background bool
// SingleProcess enables single-process mode via codesign + re-exec + setActivationPolicy.
SingleProcess bool
// Entitlements are the entitlement keys to sign the binary with (for transform mode).
Entitlements []string
// UIMode controls how the transformed app appears: "regular", "accessory", or "background".
UIMode string
// IconPath is the path to an .icns file for the Dock icon (transform mode, regular UI only).
IconPath string
}
Config contains the launch-specific configuration extracted from the main Config. This avoids importing the main package and keeps the launch package focused.
type DirectLauncher ¶
type DirectLauncher struct{}
DirectLauncher implements direct execution of the binary within the bundle.
type IOWrapper ¶
type IOWrapper struct {
// contains filtered or unexported fields
}
IOWrapper wraps IO streams to add prefixes, indentation, or styling
type Launcher ¶
type Launcher interface {
// Launch executes the application using the appropriate strategy.
Launch(ctx context.Context, bundlePath, execPath string, cfg *Config) error
}
Launcher defines the interface for launching applications.
type Logger ¶
Logger wraps slog.Logger with macgo-specific configuration
func NewLogger ¶
func NewLogger() *Logger
NewLogger creates a configured logger based on environment variables
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager coordinates different launch strategies.
func NewWithLaunchers ¶
NewWithLaunchers creates a new launch manager with custom launchers.
type ServicesLauncher ¶
type ServicesLauncher struct {
// contains filtered or unexported fields
}
ServicesLauncher implements launching via LaunchServices using the 'open' command.
Signal Handling Architecture ¶
When launching apps via LaunchServices, the child process is adopted by launchd (PPID=1) and is NOT in the parent's process group. This means terminal signals like Ctrl+C (SIGINT) only reach the parent wrapper, not the child.
To ensure proper signal handling, we implement the following:
PID Tracking: Child writes its PID to a control FIFO in the pipe directory after startup. Parent reads the PID (blocking, no polling) for signal forwarding.
Signal Forwarding: When parent receives SIGINT/SIGTERM (via context cancellation), it forwards these signals to the child before exiting. This prevents orphaned child processes.
SIGWINCH Forwarding: Terminal resize events are forwarded to the child so apps can adjust their output to the new terminal size.
Exit Codes: Parent exits with 130 (128+SIGINT) when interrupted by Ctrl+C. Child exits with 128+signal when terminated by a signal.
Note: SIGTSTP/SIGCONT (job control) is not currently supported. Pressing Ctrl+Z will suspend the parent but the child will continue running.
type SingleProcessLauncher ¶
type SingleProcessLauncher struct {
// contains filtered or unexported fields
}
SingleProcessLauncher implements single-process mode via codesign + re-exec + setActivationPolicy.
Instead of creating a .app bundle, this launcher:
- Writes an entitlements plist to a temp file
- Codesigns the current binary in-place with the requested entitlements
- Re-execs itself so the kernel picks up the new code signature
- After re-exec, calls NSApplication setActivationPolicy to become a foreground app
IMPORTANT: syscall.Exec must happen BEFORE any NSApplication/GUI initialization. After GUI init, exec hangs due to stale Mach ports from the window server.
func (*SingleProcessLauncher) Launch ¶
func (t *SingleProcessLauncher) Launch(ctx context.Context, bundlePath, execPath string, cfg *Config) error
Launch implements the Launcher interface for single-process mode.
The bundlePath argument is unused since no bundle is created. The execPath is the binary to codesign and re-exec.