Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for XDG Desktop Portal #4406

Merged
merged 20 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions app/app_xdg.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import (
"sync"

"github.com/godbus/dbus/v5"
"github.com/rymdport/portal/notification"
"github.com/rymdport/portal/openuri"
"golang.org/x/sys/execabs"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/internal/build"
"fyne.io/fyne/v2/theme"
)

Expand All @@ -28,6 +31,14 @@ func defaultVariant() fyne.ThemeVariant {
}

func (a *fyneApp) OpenURL(url *url.URL) error {
if build.IsFlatpak {
err := openuri.OpenURI("", url.String())
if err != nil {
fyne.LogError("Opening url in portal failed", err)
}
return err
}

cmd := execabs.Command("xdg-open", url.String())
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
return cmd.Start()
Expand All @@ -43,7 +54,7 @@ func findFreedestktopColorScheme() fyne.ThemeVariant {

dbusObj := dbusConn.Object("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop")
call := dbusObj.Call(
"org.freedesktop.portal.Settings.Read",
"org.freedesktop.portal.Settings.ReadOne",
dbus.FlagNoAutoStart,
"org.freedesktop.appearance",
"color-scheme",
Expand Down Expand Up @@ -81,18 +92,45 @@ func (a *fyneApp) SendNotification(n *fyne.Notification) {
return
}

appName := fyne.CurrentApp().UniqueID()
if build.IsFlatpak {
err := a.sendNotificationThroughPortal(conn, n)
if err != nil {
fyne.LogError("Sending notification using portal failed", err)
}
return
}

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),
call := obj.Call("org.freedesktop.Notifications.Notify", 0, a.uniqueID, 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)
}
}

// Sending with same ID replaces the old notification.
var notificationID uint = 0

// See https://flatpak.github.io/xdg-desktop-portal/docs/#gdbus-org.freedesktop.portal.Notification.
func (a *fyneApp) sendNotificationThroughPortal(conn *dbus.Conn, n *fyne.Notification) error {
err := notification.Add(notificationID,
&notification.Content{
Title: n.Title,
Body: n.Content,
Icon: a.uniqueID,
},
)
if err != nil {
return err
}

notificationID++
return nil
}

func (a *fyneApp) saveIconToCache(dirPath, filePath string) error {
err := os.MkdirAll(dirPath, 0700)
if err != nil {
Expand Down
8 changes: 0 additions & 8 deletions dialog/file_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,3 @@ func isHidden(file fyne.URI) bool {
func hideFile(filename string) error {
return nil
}

func fileOpenOSOverride(*FileDialog) bool {
return false
}

func fileSaveOSOverride(*FileDialog) bool {
return false
}
107 changes: 107 additions & 0 deletions dialog/file_xdg_flatpak.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//go:build flatpak && !windows && !android && !ios && !wasm && !js
// +build flatpak,!windows,!android,!ios,!wasm,!js

package dialog

import (
"fmt"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/storage"
"github.com/rymdport/portal/filechooser"
)

func fileOpenOSOverride(d *FileDialog) bool {
go func() {
folderCallback, folder := d.callback.(func(fyne.ListableURI, error))
options := &filechooser.OpenOptions{
Modal: true,
Directory: folder,
AcceptLabel: d.confirmText,
}
if d.startingLocation != nil {
options.Location = d.startingLocation.Path()
}

parentWindowHandle := d.parent.(interface{ GetWindowHandle() string }).GetWindowHandle()

if folder {
uris, err := filechooser.OpenFile(parentWindowHandle, "Open Folder", options)
if err != nil {
folderCallback(nil, err)
}

if len(uris) == 0 {
folderCallback(nil, nil)
return
}

uri, err := storage.ParseURI(uris[0])
if err != nil {
folderCallback(nil, err)
return
}

folderCallback(storage.ListerForURI(uri))
return
}

uris, err := filechooser.OpenFile(parentWindowHandle, "Open File", options)
fileCallback := d.callback.(func(fyne.URIReadCloser, error))
if err != nil {
fileCallback(nil, err)
return
}

if len(uris) == 0 {
fileCallback(nil, nil)
return
}

uri, err := storage.ParseURI(uris[0])
if err != nil {
fileCallback(nil, err)
return
}

fileCallback(storage.Reader(uri))
}()
return true
}

func fileSaveOSOverride(d *FileDialog) bool {
go func() {
options := &filechooser.SaveSingleOptions{
Modal: true,
AcceptLabel: d.confirmText,
FileName: d.initialFileName,
}
if d.startingLocation != nil {
options.Location = d.startingLocation.Path()
}

parentWindowHandle := d.parent.(interface{ GetWindowHandle() string }).GetWindowHandle()
fmt.Println(parentWindowHandle)

callback := d.callback.(func(fyne.URIWriteCloser, error))
uris, err := filechooser.SaveFile(parentWindowHandle, "Open File", options)
if err != nil {
callback(nil, err)
return
}

if len(uris) == 0 {
callback(nil, nil)
return
}

uri, err := storage.ParseURI(uris[0])
if err != nil {
callback(nil, err)
return
}

callback(storage.Writer(uri))
}()
return true
}
12 changes: 12 additions & 0 deletions dialog/file_xdg_notflatpak.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build !flatpak && !windows && !android && !ios && !wasm && !js
// +build !flatpak,!windows,!android,!ios,!wasm,!js

package dialog

func fileOpenOSOverride(d *FileDialog) bool {
return false
}

func fileSaveOSOverride(d *FileDialog) bool {
return false
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ require (
github.com/lucor/goinfo v0.9.0
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/rymdport/portal v0.0.0-20231123202536-da45518a87bb
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef
github.com/stretchr/testify v1.8.4
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/rymdport/portal v0.0.0-20231123202536-da45518a87bb h1:ejEomhJX7G4p8dFImv2zcQd2Oa0IpWIeB/FUZRnpQKg=
github.com/rymdport/portal v0.0.0-20231123202536-da45518a87bb/go.mod h1:RYYAnv4sssTQ7ceErKl7UD8auER/0yFV7CgmfS/uAD8=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
Expand Down
7 changes: 7 additions & 0 deletions internal/build/driver_flatpak.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build flatpak
// +build flatpak

package build

// IsFlatpak is true if the binary is compiled for a Flatpak package.
const IsFlatpak = true
7 changes: 7 additions & 0 deletions internal/build/driver_notflatpak.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build !flatpak
// +build !flatpak

package build

// IsFlatpak is true if the binary is compiled for a Flatpak package.
const IsFlatpak = false
5 changes: 5 additions & 0 deletions internal/driver/glfw/window_notxdg.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ func (w *window) platformResize(canvasSize fyne.Size) {
})
}
}

// GetWindowHandle returns the window handle. Only implemented for X11 currently.
func (w *window) GetWindowHandle() string {
return ""
}
10 changes: 10 additions & 0 deletions internal/driver/glfw/window_wayland.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//go:build wayland && (linux || freebsd || openbsd || netbsd)
// +build wayland
// +build linux freebsd openbsd netbsd

package glfw

// GetWindowHandle returns the window handle. Only implemented for X11 currently.
func (w *window) GetWindowHandle() string {
return "" // TODO: Find a way to get the Wayland handle for xdg_foreign protocol. Return "wayland:{id}".
}
16 changes: 16 additions & 0 deletions internal/driver/glfw/window_x11.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//go:build !wayland && (linux || freebsd || openbsd || netbsd) && !js && !wasm && !test_web_driver
// +build !wayland
// +build linux freebsd openbsd netbsd
// +build !js
// +build !wasm
// +build !test_web_driver

package glfw

import "strconv"

// GetWindowHandle returns the window handle. Only implemented for X11 currently.
func (w *window) GetWindowHandle() string {
xid := uint(w.viewport.GetX11Window())
return "x11:" + strconv.FormatUint(uint64(xid), 16)
}
Loading