From 99bb8ed83849b244e8e7add0c337ed684d1f337c Mon Sep 17 00:00:00 2001 From: aomizu Date: Fri, 20 Jun 2025 13:36:44 +0900 Subject: [PATCH] added troubleshooting --- FyneApp.toml | 2 +- go.mod | 1 + go.sum | 4 ++ pkg/ui/components.go | 7 +- pkg/ui/popup.go | 160 +++++++++++++++++++++++++++++++++++++++++++ pkg/ui/variables.go | 9 +++ 6 files changed, 181 insertions(+), 2 deletions(-) diff --git a/FyneApp.toml b/FyneApp.toml index 165bee1..aa5ccb4 100644 --- a/FyneApp.toml +++ b/FyneApp.toml @@ -3,4 +3,4 @@ Name = "TurtleSilicon" ID = "com.tairasu.turtlesilicon" Version = "1.2.2" - Build = 58 + Build = 65 diff --git a/go.mod b/go.mod index 8ee45a0..f94152f 100644 --- a/go.mod +++ b/go.mod @@ -42,4 +42,5 @@ require ( golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + howett.net/plist v1.0.1 // indirect ) diff --git a/go.sum b/go.sum index c9273a2..0400e80 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,7 @@ github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8 github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio= github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc= github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M= github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -86,5 +87,8 @@ golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= +howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= diff --git a/pkg/ui/components.go b/pkg/ui/components.go index 9151de2..021cc07 100644 --- a/pkg/ui/components.go +++ b/pkg/ui/components.go @@ -142,6 +142,11 @@ func createBottomBar(myWindow fyne.Window) fyne.CanvasObject { showOptionsPopup() }) + // Troubleshooting button + troubleshootingButton = widget.NewButton("Troubleshooting", func() { + showTroubleshootingPopup() + }) + // GitHub button githubButton := widget.NewButton("GitHub", func() { githubURL := "https://github.com/tairasu/TurtleSilicon" @@ -168,7 +173,7 @@ func createBottomBar(myWindow fyne.Window) fyne.CanvasObject { leftButtons := container.NewHBox( optionsButton, - widget.NewSeparator(), // Visual separator + troubleshootingButton, githubButton, ) diff --git a/pkg/ui/popup.go b/pkg/ui/popup.go index 59bac47..d94c24d 100644 --- a/pkg/ui/popup.go +++ b/pkg/ui/popup.go @@ -1,9 +1,20 @@ package ui import ( + "fmt" + "os" + "path/filepath" + "strings" + "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/widget" + + "howett.net/plist" + + "turtlesilicon/pkg/paths" + "turtlesilicon/pkg/utils" ) // showOptionsPopup creates and shows an integrated popup window for options @@ -123,3 +134,152 @@ func showRemapWarningPopup() { warningPopup.Show() } + +// showTroubleshootingPopup creates and shows a popup window for troubleshooting actions +func showTroubleshootingPopup() { + if currentWindow == nil { + return + } + + // --- CrossOver Version Check --- + crossoverVersion := getCrossoverVersion(paths.CrossoverPath) + var crossoverStatusShort *widget.Label + var crossoverStatusDetail *widget.Label + if crossoverVersion == "" { + crossoverStatusShort = widget.NewLabel("Not found") + crossoverStatusDetail = widget.NewLabel("") + } else if isCrossoverVersionRecommended(crossoverVersion) { + crossoverStatusShort = widget.NewLabelWithStyle("✔ "+crossoverVersion, fyne.TextAlignTrailing, fyne.TextStyle{Bold: true}) + crossoverStatusDetail = widget.NewLabelWithStyle("✔ Recommended version of CrossOver installed", fyne.TextAlignLeading, fyne.TextStyle{Italic: true}) + } else { + crossoverStatusShort = widget.NewLabelWithStyle("⚠️ "+crossoverVersion, fyne.TextAlignTrailing, fyne.TextStyle{Italic: true}) + crossoverStatusDetail = widget.NewLabelWithStyle("⚠️ Please update to CrossOver 25.0.1 or later!", fyne.TextAlignLeading, fyne.TextStyle{Italic: true}) + } + crossoverStatusDetail.Wrapping = fyne.TextWrapWord + + // --- Delete WDB Directory --- + wdbDeleteButton = widget.NewButton("Delete", func() { + wdbPath := filepath.Join(paths.TurtlewowPath, "WDB") + if !utils.DirExists(wdbPath) { + dialog.ShowInformation("WDB Not Found", "No WDB directory found in your TurtleWoW folder.", currentWindow) + return + } + dialog.NewConfirm("Delete WDB Directory", "Are you sure you want to delete the WDB directory? This will remove all cached data. No important data will be lost.", func(confirm bool) { + if confirm { + err := os.RemoveAll(wdbPath) + if err != nil { + dialog.ShowError(fmt.Errorf("Failed to delete WDB: %v", err), currentWindow) + } else { + dialog.ShowInformation("WDB Deleted", "WDB directory deleted successfully.", currentWindow) + } + } + }, currentWindow).Show() + }) + + // --- Delete Wine Prefixes --- + wineDeleteButton = widget.NewButton("Delete", func() { + homeDir, _ := os.UserHomeDir() + userWine := filepath.Join(homeDir, ".wine") + turtleWine := filepath.Join(paths.TurtlewowPath, ".wine") + msg := "Are you sure you want to delete the following Wine prefixes?\n\n- " + userWine + "\n- " + turtleWine + "\n\nThis cannot be undone." + dialog.NewConfirm("Delete Wine Prefixes", msg, func(confirm bool) { + if confirm { + err1 := os.RemoveAll(userWine) + err2 := os.RemoveAll(turtleWine) + if err1 != nil && !os.IsNotExist(err1) { + dialog.ShowError(fmt.Errorf("Failed to delete ~/.wine: %v", err1), currentWindow) + return + } + if err2 != nil && !os.IsNotExist(err2) { + dialog.ShowError(fmt.Errorf("Failed to delete TurtleWoW/.wine: %v", err2), currentWindow) + return + } + dialog.ShowInformation("Wine Prefixes Deleted", "Wine prefixes deleted successfully.", currentWindow) + } + }, currentWindow).Show() + }) + + troubleshootingTitle := widget.NewLabel("Troubleshooting") + troubleshootingTitle.TextStyle = fyne.TextStyle{Bold: true} + + rowCrossover := container.NewBorder(nil, nil, widget.NewLabel("CrossOver version:"), crossoverStatusShort, nil) + rowWDB := container.NewBorder(nil, nil, widget.NewLabel("Delete WDB directory (cache):"), wdbDeleteButton, nil) + rowWine := container.NewBorder(nil, nil, widget.NewLabel("Delete Wine prefixes (~/.wine & TurtleWoW/.wine):"), wineDeleteButton, nil) + appMgmtNote := widget.NewLabel("Please ensure TurtleSilicon is enabled in System Settings > Privacy & Security > App Management.") + appMgmtNote.Wrapping = fyne.TextWrapWord + appMgmtNote.TextStyle = fyne.TextStyle{Italic: true} + + content := container.NewVBox( + troubleshootingTitle, + widget.NewSeparator(), + rowCrossover, + crossoverStatusDetail, + rowWDB, + rowWine, + appMgmtNote, + ) + + scrollContainer := container.NewScroll(content) + + troubleshootingCloseButton = widget.NewButton("Close", func() {}) + + popupContent := container.NewBorder( + nil, // top + container.NewCenter(troubleshootingCloseButton), // bottom + nil, // left + nil, // right + container.NewPadded(scrollContainer), // center + ) + + windowSize := currentWindow.Content().Size() + popupWidth := windowSize.Width * 5 / 6 + popupHeight := windowSize.Height * 5 / 6 + + popup := widget.NewModalPopUp(popupContent, currentWindow.Canvas()) + popup.Resize(fyne.NewSize(popupWidth, popupHeight)) + + troubleshootingCloseButton.OnTapped = func() { + popup.Hide() + } + + popup.Show() +} + +// getCrossoverVersion reads the Info.plist and returns the version string, or "" if not found +func getCrossoverVersion(appPath string) string { + if appPath == "" { + return "" + } + plistPath := filepath.Join(appPath, "Contents", "Info.plist") + f, err := os.Open(plistPath) + if err != nil { + return "" + } + defer f.Close() + var data struct { + Version string `plist:"CFBundleShortVersionString"` + } + decoder := plist.NewDecoder(f) + if err := decoder.Decode(&data); err != nil { + return "" + } + return data.Version +} + +// isCrossoverVersionRecommended returns true if version >= 25.0.1 +func isCrossoverVersionRecommended(version string) bool { + parts := strings.Split(version, ".") + if len(parts) < 3 { + return false + } + major := parts[0] + minor := parts[1] + patch := parts[2] + if major > "25" { + return true + } + if major == "25" && minor >= "0" && patch >= "1" { + return true + } + return false +} diff --git a/pkg/ui/variables.go b/pkg/ui/variables.go index 164c85e..141f31e 100644 --- a/pkg/ui/variables.go +++ b/pkg/ui/variables.go @@ -53,4 +53,13 @@ var ( // Pulsing effect variables (pulsingActive is in status.go) pulsingTicker *time.Ticker + + // Troubleshooting popup and controls + troubleshootingButton *widget.Button + troubleshootingPopupActive bool + crossoverVersionStatusLabel *widget.RichText + wdbDeleteButton *widget.Button + wineDeleteButton *widget.Button + appMgmtPermissionButton *widget.Button + troubleshootingCloseButton *widget.Button )