added --silent and register values in windows registry keys
This commit is contained in:
parent
48473f98c5
commit
ca57775f8f
7 changed files with 402 additions and 165 deletions
|
@ -109,6 +109,13 @@ func (inst *Installer) doFinalInstall() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register the app in Windows (i.e. create registry entries)
|
||||||
|
spm.UpdateProgress(0, "Registering app...")
|
||||||
|
if err := spm.RegisterApp(); err != nil {
|
||||||
|
inst.LastError = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
spm.UpdateProgress(100, "Installation complete!")
|
spm.UpdateProgress(100, "Installation complete!")
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
136
main.go
136
main.go
|
@ -1,9 +1,11 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"spitfire-installer/spm"
|
"spitfire-installer/spm"
|
||||||
|
|
||||||
rl "github.com/gen2brain/raylib-go/raylib"
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
@ -30,7 +32,14 @@ var (
|
||||||
const finalStep = 3
|
const finalStep = 3
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
spm.Run()
|
// Check for the --silent flag.
|
||||||
|
silent := flag.Bool("silent", false, "run installer without GUI")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *silent {
|
||||||
|
runSilent()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
monitor := rl.GetCurrentMonitor()
|
monitor := rl.GetCurrentMonitor()
|
||||||
if monitor < 0 {
|
if monitor < 0 {
|
||||||
|
@ -96,8 +105,8 @@ func main() {
|
||||||
radius := float32(30)
|
radius := float32(30)
|
||||||
dx := mousePos.X - topRightX
|
dx := mousePos.X - topRightX
|
||||||
dy := mousePos.Y - topRightY
|
dy := mousePos.Y - topRightY
|
||||||
dist := float32(math.Sqrt(float64(dx*dx + dy*dy)))
|
dist := float32((dx*dx + dy*dy))
|
||||||
if dist < radius+30 {
|
if dist < (radius+30)*(radius+30) {
|
||||||
textHoverFade += 0.1
|
textHoverFade += 0.1
|
||||||
if textHoverFade > 1 {
|
if textHoverFade > 1 {
|
||||||
textHoverFade = 1
|
textHoverFade = 1
|
||||||
|
@ -161,6 +170,36 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runSilent() {
|
||||||
|
fmt.Println("Running installer in silent mode (default values will be used).")
|
||||||
|
|
||||||
|
// Start the download+decompress phase.
|
||||||
|
installer.StartDownloadDecompress()
|
||||||
|
|
||||||
|
// Poll until the download/decompression phase is done.
|
||||||
|
for !installer.DoneDownload {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the final installation.
|
||||||
|
installer.FinalInstall()
|
||||||
|
|
||||||
|
// Poll until installation is finished.
|
||||||
|
for !installer.DoneInstall {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
if installer.LastError != nil {
|
||||||
|
fmt.Printf("Installation failed: %v\n", installer.LastError)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Installation complete!")
|
||||||
|
// Optionally launch the app after install:
|
||||||
|
// spm.Run() or another function if needed.
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
func handleInput(mousePos rl.Vector2, screenW, screenH, buttonW, buttonH int, prevX, prevY, nextX, nextY int32) {
|
func handleInput(mousePos rl.Vector2, screenW, screenH, buttonW, buttonH int, prevX, prevY, nextX, nextY int32) {
|
||||||
switch currentStep {
|
switch currentStep {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -211,7 +250,7 @@ func drawHeader(screenW int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we pass "displayProgress" (0..100) as a float, "posAlpha" in [0..1],
|
// Now we pass "displayProgress" (0..100) as a float, "posAlpha" in [0..1],
|
||||||
// plus "doneInstall" to show the "Run App" button
|
// plus "doneInstall" to show the "Run App" button.
|
||||||
func drawInstallCircle(screenW, screenH int, posAlpha, displayProgress float32, doneInstall bool, hoverAlpha float32) {
|
func drawInstallCircle(screenW, screenH int, posAlpha, displayProgress float32, doneInstall bool, hoverAlpha float32) {
|
||||||
// Lerp position/radius
|
// Lerp position/radius
|
||||||
topRight := rl.Vector2{X: float32(screenW - 80), Y: 100}
|
topRight := rl.Vector2{X: float32(screenW - 80), Y: 100}
|
||||||
|
@ -251,7 +290,7 @@ func drawInstallCircle(screenW, screenH int, posAlpha, displayProgress float32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Run App" button logic is the same
|
// "Run App" button logic is the same.
|
||||||
func drawRunAppButton(cx, cy float32) {
|
func drawRunAppButton(cx, cy float32) {
|
||||||
w := float32(180)
|
w := float32(180)
|
||||||
h := float32(50)
|
h := float32(50)
|
||||||
|
@ -259,7 +298,8 @@ func drawRunAppButton(cx, cy float32) {
|
||||||
hovered := overRect(rl.GetMousePosition(), rect)
|
hovered := overRect(rl.GetMousePosition(), rect)
|
||||||
drawRoundedRectButton(rect, "Start Spitfire", 1.0, hovered)
|
drawRoundedRectButton(rect, "Start Spitfire", 1.0, hovered)
|
||||||
if hovered && rl.IsMouseButtonPressed(rl.MouseLeftButton) {
|
if hovered && rl.IsMouseButtonPressed(rl.MouseLeftButton) {
|
||||||
fmt.Println("Launching the app (placeholder)...")
|
fmt.Println("Launching...")
|
||||||
|
spm.Run()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -346,85 +386,3 @@ func startTransition(from, to int) {
|
||||||
targetStep = to
|
targetStep = to
|
||||||
transition.Start(from, to)
|
transition.Start(from, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPM example
|
|
||||||
|
|
||||||
// package main
|
|
||||||
|
|
||||||
// import (
|
|
||||||
// "fmt"
|
|
||||||
// "os"
|
|
||||||
// "path/filepath"
|
|
||||||
// "spitfire-installer/spm"
|
|
||||||
// "time"
|
|
||||||
// )
|
|
||||||
|
|
||||||
// func main() {
|
|
||||||
// // Start a goroutine to display progress updates
|
|
||||||
// done := make(chan bool)
|
|
||||||
// go func() {
|
|
||||||
// for {
|
|
||||||
// select {
|
|
||||||
// case <-done:
|
|
||||||
// return
|
|
||||||
// default:
|
|
||||||
// percentage, task := spm.GetProgress()
|
|
||||||
// fmt.Printf("\r[%3d%%] %s", percentage, task)
|
|
||||||
// time.Sleep(500 * time.Millisecond)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }()
|
|
||||||
|
|
||||||
// // Set up the download directory
|
|
||||||
// downloadDir := spm.GetTempDownloadDir()
|
|
||||||
// fmt.Println("\nTemporary download directory:", downloadDir)
|
|
||||||
|
|
||||||
// // Download the APPINDEX
|
|
||||||
// appIndexPath := filepath.Join(downloadDir, "APPINDEX")
|
|
||||||
// spm.UpdateProgress(0, "Starting APPINDEX download")
|
|
||||||
// if err := spm.DownloadAppIndex(appIndexPath); err != nil {
|
|
||||||
// fmt.Println("\nError downloading APPINDEX:", err)
|
|
||||||
// done <- true
|
|
||||||
// os.Exit(1)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Download the desired package version (e.g., nightly)
|
|
||||||
// packageName := "spitfire-browser"
|
|
||||||
// release := "nightly"
|
|
||||||
|
|
||||||
// spm.UpdateProgress(0, "Starting package download")
|
|
||||||
// if err := spm.DownloadPackageFromAppIndex(appIndexPath, packageName, release, downloadDir); err != nil {
|
|
||||||
// fmt.Println("\nError downloading package:", err)
|
|
||||||
// done <- true
|
|
||||||
// os.Exit(1)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Decompress and install
|
|
||||||
// packagePath := filepath.Join(downloadDir, "browser-amd64-nightly-linux.tar.gz")
|
|
||||||
// spm.UpdateProgress(0, "Starting decompression")
|
|
||||||
// tempDir, err := spm.DecompressToTemp(packagePath)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println("\nError decompressing package:", err)
|
|
||||||
// done <- true
|
|
||||||
// os.Exit(1)
|
|
||||||
// }
|
|
||||||
// fmt.Println("\nDecompressed package to:", tempDir)
|
|
||||||
|
|
||||||
// // Generate default install directory
|
|
||||||
// installDir, err := spm.GetDefaultInstallDir()
|
|
||||||
// if err != nil {
|
|
||||||
// inst.LastError = fmt.Errorf("failed to determine default install directory: %w", err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// spm.UpdateProgress(0, "Starting installation")
|
|
||||||
// if err := spm.MoveFilesToInstallDir(tempDir, installDir); err != nil {
|
|
||||||
// fmt.Println("\nError installing package:", err)
|
|
||||||
// done <- true
|
|
||||||
// os.Exit(1)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Notify progress display to stop and finalize
|
|
||||||
// done <- true
|
|
||||||
// fmt.Printf("\nSuccessfully installed %s (%s) to %s\n", packageName, release, installDir)
|
|
||||||
// }
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DecompressPackage determines the package format and decompresses it
|
// DecompressPackage now passes UpdateProgress to decompressTarGz.
|
||||||
func DecompressPackage(downloadDir, packageName, arch, osName, pkgType, release, version string) (string, error) {
|
func DecompressPackage(downloadDir, packageName, arch, osName, pkgType, release, version string) (string, error) {
|
||||||
// 1) Construct the .tar.gz name
|
// 1) Construct the .tar.gz name
|
||||||
expectedFileName := fmt.Sprintf(
|
expectedFileName := fmt.Sprintf(
|
||||||
|
@ -35,7 +35,7 @@ func DecompressPackage(downloadDir, packageName, arch, osName, pkgType, release,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Decompress everything into `decompressDir`
|
// 3) Decompress everything into `decompressDir`
|
||||||
if err := decompressTarGz(packagePath, decompressDir); err != nil {
|
if err := decompressTarGz(packagePath, decompressDir, UpdateProgress); err != nil {
|
||||||
return "", fmt.Errorf("failed to decompress: %w", err)
|
return "", fmt.Errorf("failed to decompress: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,14 +43,28 @@ func DecompressPackage(downloadDir, packageName, arch, osName, pkgType, release,
|
||||||
return decompressDir, nil
|
return decompressDir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decompressTarGz(srcFile, destDir string) error {
|
// decompressTarGz takes an additional updateProgress callback to report progress.
|
||||||
|
func decompressTarGz(srcFile, destDir string, updateProgress func(int, string)) error {
|
||||||
f, err := os.Open(srcFile)
|
f, err := os.Open(srcFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
gzr, err := gzip.NewReader(f)
|
fileInfo, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
totalSize := fileInfo.Size()
|
||||||
|
|
||||||
|
// Wrap the file reader so we can track progress.
|
||||||
|
progressReader := &ProgressReader{
|
||||||
|
Reader: f,
|
||||||
|
Total: totalSize,
|
||||||
|
Callback: updateProgress,
|
||||||
|
}
|
||||||
|
|
||||||
|
gzr, err := gzip.NewReader(progressReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -73,6 +87,11 @@ func decompressTarGz(srcFile, destDir string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case tar.TypeReg:
|
case tar.TypeReg:
|
||||||
|
outPath := filepath.Join(destDir, header.Name)
|
||||||
|
// Ensure the parent directory exists
|
||||||
|
if err := os.MkdirAll(filepath.Dir(outPath), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
outFile, err := os.Create(outPath)
|
outFile, err := os.Create(outPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -83,8 +102,38 @@ func decompressTarGz(srcFile, destDir string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// huh
|
// ignoring other types for now
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update progress after extracting each file.
|
||||||
|
if updateProgress != nil {
|
||||||
|
percent := int((progressReader.BytesRead * 100) / totalSize)
|
||||||
|
updateProgress(percent, fmt.Sprintf("Extracted: %s", header.Name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Final update: extraction complete.
|
||||||
|
if updateProgress != nil {
|
||||||
|
updateProgress(100, "Extraction complete")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProgressReader wraps an io.Reader to count bytes and update progress.
|
||||||
|
type ProgressReader struct {
|
||||||
|
io.Reader
|
||||||
|
Total int64
|
||||||
|
BytesRead int64
|
||||||
|
Callback func(int, string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *ProgressReader) Read(p []byte) (int, error) {
|
||||||
|
n, err := pr.Reader.Read(p)
|
||||||
|
pr.BytesRead += int64(n)
|
||||||
|
if pr.Callback != nil && pr.Total > 0 {
|
||||||
|
percent := int((pr.BytesRead * 100) / pr.Total)
|
||||||
|
pr.Callback(percent, "Decompressing...")
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
17
spm/register_unix.go
Normal file
17
spm/register_unix.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// run_default.go
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package spm
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// RegisterApp is not supported on non-Windows platforms.
|
||||||
|
func RegisterApp() error {
|
||||||
|
return fmt.Errorf("RegisterApp is only available on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnregisterApp is not supported on non-Windows platforms.
|
||||||
|
func UnregisterApp() error {
|
||||||
|
return fmt.Errorf("UnregisterApp is only available on Windows")
|
||||||
|
}
|
135
spm/register_win.go
Normal file
135
spm/register_win.go
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// run_windows.go
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package spm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterApp writes the necessary registry keys, making it appear as offically installed app
|
||||||
|
func RegisterApp() error {
|
||||||
|
exePath, err := GetInstallDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get install directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Create Uninstall/Modify entry
|
||||||
|
uninstallKeyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\SpitfireBrowser`
|
||||||
|
uk, _, err := registry.CreateKey(registry.LOCAL_MACHINE, uninstallKeyPath, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create uninstall key: %w", err)
|
||||||
|
}
|
||||||
|
defer uk.Close()
|
||||||
|
|
||||||
|
if err := uk.SetStringValue("DisplayName", "Spitfire"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := uk.SetStringValue("UninstallString", exePath+" --uninstall"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := uk.SetStringValue("ModifyPath", exePath+" --modify"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := uk.SetStringValue("DisplayIcon", exePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Register as a browser for default apps
|
||||||
|
clientKeyPath := `Software\Clients\StartMenuInternet\SpitfireBrowser`
|
||||||
|
ck, _, err := registry.CreateKey(registry.LOCAL_MACHINE, clientKeyPath, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create client key: %w", err)
|
||||||
|
}
|
||||||
|
defer ck.Close()
|
||||||
|
|
||||||
|
if err := ck.SetStringValue("", "Spitfire"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Capabilities subkey
|
||||||
|
capabilitiesKeyPath := clientKeyPath + `\Capabilities`
|
||||||
|
capk, _, err := registry.CreateKey(registry.LOCAL_MACHINE, capabilitiesKeyPath, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create capabilities key: %w", err)
|
||||||
|
}
|
||||||
|
defer capk.Close()
|
||||||
|
|
||||||
|
if err := capk.SetStringValue("ApplicationName", "Spitfire"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := capk.SetStringValue("ApplicationDescription", "A custom browser"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set file associations
|
||||||
|
assocKeyPath := capabilitiesKeyPath + `\FileAssociations`
|
||||||
|
ak, _, err := registry.CreateKey(registry.LOCAL_MACHINE, assocKeyPath, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create file associations key: %w", err)
|
||||||
|
}
|
||||||
|
defer ak.Close()
|
||||||
|
|
||||||
|
associations := map[string]string{
|
||||||
|
".html": "SpitfireBrowserHTML",
|
||||||
|
"HTTP": "SpitfireBrowserHTML",
|
||||||
|
"HTTPS": "SpitfireBrowserHTML",
|
||||||
|
}
|
||||||
|
for ext, progID := range associations {
|
||||||
|
if err := ak.SetStringValue(ext, progID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnregisterApp removes the registry entries created by registerApp.
|
||||||
|
func UnregisterApp() error {
|
||||||
|
// Remove the Uninstall/Modify entry.
|
||||||
|
uninstallKeyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\SpitfireBrowser`
|
||||||
|
if err := deleteRegistryTree(registry.LOCAL_MACHINE, uninstallKeyPath); err != nil {
|
||||||
|
return fmt.Errorf("failed to delete uninstall key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the browser registration entry.
|
||||||
|
clientKeyPath := `Software\Clients\StartMenuInternet\SpitfireBrowser`
|
||||||
|
if err := deleteRegistryTree(registry.LOCAL_MACHINE, clientKeyPath); err != nil {
|
||||||
|
return fmt.Errorf("failed to delete client key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteRegistryTree recursively deletes a registry key and all its subkeys.
|
||||||
|
func deleteRegistryTree(root registry.Key, path string) error {
|
||||||
|
// Open the key with ALL_ACCESS permissions.
|
||||||
|
key, err := registry.OpenKey(root, path, registry.ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
// If the key does not exist, there's nothing to do.
|
||||||
|
if err == registry.ErrNotExist {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Read the names of all subkeys.
|
||||||
|
subKeys, err := key.ReadSubKeyNames(-1)
|
||||||
|
key.Close() // Close the key so it can be deleted later.
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively delete each subkey.
|
||||||
|
for _, subKey := range subKeys {
|
||||||
|
subKeyPath := path + `\` + subKey
|
||||||
|
if err := deleteRegistryTree(root, subKeyPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, delete the (now empty) key.
|
||||||
|
return registry.DeleteKey(root, path)
|
||||||
|
}
|
74
spm/run_unix.go
Normal file
74
spm/run_unix.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// run_unix.go
|
||||||
|
//go:build !windows
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package spm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run locates and starts the installed Spitfire browser without waiting for it to exit.
|
||||||
|
func Run() error {
|
||||||
|
installDir, err := GetInstallDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
exePath := filepath.Join(installDir, "browser", "spitfire.exe")
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
exePath = filepath.Join(installDir, "browser", "spitfire")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(exePath)
|
||||||
|
cmd.Dir = filepath.Join(installDir, "browser")
|
||||||
|
return cmd.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunAndWait locates and starts the installed Spitfire browser and waits for it to exit.
|
||||||
|
func RunAndWait() error {
|
||||||
|
installDir, err := GetInstallDir()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get install directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the browser executable path
|
||||||
|
exePath := filepath.Join(installDir, "browser", "spitfire")
|
||||||
|
if _, err := os.Stat(exePath); err != nil {
|
||||||
|
return fmt.Errorf("browser executable not found at %s: %w", exePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(exePath)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// Start the process in a new process group
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Setpgid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Starting browser: %s\n", exePath)
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return fmt.Errorf("failed to start browser: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print PID and PGID for debugging
|
||||||
|
pgid, err := syscall.Getpgid(cmd.Process.Pid)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("Browser process started with PID %d (PGID %d)\n", cmd.Process.Pid, pgid)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Browser process started with PID %d\n", cmd.Process.Pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
return fmt.Errorf("browser exited with error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Browser exited successfully.")
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,3 +1,7 @@
|
||||||
|
// run_windows.go
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
package spm
|
package spm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -6,6 +10,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run locates and starts the installed Spitfire browser without waiting for it to exit.
|
// Run locates and starts the installed Spitfire browser without waiting for it to exit.
|
||||||
|
@ -34,35 +39,27 @@ func RunAndWait() error {
|
||||||
|
|
||||||
// Construct the browser executable path
|
// Construct the browser executable path
|
||||||
exePath := filepath.Join(installDir, "browser", "spitfire.exe")
|
exePath := filepath.Join(installDir, "browser", "spitfire.exe")
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
exePath = filepath.Join(installDir, "browser", "spitfire")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the browser executable exists
|
|
||||||
if _, err := os.Stat(exePath); err != nil {
|
if _, err := os.Stat(exePath); err != nil {
|
||||||
return fmt.Errorf("browser executable not found at %s: %w", exePath, err)
|
return fmt.Errorf("browser executable not found at %s: %w", exePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the command
|
|
||||||
cmd := exec.Command(exePath)
|
cmd := exec.Command(exePath)
|
||||||
|
|
||||||
// Attach standard output and error for debugging
|
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
// Start the browser process
|
// Use CREATE_NEW_PROCESS_GROUP flag for Windows
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("Starting browser: %s\n", exePath)
|
fmt.Printf("Starting browser: %s\n", exePath)
|
||||||
err = cmd.Start()
|
if err := cmd.Start(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to start browser: %w", err)
|
return fmt.Errorf("failed to start browser: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the PID for debugging
|
|
||||||
fmt.Printf("Browser process started with PID %d\n", cmd.Process.Pid)
|
fmt.Printf("Browser process started with PID %d\n", cmd.Process.Pid)
|
||||||
|
|
||||||
// Wait for the process to exit
|
if err := cmd.Wait(); err != nil {
|
||||||
err = cmd.Wait()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("browser exited with error: %w", err)
|
return fmt.Errorf("browser exited with error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue