// Package moonlight handles launching the Moonlight client. // It locates the Moonlight executable and launches a stream session // to a specific host and app. package moonlight import ( "fmt" "os/exec" "runtime" ) // DefaultPaths are the well-known install locations per platform. var DefaultPaths = map[string][]string{ "windows": { `C:\Program Files\Moonlight Game Streaming\Moonlight.exe`, `C:\Program Files (x86)\Moonlight Game Streaming\Moonlight.exe`, }, "darwin": { "/Applications/Moonlight.app/Contents/MacOS/Moonlight", }, "linux": { "/usr/bin/moonlight", "/usr/local/bin/moonlight", }, } // Launcher knows how to start Moonlight. type Launcher struct { execPath string } // New creates a Launcher, auto-detecting the Moonlight binary. // Pass an empty string to use auto-detection. func New(overridePath string) (*Launcher, error) { if overridePath != "" { if path, err := exec.LookPath(overridePath); err == nil { return &Launcher{execPath: path}, nil } return nil, fmt.Errorf("moonlight not found at %q", overridePath) } // Try PATH first if path, err := exec.LookPath("moonlight"); err == nil { return &Launcher{execPath: path}, nil } // Try well-known locations for _, candidate := range DefaultPaths[runtime.GOOS] { if path, err := exec.LookPath(candidate); err == nil { return &Launcher{execPath: path}, nil } } // Return a launcher even if not found — we'll surface the error at connect time return &Launcher{}, nil } // ExecPath returns the resolved path to the Moonlight binary. func (l *Launcher) ExecPath() string { return l.execPath } // Stream launches a Moonlight stream to the given host + app. // app is typically "Desktop" for a full desktop stream. func (l *Launcher) Stream(hostIP, app string) error { if l.execPath == "" { return fmt.Errorf("Moonlight executable not found — please install Moonlight or set MOONLIGHT_PATH") } // moonlight stream cmd := exec.Command(l.execPath, "stream", hostIP, app) cmd.Stdout = nil cmd.Stderr = nil return cmd.Start() // non-blocking — let Moonlight own its window } // Pair runs the Moonlight pairing flow for a host. func (l *Launcher) Pair(hostIP string) error { if l.execPath == "" { return fmt.Errorf("Moonlight executable not found") } cmd := exec.Command(l.execPath, "pair", hostIP) cmd.Stdout = nil cmd.Stderr = nil return cmd.Start() }