//go:build !ci && !js && !android && !ios && !wasm && !test_web_driver // +build !ci,!js,!android,!ios,!wasm,!test_web_driver package app import ( "fmt" "net/url" "os" "path/filepath" "strings" "syscall" "golang.org/x/sys/execabs" "golang.org/x/sys/windows/registry" "fyne.io/fyne/v2" "fyne.io/fyne/v2/theme" ) const notificationTemplate = `$title = "%s" $content = "%s" [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null $template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02) $toastXml = [xml] $template.GetXml() $toastXml.GetElementsByTagName("text")[0].AppendChild($toastXml.CreateTextNode($title)) > $null $toastXml.GetElementsByTagName("text")[1].AppendChild($toastXml.CreateTextNode($content)) > $null $xml = New-Object Windows.Data.Xml.Dom.XmlDocument $xml.LoadXml($toastXml.OuterXml) $toast = [Windows.UI.Notifications.ToastNotification]::new($xml) [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("%s").Show($toast);` func isDark() bool { k, err := registry.OpenKey(registry.CURRENT_USER, `SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize`, registry.QUERY_VALUE) if err != nil { // older version of Windows will not have this key return false } defer k.Close() useLight, _, err := k.GetIntegerValue("AppsUseLightTheme") if err != nil { // older version of Windows will not have this value return false } return useLight == 0 } func defaultVariant() fyne.ThemeVariant { if isDark() { return theme.VariantDark } return theme.VariantLight } func rootConfigDir() string { homeDir, _ := os.UserHomeDir() desktopConfig := filepath.Join(filepath.Join(homeDir, "AppData"), "Roaming") return filepath.Join(desktopConfig, "fyne") } func (a *fyneApp) OpenURL(url *url.URL) error { cmd := execabs.Command("rundll32", "url.dll,FileProtocolHandler", url.String()) cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr return cmd.Run() } var scriptNum = 0 func (a *fyneApp) SendNotification(n *fyne.Notification) { title := escapeNotificationString(n.Title) content := escapeNotificationString(n.Content) appID := a.UniqueID() if appID == "" || strings.Index(appID, "missing-id") == 0 { appID = a.Metadata().Name } script := fmt.Sprintf(notificationTemplate, title, content, appID) go runScript("notify", script) } // SetSystemTrayMenu creates a system tray item and attaches the specified menu. // By default this will use the application icon. func (a *fyneApp) SetSystemTrayMenu(menu *fyne.Menu) { a.Driver().(systrayDriver).SetSystemTrayMenu(menu) } // SetSystemTrayIcon sets a custom image for the system tray icon. // You should have previously called `SetSystemTrayMenu` to initialise the menu icon. func (a *fyneApp) SetSystemTrayIcon(icon fyne.Resource) { a.Driver().(systrayDriver).SetSystemTrayIcon(icon) } func escapeNotificationString(in string) string { noSlash := strings.ReplaceAll(in, "`", "``") return strings.ReplaceAll(noSlash, "\"", "`\"") } func runScript(name, script string) { scriptNum++ appID := fyne.CurrentApp().UniqueID() fileName := fmt.Sprintf("fyne-%s-%s-%d.ps1", appID, name, scriptNum) tmpFilePath := filepath.Join(os.TempDir(), fileName) err := os.WriteFile(tmpFilePath, []byte(script), 0600) if err != nil { fyne.LogError("Could not write script to show notification", err) return } defer os.Remove(tmpFilePath) launch := "(Get-Content -Encoding UTF8 -Path " + tmpFilePath + " -Raw) | Invoke-Expression" cmd := execabs.Command("PowerShell", "-ExecutionPolicy", "Bypass", launch) cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} err = cmd.Run() if err != nil { fyne.LogError("Failed to launch windows notify script", err) } } func watchTheme() { // TODO monitor the Windows theme }