diff --git a/FyneApp.toml b/FyneApp.toml index c2fa9fc..41c5356 100644 --- a/FyneApp.toml +++ b/FyneApp.toml @@ -2,5 +2,5 @@ Icon = "Icon.png" Name = "TurtleSilicon" ID = "com.tairasu.turtlesilicon" - Version = "1.0.5" + Version = "1.0.6" Build = 12 diff --git a/main.go b/main.go index 8bb1e5c..43438cc 100644 --- a/main.go +++ b/main.go @@ -1,20 +1,45 @@ package main import ( + "turtlesilicon/pkg/ui" + "turtlesilicon/pkg/utils" / + "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" - "turtlesilicon/pkg/ui" // Updated import path + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/widget" ) -const appVersion = "1.0.5" +const appVersion = "1.0.6" func main() { myApp := app.NewWithID("com.tairasu.turtlesilicon") - myWindow := myApp.NewWindow("TurtleSilicon v" + appVersion) // Updated title + myWindow := myApp.NewWindow("TurtleSilicon v" + appVersion) myWindow.Resize(fyne.NewSize(650, 450)) myWindow.SetFixedSize(true) - content := ui.CreateUI(myWindow) // Use the CreateUI function from the ui package + // Check for updates + go func() { + prefs, _ := utils.LoadPrefs() + latest, notes, update, err := utils.CheckForUpdate(appVersion) + if err == nil && update && prefs.SuppressedUpdateVersion != latest { + checkbox := widget.NewCheck("Do not show this anymore", func(bool) {}) + content := container.NewVBox( + widget.NewLabel("A new version ("+latest+") is available!"), + widget.NewLabel("Release notes:\n\n"+notes), + checkbox, + ) + dialog.ShowCustomConfirm("Update Available", "OK", "Cancel", content, func(ok bool) { + if checkbox.Checked { + prefs.SuppressedUpdateVersion = latest + utils.SavePrefs(prefs) + } + }, myWindow) + } + }() + + content := ui.CreateUI(myWindow) myWindow.SetContent(content) myWindow.ShowAndRun() diff --git a/pkg/paths/paths.go b/pkg/paths/paths.go index 13287fb..3f32743 100644 --- a/pkg/paths/paths.go +++ b/pkg/paths/paths.go @@ -6,11 +6,12 @@ import ( "os" "path/filepath" + "turtlesilicon/pkg/utils" + "fyne.io/fyne/v2" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" - "turtlesilicon/pkg/utils" ) const DefaultCrossOverPath = "/Applications/CrossOver.app" @@ -38,6 +39,10 @@ func SelectCrossOverPath(myWindow fyne.Window, crossoverPathLabel *widget.RichTe CrossoverPath = selectedPath PatchesAppliedCrossOver = false log.Println("CrossOver path set to:", CrossoverPath) + // Save to prefs + prefs, _ := utils.LoadPrefs() + prefs.CrossOverPath = selectedPath + utils.SavePrefs(prefs) } else { dialog.ShowError(fmt.Errorf("invalid selection: '%s'. Please select a valid .app bundle", selectedPath), myWindow) log.Println("Invalid CrossOver path selected:", selectedPath) @@ -62,6 +67,10 @@ func SelectTurtleWoWPath(myWindow fyne.Window, turtlewowPathLabel *widget.RichTe TurtlewowPath = selectedPath PatchesAppliedTurtleWoW = false log.Println("TurtleWoW path set to:", TurtlewowPath) + // Save to prefs + prefs, _ := utils.LoadPrefs() + prefs.TurtleWoWPath = selectedPath + utils.SavePrefs(prefs) } else { dialog.ShowError(fmt.Errorf("invalid selection: '%s' is not a valid directory", selectedPath), myWindow) log.Println("Invalid TurtleWoW path selected:", selectedPath) diff --git a/pkg/ui/ui.go b/pkg/ui/ui.go index 8e9703b..d41714f 100644 --- a/pkg/ui/ui.go +++ b/pkg/ui/ui.go @@ -7,28 +7,29 @@ import ( "path/filepath" "strings" + "turtlesilicon/pkg/launcher" // Corrected import path + "turtlesilicon/pkg/patching" // Corrected import path + "turtlesilicon/pkg/paths" // Corrected import path + "turtlesilicon/pkg/utils" // Corrected import path + "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" - "turtlesilicon/pkg/launcher" // Corrected import path - "turtlesilicon/pkg/patching" // Corrected import path - "turtlesilicon/pkg/paths" // Corrected import path - "turtlesilicon/pkg/utils" // Corrected import path ) var ( - crossoverPathLabel *widget.RichText - turtlewowPathLabel *widget.RichText - turtlewowStatusLabel *widget.RichText - crossoverStatusLabel *widget.RichText - launchButton *widget.Button - patchTurtleWoWButton *widget.Button - patchCrossOverButton *widget.Button - unpatchTurtleWoWButton *widget.Button - unpatchCrossOverButton *widget.Button - metalHudCheckbox *widget.Check + crossoverPathLabel *widget.RichText + turtlewowPathLabel *widget.RichText + turtlewowStatusLabel *widget.RichText + crossoverStatusLabel *widget.RichText + launchButton *widget.Button + patchTurtleWoWButton *widget.Button + patchCrossOverButton *widget.Button + unpatchTurtleWoWButton *widget.Button + unpatchCrossOverButton *widget.Button + metalHudCheckbox *widget.Check ) func UpdateAllStatuses() { @@ -94,8 +95,8 @@ func UpdateAllStatuses() { } } - if utils.PathExists(winerosettaDllPath) && utils.PathExists(d3d9DllPath) && utils.PathExists(libSiliconPatchDllPath) && - utils.DirExists(rosettaX87DirPath) && utils.PathExists(rosettaX87ExePath) && + if utils.PathExists(winerosettaDllPath) && utils.PathExists(d3d9DllPath) && utils.PathExists(libSiliconPatchDllPath) && + utils.DirExists(rosettaX87DirPath) && utils.PathExists(rosettaX87ExePath) && utils.PathExists(libRuntimeRosettaX87Path) && dllsFileValid { paths.PatchesAppliedTurtleWoW = true } else { @@ -138,6 +139,15 @@ func UpdateAllStatuses() { } func CreateUI(myWindow fyne.Window) fyne.CanvasObject { + // Load saved paths from prefs + prefs, _ := utils.LoadPrefs() + if prefs.TurtleWoWPath != "" { + paths.TurtlewowPath = prefs.TurtleWoWPath + } + if prefs.CrossOverPath != "" { + paths.CrossoverPath = prefs.CrossOverPath + } + crossoverPathLabel = widget.NewRichText() turtlewowPathLabel = widget.NewRichText() turtlewowStatusLabel = widget.NewRichText() @@ -148,7 +158,7 @@ func CreateUI(myWindow fyne.Window) fyne.CanvasObject { if err != nil { log.Printf("Warning: could not load logo: %v", err) } - + // Create the logo image with a fixed size var logoImage *canvas.Image if logoResource != nil { @@ -156,7 +166,7 @@ func CreateUI(myWindow fyne.Window) fyne.CanvasObject { logoImage.FillMode = canvas.ImageFillContain logoImage.SetMinSize(fyne.NewSize(100, 100)) } - + // Create a container to center the logo var logoContainer fyne.CanvasObject if logoImage != nil { diff --git a/pkg/utils/prefs.go b/pkg/utils/prefs.go new file mode 100644 index 0000000..d5d11a6 --- /dev/null +++ b/pkg/utils/prefs.go @@ -0,0 +1,50 @@ +package utils + +import ( + "encoding/json" + "os" + "path/filepath" +) + +type UserPrefs struct { + SuppressedUpdateVersion string `json:"suppressed_update_version"` + TurtleWoWPath string `json:"turtlewow_path"` + CrossOverPath string `json:"crossover_path"` +} + +func getPrefsPath() (string, error) { + dir, err := os.UserConfigDir() + if err != nil { + return "", err + } + return filepath.Join(dir, "TurtleSilicon", "prefs.json"), nil +} + +func LoadPrefs() (*UserPrefs, error) { + path, err := getPrefsPath() + if err != nil { + return nil, err + } + data, err := os.ReadFile(path) + if err != nil { + return &UserPrefs{}, nil // default prefs if not found + } + var prefs UserPrefs + if err := json.Unmarshal(data, &prefs); err != nil { + return &UserPrefs{}, nil + } + return &prefs, nil +} + +func SavePrefs(prefs *UserPrefs) error { + path, err := getPrefsPath() + if err != nil { + return err + } + os.MkdirAll(filepath.Dir(path), 0755) + data, err := json.MarshalIndent(prefs, "", " ") + if err != nil { + return err + } + return os.WriteFile(path, data, 0644) +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 93ee0bb..0d8f5f4 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -1,9 +1,11 @@ package utils import ( + "encoding/json" "fmt" "io" "log" + "net/http" "os" "os/exec" "path/filepath" @@ -117,3 +119,21 @@ func QuotePathForShell(path string) string { return fmt.Sprintf(`"%s"`, path) } +func CheckForUpdate(currentVersion string) (latestVersion, releaseNotes string, updateAvailable bool, err error) { + resp, err := http.Get("https://api.github.com/repos/tairasu/TurtleSilicon/releases/latest") + if err != nil { + return "", "", false, err + } + defer resp.Body.Close() + + var data struct { + TagName string `json:"tag_name"` + Body string `json:"body"` + } + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return "", "", false, err + } + + latest := strings.TrimPrefix(data.TagName, "v") + return latest, data.Body, latest != currentVersion, nil +}