From adb17f154ac94757ffe58fef1aa87e1d6657410e Mon Sep 17 00:00:00 2001 From: Anshuman Date: Sun, 24 Nov 2024 17:46:41 +0530 Subject: [PATCH 1/3] Added support for start on login --- v3/pkg/application/application.go | 19 +++++++ v3/pkg/application/application_darwin.go | 46 ++++++++++++++++ v3/pkg/application/application_linux.go | 67 +++++++++++++++++++++++ v3/pkg/application/application_options.go | 3 + v3/pkg/application/application_windows.go | 32 +++++++++++ 5 files changed, 167 insertions(+) diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index 9d575a14536..4fd1f96ec95 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -159,6 +159,8 @@ func New(appOptions Options) *App { result.OnShutdown(appOptions.OnShutdown) } + result.startAtLoginOnRun = appOptions.StartAtLogin + return result } @@ -192,6 +194,8 @@ type ( GetFlags(options Options) map[string]any isOnMainThread() bool isDarkMode() bool + setStartAtLogin(enabled bool) error + canStartAtLogin() bool } runnable interface { @@ -342,6 +346,8 @@ type App struct { // Wails ApplicationEvent Listener related wailsEventListenerLock sync.Mutex wailsEventListeners []WailsEventListener + + startAtLoginOnRun bool } func (a *App) handleError(err error) { @@ -618,6 +624,11 @@ func (a *App) Run() error { a.impl.setIcon(a.options.Icon) } + if err := a.SetStartAtLogin(a.startAtLoginOnRun); err != nil { + a.Logger.Error("SetStartAtLogin() failed:", + "error", err.Error()) + } + err = a.impl.run() if err != nil { return err @@ -1023,3 +1034,11 @@ func (a *App) Path(selector Path) string { func (a *App) Paths(selector Paths) []string { return pathdirs[selector] } + +func (a *App) SetStartAtLogin(enabled bool) error { + if !a.impl.canStartAtLogin() { + a.Logger.Warn("SetStartAtLogin: Not supported in current configuration") + return nil + } + return a.impl.setStartAtLogin(enabled) +} diff --git a/v3/pkg/application/application_darwin.go b/v3/pkg/application/application_darwin.go index 0e8fa1c1235..443b5c131cc 100644 --- a/v3/pkg/application/application_darwin.go +++ b/v3/pkg/application/application_darwin.go @@ -161,12 +161,19 @@ static const char* serializationNSDictionary(void *dict) { import "C" import ( "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" "unsafe" + "github.com/pkg/errors" "github.com/wailsapp/wails/v3/internal/operatingsystem" "github.com/wailsapp/wails/v3/internal/assetserver/webview" "github.com/wailsapp/wails/v3/pkg/events" + "github.com/wailsapp/wails/v3/pkg/mac" ) type macosApp struct { @@ -377,3 +384,42 @@ func HandleOpenFile(filePath *C.char) { ctx: eventContext, } } + +func (m *macosApp) setStartAtLogin(enabled bool) error { + exe, err := os.Executable() + if err != nil { + return errors.Wrap(err, "Error running os.Executable:") + } + + binName := filepath.Base(exe) + if !strings.HasSuffix(exe, "/Contents/MacOS/"+binName) { + return fmt.Errorf("app needs to be running as package.app file to start at login") + } + + appPath := strings.TrimSuffix(exe, "/Contents/MacOS/"+binName) + var command string + if enabled { + command = fmt.Sprintf("tell application \"System Events\" to make login item at end with properties {name: \"%s\",path:\"%s\", hidden:false}", binName, appPath) + } else { + command = fmt.Sprintf("tell application \"System Events\" to delete login item \"%s\"", binName) + } + + cmd := exec.Command("osascript", "-e", command) + _, err = cmd.CombinedOutput() + return err +} + +func (m *macosApp) canStartAtLogin() bool { + bundleID := mac.GetBundleID() + if bundleID == "" { + return false + } + + exe, err := os.Executable() + if err != nil { + return false + } + + binName := filepath.Base(exe) + return strings.HasSuffix(exe, "/Contents/MacOS/"+binName) +} diff --git a/v3/pkg/application/application_linux.go b/v3/pkg/application/application_linux.go index 19f789c05af..f5ea2819df7 100644 --- a/v3/pkg/application/application_linux.go +++ b/v3/pkg/application/application_linux.go @@ -16,8 +16,10 @@ import "C" import ( "fmt" "os" + "path/filepath" "strings" "sync" + "text/template" "github.com/godbus/dbus/v5" "github.com/wailsapp/wails/v3/internal/operatingsystem" @@ -260,3 +262,68 @@ func fatalHandler(errFunc func(error)) { // Stub for windows function return } + +func (l *linuxApp) setStartAtLogin(enabled bool) error { + homedir, err := os.UserHomeDir() + if err != nil { + return err + } + + bin, err := os.Executable() + if err != nil { + return err + } + + name := filepath.Base(bin) + autostartFile := fmt.Sprintf("%s-autostart.desktop", name) + autostartPath := filepath.Join(homedir, ".config", "autostart", autostartFile) + + if !enabled { + err := os.Remove(autostartPath) + if os.IsNotExist(err) { + return nil + } + return err + } + + const tpl = `[Desktop Entry] +Name={{.Name}} +Comment=Autostart service for {{.Name}} +Type=Application +Exec={{.Cmd}} +Hidden=true +X-GNOME-Autostart-enabled=true +` + if err := os.MkdirAll(filepath.Dir(autostartPath), 0755); err != nil { + return err + } + + file, err := os.OpenFile(autostartPath, os.O_RDWR|os.O_CREATE, 0600) + if err != nil { + return err + } + defer file.Close() + + t := template.Must(template.New("autostart").Parse(tpl)) + return t.Execute(file, struct { + Name string + Cmd string + }{ + Name: name, + Cmd: exe, + }) +} + +func (l *linuxApp) canStartAtLogin() bool { + homedir, err := os.UserHomeDir() + if err != nil { + return false + } + + autostartDir := filepath.Join(homedir, ".config", "autostart") + if err := os.MkdirAll(autostartDir, 0755); err != nil { + return false + } + + return true +} diff --git a/v3/pkg/application/application_options.go b/v3/pkg/application/application_options.go index 3fb304badd5..cc9aac17838 100644 --- a/v3/pkg/application/application_options.go +++ b/v3/pkg/application/application_options.go @@ -114,6 +114,9 @@ type Options struct { // The '.' is required FileAssociations []string + // StartAtLogin determines if the application should start when the user logs in + StartAtLogin bool + // This blank field ensures types from other packages // are never convertible to Options. // This property, in turn, improves the accuracy of the binding generator. diff --git a/v3/pkg/application/application_windows.go b/v3/pkg/application/application_windows.go index ee2d411aa54..f8d0427a075 100644 --- a/v3/pkg/application/application_windows.go +++ b/v3/pkg/application/application_windows.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "slices" + "strings" "sync" "syscall" "unsafe" @@ -16,6 +17,7 @@ import ( "github.com/wailsapp/wails/v3/pkg/events" "github.com/wailsapp/wails/v3/pkg/w32" + "golang.org/x/sys/windows/registry" ) type windowsApp struct { @@ -373,3 +375,33 @@ func fatalHandler(errFunc func(error)) { w32.Fatal = errFunc return } + +func (m *windowsApp) setStartAtLogin(enabled bool) error { + exePath, err := os.Executable() + if err != nil { + return fmt.Errorf("failed to get executable path: %s", err) + } + + registryKey := strings.Split(filepath.Base(exePath), ".")[0] + + key, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.ALL_ACCESS) + if err != nil { + return fmt.Errorf("failed to open registry key: %s", err) + } + defer key.Close() + + if enabled { + err = key.SetStringValue(registryKey, exePath) + } else { + err = key.DeleteValue(registryKey) + if err == registry.ErrNotExist { + return nil + } + } + return err +} + +func (m *windowsApp) canStartAtLogin() bool { + // Windows generally supports start at login via registry + return true +} From 81d670b372f0935ec4c7f769519f83ec8eb81d7c Mon Sep 17 00:00:00 2001 From: Anshuman Date: Sun, 24 Nov 2024 17:48:31 +0530 Subject: [PATCH 2/3] Updated Changelog --- mkdocs-website/docs/en/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/mkdocs-website/docs/en/changelog.md b/mkdocs-website/docs/en/changelog.md index 14ac2445009..114f7e37de1 100644 --- a/mkdocs-website/docs/en/changelog.md +++ b/mkdocs-website/docs/en/changelog.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Added Support for StartAtLogin by [ansxuman](https://github.com/ansxuman) in [#3910](https://github.com/wailsapp/wails/pull/3910) - Added Support for darwin universal builds and packages by [ansxuman](https://github.com/ansxuman) in [#3902](https://github.com/wailsapp/wails/pull/3902) - Events documentation to the mkdocs webite by [atterpac](https://github.com/atterpac) in [#3867](https://github.com/wailsapp/wails/pull/3867) - Templates for sveltekit and sveltekit-ts that are set for non-SSR development by [atterpac](https://github.com/atterpac) in [#3829](https://github.com/wailsapp/wails/pull/3829) From c25cb02c6ef38af20bd94e75237319a6a96cdbfd Mon Sep 17 00:00:00 2001 From: Anshuman Date: Sat, 30 Nov 2024 13:29:33 +0530 Subject: [PATCH 3/3] Fix exe variable reference --- v3/pkg/application/application_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v3/pkg/application/application_linux.go b/v3/pkg/application/application_linux.go index f5ea2819df7..205532d71c2 100644 --- a/v3/pkg/application/application_linux.go +++ b/v3/pkg/application/application_linux.go @@ -310,7 +310,7 @@ X-GNOME-Autostart-enabled=true Cmd string }{ Name: name, - Cmd: exe, + Cmd: bin, }) }