Add support for session restore on the linux portals backend, fixing #77

This commit is contained in:
godalming 2025-03-15 12:05:29 +00:00 committed by Unrud
parent 7a806f7b00
commit c6c54c77a7
8 changed files with 70 additions and 13 deletions

View file

@ -12,8 +12,16 @@ Supports Flatpak's RemoteDesktop portal (for Wayland), Windows and X11.
* [Snap](https://snapcraft.io/remote-touchpad)
* [Windows](https://github.com/Unrud/remote-touchpad/releases/latest)
* Golang:
* Portal & uinput & X11: `go install -tags portal,uinput,x11 github.com/unrud/remote-touchpad@latest`
* Windows: `go install github.com/unrud/remote-touchpad@latest`
* Portal & uinput & X11:
```sh
go install -tags portal,uinput,x11 github.com/unrud/remote-touchpad@latest
```
* Windows:
```sh
go install github.com/unrud/remote-touchpad@latest
```
## Screenshots

View file

@ -55,14 +55,14 @@ const (
type ControllerInfo struct {
Name string
Init func() (Controller, error)
Init func(bool) (Controller, error)
priority int
}
var Controllers []ControllerInfo
func RegisterController(name string, init func() (Controller, error), priority int) {
func RegisterController(name string, init func(bool) (Controller, error), priority int) {
Controllers = append(Controllers, ControllerInfo{name, init, priority})
sort.SliceStable(Controllers, func(i, j int) bool {
return Controllers[i].priority < Controllers[j].priority

View file

@ -31,7 +31,7 @@ func init() {
RegisterController("null", InitNullController, 1000)
}
func InitNullController() (Controller, error) {
func InitNullController(saveRestoreToken bool) (Controller, error) {
return &nullController{}, nil
}

View file

@ -25,6 +25,8 @@ import (
"errors"
"fmt"
"github.com/godbus/dbus/v5"
"os"
"path/filepath"
)
const (
@ -34,6 +36,8 @@ const (
btnReleased uint32 = 0
btnPressed uint32 = 1
untilRevoked uint32 = 2
// linux/input-event-codes.h
btnLeft int32 = 0x110
btnRight int32 = 0x111
@ -50,7 +54,26 @@ func init() {
RegisterController("RemoteDesktop portal", InitPortalController, 1)
}
func InitPortalController() (Controller, error) {
func InitPortalController(saveRestoreToken bool) (Controller, error) {
cacheDirectory, err := os.UserCacheDir()
var restoreTokenFilePath string
var restoreToken string
if err != nil {
err = errors.Join(errors.New("Cannot get user cache directory, therefore cannot get restore token file path in order to read or save the restore token. "), err)
if saveRestoreToken == true {
return nil, err
} else {
fmt.Println(err.Error())
}
} else {
restoreTokenFilePath = filepath.Join(cacheDirectory, "remote_touchpad_portals_restore_token")
restoreTokenBytes, err := os.ReadFile(restoreTokenFilePath)
if err != nil {
fmt.Printf("Failed to read restore token file: %s\n", err)
} else {
restoreToken = string(restoreTokenBytes)
}
}
bus, err := dbus.SessionBusPrivate()
if err != nil {
return nil, &UnsupportedPlatformError{err}
@ -81,10 +104,18 @@ func InitPortalController() (Controller, error) {
return nil, &UnsupportedPlatformError{
errors.New("unexpected 'AvailableDeviceTypes' return type")}
}
if availableDeviceTypes&deviceKeyboard == 0 ||
if availableDeviceTypes&deviceKeyboard == 0 &&
availableDeviceTypes&devicePointer == 0 {
return nil, &UnsupportedPlatformError{
errors.New("keyboard or pointer source type not supported")}
errors.New("keyboard and pointer source type not supported")}
}
if availableDeviceTypes&deviceKeyboard == 0 {
return nil, &UnsupportedPlatformError{
errors.New("keyboard source type not supported")}
}
if availableDeviceTypes&devicePointer == 0 {
return nil, &UnsupportedPlatformError{
errors.New("pointer source type not supported")}
}
inVardict := make(map[string]dbus.Variant)
inVardict["session_handle_token"] = dbus.MakeVariant("t")
@ -110,6 +141,12 @@ func InitPortalController() (Controller, error) {
sessionHandle := dbus.ObjectPath(sessionHandleS)
inVardict = make(map[string]dbus.Variant)
inVardict["types"] = dbus.MakeVariant(deviceKeyboard | devicePointer)
if restoreToken != "" {
inVardict["restore_token"] = dbus.MakeVariant(restoreToken)
}
if saveRestoreToken {
inVardict["persist_mode"] = dbus.MakeVariant(untilRevoked)
}
result, outVardict, err = getResponse(bus, remoteDesktop,
"org.freedesktop.portal.RemoteDesktop.SelectDevices", 0, sessionHandle, inVardict)
if err != nil {
@ -128,6 +165,17 @@ func InitPortalController() (Controller, error) {
if result != 0 {
return nil, errors.New("keyboard or pointer access denied")
}
if saveRestoreToken {
restoreToken, ok := outVardict["restore_token"].Value().(string)
if !ok {
return nil, errors.New("Failed to get new restore token")
} else {
err := os.WriteFile(restoreTokenFilePath, []byte(restoreToken), 0644)
if err != nil {
return nil, errors.Join(errors.New("Failed to write restore token: "), err)
}
}
}
devicesV, ok := outVardict["devices"]
if !ok {
return nil, &UnsupportedPlatformError{

View file

@ -43,7 +43,7 @@ func init() {
RegisterController("uinput", InitUinputController, 10)
}
func InitUinputController() (Controller, error) {
func InitUinputController(saveRestoreToken bool) (Controller, error) {
keymapName, keymapSet := os.LookupEnv("REMOTE_TOUCHPAD_UINPUT_KEYMAP")
if !keymapSet {
keymapName = "defkeymap"

View file

@ -95,7 +95,7 @@ func init() {
RegisterController("Windows", InitWindowsController, 0)
}
func InitWindowsController() (Controller, error) {
func InitWindowsController(saveRestoreToken bool) (Controller, error) {
p := &windowsController{}
if err := sendInputProc.Find(); err != nil {
return nil, &UnsupportedPlatformError{err}

View file

@ -59,7 +59,7 @@ func init() {
RegisterController("X11", InitX11Controller, 0)
}
func InitX11Controller() (Controller, error) {
func InitX11Controller(saveRestoreToken bool) (Controller, error) {
display := C.XOpenDisplay(nil)
if display == nil {
return nil, &UnsupportedPlatformError{

View file

@ -152,7 +152,7 @@ func secureRandBase64(length int) string {
func main() {
terminal.SetTitle(prettyAppName)
var bind, certFile, keyFile, secret string
var showVersion bool
var showVersion, savePlaintextRestoreToken bool
var config config
flag.BoolVar(&showVersion, "version", false, "show program's version number and exit")
flag.StringVar(&bind, "bind", defaultBind, "bind server to [HOSTNAME]:PORT")
@ -164,6 +164,7 @@ func main() {
flag.Float64Var(&config.ScrollSpeed, "scroll-speed", 1, "scroll speed multiplier")
flag.Float64Var(&config.MouseMoveSpeed, "mouse-move-speed", 1, "mouse move speed multiplier")
flag.Float64Var(&config.MouseScrollSpeed, "mouse-scroll-speed", 1, "mouse scroll speed multiplier")
flag.BoolVar(&savePlaintextRestoreToken, "save-plaintext-restore-token", false, "save a restore token in plaintext in order to avoid the confirmation dialogue that appears with some implementations of the portals backend on wayland on linux from appearing on the next run of remote-touchpad")
flag.Parse()
if showVersion {
fmt.Println(version)
@ -188,7 +189,7 @@ func main() {
for _, controllerInfo := range inputcontrol.Controllers {
controllerName = controllerInfo.Name
var err error
controller, err = controllerInfo.Init()
controller, err = controllerInfo.Init(savePlaintextRestoreToken)
if err == nil {
break
} else {