| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- //go:build !ci && !js && !wasm && !test_web_driver && (linux || openbsd || freebsd || netbsd) && !android
- // +build !ci
- // +build !js
- // +build !wasm
- // +build !test_web_driver
- // +build linux openbsd freebsd netbsd
- // +build !android
- package app
- import (
- "net/url"
- "os"
- "path/filepath"
- "sync"
- "github.com/godbus/dbus/v5"
- "golang.org/x/sys/execabs"
- "fyne.io/fyne/v2"
- "fyne.io/fyne/v2/theme"
- )
- var once sync.Once
- func defaultVariant() fyne.ThemeVariant {
- return findFreedestktopColorScheme()
- }
- func (a *fyneApp) OpenURL(url *url.URL) error {
- cmd := execabs.Command("xdg-open", url.String())
- cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
- return cmd.Start()
- }
- // fetch color variant from dbus portal desktop settings.
- func findFreedestktopColorScheme() fyne.ThemeVariant {
- dbusConn, err := dbus.SessionBus()
- if err != nil {
- fyne.LogError("Unable to connect to session D-Bus", err)
- return theme.VariantDark
- }
- dbusObj := dbusConn.Object("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop")
- call := dbusObj.Call(
- "org.freedesktop.portal.Settings.Read",
- dbus.FlagNoAutoStart,
- "org.freedesktop.appearance",
- "color-scheme",
- )
- if call.Err != nil {
- // many desktops don't have this exported yet
- return theme.VariantDark
- }
- var value uint8
- if err = call.Store(&value); err != nil {
- fyne.LogError("failed to read theme variant from D-Bus", err)
- return theme.VariantDark
- }
- // See: https://github.com/flatpak/xdg-desktop-portal/blob/1.16.0/data/org.freedesktop.impl.portal.Settings.xml#L32-L46
- // 0: No preference
- // 1: Prefer dark appearance
- // 2: Prefer light appearance
- switch value {
- case 2:
- return theme.VariantLight
- case 1:
- return theme.VariantDark
- default:
- // Default to light theme to support Gnome's default see https://github.com/fyne-io/fyne/pull/3561
- return theme.VariantLight
- }
- }
- func (a *fyneApp) SendNotification(n *fyne.Notification) {
- conn, err := dbus.SessionBus() // shared connection, don't close
- if err != nil {
- fyne.LogError("Unable to connect to session D-Bus", err)
- return
- }
- appName := fyne.CurrentApp().UniqueID()
- appIcon := a.cachedIconPath()
- timeout := int32(0) // we don't support this yet
- obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
- call := obj.Call("org.freedesktop.Notifications.Notify", 0, appName, uint32(0),
- appIcon, n.Title, n.Content, []string{}, map[string]dbus.Variant{}, timeout)
- if call.Err != nil {
- fyne.LogError("Failed to send message to bus", call.Err)
- }
- }
- func (a *fyneApp) saveIconToCache(dirPath, filePath string) error {
- err := os.MkdirAll(dirPath, 0700)
- if err != nil {
- fyne.LogError("Unable to create application cache directory", err)
- return err
- }
- file, err := os.Create(filePath)
- if err != nil {
- fyne.LogError("Unable to create icon file", err)
- return err
- }
- defer file.Close()
- if icon := a.Icon(); icon != nil {
- _, err = file.Write(icon.Content())
- if err != nil {
- fyne.LogError("Unable to write icon contents", err)
- return err
- }
- }
- return nil
- }
- func (a *fyneApp) cachedIconPath() string {
- if a.Icon() == nil {
- return ""
- }
- dirPath := filepath.Join(rootCacheDir(), a.UniqueID())
- filePath := filepath.Join(dirPath, "icon.png")
- once.Do(func() {
- err := a.saveIconToCache(dirPath, filePath)
- if err != nil {
- filePath = ""
- }
- })
- return filePath
- }
- // 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) {
- if desk, ok := a.Driver().(systrayDriver); ok { // don't use this on mobile tag
- desk.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) {
- if desk, ok := a.Driver().(systrayDriver); ok { // don't use this on mobile tag
- desk.SetSystemTrayIcon(icon)
- }
- }
- func rootConfigDir() string {
- desktopConfig, _ := os.UserConfigDir()
- return filepath.Join(desktopConfig, "fyne")
- }
- func rootCacheDir() string {
- desktopCache, _ := os.UserCacheDir()
- return filepath.Join(desktopCache, "fyne")
- }
- func watchTheme() {
- go watchFreedekstopThemeChange()
- }
- func themeChanged() {
- fyne.CurrentApp().Settings().(*settings).setupTheme()
- }
- // connect to dbus to detect color-schem theme changes in portal settings.
- func watchFreedekstopThemeChange() {
- conn, err := dbus.SessionBus()
- if err != nil {
- fyne.LogError("Unable to connect to session D-Bus", err)
- return
- }
- if err := conn.AddMatchSignal(
- dbus.WithMatchObjectPath("/org/freedesktop/portal/desktop"),
- dbus.WithMatchInterface("org.freedesktop.portal.Settings"),
- dbus.WithMatchMember("SettingChanged"),
- ); err != nil {
- fyne.LogError("D-Bus signal match failed", err)
- return
- }
- defer conn.Close()
- dbusChan := make(chan *dbus.Signal)
- conn.Signal(dbusChan)
- for sig := range dbusChan {
- for _, v := range sig.Body {
- if v == "color-scheme" {
- themeChanged()
- break
- }
- }
- }
- }
|