From c1491f4a843ffc2f72e5b730283dd4970d8fd37f Mon Sep 17 00:00:00 2001 From: Unrud Date: Mon, 26 Sep 2022 22:42:55 +0200 Subject: [PATCH 01/96] Snap: Update to core20 --- snap/snapcraft.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index f32c543..c7f1d92 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -9,7 +9,7 @@ license: GPL-3.0+ grade: stable adopt-info: remote-touchpad -base: core18 +base: core20 confinement: strict apps: @@ -20,7 +20,7 @@ apps: - desktop - x11 remote-touchpad-wait-on-error: - command: usr/bin/remote-touchpad-wait-on-error + command: bin/remote-touchpad-wait-on-error plugs: - network-bind - desktop @@ -44,9 +44,13 @@ parts: stage-packages: - libxt6 - libxtst6 + override-pull: | + snapcraftctl pull + # WORKAROUND: Point icon directly to SVG otherwise snapcraft can't find it + sed -e 's|Icon=com.github.unrud.RemoteTouchpad|Icon=/usr/share/icons/hicolor/scalable/apps/com.github.unrud.RemoteTouchpad.svg|' -i desktop/com.github.unrud.RemoteTouchpad.desktop override-build: | snapcraftctl build - install -Dm0755 -t "${SNAPCRAFT_PART_INSTALL}/usr/bin" desktop/remote-touchpad-wait-on-error + install -Dm0755 -t "${SNAPCRAFT_PART_INSTALL}/bin" desktop/remote-touchpad-wait-on-error install -Dm0644 -t "${SNAPCRAFT_PART_INSTALL}/usr/share/metainfo" desktop/com.github.unrud.RemoteTouchpad.metainfo.xml install -Dm0644 -t "${SNAPCRAFT_PART_INSTALL}/usr/share/applications" desktop/com.github.unrud.RemoteTouchpad.desktop install -Dm0644 -t "${SNAPCRAFT_PART_INSTALL}/usr/share/icons/hicolor/scalable/apps" desktop/com.github.unrud.RemoteTouchpad.svg From 2fc5d0825b7248979d662694b82983491816c10d Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 3 Nov 2022 01:56:15 +0100 Subject: [PATCH 02/96] Flatpak: Remove x11 from finish-args --- flatpak/com.github.unrud.RemoteTouchpad.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/flatpak/com.github.unrud.RemoteTouchpad.yaml b/flatpak/com.github.unrud.RemoteTouchpad.yaml index 8fecb8d..03515b2 100644 --- a/flatpak/com.github.unrud.RemoteTouchpad.yaml +++ b/flatpak/com.github.unrud.RemoteTouchpad.yaml @@ -6,7 +6,6 @@ command: remote-touchpad sdk-extensions: - org.freedesktop.Sdk.Extension.golang finish-args: - - --socket=x11 - --socket=fallback-x11 - --share=network modules: From 681d9c4f1da7f51be7ef21dfb943cf9677785c4b Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 29 Dec 2022 22:19:38 +0100 Subject: [PATCH 03/96] Improve XWayland detection --- inputcontrol/controller_x11.go | 39 ++++++++++++++++++++++++++-------- snap/snapcraft.yaml | 2 ++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/inputcontrol/controller_x11.go b/inputcontrol/controller_x11.go index 3d7c9f7..5ccf236 100644 --- a/inputcontrol/controller_x11.go +++ b/inputcontrol/controller_x11.go @@ -21,16 +21,19 @@ package inputcontrol -// #cgo LDFLAGS: -lX11 -lXtst +// #cgo LDFLAGS: -lX11 -lXrandr -lXtst // #include // #include +// #include // #include // #include +// Window MacroDefaultRootWindow(Display *dpy) { +// return DefaultRootWindow(dpy); +// } import "C" import ( "errors" - "fmt" - "os" + "strings" "sync" "time" "unsafe" @@ -55,17 +58,35 @@ func init() { } func InitX11Controller() (Controller, error) { - sessionType := os.Getenv("XDG_SESSION_TYPE") - if sessionType != "" && sessionType != "x11" { - return nil, UnsupportedPlatformError{errors.New(fmt.Sprintf( - "unsupported session type '%v'", sessionType))} - } display := C.XOpenDisplay(nil) if display == nil { return nil, UnsupportedPlatformError{ errors.New("failed to connect to X server")} } - return &x11Controller{display: display}, nil + p := &x11Controller{display: display} + if p.xIsXwayland() { + p.Close() + return nil, UnsupportedPlatformError{ + errors.New("X server is Xwayland")} + } + return p, nil +} + +func (p *x11Controller) xIsXwayland() bool { + resources := C.XRRGetScreenResourcesCurrent(p.display, C.MacroDefaultRootWindow(p.display)) + if resources == nil { + return false + } + defer C.XRRFreeScreenResources(resources) + if resources.noutput < 1 { + return false + } + output := C.XRRGetOutputInfo(p.display, resources, *resources.outputs) + if output == nil { + return false + } + defer C.XRRFreeOutputInfo(output) + return strings.HasPrefix(C.GoString(output.name), "XWAYLAND") } func (p *x11Controller) Close() error { diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index c7f1d92..26fa53a 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -40,9 +40,11 @@ parts: - gcc - libc6-dev - libxt-dev + - libxrandr-dev - libxtst-dev stage-packages: - libxt6 + - libxrandr2 - libxtst6 override-pull: | snapcraftctl pull From f30b4cf48eee8980106e7d86982010c03a90ba54 Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 29 Dec 2022 22:20:49 +0100 Subject: [PATCH 04/96] Flatpak: Add Wayland socket to disable X11 socket --- flatpak/com.github.unrud.RemoteTouchpad.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/flatpak/com.github.unrud.RemoteTouchpad.yaml b/flatpak/com.github.unrud.RemoteTouchpad.yaml index 03515b2..9df8822 100644 --- a/flatpak/com.github.unrud.RemoteTouchpad.yaml +++ b/flatpak/com.github.unrud.RemoteTouchpad.yaml @@ -7,6 +7,7 @@ sdk-extensions: - org.freedesktop.Sdk.Extension.golang finish-args: - --socket=fallback-x11 + - --socket=wayland - --share=network modules: - name: remote-touchpad From 9c5dcbee106c304686b429687d7fdd0c33ff2af6 Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 29 Dec 2022 22:28:10 +0100 Subject: [PATCH 05/96] Update dependencies --- flatpak/com.github.unrud.RemoteTouchpad.yaml | 3 ++- go.mod | 2 +- go.sum | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/flatpak/com.github.unrud.RemoteTouchpad.yaml b/flatpak/com.github.unrud.RemoteTouchpad.yaml index 9df8822..e826da7 100644 --- a/flatpak/com.github.unrud.RemoteTouchpad.yaml +++ b/flatpak/com.github.unrud.RemoteTouchpad.yaml @@ -28,7 +28,8 @@ modules: dest: vendor/github.com/skip2/go-qrcode - type: git url: https://github.com/golang/net - commit: 8be639271d50c5f812a860bf5b9c21d50f14e8d1 + tag: v0.4.0 + commit: 1e63c2f08a10a150fa02c50ece89b340ae64efe4 dest: vendor/golang.org/x/net buildsystem: simple build-commands: diff --git a/go.mod b/go.mod index d774cf9..d593471 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,5 @@ go 1.17 require ( github.com/godbus/dbus/v5 v5.1.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - golang.org/x/net v0.0.0-20220923203811-8be639271d50 + golang.org/x/net v0.4.0 ) diff --git a/go.sum b/go.sum index 792c79a..fcbadb0 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI= golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 79a88dc6763d4bcb923e4f0fecca9081b77ce240 Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 29 Dec 2022 22:32:23 +0100 Subject: [PATCH 06/96] Bump version to 1.2.3 --- CHANGELOG.md | 6 ++++++ ...com.github.unrud.RemoteTouchpad.metainfo.xml | 17 +++++++++++++---- main.go | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d42f03f..1f7f63a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.2.3 (2022-12-29) + +* Improve XWayland detection +* Update dependencies +* Flatpak: Add Wayland socket to disable X11 socket + ## 1.2.2 (2022-09-23) * Update dependencies diff --git a/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml b/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml index 87ba074..7096033 100644 --- a/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml +++ b/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml @@ -19,6 +19,15 @@ https://github.com/Unrud/remote-touchpad Unrud + + +
    +
  • Improve XWayland detection
  • +
  • Update dependencies
  • +
  • Flatpak: Add Wayland socket to disable X11 socket
  • +
+
+
    @@ -250,16 +259,16 @@ - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.2/screenshots/1.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.3/screenshots/1.png - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.2/screenshots/2.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.3/screenshots/2.png - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.2/screenshots/3.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.3/screenshots/3.png - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.2/screenshots/4.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.3/screenshots/4.png diff --git a/main.go b/main.go index 5a7e7a0..295ec5c 100644 --- a/main.go +++ b/main.go @@ -47,7 +47,7 @@ const ( authenticationRateBurst int = 10 challengeLength int = 8 defaultBind string = ":0" - version string = "1.2.2" + version string = "1.2.3" prettyAppName string = "Remote Touchpad" ) From 6f03281fc753ac1ef8cf247529488cdad4384b71 Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 23 Feb 2023 01:09:03 +0100 Subject: [PATCH 07/96] Refactor: Use switch statement --- inputcontrol/controller_portal.go | 9 ++--- inputcontrol/controller_windows.go | 39 ++++++++++---------- inputcontrol/controller_x11.go | 12 +++---- inputcontrol/keysyms.go | 57 +++++++++++------------------- 4 files changed, 52 insertions(+), 65 deletions(-) diff --git a/inputcontrol/controller_portal.go b/inputcontrol/controller_portal.go index a9e503f..b5db467 100644 --- a/inputcontrol/controller_portal.go +++ b/inputcontrol/controller_portal.go @@ -237,13 +237,14 @@ func (p *portalController) PointerButton(button PointerButton, press bool) error return errors.New("dbus connection closed") } var btn int32 - if button == PointerButtonLeft { + switch button { + case PointerButtonLeft: btn = btnLeft - } else if button == PointerButtonMiddle { + case PointerButtonMiddle: btn = btnMiddle - } else if button == PointerButtonRight { + case PointerButtonRight: btn = btnRight - } else { + default: return errors.New("unsupported pointer button") } state := btnReleased diff --git a/inputcontrol/controller_windows.go b/inputcontrol/controller_windows.go index eb844b2..6184187 100644 --- a/inputcontrol/controller_windows.go +++ b/inputcontrol/controller_windows.go @@ -135,43 +135,44 @@ func (p *windowsController) KeyboardText(text string) error { func (p *windowsController) KeyboardKey(key Key) error { input := keybdInput{typ: inputKeyboard} - if key == KeyBackSpace { + switch key { + case KeyBackSpace: input.wVk = vkBack - } else if key == KeyReturn { + case KeyReturn: input.wVk = vkReturn - } else if key == KeyEnd { + case KeyEnd: input.wVk = vkEnd - } else if key == KeyHome { + case KeyHome: input.wVk = vkHome - } else if key == KeyLeft { + case KeyLeft: input.wVk = vkLeft - } else if key == KeyUp { + case KeyUp: input.wVk = vkUp - } else if key == KeyRight { + case KeyRight: input.wVk = vkRight - } else if key == KeyDown { + case KeyDown: input.wVk = vkDown - } else if key == KeyDelete { + case KeyDelete: input.wVk = vkDelete - } else if key == KeySuper { + case KeySuper: input.wVk = vkLwin - } else if key == KeyBrowserBack { + case KeyBrowserBack: input.wVk = vkBrowserBack - } else if key == KeyBrowserForward { + case KeyBrowserForward: input.wVk = vkBrowserForward - } else if key == KeyVolumeMute { + case KeyVolumeMute: input.wVk = vkVolumeMute - } else if key == KeyVolumeDown { + case KeyVolumeDown: input.wVk = vkVolumeDown - } else if key == KeyVolumeUp { + case KeyVolumeUp: input.wVk = vkVolumeUp - } else if key == KeyMediaNextTrack { + case KeyMediaNextTrack: input.wVk = vkMediaNextTrack - } else if key == KeyMediaPrevTrack { + case KeyMediaPrevTrack: input.wVk = vkMediaPrevTrack - } else if key == KeyMediaPlayPause { + case KeyMediaPlayPause: input.wVk = vkMediaPlayPause - } else { + default: return errors.New("key not mapped to virtual-key code") } inputs := [...]keybdInput{input, input} diff --git a/inputcontrol/controller_x11.go b/inputcontrol/controller_x11.go index 5ccf236..87c29bd 100644 --- a/inputcontrol/controller_x11.go +++ b/inputcontrol/controller_x11.go @@ -301,16 +301,16 @@ func (p *x11Controller) sendButton(button uint, press bool) error { } func (p *x11Controller) PointerButton(button PointerButton, press bool) error { - if button == PointerButtonLeft { + switch button { + case PointerButtonLeft: return p.sendButton(1, press) - } - if button == PointerButtonRight { + case PointerButtonRight: return p.sendButton(3, press) - } - if button == PointerButtonMiddle { + case PointerButtonMiddle: return p.sendButton(2, press) + default: + return errors.New("unsupported pointer button") } - return errors.New("unsupported pointer button") } func (p *x11Controller) PointerMove(deltaX, deltaY int) error { diff --git a/inputcontrol/keysyms.go b/inputcontrol/keysyms.go index 5402120..2af1bb8 100644 --- a/inputcontrol/keysyms.go +++ b/inputcontrol/keysyms.go @@ -66,59 +66,44 @@ func RuneToKeysym(runeValue rune) (Keysym, error) { } func KeyToKeysym(key Key) (Keysym, error) { - if key == KeyBackSpace { + switch key { + case KeyBackSpace: return xkBackSpace, nil - } - if key == KeyReturn { + case KeyReturn: return xkReturn, nil - } - if key == KeyDelete { + case KeyDelete: return xkDelete, nil - } - if key == KeyHome { + case KeyHome: return xkHome, nil - } - if key == KeyLeft { + case KeyLeft: return xkLeft, nil - } - if key == KeyUp { + case KeyUp: return xkUp, nil - } - if key == KeyRight { + case KeyRight: return xkRight, nil - } - if key == KeyDown { + case KeyDown: return xkDown, nil - } - if key == KeyEnd { + case KeyEnd: return xkEnd, nil - } - if key == KeySuper { + case KeySuper: return xkSuperL, nil - } - if key == KeyVolumeMute { + case KeyVolumeMute: return xf86xkAudioMute, nil - } - if key == KeyVolumeDown { + case KeyVolumeDown: return xf86xkAudioLowerVolume, nil - } - if key == KeyVolumeUp { + case KeyVolumeUp: return xf86xkAudioRaiseVolume, nil - } - if key == KeyMediaPlayPause { + case KeyMediaPlayPause: return xf86xkAudioPlay, nil - } - if key == KeyMediaPrevTrack { + case KeyMediaPrevTrack: return xf86xkAudioPrev, nil - } - if key == KeyMediaNextTrack { + case KeyMediaNextTrack: return xf86xkAudioNext, nil - } - if key == KeyBrowserBack { + case KeyBrowserBack: return xf86xkBack, nil - } - if key == KeyBrowserForward { + case KeyBrowserForward: return xf86xkForward, nil + default: + return 0, errors.New("key not mapped to keysym") } - return 0, errors.New("key not mapped to keysym") } From 124152f7ece0bf3588d0b96c43d57c30a84db38a Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 23 Feb 2023 01:09:04 +0100 Subject: [PATCH 08/96] Include missing rune, key or button in error --- inputcontrol/controller_portal.go | 2 +- inputcontrol/controller_windows.go | 6 +++--- inputcontrol/controller_x11.go | 3 ++- inputcontrol/keysyms.go | 8 ++++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/inputcontrol/controller_portal.go b/inputcontrol/controller_portal.go index b5db467..05ffb1d 100644 --- a/inputcontrol/controller_portal.go +++ b/inputcontrol/controller_portal.go @@ -245,7 +245,7 @@ func (p *portalController) PointerButton(button PointerButton, press bool) error case PointerButtonRight: btn = btnRight default: - return errors.New("unsupported pointer button") + return fmt.Errorf("unsupported pointer button: %#v", button) } state := btnReleased if press { diff --git a/inputcontrol/controller_windows.go b/inputcontrol/controller_windows.go index 6184187..8cb73a7 100644 --- a/inputcontrol/controller_windows.go +++ b/inputcontrol/controller_windows.go @@ -22,7 +22,7 @@ package inputcontrol import ( - "errors" + "fmt" "syscall" "unsafe" ) @@ -173,7 +173,7 @@ func (p *windowsController) KeyboardKey(key Key) error { case KeyMediaPlayPause: input.wVk = vkMediaPlayPause default: - return errors.New("key not mapped to virtual-key code") + return fmt.Errorf("key not mapped to virtual-key code: %#v", key) } inputs := [...]keybdInput{input, input} inputs[1].dwFlags |= keyeventfKeyup @@ -195,7 +195,7 @@ func (p *windowsController) PointerButton(button PointerButton, press bool) erro } else if button == PointerButtonRight { input.dwFlags = mouseeventfRightup } else { - return errors.New("unsupported pointer button") + return fmt.Errorf("unsupported pointer button: %#v", button) } if r, _, err := sendInputProc.Call(1, uintptr(unsafe.Pointer(&input)), unsafe.Sizeof(input)); int(r) != 1 { diff --git a/inputcontrol/controller_x11.go b/inputcontrol/controller_x11.go index 87c29bd..bc1ec6b 100644 --- a/inputcontrol/controller_x11.go +++ b/inputcontrol/controller_x11.go @@ -33,6 +33,7 @@ package inputcontrol import "C" import ( "errors" + "fmt" "strings" "sync" "time" @@ -309,7 +310,7 @@ func (p *x11Controller) PointerButton(button PointerButton, press bool) error { case PointerButtonMiddle: return p.sendButton(2, press) default: - return errors.New("unsupported pointer button") + return fmt.Errorf("unsupported pointer button: %#v", button) } } diff --git a/inputcontrol/keysyms.go b/inputcontrol/keysyms.go index 2af1bb8..de183a1 100644 --- a/inputcontrol/keysyms.go +++ b/inputcontrol/keysyms.go @@ -23,7 +23,7 @@ package inputcontrol //go:generate go run keysyms.generator.go -import "errors" +import "fmt" type Keysym int32 @@ -57,8 +57,8 @@ func RuneToKeysym(runeValue rune) (Keysym, error) { keysym, found := keysymsMap[runeValue] if !found { if runeValue < 0x100 || runeValue > 0x10ffff { - return 0, errors.New("rune not mappend to keysym and " + - "out of range for direct unicode mapping") + return 0, fmt.Errorf("rune not mappend to keysym and "+ + "out of range for direct unicode mapping: %q", runeValue) } keysym = Keysym(0x01000000 + runeValue) } @@ -104,6 +104,6 @@ func KeyToKeysym(key Key) (Keysym, error) { case KeyBrowserForward: return xf86xkForward, nil default: - return 0, errors.New("key not mapped to keysym") + return 0, fmt.Errorf("key not mapped to keysym: %#v", key) } } From 342334ac184f6cb3c947747093bfe17ca4a88b40 Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 23 Feb 2023 01:09:05 +0100 Subject: [PATCH 09/96] Update dependencies --- flatpak/com.github.unrud.RemoteTouchpad.yaml | 4 +-- go.mod | 2 +- go.sum | 29 ++++++++++++++++---- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/flatpak/com.github.unrud.RemoteTouchpad.yaml b/flatpak/com.github.unrud.RemoteTouchpad.yaml index e826da7..2883668 100644 --- a/flatpak/com.github.unrud.RemoteTouchpad.yaml +++ b/flatpak/com.github.unrud.RemoteTouchpad.yaml @@ -28,8 +28,8 @@ modules: dest: vendor/github.com/skip2/go-qrcode - type: git url: https://github.com/golang/net - tag: v0.4.0 - commit: 1e63c2f08a10a150fa02c50ece89b340ae64efe4 + tag: v0.7.0 + commit: 8e2b117aee74f6b86c207a808b0255de45c0a18a dest: vendor/golang.org/x/net buildsystem: simple build-commands: diff --git a/go.mod b/go.mod index d593471..1263b83 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,5 @@ go 1.17 require ( github.com/godbus/dbus/v5 v5.1.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - golang.org/x/net v0.4.0 + golang.org/x/net v0.7.0 ) diff --git a/go.sum b/go.sum index fcbadb0..21187b6 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,31 @@ github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From f5d19a035494d5389d40fddba38616901c84c152 Mon Sep 17 00:00:00 2001 From: De_Coder Date: Sun, 19 Feb 2023 16:29:46 +0100 Subject: [PATCH 10/96] Add uinput-based controller --- go.mod | 2 + go.sum | 2 + inputcontrol/controller_uinput.go | 195 ++++++++++++++++++++++++++++++ inputcontrol/keysyms.generated.go | 2 +- inputcontrol/keysyms.generator.go | 2 +- inputcontrol/keysyms.go | 2 +- 6 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 inputcontrol/controller_uinput.go diff --git a/go.mod b/go.mod index 1263b83..d0cdff7 100644 --- a/go.mod +++ b/go.mod @@ -7,3 +7,5 @@ require ( github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e golang.org/x/net v0.7.0 ) + +require github.com/bendahl/uinput v1.6.0 // indirect diff --git a/go.sum b/go.sum index 21187b6..bb1487e 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/bendahl/uinput v1.6.0 h1:fM6r3OSC17rHh758mizKjSBuqi+XinhiGd4N3pWvZiI= +github.com/bendahl/uinput v1.6.0/go.mod h1:Np7w3DINc9wB83p12fTAM3DPPhFnAKP0WTXRqCQJ6Z8= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go new file mode 100644 index 0000000..0b16f10 --- /dev/null +++ b/inputcontrol/controller_uinput.go @@ -0,0 +1,195 @@ +//go:build uinput + +/* + * Copyright (c) 2023 De_Coder github.com/ps100000 + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +package inputcontrol + +import "github.com/bendahl/uinput" +import "errors" + +var ukeysMap = map[Keysym]int { + 0x0061: uinput.KeyA, + 0x0062: uinput.KeyB, + 0x0063: uinput.KeyC, + 0x0064: uinput.KeyD, + 0x0065: uinput.KeyE, + 0x0066: uinput.KeyF, + 0x0067: uinput.KeyG, + 0x0068: uinput.KeyH, + 0x0069: uinput.KeyI, + 0x006a: uinput.KeyJ, + 0x006b: uinput.KeyK, + 0x006c: uinput.KeyL, + 0x006d: uinput.KeyM, + 0x006e: uinput.KeyN, + 0x006f: uinput.KeyO, + 0x0070: uinput.KeyP, + 0x0071: uinput.KeyQ, + 0x0072: uinput.KeyR, + 0x0073: uinput.KeyS, + 0x0074: uinput.KeyT, + 0x0075: uinput.KeyU, + 0x0076: uinput.KeyV, + 0x0077: uinput.KeyW, + 0x0078: uinput.KeyX, + 0x0079: uinput.KeyY, + 0x007a: uinput.KeyZ, + + 0x0030: uinput.Key0, + 0x0031: uinput.Key1, + 0x0032: uinput.Key2, + 0x0033: uinput.Key3, + 0x0034: uinput.Key4, + 0x0035: uinput.Key5, + 0x0036: uinput.Key6, + 0x0037: uinput.Key7, + 0x0038: uinput.Key8, + 0x0039: uinput.Key9, + + 0xff1b: uinput.KeyEsc, + 0x002d: uinput.KeyMinus, + 0x002b: uinput.KeyKpplus, + 0x002a: uinput.KeyKpasterisk, + 0x003d: uinput.KeyEqual, + 0xff08: uinput.KeyBackspace, + 0xff09: uinput.KeyTab, + 0x0028: uinput.KeyLeftbrace, + 0x0029: uinput.KeyRightbrace, + 0xff0d: uinput.KeyEnter, + 0x003b: uinput.KeySemicolon, + 0x0027: uinput.KeyApostrophe, + 0x0060: uinput.KeyGrave, + 0x005c: uinput.KeyBackslash, + 0x002c: uinput.KeyComma, + 0x0abd: uinput.KeyDot, + 0x002f: uinput.KeySlash, + 0x0020: uinput.KeySpace, + 0xff50: uinput.KeyHome, + 0xff52: uinput.KeyUp, + 0xff51: uinput.KeyLeft, + 0xff53: uinput.KeyRight, + 0xff57: uinput.KeyEnd, + 0xff54: uinput.KeyDown, + 0xffff: uinput.KeyDelete, + 0x1008FF12: uinput.KeyMute, + 0x1008FF11: uinput.KeyVolumedown, + 0x1008FF13: uinput.KeyVolumeup, + 0xffeb: uinput.KeyLeftmeta, + 0x1008ff26: uinput.KeyBack, + 0x1008ff27: uinput.KeyForward, + 0x1008ff17: uinput.KeyNextsong, + 0x1008ff14: uinput.KeyPlaypause, + 0x1008ff16: uinput.KeyPrevioussong, +} + +type uinputController struct { + keyboard uinput.Keyboard + mouse uinput.Mouse +} + +func init() { + RegisterController("Uinput", InitUinputController, 0) +} + +func InitUinputController() (Controller, error) { + keyboard, err := uinput.CreateKeyboard("/dev/uinput", []byte("remote-tp-keyboard")) + if err != nil { + return nil, err + } + mouse, err := uinput.CreateMouse("/dev/uinput", []byte("remote-tp-mouse")) + if err != nil { + return nil, err + } + p := &uinputController{keyboard, mouse} + return p, nil +} + +func (p *uinputController) Close() error { + p.keyboard.Close() + p.mouse.Close() + return nil +} + +func (p *uinputController) sendKeysym(sym Keysym) error { + if sym >= 0x0041 && sym <= 0x005a { + p.keyboard.KeyDown(uinput.KeyLeftshift) + p.keyboard.KeyPress(ukeysMap[sym + 0x20]) + p.keyboard.KeyUp(uinput.KeyLeftshift) + } + p.keyboard.KeyPress(ukeysMap[sym]) + return nil +} + +func (p *uinputController) KeyboardText(text string) error { + for _, runeValue := range text { + keysym, err := RuneToKeysym(runeValue) + if err != nil { + return err + } + err = p.sendKeysym(keysym); + + } + return nil +} + +func (p *uinputController) KeyboardKey(key Key) error { + keysym, err := KeyToKeysym(key) + if err != nil { + return err + } + return p.sendKeysym(keysym) +} + +func (p *uinputController) PointerButton(button PointerButton, press bool) error { + switch button { + case PointerButtonLeft: + if press { + p.mouse.LeftPress() + } else { + p.mouse.LeftRelease() + } + case PointerButtonRight: + if press { + p.mouse.RightPress() + } else { + p.mouse.RightRelease() + } + case PointerButtonMiddle: + if press { + p.mouse.MiddlePress() + } else { + p.mouse.MiddleRelease() + } + default: + return errors.New("unsupported pointer button") + } + return nil +} + +func (p *uinputController) PointerMove(deltaX, deltaY int) error { + p.mouse.Move(int32(deltaX), int32(deltaY)) + return nil +} + +func (p *uinputController) PointerScroll(deltaHorizontal, deltaVertical int, finish bool) error { + p.mouse.Wheel(false, int32(deltaVertical)) + p.mouse.Wheel(true, int32(deltaHorizontal)) + return nil +} diff --git a/inputcontrol/keysyms.generated.go b/inputcontrol/keysyms.generated.go index 8734718..3e0ead8 100644 --- a/inputcontrol/keysyms.generated.go +++ b/inputcontrol/keysyms.generated.go @@ -1,5 +1,5 @@ // Code generated by keysyms.generator.go. DO NOT EDIT. -//go:build portal || x11 +//go:build portal || x11 || uinput package inputcontrol diff --git a/inputcontrol/keysyms.generator.go b/inputcontrol/keysyms.generator.go index cc18417..71b4094 100644 --- a/inputcontrol/keysyms.generator.go +++ b/inputcontrol/keysyms.generator.go @@ -102,7 +102,7 @@ func main() { keysymsMap[unicode] = keysym } content := "// Code generated by keysyms.generator.go. DO NOT EDIT.\n" + - "//go:build portal || x11\n\n" + + "//go:build portal || x11 || uinput\n\n" + "package inputcontrol\n\n" + "var keysymsMap = map[rune]Keysym{\n" keys := make([]rune, 0) diff --git a/inputcontrol/keysyms.go b/inputcontrol/keysyms.go index de183a1..f0dcf0a 100644 --- a/inputcontrol/keysyms.go +++ b/inputcontrol/keysyms.go @@ -1,4 +1,4 @@ -//go:build portal || x11 +//go:build portal || x11 || uinput /* * Copyright (c) 2018 Unrud From feaf02217cf85a74442eb2421e736bfa28733fbe Mon Sep 17 00:00:00 2001 From: De_Coder Date: Wed, 22 Feb 2023 17:04:16 +0100 Subject: [PATCH 11/96] improve uinput controller --- inputcontrol/controller_uinput.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go index 0b16f10..eb39a3b 100644 --- a/inputcontrol/controller_uinput.go +++ b/inputcontrol/controller_uinput.go @@ -115,6 +115,7 @@ func InitUinputController() (Controller, error) { } mouse, err := uinput.CreateMouse("/dev/uinput", []byte("remote-tp-mouse")) if err != nil { + keyboard.Close() return nil, err } p := &uinputController{keyboard, mouse} @@ -128,12 +129,13 @@ func (p *uinputController) Close() error { } func (p *uinputController) sendKeysym(sym Keysym) error { - if sym >= 0x0041 && sym <= 0x005a { + if sym >= 0x0041 && sym <= 0x005a { // capital letters p.keyboard.KeyDown(uinput.KeyLeftshift) p.keyboard.KeyPress(ukeysMap[sym + 0x20]) p.keyboard.KeyUp(uinput.KeyLeftshift) + } else if key, ok := ukeysMap[sym]; ok { + p.keyboard.KeyPress(key) } - p.keyboard.KeyPress(ukeysMap[sym]) return nil } From 809d3a0064a6d3a095b1da090aeb679cd6ee6f07 Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 23 Feb 2023 01:09:29 +0100 Subject: [PATCH 12/96] uinput-controller: Format code --- go.mod | 3 +- inputcontrol/controller_uinput.go | 74 ++++++++++++++++--------------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/go.mod b/go.mod index d0cdff7..bf7017e 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,8 @@ module github.com/unrud/remote-touchpad go 1.17 require ( + github.com/bendahl/uinput v1.6.0 github.com/godbus/dbus/v5 v5.1.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e golang.org/x/net v0.7.0 ) - -require github.com/bendahl/uinput v1.6.0 // indirect diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go index eb39a3b..bfd9b91 100644 --- a/inputcontrol/controller_uinput.go +++ b/inputcontrol/controller_uinput.go @@ -21,10 +21,12 @@ package inputcontrol -import "github.com/bendahl/uinput" -import "errors" +import ( + "errors" + "github.com/bendahl/uinput" +) -var ukeysMap = map[Keysym]int { +var ukeysMap = map[Keysym]int{ 0x0061: uinput.KeyA, 0x0062: uinput.KeyB, 0x0063: uinput.KeyC, @@ -63,35 +65,35 @@ var ukeysMap = map[Keysym]int { 0x0038: uinput.Key8, 0x0039: uinput.Key9, - 0xff1b: uinput.KeyEsc, - 0x002d: uinput.KeyMinus, - 0x002b: uinput.KeyKpplus, - 0x002a: uinput.KeyKpasterisk, - 0x003d: uinput.KeyEqual, - 0xff08: uinput.KeyBackspace, - 0xff09: uinput.KeyTab, - 0x0028: uinput.KeyLeftbrace, - 0x0029: uinput.KeyRightbrace, - 0xff0d: uinput.KeyEnter, - 0x003b: uinput.KeySemicolon, - 0x0027: uinput.KeyApostrophe, - 0x0060: uinput.KeyGrave, - 0x005c: uinput.KeyBackslash, - 0x002c: uinput.KeyComma, - 0x0abd: uinput.KeyDot, - 0x002f: uinput.KeySlash, - 0x0020: uinput.KeySpace, - 0xff50: uinput.KeyHome, - 0xff52: uinput.KeyUp, - 0xff51: uinput.KeyLeft, - 0xff53: uinput.KeyRight, - 0xff57: uinput.KeyEnd, - 0xff54: uinput.KeyDown, - 0xffff: uinput.KeyDelete, + 0xff1b: uinput.KeyEsc, + 0x002d: uinput.KeyMinus, + 0x002b: uinput.KeyKpplus, + 0x002a: uinput.KeyKpasterisk, + 0x003d: uinput.KeyEqual, + 0xff08: uinput.KeyBackspace, + 0xff09: uinput.KeyTab, + 0x0028: uinput.KeyLeftbrace, + 0x0029: uinput.KeyRightbrace, + 0xff0d: uinput.KeyEnter, + 0x003b: uinput.KeySemicolon, + 0x0027: uinput.KeyApostrophe, + 0x0060: uinput.KeyGrave, + 0x005c: uinput.KeyBackslash, + 0x002c: uinput.KeyComma, + 0x0abd: uinput.KeyDot, + 0x002f: uinput.KeySlash, + 0x0020: uinput.KeySpace, + 0xff50: uinput.KeyHome, + 0xff52: uinput.KeyUp, + 0xff51: uinput.KeyLeft, + 0xff53: uinput.KeyRight, + 0xff57: uinput.KeyEnd, + 0xff54: uinput.KeyDown, + 0xffff: uinput.KeyDelete, 0x1008FF12: uinput.KeyMute, 0x1008FF11: uinput.KeyVolumedown, 0x1008FF13: uinput.KeyVolumeup, - 0xffeb: uinput.KeyLeftmeta, + 0xffeb: uinput.KeyLeftmeta, 0x1008ff26: uinput.KeyBack, 0x1008ff27: uinput.KeyForward, 0x1008ff17: uinput.KeyNextsong, @@ -101,7 +103,7 @@ var ukeysMap = map[Keysym]int { type uinputController struct { keyboard uinput.Keyboard - mouse uinput.Mouse + mouse uinput.Mouse } func init() { @@ -131,7 +133,7 @@ func (p *uinputController) Close() error { func (p *uinputController) sendKeysym(sym Keysym) error { if sym >= 0x0041 && sym <= 0x005a { // capital letters p.keyboard.KeyDown(uinput.KeyLeftshift) - p.keyboard.KeyPress(ukeysMap[sym + 0x20]) + p.keyboard.KeyPress(ukeysMap[sym+0x20]) p.keyboard.KeyUp(uinput.KeyLeftshift) } else if key, ok := ukeysMap[sym]; ok { p.keyboard.KeyPress(key) @@ -145,7 +147,7 @@ func (p *uinputController) KeyboardText(text string) error { if err != nil { return err } - err = p.sendKeysym(keysym); + err = p.sendKeysym(keysym) } return nil @@ -161,25 +163,25 @@ func (p *uinputController) KeyboardKey(key Key) error { func (p *uinputController) PointerButton(button PointerButton, press bool) error { switch button { - case PointerButtonLeft: + case PointerButtonLeft: if press { p.mouse.LeftPress() } else { p.mouse.LeftRelease() } - case PointerButtonRight: + case PointerButtonRight: if press { p.mouse.RightPress() } else { p.mouse.RightRelease() } - case PointerButtonMiddle: + case PointerButtonMiddle: if press { p.mouse.MiddlePress() } else { p.mouse.MiddleRelease() } - default: + default: return errors.New("unsupported pointer button") } return nil From d254ae5b0d8c4add7f114533d5c938d63a44b49c Mon Sep 17 00:00:00 2001 From: Unrud Date: Thu, 23 Feb 2023 01:09:30 +0100 Subject: [PATCH 13/96] uinput-controller: Map runes/keys directly and handle errors --- inputcontrol/controller_uinput.go | 273 ++++++++++++++++-------------- inputcontrol/keysyms.generated.go | 2 +- inputcontrol/keysyms.generator.go | 2 +- inputcontrol/keysyms.go | 2 +- 4 files changed, 150 insertions(+), 129 deletions(-) diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go index bfd9b91..df6b5a2 100644 --- a/inputcontrol/controller_uinput.go +++ b/inputcontrol/controller_uinput.go @@ -2,6 +2,7 @@ /* * Copyright (c) 2023 De_Coder github.com/ps100000 + * Copyright (c) 2023 Unrud * * This file is part of Remote-Touchpad. * @@ -22,83 +23,66 @@ package inputcontrol import ( - "errors" + "fmt" "github.com/bendahl/uinput" + "unicode" ) -var ukeysMap = map[Keysym]int{ - 0x0061: uinput.KeyA, - 0x0062: uinput.KeyB, - 0x0063: uinput.KeyC, - 0x0064: uinput.KeyD, - 0x0065: uinput.KeyE, - 0x0066: uinput.KeyF, - 0x0067: uinput.KeyG, - 0x0068: uinput.KeyH, - 0x0069: uinput.KeyI, - 0x006a: uinput.KeyJ, - 0x006b: uinput.KeyK, - 0x006c: uinput.KeyL, - 0x006d: uinput.KeyM, - 0x006e: uinput.KeyN, - 0x006f: uinput.KeyO, - 0x0070: uinput.KeyP, - 0x0071: uinput.KeyQ, - 0x0072: uinput.KeyR, - 0x0073: uinput.KeyS, - 0x0074: uinput.KeyT, - 0x0075: uinput.KeyU, - 0x0076: uinput.KeyV, - 0x0077: uinput.KeyW, - 0x0078: uinput.KeyX, - 0x0079: uinput.KeyY, - 0x007a: uinput.KeyZ, - - 0x0030: uinput.Key0, - 0x0031: uinput.Key1, - 0x0032: uinput.Key2, - 0x0033: uinput.Key3, - 0x0034: uinput.Key4, - 0x0035: uinput.Key5, - 0x0036: uinput.Key6, - 0x0037: uinput.Key7, - 0x0038: uinput.Key8, - 0x0039: uinput.Key9, - - 0xff1b: uinput.KeyEsc, - 0x002d: uinput.KeyMinus, - 0x002b: uinput.KeyKpplus, - 0x002a: uinput.KeyKpasterisk, - 0x003d: uinput.KeyEqual, - 0xff08: uinput.KeyBackspace, - 0xff09: uinput.KeyTab, - 0x0028: uinput.KeyLeftbrace, - 0x0029: uinput.KeyRightbrace, - 0xff0d: uinput.KeyEnter, - 0x003b: uinput.KeySemicolon, - 0x0027: uinput.KeyApostrophe, - 0x0060: uinput.KeyGrave, - 0x005c: uinput.KeyBackslash, - 0x002c: uinput.KeyComma, - 0x0abd: uinput.KeyDot, - 0x002f: uinput.KeySlash, - 0x0020: uinput.KeySpace, - 0xff50: uinput.KeyHome, - 0xff52: uinput.KeyUp, - 0xff51: uinput.KeyLeft, - 0xff53: uinput.KeyRight, - 0xff57: uinput.KeyEnd, - 0xff54: uinput.KeyDown, - 0xffff: uinput.KeyDelete, - 0x1008FF12: uinput.KeyMute, - 0x1008FF11: uinput.KeyVolumedown, - 0x1008FF13: uinput.KeyVolumeup, - 0xffeb: uinput.KeyLeftmeta, - 0x1008ff26: uinput.KeyBack, - 0x1008ff27: uinput.KeyForward, - 0x1008ff17: uinput.KeyNextsong, - 0x1008ff14: uinput.KeyPlaypause, - 0x1008ff16: uinput.KeyPrevioussong, +var ukeysMap = map[rune]int{ + 'a': uinput.KeyA, + 'b': uinput.KeyB, + 'c': uinput.KeyC, + 'd': uinput.KeyD, + 'e': uinput.KeyE, + 'f': uinput.KeyF, + 'g': uinput.KeyG, + 'h': uinput.KeyH, + 'i': uinput.KeyI, + 'j': uinput.KeyJ, + 'k': uinput.KeyK, + 'l': uinput.KeyL, + 'm': uinput.KeyM, + 'n': uinput.KeyN, + 'o': uinput.KeyO, + 'p': uinput.KeyP, + 'q': uinput.KeyQ, + 'r': uinput.KeyR, + 's': uinput.KeyS, + 't': uinput.KeyT, + 'u': uinput.KeyU, + 'v': uinput.KeyV, + 'w': uinput.KeyW, + 'x': uinput.KeyX, + 'y': uinput.KeyY, + 'z': uinput.KeyZ, + '0': uinput.Key0, + '1': uinput.Key1, + '2': uinput.Key2, + '3': uinput.Key3, + '4': uinput.Key4, + '5': uinput.Key5, + '6': uinput.Key6, + '7': uinput.Key7, + '8': uinput.Key8, + '9': uinput.Key9, + 0x1b: uinput.KeyEsc, + '-': uinput.KeyMinus, + '+': uinput.KeyKpplus, + '*': uinput.KeyKpasterisk, + '=': uinput.KeyEqual, + 0x08: uinput.KeyBackspace, + '\t': uinput.KeyTab, + '(': uinput.KeyLeftbrace, + ')': uinput.KeyRightbrace, + '\n': uinput.KeyEnter, + ';': uinput.KeySemicolon, + '\'': uinput.KeyApostrophe, + '`': uinput.KeyGrave, + '\\': uinput.KeyBackslash, + ',': uinput.KeyComma, + '.': uinput.KeyDot, + '/': uinput.KeySlash, + ' ': uinput.KeySpace, } type uinputController struct { @@ -107,93 +91,130 @@ type uinputController struct { } func init() { - RegisterController("Uinput", InitUinputController, 0) + RegisterController("uinput", InitUinputController, 0) } func InitUinputController() (Controller, error) { - keyboard, err := uinput.CreateKeyboard("/dev/uinput", []byte("remote-tp-keyboard")) + keyboard, err := uinput.CreateKeyboard("/dev/uinput", []byte("remote-touchpad-keyboard")) if err != nil { return nil, err } - mouse, err := uinput.CreateMouse("/dev/uinput", []byte("remote-tp-mouse")) + mouse, err := uinput.CreateMouse("/dev/uinput", []byte("remote-touchpad-mouse")) if err != nil { keyboard.Close() return nil, err } - p := &uinputController{keyboard, mouse} - return p, nil + return &uinputController{keyboard, mouse}, nil } func (p *uinputController) Close() error { - p.keyboard.Close() - p.mouse.Close() - return nil -} - -func (p *uinputController) sendKeysym(sym Keysym) error { - if sym >= 0x0041 && sym <= 0x005a { // capital letters - p.keyboard.KeyDown(uinput.KeyLeftshift) - p.keyboard.KeyPress(ukeysMap[sym+0x20]) - p.keyboard.KeyUp(uinput.KeyLeftshift) - } else if key, ok := ukeysMap[sym]; ok { - p.keyboard.KeyPress(key) + if err := p.keyboard.Close(); err != nil { + p.mouse.Close() + return err } - return nil + return p.mouse.Close() } +// No support for keyboard layouts, only works with QWERTY func (p *uinputController) KeyboardText(text string) error { for _, runeValue := range text { - keysym, err := RuneToKeysym(runeValue) - if err != nil { + modifierShift := false + if unicode.IsUpper(runeValue) { + modifierShift = true + runeValue = unicode.ToLower(runeValue) + } + uinputKey, found := ukeysMap[runeValue] + if !found { + return fmt.Errorf("unsupported rune: %q", runeValue) + } + if modifierShift { + if err := p.keyboard.KeyDown(uinput.KeyLeftshift); err != nil { + return err + } + } + if err := p.keyboard.KeyPress(uinputKey); err != nil { return err } - err = p.sendKeysym(keysym) - + if modifierShift { + if err := p.keyboard.KeyUp(uinput.KeyLeftshift); err != nil { + return err + } + } } return nil } func (p *uinputController) KeyboardKey(key Key) error { - keysym, err := KeyToKeysym(key) - if err != nil { - return err + var uinputKey int + switch key { + case KeyBackSpace: + uinputKey = uinput.KeyBackspace + case KeyReturn: + uinputKey = uinput.KeyEnter + case KeyDelete: + uinputKey = uinput.KeyDelete + case KeyHome: + uinputKey = uinput.KeyHome + case KeyLeft: + uinputKey = uinput.KeyLeft + case KeyUp: + uinputKey = uinput.KeyUp + case KeyRight: + uinputKey = uinput.KeyRight + case KeyDown: + uinputKey = uinput.KeyDown + case KeyEnd: + uinputKey = uinput.KeyEnd + case KeySuper: + uinputKey = uinput.KeyLeftmeta + case KeyVolumeMute: + uinputKey = uinput.KeyMute + case KeyVolumeDown: + uinputKey = uinput.KeyVolumedown + case KeyVolumeUp: + uinputKey = uinput.KeyVolumeup + case KeyMediaPlayPause: + uinputKey = uinput.KeyPlaypause + case KeyMediaPrevTrack: + uinputKey = uinput.KeyPrevioussong + case KeyMediaNextTrack: + uinputKey = uinput.KeyNextsong + case KeyBrowserBack: + uinputKey = uinput.KeyBack + case KeyBrowserForward: + uinputKey = uinput.KeyForward + default: + return fmt.Errorf("unsupported key: %#v", key) } - return p.sendKeysym(keysym) + return p.keyboard.KeyPress(uinputKey) } func (p *uinputController) PointerButton(button PointerButton, press bool) error { - switch button { - case PointerButtonLeft: - if press { - p.mouse.LeftPress() - } else { - p.mouse.LeftRelease() - } - case PointerButtonRight: - if press { - p.mouse.RightPress() - } else { - p.mouse.RightRelease() - } - case PointerButtonMiddle: - if press { - p.mouse.MiddlePress() - } else { - p.mouse.MiddleRelease() - } + switch { + case button == PointerButtonLeft && press: + return p.mouse.LeftPress() + case button == PointerButtonLeft && !press: + return p.mouse.LeftRelease() + case button == PointerButtonRight && press: + return p.mouse.RightPress() + case button == PointerButtonRight && !press: + return p.mouse.RightRelease() + case button == PointerButtonMiddle && press: + return p.mouse.MiddlePress() + case button == PointerButtonMiddle && !press: + return p.mouse.MiddleRelease() default: - return errors.New("unsupported pointer button") + return fmt.Errorf("unsupported pointer button: %#v", button) } - return nil } func (p *uinputController) PointerMove(deltaX, deltaY int) error { - p.mouse.Move(int32(deltaX), int32(deltaY)) - return nil + return p.mouse.Move(int32(deltaX), int32(deltaY)) } func (p *uinputController) PointerScroll(deltaHorizontal, deltaVertical int, finish bool) error { - p.mouse.Wheel(false, int32(deltaVertical)) - p.mouse.Wheel(true, int32(deltaHorizontal)) - return nil + if err := p.mouse.Wheel(false, int32(deltaVertical)); err != nil { + return err + } + return p.mouse.Wheel(true, int32(deltaHorizontal)) } diff --git a/inputcontrol/keysyms.generated.go b/inputcontrol/keysyms.generated.go index 3e0ead8..8734718 100644 --- a/inputcontrol/keysyms.generated.go +++ b/inputcontrol/keysyms.generated.go @@ -1,5 +1,5 @@ // Code generated by keysyms.generator.go. DO NOT EDIT. -//go:build portal || x11 || uinput +//go:build portal || x11 package inputcontrol diff --git a/inputcontrol/keysyms.generator.go b/inputcontrol/keysyms.generator.go index 71b4094..cc18417 100644 --- a/inputcontrol/keysyms.generator.go +++ b/inputcontrol/keysyms.generator.go @@ -102,7 +102,7 @@ func main() { keysymsMap[unicode] = keysym } content := "// Code generated by keysyms.generator.go. DO NOT EDIT.\n" + - "//go:build portal || x11 || uinput\n\n" + + "//go:build portal || x11\n\n" + "package inputcontrol\n\n" + "var keysymsMap = map[rune]Keysym{\n" keys := make([]rune, 0) diff --git a/inputcontrol/keysyms.go b/inputcontrol/keysyms.go index f0dcf0a..de183a1 100644 --- a/inputcontrol/keysyms.go +++ b/inputcontrol/keysyms.go @@ -1,4 +1,4 @@ -//go:build portal || x11 || uinput +//go:build portal || x11 /* * Copyright (c) 2018 Unrud From bbb6cc4dc1fb25ac5df48b30540268a1bf0b99a3 Mon Sep 17 00:00:00 2001 From: Unrud Date: Sun, 26 Feb 2023 16:45:37 +0100 Subject: [PATCH 14/96] uinput-controller: Initial support for keymaps Keymap can be set with environment variable REMOTE_TOUCHPAD_UINPUT_KEYMAP --- inputcontrol/controller_uinput.go | 127 ++++++++++++----------------- inputcontrol/keymap.go | 130 ++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 76 deletions(-) create mode 100644 inputcontrol/keymap.go diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go index df6b5a2..d6adedc 100644 --- a/inputcontrol/controller_uinput.go +++ b/inputcontrol/controller_uinput.go @@ -25,67 +25,14 @@ package inputcontrol import ( "fmt" "github.com/bendahl/uinput" - "unicode" + "os" + "time" ) -var ukeysMap = map[rune]int{ - 'a': uinput.KeyA, - 'b': uinput.KeyB, - 'c': uinput.KeyC, - 'd': uinput.KeyD, - 'e': uinput.KeyE, - 'f': uinput.KeyF, - 'g': uinput.KeyG, - 'h': uinput.KeyH, - 'i': uinput.KeyI, - 'j': uinput.KeyJ, - 'k': uinput.KeyK, - 'l': uinput.KeyL, - 'm': uinput.KeyM, - 'n': uinput.KeyN, - 'o': uinput.KeyO, - 'p': uinput.KeyP, - 'q': uinput.KeyQ, - 'r': uinput.KeyR, - 's': uinput.KeyS, - 't': uinput.KeyT, - 'u': uinput.KeyU, - 'v': uinput.KeyV, - 'w': uinput.KeyW, - 'x': uinput.KeyX, - 'y': uinput.KeyY, - 'z': uinput.KeyZ, - '0': uinput.Key0, - '1': uinput.Key1, - '2': uinput.Key2, - '3': uinput.Key3, - '4': uinput.Key4, - '5': uinput.Key5, - '6': uinput.Key6, - '7': uinput.Key7, - '8': uinput.Key8, - '9': uinput.Key9, - 0x1b: uinput.KeyEsc, - '-': uinput.KeyMinus, - '+': uinput.KeyKpplus, - '*': uinput.KeyKpasterisk, - '=': uinput.KeyEqual, - 0x08: uinput.KeyBackspace, - '\t': uinput.KeyTab, - '(': uinput.KeyLeftbrace, - ')': uinput.KeyRightbrace, - '\n': uinput.KeyEnter, - ';': uinput.KeySemicolon, - '\'': uinput.KeyApostrophe, - '`': uinput.KeyGrave, - '\\': uinput.KeyBackslash, - ',': uinput.KeyComma, - '.': uinput.KeyDot, - '/': uinput.KeySlash, - ' ': uinput.KeySpace, -} +const shiftKeysDelay time.Duration = 50 * time.Millisecond type uinputController struct { + keymap *Keymap keyboard uinput.Keyboard mouse uinput.Mouse } @@ -95,6 +42,14 @@ func init() { } func InitUinputController() (Controller, error) { + keymapName := "defkeymap" + if s, ok := os.LookupEnv("REMOTE_TOUCHPAD_UINPUT_KEYMAP"); ok { + keymapName = s + } + keymap, err := LoadKeymap(keymapName) + if err != nil { + return nil, err + } keyboard, err := uinput.CreateKeyboard("/dev/uinput", []byte("remote-touchpad-keyboard")) if err != nil { return nil, err @@ -104,7 +59,7 @@ func InitUinputController() (Controller, error) { keyboard.Close() return nil, err } - return &uinputController{keyboard, mouse}, nil + return &uinputController{keymap, keyboard, mouse}, nil } func (p *uinputController) Close() error { @@ -115,33 +70,53 @@ func (p *uinputController) Close() error { return p.mouse.Close() } -// No support for keyboard layouts, only works with QWERTY func (p *uinputController) KeyboardText(text string) error { - for _, runeValue := range text { - modifierShift := false - if unicode.IsUpper(runeValue) { - modifierShift = true - runeValue = unicode.ToLower(runeValue) + var activeShiftKeys KeyCombo + updateShiftKeys := func(keyCombo KeyCombo) error { + if activeShiftKeys.ShiftKeys == keyCombo.ShiftKeys { + activeShiftKeys.Key = keyCombo.Key + return nil } - uinputKey, found := ukeysMap[runeValue] + if activeShiftKeys.Key != 0 { + time.Sleep(shiftKeysDelay) + activeShiftKeys.Key = 0 + } + for i := range keyCombo.ShiftKeys { + if activeShiftKeys.ShiftKeys[i] != keyCombo.ShiftKeys[i] { + if activeShiftKeys.ShiftKeys[i] != 0 { + if err := p.keyboard.KeyUp(activeShiftKeys.ShiftKeys[i]); err != nil { + return err + } + activeShiftKeys.ShiftKeys[i] = 0 + } + if keyCombo.ShiftKeys[i] != 0 { + if err := p.keyboard.KeyDown(keyCombo.ShiftKeys[i]); err != nil { + return err + } + activeShiftKeys.ShiftKeys[i] = keyCombo.ShiftKeys[i] + } + } + } + if keyCombo.Key != 0 { + activeShiftKeys.Key = keyCombo.Key + time.Sleep(shiftKeysDelay) + } + return nil + } + defer updateShiftKeys(KeyCombo{}) + for _, runeValue := range text { + keyCombo, found := p.keymap.Get(runeValue) if !found { return fmt.Errorf("unsupported rune: %q", runeValue) } - if modifierShift { - if err := p.keyboard.KeyDown(uinput.KeyLeftshift); err != nil { - return err - } - } - if err := p.keyboard.KeyPress(uinputKey); err != nil { + if err := updateShiftKeys(keyCombo); err != nil { return err } - if modifierShift { - if err := p.keyboard.KeyUp(uinput.KeyLeftshift); err != nil { - return err - } + if err := p.keyboard.KeyPress(keyCombo.Key); err != nil { + return err } } - return nil + return updateShiftKeys(KeyCombo{Key: 1}) } func (p *uinputController) KeyboardKey(key Key) error { diff --git a/inputcontrol/keymap.go b/inputcontrol/keymap.go new file mode 100644 index 0000000..3989bb8 --- /dev/null +++ b/inputcontrol/keymap.go @@ -0,0 +1,130 @@ +//go:build uinput + +/* + * Copyright (c) 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +package inputcontrol + +import ( + "fmt" + "os/exec" + "strings" + "unsafe" +) + +const bkeymapFileSignature = "bkeymap" + +// uapi/linux/keyboard.h +const ( + nrKeys = 128 + maxNrKeymaps = 256 + nrShift = 9 + + ktLatin = 0 + ktShift = 7 + ktLetter = 11 +) + +type KeyCombo struct { + Key int + ShiftKeys [nrShift]int +} + +type Keymap struct { + keyComboMap map[rune]KeyCombo +} + +func (keymap Keymap) Get(character rune) (keyCombo KeyCombo, found bool) { + keyCombo, found = keymap.keyComboMap[character] + return +} + +func LoadKeymap(name string) (*Keymap, error) { + if len(name) == 0 || strings.HasPrefix(name, "-") { + return nil, fmt.Errorf("invalid keymap name: %#v", name) + } + bkeymap, err := exec.Command("loadkeys", "--bkeymap", name).Output() + if err != nil { + return nil, err + } + if !strings.HasPrefix(string(bkeymap), bkeymapFileSignature) { + return nil, fmt.Errorf("invalid bkeymap: signature not found") + } + bkeymapHeader := bkeymap[len(bkeymapFileSignature):] + var keymaps [][nrKeys]int + if len(bkeymapHeader) < maxNrKeymaps { + return nil, fmt.Errorf("invalid bkeymap: EOF") + } + currentBkeymap := bkeymapHeader[maxNrKeymaps:] + for keymapIndex := 0; keymapIndex < maxNrKeymaps; keymapIndex += 1 { + if bkeymapHeader[keymapIndex] == 0 { + continue + } + if len(currentBkeymap) < nrKeys*2 { + return nil, fmt.Errorf("invalid bkeymap: EOF") + } + for len(keymaps) <= keymapIndex { + keymaps = append(keymaps, [nrKeys]int{}) + } + for key := 0; key < nrKeys; key += 1 { + var keysym uint16 + copy(unsafe.Slice((*byte)(unsafe.Pointer(&keysym)), 2), + currentBkeymap[key*2:]) + keymaps[keymapIndex][key] = int(keysym) + } + currentBkeymap = currentBkeymap[nrKeys*2:] + } + keymap := Keymap{keyComboMap: make(map[rune]KeyCombo)} + if len(keymaps) == 0 { + return &keymap, nil + } + var shiftKeys [nrShift]int + for key, keysym := range keymaps[0] { + keyType := keysym >> 8 + keyValue := keysym & 0xff + if keyType == ktShift && keyValue < nrShift && shiftKeys[keyValue] == 0 { + shiftKeys[keyValue] = key + } + } +keymapLoop: + for keymapIndex := range keymaps { + var activeShiftKeys [nrShift]int + for i, shiftKey := range shiftKeys { + if keymapIndex&(1<> 8 + keyValue := keysym & 0xff + if (keyType != ktLatin && keyType != ktLetter) || keyValue == 0 { + continue + } + character := rune(keyValue) + if _, ok := keymap.keyComboMap[character]; ok { + continue + } + keymap.keyComboMap[character] = KeyCombo{key, activeShiftKeys} + } + } + return &keymap, nil +} From e69bd157331a06331f0ec53b26984ac512288cdf Mon Sep 17 00:00:00 2001 From: Unrud Date: Mon, 27 Feb 2023 23:42:16 +0100 Subject: [PATCH 15/96] uinput-controller: Lower priority --- inputcontrol/controller_uinput.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go index d6adedc..b0bf5fc 100644 --- a/inputcontrol/controller_uinput.go +++ b/inputcontrol/controller_uinput.go @@ -38,7 +38,7 @@ type uinputController struct { } func init() { - RegisterController("uinput", InitUinputController, 0) + RegisterController("uinput", InitUinputController, 10) } func InitUinputController() (Controller, error) { From 0bd7260a0cadef7b8a85a8c6730c0e56c9f110d9 Mon Sep 17 00:00:00 2001 From: Unrud Date: Mon, 27 Feb 2023 23:43:10 +0100 Subject: [PATCH 16/96] uinput-controller: Show hint for setting keymap --- inputcontrol/controller_uinput.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go index b0bf5fc..3bdff55 100644 --- a/inputcontrol/controller_uinput.go +++ b/inputcontrol/controller_uinput.go @@ -25,6 +25,7 @@ package inputcontrol import ( "fmt" "github.com/bendahl/uinput" + "log" "os" "time" ) @@ -42,9 +43,9 @@ func init() { } func InitUinputController() (Controller, error) { - keymapName := "defkeymap" - if s, ok := os.LookupEnv("REMOTE_TOUCHPAD_UINPUT_KEYMAP"); ok { - keymapName = s + keymapName, keymapSet := os.LookupEnv("REMOTE_TOUCHPAD_UINPUT_KEYMAP") + if !keymapSet { + keymapName = "defkeymap" } keymap, err := LoadKeymap(keymapName) if err != nil { @@ -59,6 +60,9 @@ func InitUinputController() (Controller, error) { keyboard.Close() return nil, err } + if !keymapSet { + log.Print("Hint: Set the keyboard mapping with the REMOTE_TOUCHPAD_UINPUT_KEYMAP environment variable") + } return &uinputController{keymap, keyboard, mouse}, nil } From d81b57b1f9e6ca0bfb2f2b0e504b5035b89c92c7 Mon Sep 17 00:00:00 2001 From: Unrud Date: Fri, 3 Mar 2023 23:06:34 +0100 Subject: [PATCH 17/96] Add uinput build example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cd73c2..2740e73 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ 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 & X11: `go install -tags portal,x11 github.com/unrud/remote-touchpad@latest` + * Portal & uinput & X11: `go install -tags portal,uinput,x11 github.com/unrud/remote-touchpad@latest` * Windows: `go install github.com/unrud/remote-touchpad@latest` ## Screenshots From 91a37de0fbe560eab5b2726644d6318ae09bf018 Mon Sep 17 00:00:00 2001 From: Unrud Date: Fri, 3 Mar 2023 23:14:34 +0100 Subject: [PATCH 18/96] Bump version to 1.3.0 --- CHANGELOG.md | 5 +++++ .../com.github.unrud.RemoteTouchpad.metainfo.xml | 16 ++++++++++++---- main.go | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f7f63a..4ebb9ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.3.0 (2023-03-03) + +* Initial support for uinput +* Update dependencies + ## 1.2.3 (2022-12-29) * Improve XWayland detection diff --git a/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml b/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml index 7096033..9578960 100644 --- a/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml +++ b/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml @@ -19,6 +19,14 @@ https://github.com/Unrud/remote-touchpad Unrud + + +
      +
    • Initial support for uinput
    • +
    • Update dependencies
    • +
    +
    +
      @@ -259,16 +267,16 @@ - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.3/screenshots/1.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.3.0/screenshots/1.png - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.3/screenshots/2.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.3.0/screenshots/2.png - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.3/screenshots/3.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.3.0/screenshots/3.png - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.2.3/screenshots/4.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.3.0/screenshots/4.png diff --git a/main.go b/main.go index 295ec5c..63d32fb 100644 --- a/main.go +++ b/main.go @@ -47,7 +47,7 @@ const ( authenticationRateBurst int = 10 challengeLength int = 8 defaultBind string = ":0" - version string = "1.2.3" + version string = "1.3.0" prettyAppName string = "Remote Touchpad" ) From 891fde70513f2e637d453520c89b1098af713666 Mon Sep 17 00:00:00 2001 From: Unrud Date: Tue, 25 Apr 2023 01:38:48 +0200 Subject: [PATCH 19/96] Refactor errors --- inputcontrol/controller.go | 10 +++++-- inputcontrol/controller_portal.go | 46 +++++++++++++++--------------- inputcontrol/controller_windows.go | 2 +- inputcontrol/controller_x11.go | 4 +-- main.go | 16 +++++++---- 5 files changed, 43 insertions(+), 35 deletions(-) diff --git a/inputcontrol/controller.go b/inputcontrol/controller.go index 7e9e768..06ed8e4 100644 --- a/inputcontrol/controller.go +++ b/inputcontrol/controller.go @@ -70,11 +70,15 @@ func RegisterController(name string, init func() (Controller, error), priority i } type UnsupportedPlatformError struct { - err error + Err error } -func (e UnsupportedPlatformError) Error() string { - return e.err.Error() +func (e *UnsupportedPlatformError) Error() string { + return e.Err.Error() +} + +func (e *UnsupportedPlatformError) Unwrap() error { + return e.Err } type Controller interface { diff --git a/inputcontrol/controller_portal.go b/inputcontrol/controller_portal.go index 05ffb1d..d8e2700 100644 --- a/inputcontrol/controller_portal.go +++ b/inputcontrol/controller_portal.go @@ -55,7 +55,7 @@ func init() { func InitPortalController() (Controller, error) { bus, err := dbus.SessionBusPrivate() if err != nil { - return nil, UnsupportedPlatformError{err} + return nil, &UnsupportedPlatformError{err} } cleanupBus := true defer func() { @@ -65,49 +65,49 @@ func InitPortalController() (Controller, error) { }() err = bus.Auth(nil) if err != nil { - return nil, UnsupportedPlatformError{err} + return nil, &UnsupportedPlatformError{err} } err = bus.Hello() if err != nil { - return nil, UnsupportedPlatformError{err} + return nil, &UnsupportedPlatformError{err} } remoteDesktop := bus.Object("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop") availableDeviceTypesV, err := remoteDesktop.GetProperty( "org.freedesktop.portal.RemoteDesktop.AvailableDeviceTypes") if err != nil { - return nil, UnsupportedPlatformError{err} + return nil, &UnsupportedPlatformError{err} } availableDeviceTypes, ok := availableDeviceTypesV.Value().(uint32) if !ok { - return nil, UnsupportedPlatformError{errors.New( - "unexpected 'AvailableDeviceTypes' return type")} + return nil, &UnsupportedPlatformError{ + errors.New("unexpected 'AvailableDeviceTypes' return type")} } if availableDeviceTypes&deviceKeyboard == 0 || availableDeviceTypes&devicePointer == 0 { - return nil, UnsupportedPlatformError{errors.New( - "keyboard or pointer source type not supported")} + return nil, &UnsupportedPlatformError{ + errors.New("keyboard or pointer source type not supported")} } inVardict := make(map[string]dbus.Variant) inVardict["session_handle_token"] = dbus.MakeVariant("t") result, outVardict, err := getResponse(bus, remoteDesktop, "org.freedesktop.portal.RemoteDesktop.CreateSession", 0, inVardict) if err != nil { - return nil, UnsupportedPlatformError{err} + return nil, &UnsupportedPlatformError{err} } if result != 0 { - return nil, UnsupportedPlatformError{errors.New(fmt.Sprintf( - "Calling 'CreateSession' failed (%v)", result))} + return nil, &UnsupportedPlatformError{ + fmt.Errorf("Calling 'CreateSession' failed (%v)", result)} } sessionHandleV, ok := outVardict["session_handle"] if !ok { - return nil, UnsupportedPlatformError{errors.New( - "'session_handle' missing from 'CreateSession' return value")} + return nil, &UnsupportedPlatformError{ + errors.New("'session_handle' missing from 'CreateSession' return value")} } sessionHandleS, ok := sessionHandleV.Value().(string) if !ok { - return nil, UnsupportedPlatformError{errors.New( - "unexpected 'session_handle' type in 'CreateSession' return value")} + return nil, &UnsupportedPlatformError{ + errors.New("unexpected 'session_handle' type in 'CreateSession' return value")} } sessionHandle := dbus.ObjectPath(sessionHandleS) inVardict = make(map[string]dbus.Variant) @@ -115,30 +115,30 @@ func InitPortalController() (Controller, error) { result, outVardict, err = getResponse(bus, remoteDesktop, "org.freedesktop.portal.RemoteDesktop.SelectDevices", 0, sessionHandle, inVardict) if err != nil { - return nil, UnsupportedPlatformError{err} + return nil, &UnsupportedPlatformError{err} } if result != 0 { - return nil, UnsupportedPlatformError{errors.New(fmt.Sprintf( - "Calling 'SelectDevices' failed (%v)", result))} + return nil, &UnsupportedPlatformError{ + fmt.Errorf("Calling 'SelectDevices' failed (%v)", result)} } inVardict = make(map[string]dbus.Variant) result, outVardict, err = getResponse(bus, remoteDesktop, "org.freedesktop.portal.RemoteDesktop.Start", 0, sessionHandle, "", inVardict) if err != nil { - return nil, UnsupportedPlatformError{err} + return nil, &UnsupportedPlatformError{err} } if result != 0 { return nil, errors.New("keyboard or pointer access denied") } devicesV, ok := outVardict["devices"] if !ok { - return nil, UnsupportedPlatformError{errors.New( - "'devices' missing from 'Start' return value")} + return nil, &UnsupportedPlatformError{ + errors.New("'devices' missing from 'Start' return value")} } devices, ok := devicesV.Value().(uint32) if !ok { - return nil, UnsupportedPlatformError{errors.New( - "unexpected 'devices' type in 'Start' return value")} + return nil, &UnsupportedPlatformError{ + errors.New("unexpected 'devices' type in 'Start' return value")} } if devices&deviceKeyboard == 0 || devices&devicePointer == 0 { return nil, errors.New("keyboard or pointer access denied") diff --git a/inputcontrol/controller_windows.go b/inputcontrol/controller_windows.go index 8cb73a7..78c36a8 100644 --- a/inputcontrol/controller_windows.go +++ b/inputcontrol/controller_windows.go @@ -98,7 +98,7 @@ func init() { func InitWindowsController() (Controller, error) { p := &windowsController{} if err := sendInputProc.Find(); err != nil { - return nil, UnsupportedPlatformError{err} + return nil, &UnsupportedPlatformError{err} } return p, nil } diff --git a/inputcontrol/controller_x11.go b/inputcontrol/controller_x11.go index bc1ec6b..c5d19d9 100644 --- a/inputcontrol/controller_x11.go +++ b/inputcontrol/controller_x11.go @@ -61,13 +61,13 @@ func init() { func InitX11Controller() (Controller, error) { display := C.XOpenDisplay(nil) if display == nil { - return nil, UnsupportedPlatformError{ + return nil, &UnsupportedPlatformError{ errors.New("failed to connect to X server")} } p := &x11Controller{display: display} if p.xIsXwayland() { p.Close() - return nil, UnsupportedPlatformError{ + return nil, &UnsupportedPlatformError{ errors.New("X server is Xwayland")} } return p, nil diff --git a/main.go b/main.go index 63d32fb..be123cd 100644 --- a/main.go +++ b/main.go @@ -184,21 +184,25 @@ func main() { } var controller inputcontrol.Controller var controllerName string - platformErrors := "" + var platformErrs []error for _, controllerInfo := range inputcontrol.Controllers { controllerName = controllerInfo.Name var err error controller, err = controllerInfo.Init() if err == nil { break - } else if _, ok := err.(inputcontrol.UnsupportedPlatformError); ok { - platformErrors += fmt.Sprintf("%s controller: %v\n", controllerName, err) } else { - log.Fatalf("%s controller: %v", controllerName, err) + var unsupportedErr *inputcontrol.UnsupportedPlatformError + wrappedErr := fmt.Errorf("%v controller: %w", controllerName, err) + if errors.As(err, &unsupportedErr) { + platformErrs = append(platformErrs, wrappedErr) + } else { + log.Fatal(wrappedErr) + } } } if controller == nil { - log.Fatal("unsupported platform:\n" + platformErrors) + log.Fatal(fmt.Errorf("unsupported platform:\n%w", errors.Join(platformErrs...))) } defer controller.Close() authenticationChallenges := make(chan challenge, authenticationRateBurst) @@ -241,7 +245,7 @@ func main() { return } if err := processCommand(controller, message); err != nil { - log.Printf("%s controller: %v", controllerName, err) + log.Print(fmt.Errorf("%s controller: %w", controllerName, err)) return } } From 37dff3cb6b6e71c30e84cbaa31299dcbaf658ec2 Mon Sep 17 00:00:00 2001 From: Unrud Date: Tue, 25 Apr 2023 01:38:48 +0200 Subject: [PATCH 20/96] uinput: Detect unsupported platform --- inputcontrol/controller_uinput.go | 7 +++++++ inputcontrol/keymap.go | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go index 3bdff55..8e6023b 100644 --- a/inputcontrol/controller_uinput.go +++ b/inputcontrol/controller_uinput.go @@ -23,6 +23,7 @@ package inputcontrol import ( + "errors" "fmt" "github.com/bendahl/uinput" "log" @@ -52,10 +53,16 @@ func InitUinputController() (Controller, error) { return nil, err } keyboard, err := uinput.CreateKeyboard("/dev/uinput", []byte("remote-touchpad-keyboard")) + if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) { + err = &UnsupportedPlatformError{err} + } if err != nil { return nil, err } mouse, err := uinput.CreateMouse("/dev/uinput", []byte("remote-touchpad-mouse")) + if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) { + err = &UnsupportedPlatformError{err} + } if err != nil { keyboard.Close() return nil, err diff --git a/inputcontrol/keymap.go b/inputcontrol/keymap.go index 3989bb8..353e338 100644 --- a/inputcontrol/keymap.go +++ b/inputcontrol/keymap.go @@ -61,7 +61,7 @@ func LoadKeymap(name string) (*Keymap, error) { } bkeymap, err := exec.Command("loadkeys", "--bkeymap", name).Output() if err != nil { - return nil, err + return nil, &UnsupportedPlatformError{err} } if !strings.HasPrefix(string(bkeymap), bkeymapFileSignature) { return nil, fmt.Errorf("invalid bkeymap: signature not found") From 51108765e391384b00f1f7f8bb745908531464e4 Mon Sep 17 00:00:00 2001 From: Unrud Date: Tue, 25 Apr 2023 01:38:49 +0200 Subject: [PATCH 21/96] Add null controller --- inputcontrol/controller_null.go | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 inputcontrol/controller_null.go diff --git a/inputcontrol/controller_null.go b/inputcontrol/controller_null.go new file mode 100644 index 0000000..6188cce --- /dev/null +++ b/inputcontrol/controller_null.go @@ -0,0 +1,66 @@ +//go:build null + +/* + * Copyright (c) 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +package inputcontrol + +import ( + "log" +) + +type nullController struct{} + +func init() { + RegisterController("null", InitNullController, 1000) +} + +func InitNullController() (Controller, error) { + return &nullController{}, nil +} + +func (p *nullController) Close() error { + return nil +} + +func (p *nullController) KeyboardText(text string) error { + log.Printf("KeyboardText(text: %#v)", text) + return nil +} + +func (p *nullController) KeyboardKey(key Key) error { + log.Printf("KeyboardKey(key: %#v)", key) + return nil +} + +func (p *nullController) PointerButton(button PointerButton, press bool) error { + log.Printf("PointerButton(button: %#v, press: %#v)", button, press) + return nil +} + +func (p *nullController) PointerMove(deltaX, deltaY int) error { + log.Printf("PointerMove(deltaX: %#v, deltaY: %#v)", deltaX, deltaY) + return nil +} + +func (p *nullController) PointerScroll(deltaHorizontal, deltaVertical int, finish bool) error { + log.Printf("PointerScroll(deltaHorizontal: %#v, deltaVertical: %#v, finish: %#v)", + deltaHorizontal, deltaVertical, finish) + return nil +} From e5369af219eb5a87bebca3fc9155bdbe02b429c3 Mon Sep 17 00:00:00 2001 From: Unrud Date: Tue, 25 Apr 2023 01:38:49 +0200 Subject: [PATCH 22/96] Refactor web --- webdata/fn.js | 661 ++++++++++++++++++++------------------------- webdata/index.html | 92 ++++--- webdata/main.css | 211 +++++++-------- 3 files changed, 450 insertions(+), 514 deletions(-) diff --git a/webdata/fn.js b/webdata/fn.js index bf1d1ce..c2f8fac 100644 --- a/webdata/fn.js +++ b/webdata/fn.js @@ -1,6 +1,6 @@ "use strict"; /* - * Copyright (c) 2018-2019 Unrud + * Copyright (c) 2018-2019, 2023 Unrud * * This file is part of Remote-Touchpad. * @@ -18,88 +18,81 @@ * along with Remote-Touchpad. If not, see . */ +(() => { + // [1 Touch, 2 Touches, 3 Touches] (as pixel) -var TOUCH_MOVE_THRESHOLD = [10, 15, 15]; +const TOUCH_MOVE_THRESHOLD = [10, 15, 15]; // Max time between consecutive touches for clicking or dragging (as milliseconds) -var TOUCH_TIMEOUT = 250; +const TOUCH_TIMEOUT = 250; // [[pixel/second, multiplicator], ...] -var POINTER_ACCELERATION = [ +const POINTER_ACCELERATION = [ [0, 0], [87, 1], [173, 1], - [553, 2] + [553, 2], ]; -var POINTER_BUTTON_LEFT = 0; -var POINTER_BUTTON_RIGHT = 1; -var POINTER_BUTTON_MIDDLE = 2; +const POINTER_BUTTON_LEFT = 0; +const POINTER_BUTTON_RIGHT = 1; +const POINTER_BUTTON_MIDDLE = 2; -var KEY_VOLUME_MUTE = 0; -var KEY_VOLUME_DOWN = 1; -var KEY_VOLUME_UP = 2; -var KEY_MEDIA_PLAY_PAUSE = 3; -var KEY_MEDIA_PREV_TRACK = 4; -var KEY_MEDIA_NEXT_TRACK = 5; -var KEY_BROWSER_BACK = 6; -var KEY_BROWSER_FORWARD = 7; -var KEY_SUPER = 8; -var KEY_LEFT = 9; -var KEY_RIGHT = 10; -var KEY_UP = 11; -var KEY_DOWN = 12; -var KEY_HOME = 13; -var KEY_END = 14; -var KEY_BACK_SPACE = 15; -var KEY_DELETE = 16; -var KEY_RETURN = 17; +const KEY_VOLUME_MUTE = 0; +const KEY_VOLUME_DOWN = 1; +const KEY_VOLUME_UP = 2; +const KEY_MEDIA_PLAY_PAUSE = 3; +const KEY_MEDIA_PREV_TRACK = 4; +const KEY_MEDIA_NEXT_TRACK = 5; +const KEY_BROWSER_BACK = 6; +const KEY_BROWSER_FORWARD = 7; +const KEY_SUPER = 8; +const KEY_LEFT = 9; +const KEY_RIGHT = 10; +const KEY_UP = 11; +const KEY_DOWN = 12; +const KEY_HOME = 13; +const KEY_END = 14; +const KEY_BACK_SPACE = 15; +const KEY_DELETE = 16; +const KEY_RETURN = 17; -var ws = null; -var config = null; +const wsURL = new URL("ws", location.href); +wsURL.protocol = wsURL.protocol == "http:" ? "ws:" : "wss:"; +const ws = new WebSocket(wsURL); -var util = (function() { - var util = {}; +let config = null; - util.fullscreenEnabled = function() { +const compat = (() => { + const compat = {}; + + compat.fullscreenEnabled = () => { return (document.fullscreenEnabled || document.webkitFullscreenEnabled || - document.mozFullScreenEnabled || - document.msFullscreenEnabled || false); }; - util.requestFullscreen = function(element, options) { + compat.requestFullscreen = (element, options) => { if (element.requestFullscreen) { element.requestFullscreen(options); } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen(options); - } else if (element.mozRequestFullScreen) { - element.mozRequestFullScreen(options); - } else if (element.msRequestFullscreen) { - element.msRequestFullscreen(options); } }; - util.exitFullscreen = function() { + compat.exitFullscreen = () => { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); } }; - util.fullscreenElement = function() { + compat.fullscreenElement = () => { return (document.fullscreenElement || document.webkitFullscreenElement || - document.mozFullScreenElement || - document.msFullscreenElement || null); }; - util.addFullscreenchangeEventListener = function(listener) { + compat.addFullscreenchangeEventListener = (listener) => { if ("onfullscreenchange" in document) { document.addEventListener("fullscreenchange", listener); } else if ("onwebkitfullscreenchange" in document) { @@ -107,66 +100,58 @@ var util = (function() { } }; - util.requestPointerLock = function(element) { + compat.requestPointerLock = (element) => { if (element.requestPointerLock) { element.requestPointerLock(); - } else if (element.mozRequestPointerLock) { - element.mozRequestPointerLock(); } }; - util.exitPointerLock = function() { + compat.exitPointerLock = () => { if (document.exitPointerLock) { document.exitPointerLock(); - } else if (document.mozExitPointerLock) { - document.mozExitPointerLock(); } }; - util.pointerLockElement = function() { - return (document.pointerLockElement || - document.mozPointerLockElement || - null); + compat.pointerLockElement = () => { + return document.pointerLockElement || null; }; - util.addPointerlockchangeEventListener = function(listener) { + compat.addPointerlockchangeEventListener = (listener) => { if ("onpointerlockchange" in document) { document.addEventListener("pointerlockchange", listener); - } else if ("onmozpointerlockchange" in document) { - document.addEventListener("mozpointerlockchange", listener); } }; - return util; + return compat; })(); -var controller = (function() { - var controller = {}; +const controller = (() => { + const controller = {}; - var moveXSum = 0; - var moveYSum = 0; - var scrollHSum = 0; - var scrollVSum = 0; - var scrolling = false; - var scrollFinish = false; - var updateTimoueActive = false; + let moveXSum = 0; + let moveYSum = 0; + let scrollHSum = 0; + let scrollVSum = 0; + let scrolling = false; + let scrollFinish = false; + let updateTimeoutActive = false; - function startUpdate(fromTimeout) { - if (updateTimoueActive && !fromTimeout) { + const startUpdate = (fromTimeout) => { + if (updateTimeoutActive && !fromTimeout) { return; } - updateTimoueActive = false; - var finished = true; - var xInt = Math.trunc(moveXSum); - var yInt = Math.trunc(moveYSum); + updateTimeoutActive = false; + let finished = true; + const xInt = Math.trunc(moveXSum); + const yInt = Math.trunc(moveYSum); if (xInt != 0 || yInt != 0) { ws.send("m" + xInt + ";" + yInt); moveXSum -= xInt; moveYSum -= yInt; finished = false; } - var hInt = Math.trunc(scrollHSum); - var vInt = Math.trunc(scrollVSum); + const hInt = Math.trunc(scrollHSum); + const vInt = Math.trunc(scrollVSum); if (hInt != 0 || vInt != 0) { ws.send((scrollFinish ? "S" : "s") + hInt + ";" + vInt); scrollHSum -= hInt; @@ -179,112 +164,112 @@ var controller = (function() { scrolling = false; scrollFinish = false; } - updateTimoueActive = !finished && config.updateRate > 0; - if (updateTimoueActive) { + updateTimeoutActive = !finished && config.updateRate > 0; + if (updateTimeoutActive) { setTimeout(startUpdate, 1000/config.updateRate, true); } - } + }; - controller.pointerMove = function(deltaX, deltaY) { + controller.pointerMove = (deltaX, deltaY) => { moveXSum += deltaX; moveYSum += deltaY; startUpdate(); }; - controller.pointerScroll = function(deltaHorizontal, deltaVertical, finish) { + controller.pointerScroll = (deltaHorizontal, deltaVertical, finish) => { scrollHSum += deltaHorizontal; scrollVSum += deltaVertical; scrollFinish |= finish; startUpdate(); }; - controller.pointerButton = function(button, press) { + controller.pointerButton = (button, press) => { ws.send("b" + button + ";" + (press ? 1 : 0)); }; - controller.keyboardKey = function(key) { + controller.keyboardKey = (key) => { ws.send("k" + key); }; - controller.keyboardText = function(text) { + controller.keyboardText = (text) => { ws.send("t" + text); }; return controller; })(); -var touchpad = (function() { - var touchpad = {}; +const touchpad = (() => { + const touchpad = {}; - var moved = false; - var startTimeStamp = 0; - var lastEndTimeStamp = 0; - var releasedCount = 0; - var ongoingTouches = []; - var dragging = false; - var draggingTimeout = null; + let moved = false; + let startTimeStamp = 0; + let lastEndTimeStamp = 0; + let releasedCount = 0; + let ongoingTouches = []; + let dragging = false; + let draggingTimeout = null; - function copyTouch(touch, timeStamp) { + const copyTouch = (touch, timeStamp) => { return { identifier: touch.identifier, pageX: touch.pageX, pageXStart: touch.pageX, pageY: touch.pageY, pageYStart: touch.pageY, - timeStamp: timeStamp + timeStamp: timeStamp, }; - } + }; - function ongoingTouchIndexById(idToFind) { - for (var i = 0; i < ongoingTouches.length; i += 1) { + const ongoingTouchIndexById = (idToFind) => { + for (let i = 0; i < ongoingTouches.length; i += 1) { if (ongoingTouches[i].identifier == idToFind) { return i; } } return -1; - } + }; - function calculateAccelerationMult(speed) { - for (var i = 0; i < POINTER_ACCELERATION.length; i += 1) { - var s2 = POINTER_ACCELERATION[i][0]; - var a2 = POINTER_ACCELERATION[i][1]; + const calculateAccelerationMult = (speed) => { + for (let i = 0; i < POINTER_ACCELERATION.length; i += 1) { + const s2 = POINTER_ACCELERATION[i][0]; + const a2 = POINTER_ACCELERATION[i][1]; if (s2 <= speed) { continue; } if (i == 0) { return a2; } - var s1 = POINTER_ACCELERATION[i - 1][0]; - var a1 = POINTER_ACCELERATION[i - 1][1]; + const s1 = POINTER_ACCELERATION[i - 1][0]; + const a1 = POINTER_ACCELERATION[i - 1][1]; return ((speed - s1) / (s2 - s1)) * (a2 - a1) + a1; } if (POINTER_ACCELERATION.length > 0) { return POINTER_ACCELERATION[POINTER_ACCELERATION.length - 1][1]; } return 1; - } + }; - function onDraggingTimeout() { + const onDraggingTimeout = () => { draggingTimeout = null; controller.pointerButton(POINTER_BUTTON_LEFT, false); - } + }; - touchpad.handleTouchstart = function(evt) { + touchpad.handleTouchstart = (evt) => { // Might get called multiple times for the same touches if (ongoingTouches.length == 0) { startTimeStamp = evt.timeStamp; moved = false; } - var touches = evt.changedTouches; - var foundTouch = false; - for (var i = 0; i < touches.length; i += 1) { - if (ongoingTouches.length == 0 && !touches[i].target.classList.contains("touch")) { + const touches = evt.changedTouches; + let foundTouch = false; + for (let i = 0; i < touches.length; i += 1) { + if (ongoingTouches.length == 0 && !touches[i].target.classList.contains("touch-input")) { continue; } foundTouch = true; - var touch = copyTouch(touches[i], evt.timeStamp); - var idx = ongoingTouchIndexById(touch.identifier); + const touch = copyTouch(touches[i], evt.timeStamp); + const idx = ongoingTouchIndexById(touch.identifier); if (idx < 0) { ongoingTouches.push(touch); } else { @@ -304,11 +289,11 @@ var touchpad = (function() { controller.pointerScroll(0, 0, true); }; - touchpad.handleTouchend = touchpad.handleTouchcancel = function(evt) { - var touches = evt.changedTouches; - var foundTouch = false; - for (var i = 0; i < touches.length; i += 1) { - var idx = ongoingTouchIndexById(touches[i].identifier); + touchpad.handleTouchend = touchpad.handleTouchcancel = (evt) => { + const touches = evt.changedTouches; + let foundTouch = false; + for (let i = 0; i < touches.length; i += 1) { + const idx = ongoingTouchIndexById(touches[i].identifier); if (idx < 0) { continue; } @@ -331,7 +316,7 @@ var touchpad = (function() { controller.pointerButton(POINTER_BUTTON_LEFT, false); } if (!moved && evt.timeStamp - startTimeStamp < TOUCH_TIMEOUT) { - var button = 0; + let button = 0; if (releasedCount == 1) { button = POINTER_BUTTON_LEFT; } else if (releasedCount == 2) { @@ -350,19 +335,19 @@ var touchpad = (function() { } }; - touchpad.handleTouchmove = function(evt) { - var sumX = 0; - var sumY = 0; - var touches = evt.changedTouches; - var foundTouch = false; - for (var i = 0; i < touches.length; i += 1) { - var idx = ongoingTouchIndexById(touches[i].identifier); + touchpad.handleTouchmove = (evt) => { + let sumX = 0; + let sumY = 0; + const touches = evt.changedTouches; + let foundTouch = false; + for (let i = 0; i < touches.length; i += 1) { + const idx = ongoingTouchIndexById(touches[i].identifier); if (idx < 0) { continue; } foundTouch = true; if (!moved) { - var dist = Math.sqrt(Math.pow(touches[i].pageX - ongoingTouches[idx].pageXStart, 2) + + const dist = Math.sqrt(Math.pow(touches[i].pageX - ongoingTouches[idx].pageXStart, 2) + Math.pow(touches[i].pageY - ongoingTouches[idx].pageYStart, 2)); if (ongoingTouches.length > TOUCH_MOVE_THRESHOLD.length || dist > TOUCH_MOVE_THRESHOLD[ongoingTouches.length - 1] || @@ -370,9 +355,9 @@ var touchpad = (function() { moved = true; } } - var dx = touches[i].pageX - ongoingTouches[idx].pageX; - var dy = touches[i].pageY - ongoingTouches[idx].pageY; - var timeDelta = evt.timeStamp - ongoingTouches[idx].timeStamp; + const dx = touches[i].pageX - ongoingTouches[idx].pageX; + const dy = touches[i].pageY - ongoingTouches[idx].pageY; + const timeDelta = evt.timeStamp - ongoingTouches[idx].timeStamp; sumX += dx * calculateAccelerationMult(Math.abs(dx) / timeDelta * 1000); sumY += dy * calculateAccelerationMult(Math.abs(dy) / timeDelta * 1000); ongoingTouches[idx].pageX = touches[i].pageX; @@ -395,14 +380,14 @@ var touchpad = (function() { return touchpad; })(); -var keyboard = (function() { - var keyboard = {}; +const keyboard = (() => { + const keyboard = {}; - keyboard.handleKeydown = function(evt) { + keyboard.handleKeydown = (evt) => { if (evt.ctrlKey || evt.altKey || evt.isComposing) { return; } - var key = null; + let key = null; if (evt.key == "OS" || evt.key == "Super" || evt.key == "Meta") { key = KEY_SUPER; } else if (evt.key == "Backspace") { @@ -438,30 +423,30 @@ var keyboard = (function() { return keyboard; })(); -var mouse = (function() { - var mouse = {}; +const mouse = (() => { + const mouse = {}; - var buttons = 0; + let buttons = 0; - function updateButtons(newButtons) { - for (var button = 0; button < 3; button += 1) { - var flag = 1 << button; + const updateButtons = (newButtons) => { + for (let button = 0; button < 3; button += 1) { + const flag = 1 << button; if ((newButtons&flag) != (buttons&flag)) { controller.pointerButton(button, newButtons&flag); } } buttons = newButtons; - } + }; - mouse.handleMousedown = mouse.handleMouseup = function(evt) { + mouse.handleMousedown = mouse.handleMouseup = (evt) => { updateButtons(evt.buttons); }; - mouse.handleMousemove = function(evt) { + mouse.handleMousemove = (evt) => { controller.pointerMove(evt.movementX*config.mouseMoveSpeed, evt.movementY*config.mouseMoveSpeed); }; - mouse.handleWheel = function(evt) { + mouse.handleWheel = (evt) => { if (evt.deltaMode == WheelEvent.DOM_DELTA_PIXEL) { controller.pointerScroll(evt.deltaX*config.mouseScrollSpeed, evt.deltaY*config.mouseScrollSpeed, true); } else if (evt.deltaMode == WheelEvent.DOM_DELTA_LINE) { @@ -472,225 +457,179 @@ var mouse = (function() { return mouse; })(); -function challengeResponse(message) { - var shaObj = new jsSHA("SHA-256", "TEXT"); +const challengeResponse = (message) => { + const shaObj = new window.jsSHA("SHA-256", "TEXT"); shaObj.setHMACKey(message, "TEXT"); shaObj.update(window.location.hash.substr(1)); return btoa(shaObj.getHMAC("BYTES")); +}; + +const scenes = document.querySelectorAll("body > .scene"); +const openingScene = document.getElementById("opening"); +const closedScene = document.getElementById("closed"); +const padScene = document.getElementById("pad"); +const keysScene = document.getElementById("keys"); +const keysPages = keysScene.querySelectorAll(":scope > .page"); +const textInputScene = document.getElementById("text-input"); +const textInput = textInputScene.querySelector("textarea"); +const mouseScene = document.getElementById("mouse"); + +let ready = false; +let closed = false; +let activeScene = null; +let keysActiveName = ""; + +const showScene = (scene) => { + activeScene = scene; + if (compat.fullscreenElement() && !scene.classList.contains("allow-fullscreen")) { + compat.exitFullscreen(); + } + if (compat.pointerLockElement() && activeScene != mouseScene) { + compat.exitPointerLock(); + } + textInput.value = ""; + for (const otherScene of scenes) { + otherScene.classList.toggle("hidden", otherScene != scene); + } +}; + +const setKeysPage = (index, relative=false) => { + if (relative) { + for (let i = 0; i < keysPages.length && keysPages[i].classList.contains("hidden"); i += 1, index += 1); + } + index = ((index % keysPages.length) + keysPages.length) % keysPages.length; + sessionStorage.setItem(keysActiveName, index); + for (let i = 0; i < keysPages.length; i += 1) { + keysPages[i].classList.toggle("hidden", i != index); + } +}; + +const showKeys = (name = "", defaultIndex = 0) => { + showScene(keysScene); + keysActiveName = "keys" + (name ? ":" + name : ""); + let keysIndex = parseInt(sessionStorage.getItem(keysActiveName)); + if (isNaN(keysIndex)) { + keysIndex = defaultIndex; + } + setKeysPage(keysIndex); + if (history.state != keysActiveName) { + history.pushState(keysActiveName, ""); + } +}; + +const showTextInput = () => { + showScene(textInputScene); + textInput.value = sessionStorage.getItem("text-input") || ""; + textInput.focus(); + if (history.state != "text-input") { + history.pushState("text-input", ""); + } +}; + +textInput.oninput = () => { + sessionStorage.setItem("text-input", textInput.value); +}; + +const updateUI = () => { + if (!ready) { + showScene(closed ? closedScene : openingScene); + } else if (compat.pointerLockElement()) { + showScene(mouseScene); + } else if ((history.state || "").split(":")[0] == "keys") { + showKeys(history.state.substr("keys:".length)); + } else if (history.state == "text-input") { + showTextInput(); + } else { + showScene(padScene); + } +}; + +let authenticated = false; +ws.onmessage = (evt) => { + if (!authenticated) { + ws.send(challengeResponse(evt.data)); + authenticated = true; + return; + } + try { + config = JSON.parse(evt.data); + } catch (e) { + ws.close(); + throw (e); + } + ready = true; + updateUI(); +}; + +ws.onclose = () => { + ready = false; + closed = true; + updateUI(); +}; + +compat.addFullscreenchangeEventListener(() => { + updateUI(); +}); +for (const element of document.querySelectorAll(".visble-if-fullscreen-enabled")) { + element.classList.toggle("hidden", !compat.fullscreenEnabled()); } -window.addEventListener("load", function() { - var DEFAULT_KEYS_SCENE = { - "": 0, - "keyboard": 1 - }; - var ready = false; - var closed = false; - var scenes = document.querySelectorAll("body > .scene"); - var openingScene = document.getElementById("opening"); - var closedScene = document.getElementById("closed"); - var padScene = document.getElementById("pad"); - var keysScene = document.getElementById("keys"); - var keysSubScenes = keysScene.querySelectorAll(".scene"); - var keyboardScene = document.getElementById("keyboard"); - var fullscreenbutton = document.getElementById("fullscreenbutton"); - var keyboardTextarea = keyboardScene.querySelector("textarea"); - var mouseScene = document.getElementById("mouse"); - var activeScene = null; - var keysActiveName = ""; - - function showScene(scene) { - activeScene = scene; - if (util.fullscreenElement() && !scene.classList.contains("fullscreen")) { - util.exitFullscreen(); - } - if (util.pointerLockElement() && activeScene != mouseScene) { - util.exitPointerLock(); - } - keyboardTextarea.value = ""; - scenes.forEach(function(otherScene) { - otherScene.classList.toggle("hidden", otherScene != scene); - }); +const toggleFullscreen = () => { + if (compat.fullscreenElement()) { + compat.exitFullscreen(); + } else { + compat.requestFullscreen(document.documentElement, {navigationUI: "hide"}); } +}; - function showKeysScene(index) { - if (!Number.isInteger(index) || index < 0 || keysSubScenes.length <= index) { - index = 0; - } - sessionStorage.setItem(keysActiveName, index); - for (var i = 0; i < keysSubScenes.length; i += 1) { - keysSubScenes[i].classList.toggle("hidden", i != index); - } +document.getElementById("send-text").addEventListener("click", () => { + if (textInput.value) { + // normalize line endings + controller.keyboardText(textInput.value.replace(/\r\n?/g, "\n")); + textInput.value = ""; + textInput.oninput(); } - - function showKeys(name) { - showScene(keysScene); - keysActiveName = "keys" + (name ? ":" + name : ""); - var keysIndex = parseInt(sessionStorage.getItem(keysActiveName)); - if (isNaN(keysIndex)) { - keysIndex = DEFAULT_KEYS_SCENE[name || ""] || 0; - } - showKeysScene(keysIndex); - if (history.state != keysActiveName) { - history.pushState(keysActiveName, ""); - } - } - - function showKeyboard() { - showScene(keyboardScene); - keyboardTextarea.value = sessionStorage.getItem("keyboard") || ""; - keyboardTextarea.focus(); - if (history.state != "keyboard") { - history.pushState("keyboard", ""); - } - } - - keyboardTextarea.oninput = function() { - sessionStorage.setItem("keyboard", keyboardTextarea.value); - }; - - function updateUI() { - if (!ready) { - showScene(closed ? closedScene : openingScene); - } else if (util.pointerLockElement()) { - showScene(mouseScene); - } else if ((history.state || "").split(":")[0] == "keys") { - showKeys(history.state.substr("keys:".length)); - } else if (history.state == "keyboard") { - showKeyboard(); - } else { - showScene(padScene); - } - } - - updateUI(); - - var wsURL = new URL("ws", location.href); - wsURL.protocol = wsURL.protocol == "http:" ? "ws:" : "wss:"; - ws = new WebSocket(wsURL); - - var authenticated = false; - ws.onmessage = function(evt) { - if (!authenticated) { - ws.send(challengeResponse(evt.data)); - authenticated = true; - return; - } - try { - config = JSON.parse(evt.data); - } catch (e) { - ws.close(); - throw (e); - } - ready = true; - updateUI(); - }; - - ws.onclose = function() { - ready = false; - closed = true; - updateUI(); - }; - - document.getElementById("keysbutton").addEventListener("click", function() { - showKeys(); - }); - document.getElementById("keyboardbutton").addEventListener("click", function() { - showKeyboard(); - }); - util.addFullscreenchangeEventListener(function() { - updateUI(); - }); - if (!util.fullscreenEnabled()) { - fullscreenbutton.classList.add("hidden"); - } - fullscreenbutton.addEventListener("click", function() { - if (util.fullscreenElement()) { - util.exitFullscreen(); - } else { - util.requestFullscreen(document.documentElement, {navigationUI: "hide"}); - } - }); - document.getElementById("switchbutton").addEventListener("click", function() { - var keysIndex = 0; - for (var i = 0; i < keysSubScenes.length; i += 1) { - if (!keysSubScenes[i].classList.contains("hidden")) { - keysIndex = i; - } - } - showKeysScene(keysIndex + 1); - }); - [ - {id: "browserbackbutton", key: KEY_BROWSER_BACK}, - {id: "superbutton", key: KEY_SUPER}, - {id: "browserforwardbutton", key: KEY_BROWSER_FORWARD}, - {id: "prevtrackbutton", key: KEY_MEDIA_PREV_TRACK}, - {id: "playpausebutton", key: KEY_MEDIA_PLAY_PAUSE}, - {id: "nexttrackbutton", key: KEY_MEDIA_NEXT_TRACK}, - {id: "volumedownbutton", key: KEY_VOLUME_DOWN}, - {id: "volumemutebutton", key: KEY_VOLUME_MUTE}, - {id: "volumeupbutton", key: KEY_VOLUME_UP}, - {id: "backspacebutton", key: KEY_BACK_SPACE}, - {id: "returnbutton", key: KEY_RETURN}, - {id: "deletebutton", key: KEY_DELETE}, - {id: "homebutton", key: KEY_HOME}, - {id: "endbutton", key: KEY_END}, - {id: "leftbutton", key: KEY_LEFT}, - {id: "rightbutton", key: KEY_RIGHT}, - {id: "upbutton", key: KEY_UP}, - {id: "downbutton", key: KEY_DOWN} - ].forEach(function(o) { - document.getElementById(o.id).addEventListener("click", function() { - controller.keyboardKey(o.key); - }); - }); - document.getElementById("keyboardkeysbutton").addEventListener("click", function() { - showKeys("keyboard"); - }); - document.getElementById("sendbutton").addEventListener("click", function() { - if (keyboardTextarea.value) { - // normalize line endings - controller.keyboardText(keyboardTextarea.value.replace(/\r\n?/g, "\n")); - keyboardTextarea.value = ""; - keyboardTextarea.oninput(); - } - history.back(); - }); - window.addEventListener("popstate", function() { - updateUI(); - }); - document.getElementById("reloadbutton").addEventListener("click", function() { - location.reload(); - }); - document.querySelectorAll(".backbutton").forEach(function(button) { - button.addEventListener("click", function() { - history.back(); - }); - }); - document.addEventListener("touchstart", touchpad.handleTouchstart); - document.addEventListener("touchend", touchpad.handleTouchend); - document.addEventListener("touchcancel", touchpad.handleTouchcancel); - document.addEventListener("touchmove", touchpad.handleTouchmove); - document.addEventListener("keydown", function(evt) { - if (activeScene && activeScene.classList.contains("key")) { - keyboard.handleKeydown(evt); - } - }); - util.addPointerlockchangeEventListener(function() { - updateUI(); - }); - document.addEventListener("mousedown", function(event) { - if (activeScene != mouseScene && event.buttons == 1 && event.target.classList.contains("touch")) { - util.requestPointerLock(mouseScene); - } - }); - ["touchstart", "touchend", "touchcancel", "touchmove"].forEach(function(type) { - mouseScene.addEventListener(type, function(evt) { - evt.preventDefault(); - }); - }); - mouseScene.addEventListener("mousedown", mouse.handleMousedown); - mouseScene.addEventListener("mouseup", mouse.handleMouseup); - mouseScene.addEventListener("mousemove", mouse.handleMousemove); - mouseScene.addEventListener("wheel", mouse.handleWheel); + history.back(); }); +window.addEventListener("popstate", () => { + updateUI(); +}); +document.addEventListener("touchstart", touchpad.handleTouchstart); +document.addEventListener("touchend", touchpad.handleTouchend); +document.addEventListener("touchcancel", touchpad.handleTouchcancel); +document.addEventListener("touchmove", touchpad.handleTouchmove); +document.addEventListener("keydown", (evt) => { + if (activeScene && activeScene.classList.contains("keyboard-input")) { + keyboard.handleKeydown(evt); + } +}); +compat.addPointerlockchangeEventListener(() => { + updateUI(); +}); +document.addEventListener("mousedown", (event) => { + if (activeScene != mouseScene && event.buttons == 1 && event.target.classList.contains("mouse-input")) { + compat.requestPointerLock(mouseScene); + } +}); +for (const type of ["touchstart", "touchend", "touchcancel", "touchmove"]) { + mouseScene.addEventListener(type, (evt) => { + evt.preventDefault(); + }); +} +mouseScene.addEventListener("mousedown", mouse.handleMousedown); +mouseScene.addEventListener("mouseup", mouse.handleMouseup); +mouseScene.addEventListener("mousemove", mouse.handleMousemove); +mouseScene.addEventListener("wheel", mouse.handleWheel); + +window.app = { + KEY_VOLUME_MUTE, KEY_VOLUME_DOWN, KEY_VOLUME_UP, KEY_MEDIA_PLAY_PAUSE, + KEY_MEDIA_PREV_TRACK, KEY_MEDIA_NEXT_TRACK, KEY_BROWSER_BACK, KEY_BROWSER_FORWARD, + KEY_SUPER, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_HOME, KEY_END, KEY_BACK_SPACE, + KEY_DELETE, KEY_RETURN, + showKeys, showTextInput, setKeysPage, toggleFullscreen, + key: controller.keyboardKey, + text: controller.keyboardText, +}; +updateUI(); + +})(); diff --git a/webdata/index.html b/webdata/index.html index ac1968d..1f8b8db 100644 --- a/webdata/index.html +++ b/webdata/index.html @@ -1,9 +1,8 @@ - - + Remote Touchpad @@ -13,61 +12,66 @@

      Connecting…

      -