From 05259f6db5bf6bd63096c1193f0546ad39387af7 Mon Sep 17 00:00:00 2001 From: godalming Date: Sat, 15 Mar 2025 12:05:29 +0000 Subject: [PATCH] Add support for session restore on the linux portals backend, fixing #77 --- README.md | 12 +++++-- inputcontrol/controller.go | 4 +-- inputcontrol/controller_null.go | 2 +- inputcontrol/controller_portal.go | 54 ++++++++++++++++++++++++++++-- inputcontrol/controller_uinput.go | 2 +- inputcontrol/controller_windows.go | 2 +- inputcontrol/controller_x11.go | 2 +- main.go | 5 +-- 8 files changed, 70 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 2740e73..fd05b7a 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/inputcontrol/controller.go b/inputcontrol/controller.go index 06ed8e4..3a64df0 100644 --- a/inputcontrol/controller.go +++ b/inputcontrol/controller.go @@ -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 diff --git a/inputcontrol/controller_null.go b/inputcontrol/controller_null.go index 6188cce..8b1ba67 100644 --- a/inputcontrol/controller_null.go +++ b/inputcontrol/controller_null.go @@ -31,7 +31,7 @@ func init() { RegisterController("null", InitNullController, 1000) } -func InitNullController() (Controller, error) { +func InitNullController(saveRestoreToken bool) (Controller, error) { return &nullController{}, nil } diff --git a/inputcontrol/controller_portal.go b/inputcontrol/controller_portal.go index 7d19efc..62512c6 100644 --- a/inputcontrol/controller_portal.go +++ b/inputcontrol/controller_portal.go @@ -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{ diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go index df51e65..b9723a2 100644 --- a/inputcontrol/controller_uinput.go +++ b/inputcontrol/controller_uinput.go @@ -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" diff --git a/inputcontrol/controller_windows.go b/inputcontrol/controller_windows.go index 58b5e9a..0a19556 100644 --- a/inputcontrol/controller_windows.go +++ b/inputcontrol/controller_windows.go @@ -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} diff --git a/inputcontrol/controller_x11.go b/inputcontrol/controller_x11.go index bb37d37..98fcbc4 100644 --- a/inputcontrol/controller_x11.go +++ b/inputcontrol/controller_x11.go @@ -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{ diff --git a/main.go b/main.go index 3d05c7e..44cf381 100644 --- a/main.go +++ b/main.go @@ -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 {