From fb31a40759acf32f45fa189f5576c0eb18c39e31 Mon Sep 17 00:00:00 2001 From: Luca Berneking Date: Sun, 6 Jul 2025 21:32:19 +0200 Subject: [PATCH] initial bluetooth poc, gamepad acting weird, kb+mouse working, no gui --- go.mod | 22 +- go.sum | 28 +- main.go | 22 +- pkg/bluetooth/bluez.go | 103 +++++ pkg/bluetooth/device.go | 32 ++ pkg/bluetooth/l2cap.go | 64 +++ pkg/bluetooth/sdp.go | 136 ++++++ pkg/cmd/daemon.go | 2 + pkg/cmd/gui.go | 6 +- pkg/{deck => config}/info.go | 2 +- pkg/daemon/client.go | 2 +- pkg/daemon/server.go | 94 +++- pkg/gui/gui.go | 8 + pkg/hid/device.go | 36 +- pkg/hid/joystick.go | 14 +- pkg/hid/keyboard.go | 8 +- pkg/hid/mouse.go | 8 +- pkg/hid/reportid.go | 31 ++ pkg/ipc/daemon.pb.go | 617 +++++++-------------------- pkg/ipc/daemon_grpc.pb.go | 387 +++++++++++++++++ pkg/ipc/proto/daemon.proto | 15 +- pkg/ipc/proto/generate.go | 2 +- pkg/service/deck.go | 56 ++- pkg/{setup => steamworks}/install.go | 2 +- pkg/{setup => usb}/modprobe.go | 2 +- pkg/{setup => usb}/usb.go | 32 +- pkg/util/root.go | 8 +- 27 files changed, 1183 insertions(+), 556 deletions(-) create mode 100644 pkg/bluetooth/bluez.go create mode 100644 pkg/bluetooth/device.go create mode 100644 pkg/bluetooth/l2cap.go create mode 100644 pkg/bluetooth/sdp.go rename pkg/{deck => config}/info.go (92%) create mode 100644 pkg/hid/reportid.go create mode 100644 pkg/ipc/daemon_grpc.pb.go rename pkg/{setup => steamworks}/install.go (98%) rename pkg/{setup => usb}/modprobe.go (92%) rename pkg/{setup => usb}/usb.go (83%) diff --git a/go.mod b/go.mod index d5805f7..ef99247 100644 --- a/go.mod +++ b/go.mod @@ -1,24 +1,29 @@ module github.com/lucaber/deckjoy -go 1.20 +go 1.23.0 + +toolchain go1.24.4 require ( fyne.io/fyne/v2 v2.3.4 github.com/ebitengine/purego v0.3.2 - github.com/pkg/errors v0.8.1 + github.com/godbus/dbus/v5 v5.1.0 + github.com/muka/go-bluetooth v0.0.0-20240701044517-04c4f09c514e + github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.0 github.com/urfave/cli/v2 v2.25.1 github.com/veandco/go-sdl2 v0.4.35 golang.org/x/image v0.7.0 - golang.org/x/sys v0.7.0 - google.golang.org/grpc v1.54.0 - google.golang.org/protobuf v1.28.1 + golang.org/x/sys v0.31.0 + google.golang.org/grpc v1.73.0 + google.golang.org/protobuf v1.36.6 ) require ( fyne.io/systray v1.10.1-0.20230403195833-7dc3c09283d6 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/structs v1.1.0 // indirect github.com/fredbi/uri v0.1.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect @@ -27,9 +32,8 @@ require ( github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect github.com/go-text/typesetting v0.0.0-20230405155246-bf9c697c6e16 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect github.com/kr/pretty v0.3.1 // indirect @@ -43,8 +47,8 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yuin/goldmark v1.4.13 // indirect golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/text v0.23.0 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 9aad961..83f3252 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fredbi/uri v0.1.0 h1:8XBBD74STBLcWJ5smjEkKCZivSxSKMhFB0FbQUKeNyM= github.com/fredbi/uri v0.1.0/go.mod h1:1xC40RnIOGCaQzswaOvrzvG/3M3F0hyDVb3aO/1iGy0= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -104,6 +106,7 @@ github.com/go-text/typesetting v0.0.0-20230405155246-bf9c697c6e16 h1:DvHeDNqK8cx github.com/go-text/typesetting v0.0.0-20230405155246-bf9c697c6e16/go.mod h1:zvWM81wAVW6QfVDI6yxfbCuoLnobSYTuMsrXU/u11y8= github.com/go-text/typesetting-utils v0.0.0-20230326210548-458646692de6 h1:zAAA1U4ykFwqPbcj6YDxvq3F2g0wc/ngPfLJjkR/8zs= github.com/go-text/typesetting-utils v0.0.0-20230326210548-458646692de6/go.mod h1:RaqFwjcYyM5BjbYGwON0H5K0UqwO3sJlo9ukKha80ZE= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -140,6 +143,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -155,6 +159,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -171,6 +176,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -214,6 +220,7 @@ github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6U github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -240,15 +247,19 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/muka/go-bluetooth v0.0.0-20240701044517-04c4f09c514e h1:1Sc4DqlgszKejMkjydCSq8zOKmF+hr8odAl5JoBZ+ec= +github.com/muka/go-bluetooth v0.0.0-20240701044517-04c4f09c514e/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -268,6 +279,7 @@ github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxr github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -296,6 +308,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/suapapa/go_eddystone v1.3.1/go.mod h1:bXC11TfJOS+3g3q/Uzd7FKd5g62STQEfeEIhcKe4Qy8= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA= github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg= @@ -423,6 +436,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -454,6 +469,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -477,6 +493,7 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -502,6 +519,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 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= @@ -518,6 +537,8 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -565,6 +586,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20200925191224-5d1fdd8fa346/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -674,6 +696,8 @@ google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -688,6 +712,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main.go b/main.go index af3bdf2..b13e61b 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,12 @@ package main import ( + "github.com/lucaber/deckjoy/pkg/bluetooth" "github.com/lucaber/deckjoy/pkg/cmd" - "github.com/lucaber/deckjoy/pkg/setup" + "github.com/lucaber/deckjoy/pkg/hid" + "github.com/lucaber/deckjoy/pkg/usb" "github.com/urfave/cli/v2" - "log" + log "github.com/sirupsen/logrus" "os" "runtime" ) @@ -22,11 +24,11 @@ func main() { Name: "cleanup", Usage: "remove usb gadget", Action: func(*cli.Context) error { - deckSetup, err := setup.NewDeck() + deckUSB, err := usb.NewUSB() if err != nil { return err } - err = deckSetup.Destroy() + err = deckUSB.Destroy() if err != nil { return err } @@ -38,6 +40,18 @@ func main() { Usage: "show gui", Action: cmd.RunGui, }, + { + Name: "test", + Usage: "test", + Action: func(context *cli.Context) error { + log.SetLevel(log.DebugLevel) + err := bluetooth.NewBluetooth().Run(context.Context, bluetooth.SDPRecord{HIDDescriptor: hid.KeyboardReportDesc}) + if err != nil { + return err + } + select {} + }, + }, }, Action: cmd.RunGui, } diff --git a/pkg/bluetooth/bluez.go b/pkg/bluetooth/bluez.go new file mode 100644 index 0000000..b21ea81 --- /dev/null +++ b/pkg/bluetooth/bluez.go @@ -0,0 +1,103 @@ +package bluetooth + +import ( + "context" + "errors" + "github.com/godbus/dbus/v5" + "github.com/muka/go-bluetooth/bluez/profile/profile" + log "github.com/sirupsen/logrus" +) + +type Bluetooth struct { + sockets []*Socket +} + +func NewBluetooth() *Bluetooth { + return &Bluetooth{} +} + +func (b *Bluetooth) Run(ctx context.Context, record SDPRecord) error { + profileManager1, err := profile.NewProfileManager1() + if err != nil { + return err + } + + path := dbus.ObjectPath("/bluez/lucaber/deckjoy") + profile1, err := profile.NewProfile1(string(path), path) + if err != nil { + return err + } + log.Debugf("bluetooth profile1: %+v\n", profile1) + ch, _, err := profile1.GetObjectManagerSignal() + if err != nil { + return err + } + + uuid := "00001124-0000-1000-8000-00805f9b34fb" + err = profileManager1.RegisterProfile(path, uuid, map[string]interface{}{ + "ServiceRecord": string(record.String()), + //"RequireAuthentication": false, + //"RequireAuthorization": false, + "AutoConnect": true, + //"Role": "server", + }) + if err != nil { + return err + } + + go func() { + for { + select { + case ev := <-ch: + log.Infof("bluetooth event: %+v\n", ev) + } + } + }() + + c, err := NewL2CAPListener(0x11) + if err != nil { + return err + } + s, err := NewL2CAPListener(0x13) + if err != nil { + return err + } + + go func() { + for { + _, err := c.Accept() + if err != nil { + log.Errorf("bluetooth accept err 0x11: %+v\n", err) + } + d, err := s.Accept() + if err != nil { + log.Errorf("bluetooth accept err 0x13: %+v\n", err) + } + b.sockets = append(b.sockets, d) + log.Infof("new device connected") + } + }() + + return nil +} + +func (b *Bluetooth) Write(data []byte) error { + if len(b.sockets) == 0 { + return errors.New("not connected") + } + ok := false + var lasterr error + for _, d := range b.sockets { + _, err := d.Write(data) + if err != nil { + lasterr = err + } else { + ok = true + } + } + + if !ok { + return lasterr + } + return nil +} diff --git a/pkg/bluetooth/device.go b/pkg/bluetooth/device.go new file mode 100644 index 0000000..71db574 --- /dev/null +++ b/pkg/bluetooth/device.go @@ -0,0 +1,32 @@ +package bluetooth + +import ( + "context" + "fmt" + "github.com/lucaber/deckjoy/pkg/hid" + "github.com/lucaber/deckjoy/pkg/ipc" +) + +type BluetoothDevice struct { + Daemon ipc.DeckJoyDaemonClient +} + +func NewBluetoothDevice(daemon ipc.DeckJoyDaemonClient) *BluetoothDevice { + return &BluetoothDevice{daemon} +} + +func (d *BluetoothDevice) Open() error { + if d.Daemon == nil { + return fmt.Errorf("no daemon connection") + } + return nil +} + +func (d *BluetoothDevice) Write(data []byte) error { + _, err := d.Daemon.WriteBluetoothHIDData(context.Background(), &ipc.WriteBluetoothHIDDataRequest{ + Data: data, + }) + return err +} + +var _ hid.Device = (*BluetoothDevice)(nil) diff --git a/pkg/bluetooth/l2cap.go b/pkg/bluetooth/l2cap.go new file mode 100644 index 0000000..c4df031 --- /dev/null +++ b/pkg/bluetooth/l2cap.go @@ -0,0 +1,64 @@ +package bluetooth + +import ( + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" + "sync" +) + +type Listener struct { + fd int +} + +func NewL2CAPListener(psm uint16) (*Listener, error) { + var err error + fd, err := unix.Socket(unix.AF_BLUETOOTH, unix.SOCK_SEQPACKET, unix.BTPROTO_L2CAP) + if err != nil { + return nil, errors.Wrap(err, "can't create socket") + } + + if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil { + return nil, errors.Wrap(err, "can't set socket flags") + } + + if err := unix.Bind(fd, &unix.SockaddrL2{ + PSM: psm, + Addr: [6]uint8{}, + CID: 0, + AddrType: 0, + }); err != nil { + return nil, errors.Wrap(err, "can't bind") + } + + if err := unix.Listen(fd, 1); err != nil { + return nil, errors.Wrap(err, "can't listen") + } + + return &Listener{fd: fd}, nil +} + +func (l *Listener) Accept() (*Socket, error) { + fd, _, err := unix.Accept(l.fd) + if err != nil { + return nil, errors.Wrap(err, "failed to accept") + } + return &Socket{fd: fd, lock: &sync.Mutex{}}, nil +} + +type Socket struct { + fd int + lock *sync.Mutex +} + +func (s *Socket) Write(p []byte) (int, error) { + s.lock.Lock() + defer s.lock.Unlock() + n, err := unix.Write(s.fd, p) + log.Debugf("wrote %v", p) + return n, errors.Wrap(err, "can't write socket") +} + +func (s *Socket) Close() error { + return errors.Wrap(unix.Close(s.fd), "can't close socket") +} diff --git a/pkg/bluetooth/sdp.go b/pkg/bluetooth/sdp.go new file mode 100644 index 0000000..2f33aea --- /dev/null +++ b/pkg/bluetooth/sdp.go @@ -0,0 +1,136 @@ +package bluetooth + +import ( + "encoding/hex" + "strings" +) + +const sdpTemplate = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +` + +type SDPRecord struct { + HIDDescriptor []byte +} + +func (s *SDPRecord) String() string { + descriptorHex := hex.EncodeToString(s.HIDDescriptor) + + return strings.Replace(sdpTemplate, "HIDDESCRIPTOR", descriptorHex, 1) + +} diff --git a/pkg/cmd/daemon.go b/pkg/cmd/daemon.go index af9ad08..c8883a9 100644 --- a/pkg/cmd/daemon.go +++ b/pkg/cmd/daemon.go @@ -11,6 +11,8 @@ import ( ) func RunDaemon(*cli.Context) error { + log.SetLevel(log.DebugLevel) + d := daemon.NewServer("/run/deckjoy.sock") stopOnce := &sync.Once{} diff --git a/pkg/cmd/gui.go b/pkg/cmd/gui.go index a2f4834..778dd4c 100644 --- a/pkg/cmd/gui.go +++ b/pkg/cmd/gui.go @@ -3,7 +3,6 @@ package cmd import ( "github.com/lucaber/deckjoy/pkg/gui" "github.com/lucaber/deckjoy/pkg/service" - "github.com/lucaber/deckjoy/pkg/setup" "github.com/lucaber/deckjoy/pkg/steamworks" log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" @@ -15,7 +14,9 @@ import ( func RunGui(c *cli.Context) error { - err := setup.Install() + log.SetLevel(log.DebugLevel) + + err := steamworks.Install() if err != nil { log.WithError(err).Error("installation failed") } @@ -29,7 +30,6 @@ func RunGui(c *cli.Context) error { } deck := service.NewDeck() - go deck.Run(c.Context) stopOnce := &sync.Once{} stopFunc := func() { diff --git a/pkg/deck/info.go b/pkg/config/info.go similarity index 92% rename from pkg/deck/info.go rename to pkg/config/info.go index 1991096..9278076 100644 --- a/pkg/deck/info.go +++ b/pkg/config/info.go @@ -1,4 +1,4 @@ -package deck +package config import "os" diff --git a/pkg/daemon/client.go b/pkg/daemon/client.go index 77475cb..2e8f877 100644 --- a/pkg/daemon/client.go +++ b/pkg/daemon/client.go @@ -12,7 +12,7 @@ import ( ) func RunSelfAsRoot(ctx context.Context, args ...string) error { - exePath, err := os.Readlink("/proc/self/exe") + exePath, err := os.Executable() if err != nil { return err } diff --git a/pkg/daemon/server.go b/pkg/daemon/server.go index cc2eb1c..0e3c0f8 100644 --- a/pkg/daemon/server.go +++ b/pkg/daemon/server.go @@ -2,8 +2,10 @@ package daemon import ( context "context" + "github.com/lucaber/deckjoy/pkg/bluetooth" + "github.com/lucaber/deckjoy/pkg/hid" "github.com/lucaber/deckjoy/pkg/ipc" - "github.com/lucaber/deckjoy/pkg/setup" + "github.com/lucaber/deckjoy/pkg/usb" log "github.com/sirupsen/logrus" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -15,9 +17,27 @@ import ( ) type Server struct { - path string - server *grpc.Server - deck *setup.Deck + ipc.UnimplementedDeckJoyDaemonServer + path string + server *grpc.Server + usb *usb.USB + bluetooth *bluetooth.Bluetooth +} + +func (s *Server) InstallSudoers(ctx context.Context, empty *ipc.Empty) (*ipc.Empty, error) { + filename := "/etc/sudoers.d/zzzdeckjoy" + + exe, err := os.Executable() + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get executable path: %v", err) + } + + err = os.WriteFile(filename, []byte("deck ALL=(ALL) NOPASSWD: "+exe+" daemon"), 0440) + if err != nil { + return nil, status.Errorf(codes.Internal, "install sudoers failed: %v", err) + } + + return &ipc.Empty{}, nil } func (s *Server) Stop(ctx context.Context, empty *ipc.Empty) (*ipc.Empty, error) { @@ -30,26 +50,26 @@ func (s *Server) Stop(ctx context.Context, empty *ipc.Empty) (*ipc.Empty, error) return &ipc.Empty{}, nil } -func (s *Server) Init(ctx context.Context, request *ipc.Empty) (*ipc.Empty, error) { - deck, err := setup.NewDeck() +func (s *Server) InitUSB(ctx context.Context, request *ipc.Empty) (*ipc.Empty, error) { + usb, err := usb.NewUSB() if err != nil { return nil, status.Errorf(codes.Internal, "could not setup deck: %s", err.Error()) } - s.deck = deck + s.usb = usb - _ = deck.Destroy() + _ = usb.Destroy() - err = s.deck.SetupModules() + err = s.usb.SetupModules() if err != nil { return nil, status.Errorf(codes.Internal, "could not setup kernel modules: %s", err.Error()) } - err = s.deck.SetupDeviceModules() + err = s.usb.SetupDeviceModules() if err != nil { log.WithError(err).Info("could not setup modules for usb device") } - err = s.deck.SetupGadget() + err = s.usb.SetupGadget() if err != nil { return nil, status.Errorf(codes.Internal, "could not setup gadget: %s", err.Error()) } @@ -57,11 +77,11 @@ func (s *Server) Init(ctx context.Context, request *ipc.Empty) (*ipc.Empty, erro return &ipc.Empty{}, nil } -func (s *Server) SetupJoystick(ctx context.Context, request *ipc.SetupJoystickRequest) (*ipc.SetupJoystickResponse, error) { - if s.deck == nil { +func (s *Server) SetupUSBJoystick(ctx context.Context, request *ipc.SetupJoystickRequest) (*ipc.SetupJoystickResponse, error) { + if s.usb == nil { return nil, status.Error(codes.Unavailable, "gadget not setup") } - path, err := s.deck.SetupJoystick(request.UserPermissions) + path, err := s.usb.SetupJoystick(request.UserPermissions) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } @@ -71,11 +91,11 @@ func (s *Server) SetupJoystick(ctx context.Context, request *ipc.SetupJoystickRe }, nil } -func (s *Server) SetupKeyboard(ctx context.Context, request *ipc.SetupKeyboardRequest) (*ipc.SetupKeyboardResponse, error) { - if s.deck == nil { +func (s *Server) SetupUSBKeyboard(ctx context.Context, request *ipc.SetupKeyboardRequest) (*ipc.SetupKeyboardResponse, error) { + if s.usb == nil { return nil, status.Error(codes.Unavailable, "gadget not setup") } - path, err := s.deck.SetupKeyboard(request.UserPermissions) + path, err := s.usb.SetupKeyboard(request.UserPermissions) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } @@ -85,11 +105,11 @@ func (s *Server) SetupKeyboard(ctx context.Context, request *ipc.SetupKeyboardRe }, nil } -func (s *Server) SetupMouse(ctx context.Context, request *ipc.SetupMouseRequest) (*ipc.SetupMouseResponse, error) { - if s.deck == nil { +func (s *Server) SetupUSBMouse(ctx context.Context, request *ipc.SetupMouseRequest) (*ipc.SetupMouseResponse, error) { + if s.usb == nil { return nil, status.Error(codes.Unavailable, "gadget not setup") } - path, err := s.deck.SetupMouse(request.UserPermissions) + path, err := s.usb.SetupMouse(request.UserPermissions) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } @@ -99,6 +119,34 @@ func (s *Server) SetupMouse(ctx context.Context, request *ipc.SetupMouseRequest) }, nil } +func (s *Server) InitBluetooth(ctx context.Context, request *ipc.Empty) (*ipc.Empty, error) { + s.bluetooth = bluetooth.NewBluetooth() + + descriptor := []byte{} + descriptor = append(descriptor, hid.AddReportID(hid.JoystickReportDesc, 1)...) + descriptor = append(descriptor, hid.AddReportID(hid.KeyboardReportDesc, 2)...) + descriptor = append(descriptor, hid.AddReportID(hid.MouseReportDesc, 3)...) + + err := s.bluetooth.Run(ctx, bluetooth.SDPRecord{HIDDescriptor: descriptor}) + if err != nil { + return nil, status.Errorf(codes.Internal, "could not setup bluetooth: %v", err) + } + + return &ipc.Empty{}, nil +} + +func (s *Server) WriteBluetoothHIDData(ctx context.Context, request *ipc.WriteBluetoothHIDDataRequest) (*ipc.Empty, error) { + if s.bluetooth == nil { + return nil, status.Error(codes.Unavailable, "bluetooth not setup") + } + err := s.bluetooth.Write(request.Data) + if err != nil { + return nil, status.Errorf(codes.Internal, "could not send bluetooth: %v", err) + } + + return &ipc.Empty{}, nil +} + var _ ipc.DeckJoyDaemonServer = &Server{} func NewServer(path string) *Server { @@ -131,12 +179,12 @@ func (s *Server) Run() error { func (s *Server) Close() error { s.server.Stop() - if s.deck != nil { - err := s.deck.Destroy() + if s.usb != nil { + err := s.usb.Destroy() if err != nil { return err } - s.deck = nil + s.usb = nil } return nil } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index e836437..fdb0c8b 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -37,7 +37,13 @@ func (g *GUI) Run() { errLabel := widget.NewLabel("") startUSBButton := widget.NewButton("Start USB", func() { g.deck.StartDaemon(context.Background()) + go g.deck.RunUSB(context.Background()) }) + startBluetoothButton := widget.NewButton("Start Bluetooth", func() { + g.deck.StartDaemon(context.Background()) + go g.deck.RunBluetooth(context.Background()) + }) + startInputWindowButton := widget.NewButton("Show Mouse/Keyboard", func() { go func() { if g.inputWindow == nil { @@ -65,6 +71,7 @@ func (g *GUI) Run() { } } startUSBButton.Disable() + startBluetoothButton.Disable() startInputWindowButton.Enable() }() @@ -74,6 +81,7 @@ func (g *GUI) Run() { g.window.SetContent(container.NewVBox( widget.NewLabel(fmt.Sprintf("DeckJoy")), startUSBButton, + startBluetoothButton, startInputWindowButton, errLabel, layout.NewSpacer(), diff --git a/pkg/hid/device.go b/pkg/hid/device.go index 1565a5f..c55716d 100644 --- a/pkg/hid/device.go +++ b/pkg/hid/device.go @@ -5,13 +5,22 @@ import ( "os" ) -type Device struct { - path string +type Device interface { + Open() error + Write(b []byte) error +} + +type FileDevice struct { + Path string file *os.File } -func (d *Device) Open() error { - file, err := os.OpenFile(d.path, os.O_RDWR, os.ModeCharDevice) +func NewFileDevice(path string) *FileDevice { + return &FileDevice{path, nil} +} + +func (d *FileDevice) Open() error { + file, err := os.OpenFile(d.Path, os.O_RDWR, os.ModeCharDevice) if err != nil { return err } @@ -19,7 +28,7 @@ func (d *Device) Open() error { return nil } -func (d *Device) Write(b []byte) error { +func (d *FileDevice) Write(b []byte) error { if d.file == nil { if err := d.Open(); err != nil { return err @@ -33,3 +42,20 @@ func (d *Device) Write(b []byte) error { } return nil } + +type NullDevice struct { +} + +func (n NullDevice) Open() error { + return nil +} + +func (n NullDevice) Write(b []byte) error { + return nil +} + +func NewNullDevice() *NullDevice { + return &NullDevice{} +} + +var _ Device = &NullDevice{} diff --git a/pkg/hid/joystick.go b/pkg/hid/joystick.go index 9aae48a..6b74188 100644 --- a/pkg/hid/joystick.go +++ b/pkg/hid/joystick.go @@ -47,9 +47,10 @@ var JoystickReportDesc = []byte{ } type Joystick struct { - *Device + Device state []byte sendMutex *sync.Mutex + last time.Time } func (j *Joystick) PressButton(num uint8) error { @@ -86,20 +87,23 @@ func (j *Joystick) SetAxis(num uint8, value int16) error { func (j *Joystick) SendState() error { j.sendMutex.Lock() defer j.sendMutex.Unlock() + if time.Since(j.last) < 1*time.Millisecond { + return nil + } err := j.Write(j.state) + j.last = time.Now() if err != nil { return fmt.Errorf("failed to set joystick state: %w", err) } return nil } -func NewJoystick(path string) *Joystick { +func NewJoystick(device Device) *Joystick { j := &Joystick{ - Device: &Device{ - path: path, - }, + Device: device, state: make([]byte, 10), sendMutex: &sync.Mutex{}, + last: time.Now(), } // set initial trigger states diff --git a/pkg/hid/keyboard.go b/pkg/hid/keyboard.go index 8e0705f..731e820 100644 --- a/pkg/hid/keyboard.go +++ b/pkg/hid/keyboard.go @@ -36,7 +36,7 @@ var KeyboardReportDesc = []byte{ } type Keyboard struct { - *Device + Device modKeysPressed map[KeyboardModKey]any keysPressed map[KeyboardKey]any } @@ -81,11 +81,9 @@ func (k *Keyboard) SendState() error { return k.Write(state) } -func NewKeyboard(path string) *Keyboard { +func NewKeyboard(device Device) *Keyboard { k := &Keyboard{ - Device: &Device{ - path: path, - }, + Device: device, keysPressed: map[KeyboardKey]any{}, modKeysPressed: map[KeyboardModKey]any{}, } diff --git a/pkg/hid/mouse.go b/pkg/hid/mouse.go index c47226e..d95abda 100644 --- a/pkg/hid/mouse.go +++ b/pkg/hid/mouse.go @@ -32,7 +32,7 @@ var MouseReportDesc = []byte{ } type Mouse struct { - *Device + Device buttons byte } @@ -64,11 +64,9 @@ func (k *Mouse) ReleaseButton(button MouseButton) error { return k.Write([]byte{k.buttons, 0x00, 0x00, 0x00, 0x00}) } -func NewMouse(path string) *Mouse { +func NewMouse(device Device) *Mouse { k := &Mouse{ - Device: &Device{ - path: path, - }, + Device: device, buttons: 0x00, } return k diff --git a/pkg/hid/reportid.go b/pkg/hid/reportid.go new file mode 100644 index 0000000..fc6c0d8 --- /dev/null +++ b/pkg/hid/reportid.go @@ -0,0 +1,31 @@ +package hid + +func AddReportID(descriptor []byte, id byte) []byte { + x := descriptor[:6] + x = append(x, 0x85) + x = append(x, id) + x = append(x, descriptor[6:]...) + return x +} + +type ReportIDDevice struct { + inner Device + id byte +} + +func (r *ReportIDDevice) Open() error { + return r.inner.Open() +} + +func (r *ReportIDDevice) Write(b []byte) error { + return r.inner.Write(append([]byte{0xA1, r.id}, b...)) +} + +var _ Device = &ReportIDDevice{} + +func NewReportIDDevice(inner Device, id byte) *ReportIDDevice { + return &ReportIDDevice{ + inner: inner, + id: id, + } +} diff --git a/pkg/ipc/daemon.pb.go b/pkg/ipc/daemon.pb.go index 1820e29..6d50e18 100644 --- a/pkg/ipc/daemon.pb.go +++ b/pkg/ipc/daemon.pb.go @@ -1,20 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 -// protoc v3.21.12 +// protoc-gen-go v1.36.6 +// protoc v6.31.1 // source: daemon.proto package ipc import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -25,18 +22,16 @@ const ( ) type Empty struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Empty) Reset() { *x = Empty{} - if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_daemon_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Empty) String() string { @@ -47,7 +42,7 @@ func (*Empty) ProtoMessage() {} func (x *Empty) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -63,20 +58,17 @@ func (*Empty) Descriptor() ([]byte, []int) { } type SetupJoystickRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - UserPermissions bool `protobuf:"varint,1,opt,name=userPermissions,proto3" json:"userPermissions,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + UserPermissions bool `protobuf:"varint,1,opt,name=userPermissions,proto3" json:"userPermissions,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SetupJoystickRequest) Reset() { *x = SetupJoystickRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_daemon_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SetupJoystickRequest) String() string { @@ -87,7 +79,7 @@ func (*SetupJoystickRequest) ProtoMessage() {} func (x *SetupJoystickRequest) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -110,20 +102,17 @@ func (x *SetupJoystickRequest) GetUserPermissions() bool { } type SetupJoystickResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` unknownFields protoimpl.UnknownFields - - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + sizeCache protoimpl.SizeCache } func (x *SetupJoystickResponse) Reset() { *x = SetupJoystickResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_daemon_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SetupJoystickResponse) String() string { @@ -134,7 +123,7 @@ func (*SetupJoystickResponse) ProtoMessage() {} func (x *SetupJoystickResponse) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -157,20 +146,17 @@ func (x *SetupJoystickResponse) GetPath() string { } type SetupKeyboardRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - UserPermissions bool `protobuf:"varint,1,opt,name=userPermissions,proto3" json:"userPermissions,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + UserPermissions bool `protobuf:"varint,1,opt,name=userPermissions,proto3" json:"userPermissions,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SetupKeyboardRequest) Reset() { *x = SetupKeyboardRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_daemon_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SetupKeyboardRequest) String() string { @@ -181,7 +167,7 @@ func (*SetupKeyboardRequest) ProtoMessage() {} func (x *SetupKeyboardRequest) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -204,20 +190,17 @@ func (x *SetupKeyboardRequest) GetUserPermissions() bool { } type SetupKeyboardResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` unknownFields protoimpl.UnknownFields - - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + sizeCache protoimpl.SizeCache } func (x *SetupKeyboardResponse) Reset() { *x = SetupKeyboardResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_daemon_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SetupKeyboardResponse) String() string { @@ -228,7 +211,7 @@ func (*SetupKeyboardResponse) ProtoMessage() {} func (x *SetupKeyboardResponse) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -251,20 +234,17 @@ func (x *SetupKeyboardResponse) GetPath() string { } type SetupMouseRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - UserPermissions bool `protobuf:"varint,1,opt,name=userPermissions,proto3" json:"userPermissions,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + UserPermissions bool `protobuf:"varint,1,opt,name=userPermissions,proto3" json:"userPermissions,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SetupMouseRequest) Reset() { *x = SetupMouseRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_daemon_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SetupMouseRequest) String() string { @@ -275,7 +255,7 @@ func (*SetupMouseRequest) ProtoMessage() {} func (x *SetupMouseRequest) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -298,20 +278,17 @@ func (x *SetupMouseRequest) GetUserPermissions() bool { } type SetupMouseResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` unknownFields protoimpl.UnknownFields - - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + sizeCache protoimpl.SizeCache } func (x *SetupMouseResponse) Reset() { *x = SetupMouseResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_daemon_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_daemon_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SetupMouseResponse) String() string { @@ -322,7 +299,7 @@ func (*SetupMouseResponse) ProtoMessage() {} func (x *SetupMouseResponse) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -344,93 +321,122 @@ func (x *SetupMouseResponse) GetPath() string { return "" } -var File_daemon_proto protoreflect.FileDescriptor +type WriteBluetoothHIDDataRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WriteBluetoothHIDDataRequest) Reset() { + *x = WriteBluetoothHIDDataRequest{} + mi := &file_daemon_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} -var file_daemon_proto_rawDesc = []byte{ - 0x0a, 0x0c, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, - 0x64, 0x65, 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x40, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, - 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0x2b, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4a, 0x6f, 0x79, 0x73, 0x74, - 0x69, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, - 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, - 0x40, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x2b, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, - 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x3d, - 0x0a, 0x11, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x73, - 0x65, 0x72, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x28, 0x0a, - 0x12, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x32, 0xd0, 0x02, 0x0a, 0x0d, 0x44, 0x65, 0x63, 0x6b, - 0x4a, 0x6f, 0x79, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x04, 0x53, 0x74, 0x6f, - 0x70, 0x12, 0x0e, 0x2e, 0x64, 0x65, 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x0e, 0x2e, 0x64, 0x65, 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x22, 0x00, 0x12, 0x28, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x0e, 0x2e, 0x64, 0x65, - 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x64, 0x65, - 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x50, 0x0a, - 0x0d, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4a, 0x6f, 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x12, 0x1d, - 0x2e, 0x64, 0x65, 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4a, 0x6f, - 0x79, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x64, 0x65, 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4a, 0x6f, 0x79, - 0x73, 0x74, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x50, 0x0a, 0x0d, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, - 0x12, 0x1d, 0x2e, 0x64, 0x65, 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, - 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x64, 0x65, 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4b, - 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x12, - 0x1a, 0x2e, 0x64, 0x65, 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4d, - 0x6f, 0x75, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x65, - 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x4d, 0x6f, 0x75, 0x73, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x75, 0x63, 0x61, 0x62, 0x65, 0x72, - 0x2f, 0x64, 0x65, 0x63, 0x6b, 0x6a, 0x6f, 0x79, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x69, 0x70, 0x63, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +func (x *WriteBluetoothHIDDataRequest) String() string { + return protoimpl.X.MessageStringOf(x) } +func (*WriteBluetoothHIDDataRequest) ProtoMessage() {} + +func (x *WriteBluetoothHIDDataRequest) ProtoReflect() protoreflect.Message { + mi := &file_daemon_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WriteBluetoothHIDDataRequest.ProtoReflect.Descriptor instead. +func (*WriteBluetoothHIDDataRequest) Descriptor() ([]byte, []int) { + return file_daemon_proto_rawDescGZIP(), []int{7} +} + +func (x *WriteBluetoothHIDDataRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +var File_daemon_proto protoreflect.FileDescriptor + +const file_daemon_proto_rawDesc = "" + + "\n" + + "\fdaemon.proto\x12\adeckjoy\"\a\n" + + "\x05Empty\"@\n" + + "\x14SetupJoystickRequest\x12(\n" + + "\x0fuserPermissions\x18\x01 \x01(\bR\x0fuserPermissions\"+\n" + + "\x15SetupJoystickResponse\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\"@\n" + + "\x14SetupKeyboardRequest\x12(\n" + + "\x0fuserPermissions\x18\x01 \x01(\bR\x0fuserPermissions\"+\n" + + "\x15SetupKeyboardResponse\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\"=\n" + + "\x11SetupMouseRequest\x12(\n" + + "\x0fuserPermissions\x18\x01 \x01(\bR\x0fuserPermissions\"(\n" + + "\x12SetupMouseResponse\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\"2\n" + + "\x1cWriteBluetoothHIDDataRequest\x12\x12\n" + + "\x04data\x18\x01 \x01(\fR\x04data2\x95\x04\n" + + "\rDeckJoyDaemon\x12(\n" + + "\x04Stop\x12\x0e.deckjoy.Empty\x1a\x0e.deckjoy.Empty\"\x00\x12+\n" + + "\aInitUSB\x12\x0e.deckjoy.Empty\x1a\x0e.deckjoy.Empty\"\x00\x12S\n" + + "\x10SetupUSBJoystick\x12\x1d.deckjoy.SetupJoystickRequest\x1a\x1e.deckjoy.SetupJoystickResponse\"\x00\x12S\n" + + "\x10SetupUSBKeyboard\x12\x1d.deckjoy.SetupKeyboardRequest\x1a\x1e.deckjoy.SetupKeyboardResponse\"\x00\x12J\n" + + "\rSetupUSBMouse\x12\x1a.deckjoy.SetupMouseRequest\x1a\x1b.deckjoy.SetupMouseResponse\"\x00\x122\n" + + "\x0eInstallSudoers\x12\x0e.deckjoy.Empty\x1a\x0e.deckjoy.Empty\"\x00\x121\n" + + "\rInitBluetooth\x12\x0e.deckjoy.Empty\x1a\x0e.deckjoy.Empty\"\x00\x12P\n" + + "\x15WriteBluetoothHIDData\x12%.deckjoy.WriteBluetoothHIDDataRequest\x1a\x0e.deckjoy.Empty\"\x00B$Z\"github.com/lucaber/deckjoy/pkg/ipcb\x06proto3" + var ( file_daemon_proto_rawDescOnce sync.Once - file_daemon_proto_rawDescData = file_daemon_proto_rawDesc + file_daemon_proto_rawDescData []byte ) func file_daemon_proto_rawDescGZIP() []byte { file_daemon_proto_rawDescOnce.Do(func() { - file_daemon_proto_rawDescData = protoimpl.X.CompressGZIP(file_daemon_proto_rawDescData) + file_daemon_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_daemon_proto_rawDesc), len(file_daemon_proto_rawDesc))) }) return file_daemon_proto_rawDescData } -var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 7) -var file_daemon_proto_goTypes = []interface{}{ - (*Empty)(nil), // 0: deckjoy.Empty - (*SetupJoystickRequest)(nil), // 1: deckjoy.SetupJoystickRequest - (*SetupJoystickResponse)(nil), // 2: deckjoy.SetupJoystickResponse - (*SetupKeyboardRequest)(nil), // 3: deckjoy.SetupKeyboardRequest - (*SetupKeyboardResponse)(nil), // 4: deckjoy.SetupKeyboardResponse - (*SetupMouseRequest)(nil), // 5: deckjoy.SetupMouseRequest - (*SetupMouseResponse)(nil), // 6: deckjoy.SetupMouseResponse +var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_daemon_proto_goTypes = []any{ + (*Empty)(nil), // 0: deckjoy.Empty + (*SetupJoystickRequest)(nil), // 1: deckjoy.SetupJoystickRequest + (*SetupJoystickResponse)(nil), // 2: deckjoy.SetupJoystickResponse + (*SetupKeyboardRequest)(nil), // 3: deckjoy.SetupKeyboardRequest + (*SetupKeyboardResponse)(nil), // 4: deckjoy.SetupKeyboardResponse + (*SetupMouseRequest)(nil), // 5: deckjoy.SetupMouseRequest + (*SetupMouseResponse)(nil), // 6: deckjoy.SetupMouseResponse + (*WriteBluetoothHIDDataRequest)(nil), // 7: deckjoy.WriteBluetoothHIDDataRequest } var file_daemon_proto_depIdxs = []int32{ 0, // 0: deckjoy.DeckJoyDaemon.Stop:input_type -> deckjoy.Empty - 0, // 1: deckjoy.DeckJoyDaemon.Init:input_type -> deckjoy.Empty - 1, // 2: deckjoy.DeckJoyDaemon.SetupJoystick:input_type -> deckjoy.SetupJoystickRequest - 3, // 3: deckjoy.DeckJoyDaemon.SetupKeyboard:input_type -> deckjoy.SetupKeyboardRequest - 5, // 4: deckjoy.DeckJoyDaemon.SetupMouse:input_type -> deckjoy.SetupMouseRequest - 0, // 5: deckjoy.DeckJoyDaemon.Stop:output_type -> deckjoy.Empty - 0, // 6: deckjoy.DeckJoyDaemon.Init:output_type -> deckjoy.Empty - 2, // 7: deckjoy.DeckJoyDaemon.SetupJoystick:output_type -> deckjoy.SetupJoystickResponse - 4, // 8: deckjoy.DeckJoyDaemon.SetupKeyboard:output_type -> deckjoy.SetupKeyboardResponse - 6, // 9: deckjoy.DeckJoyDaemon.SetupMouse:output_type -> deckjoy.SetupMouseResponse - 5, // [5:10] is the sub-list for method output_type - 0, // [0:5] is the sub-list for method input_type + 0, // 1: deckjoy.DeckJoyDaemon.InitUSB:input_type -> deckjoy.Empty + 1, // 2: deckjoy.DeckJoyDaemon.SetupUSBJoystick:input_type -> deckjoy.SetupJoystickRequest + 3, // 3: deckjoy.DeckJoyDaemon.SetupUSBKeyboard:input_type -> deckjoy.SetupKeyboardRequest + 5, // 4: deckjoy.DeckJoyDaemon.SetupUSBMouse:input_type -> deckjoy.SetupMouseRequest + 0, // 5: deckjoy.DeckJoyDaemon.InstallSudoers:input_type -> deckjoy.Empty + 0, // 6: deckjoy.DeckJoyDaemon.InitBluetooth:input_type -> deckjoy.Empty + 7, // 7: deckjoy.DeckJoyDaemon.WriteBluetoothHIDData:input_type -> deckjoy.WriteBluetoothHIDDataRequest + 0, // 8: deckjoy.DeckJoyDaemon.Stop:output_type -> deckjoy.Empty + 0, // 9: deckjoy.DeckJoyDaemon.InitUSB:output_type -> deckjoy.Empty + 2, // 10: deckjoy.DeckJoyDaemon.SetupUSBJoystick:output_type -> deckjoy.SetupJoystickResponse + 4, // 11: deckjoy.DeckJoyDaemon.SetupUSBKeyboard:output_type -> deckjoy.SetupKeyboardResponse + 6, // 12: deckjoy.DeckJoyDaemon.SetupUSBMouse:output_type -> deckjoy.SetupMouseResponse + 0, // 13: deckjoy.DeckJoyDaemon.InstallSudoers:output_type -> deckjoy.Empty + 0, // 14: deckjoy.DeckJoyDaemon.InitBluetooth:output_type -> deckjoy.Empty + 0, // 15: deckjoy.DeckJoyDaemon.WriteBluetoothHIDData:output_type -> deckjoy.Empty + 8, // [8:16] is the sub-list for method output_type + 0, // [0:8] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -441,99 +447,13 @@ func file_daemon_proto_init() { if File_daemon_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_daemon_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Empty); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_daemon_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetupJoystickRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_daemon_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetupJoystickResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_daemon_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetupKeyboardRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_daemon_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetupKeyboardResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_daemon_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetupMouseRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_daemon_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetupMouseResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_daemon_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_proto_rawDesc), len(file_daemon_proto_rawDesc)), NumEnums: 0, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, @@ -542,231 +462,6 @@ func file_daemon_proto_init() { MessageInfos: file_daemon_proto_msgTypes, }.Build() File_daemon_proto = out.File - file_daemon_proto_rawDesc = nil file_daemon_proto_goTypes = nil file_daemon_proto_depIdxs = nil } - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConnInterface - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 - -// DeckJoyDaemonClient is the client API for DeckJoyDaemon service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type DeckJoyDaemonClient interface { - Stop(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) - Init(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) - SetupJoystick(ctx context.Context, in *SetupJoystickRequest, opts ...grpc.CallOption) (*SetupJoystickResponse, error) - SetupKeyboard(ctx context.Context, in *SetupKeyboardRequest, opts ...grpc.CallOption) (*SetupKeyboardResponse, error) - SetupMouse(ctx context.Context, in *SetupMouseRequest, opts ...grpc.CallOption) (*SetupMouseResponse, error) -} - -type deckJoyDaemonClient struct { - cc grpc.ClientConnInterface -} - -func NewDeckJoyDaemonClient(cc grpc.ClientConnInterface) DeckJoyDaemonClient { - return &deckJoyDaemonClient{cc} -} - -func (c *deckJoyDaemonClient) Stop(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { - out := new(Empty) - err := c.cc.Invoke(ctx, "/deckjoy.DeckJoyDaemon/Stop", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *deckJoyDaemonClient) Init(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { - out := new(Empty) - err := c.cc.Invoke(ctx, "/deckjoy.DeckJoyDaemon/Init", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *deckJoyDaemonClient) SetupJoystick(ctx context.Context, in *SetupJoystickRequest, opts ...grpc.CallOption) (*SetupJoystickResponse, error) { - out := new(SetupJoystickResponse) - err := c.cc.Invoke(ctx, "/deckjoy.DeckJoyDaemon/SetupJoystick", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *deckJoyDaemonClient) SetupKeyboard(ctx context.Context, in *SetupKeyboardRequest, opts ...grpc.CallOption) (*SetupKeyboardResponse, error) { - out := new(SetupKeyboardResponse) - err := c.cc.Invoke(ctx, "/deckjoy.DeckJoyDaemon/SetupKeyboard", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *deckJoyDaemonClient) SetupMouse(ctx context.Context, in *SetupMouseRequest, opts ...grpc.CallOption) (*SetupMouseResponse, error) { - out := new(SetupMouseResponse) - err := c.cc.Invoke(ctx, "/deckjoy.DeckJoyDaemon/SetupMouse", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// DeckJoyDaemonServer is the server API for DeckJoyDaemon service. -type DeckJoyDaemonServer interface { - Stop(context.Context, *Empty) (*Empty, error) - Init(context.Context, *Empty) (*Empty, error) - SetupJoystick(context.Context, *SetupJoystickRequest) (*SetupJoystickResponse, error) - SetupKeyboard(context.Context, *SetupKeyboardRequest) (*SetupKeyboardResponse, error) - SetupMouse(context.Context, *SetupMouseRequest) (*SetupMouseResponse, error) -} - -// UnimplementedDeckJoyDaemonServer can be embedded to have forward compatible implementations. -type UnimplementedDeckJoyDaemonServer struct { -} - -func (*UnimplementedDeckJoyDaemonServer) Stop(context.Context, *Empty) (*Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") -} -func (*UnimplementedDeckJoyDaemonServer) Init(context.Context, *Empty) (*Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") -} -func (*UnimplementedDeckJoyDaemonServer) SetupJoystick(context.Context, *SetupJoystickRequest) (*SetupJoystickResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetupJoystick not implemented") -} -func (*UnimplementedDeckJoyDaemonServer) SetupKeyboard(context.Context, *SetupKeyboardRequest) (*SetupKeyboardResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetupKeyboard not implemented") -} -func (*UnimplementedDeckJoyDaemonServer) SetupMouse(context.Context, *SetupMouseRequest) (*SetupMouseResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetupMouse not implemented") -} - -func RegisterDeckJoyDaemonServer(s *grpc.Server, srv DeckJoyDaemonServer) { - s.RegisterService(&_DeckJoyDaemon_serviceDesc, srv) -} - -func _DeckJoyDaemon_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Empty) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DeckJoyDaemonServer).Stop(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/deckjoy.DeckJoyDaemon/Stop", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DeckJoyDaemonServer).Stop(ctx, req.(*Empty)) - } - return interceptor(ctx, in, info, handler) -} - -func _DeckJoyDaemon_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Empty) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DeckJoyDaemonServer).Init(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/deckjoy.DeckJoyDaemon/Init", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DeckJoyDaemonServer).Init(ctx, req.(*Empty)) - } - return interceptor(ctx, in, info, handler) -} - -func _DeckJoyDaemon_SetupJoystick_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetupJoystickRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DeckJoyDaemonServer).SetupJoystick(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/deckjoy.DeckJoyDaemon/SetupJoystick", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DeckJoyDaemonServer).SetupJoystick(ctx, req.(*SetupJoystickRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _DeckJoyDaemon_SetupKeyboard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetupKeyboardRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DeckJoyDaemonServer).SetupKeyboard(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/deckjoy.DeckJoyDaemon/SetupKeyboard", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DeckJoyDaemonServer).SetupKeyboard(ctx, req.(*SetupKeyboardRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _DeckJoyDaemon_SetupMouse_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetupMouseRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(DeckJoyDaemonServer).SetupMouse(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/deckjoy.DeckJoyDaemon/SetupMouse", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DeckJoyDaemonServer).SetupMouse(ctx, req.(*SetupMouseRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _DeckJoyDaemon_serviceDesc = grpc.ServiceDesc{ - ServiceName: "deckjoy.DeckJoyDaemon", - HandlerType: (*DeckJoyDaemonServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Stop", - Handler: _DeckJoyDaemon_Stop_Handler, - }, - { - MethodName: "Init", - Handler: _DeckJoyDaemon_Init_Handler, - }, - { - MethodName: "SetupJoystick", - Handler: _DeckJoyDaemon_SetupJoystick_Handler, - }, - { - MethodName: "SetupKeyboard", - Handler: _DeckJoyDaemon_SetupKeyboard_Handler, - }, - { - MethodName: "SetupMouse", - Handler: _DeckJoyDaemon_SetupMouse_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "daemon.proto", -} diff --git a/pkg/ipc/daemon_grpc.pb.go b/pkg/ipc/daemon_grpc.pb.go new file mode 100644 index 0000000..c4fac73 --- /dev/null +++ b/pkg/ipc/daemon_grpc.pb.go @@ -0,0 +1,387 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v6.31.1 +// source: daemon.proto + +package ipc + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + DeckJoyDaemon_Stop_FullMethodName = "/deckjoy.DeckJoyDaemon/Stop" + DeckJoyDaemon_InitUSB_FullMethodName = "/deckjoy.DeckJoyDaemon/InitUSB" + DeckJoyDaemon_SetupUSBJoystick_FullMethodName = "/deckjoy.DeckJoyDaemon/SetupUSBJoystick" + DeckJoyDaemon_SetupUSBKeyboard_FullMethodName = "/deckjoy.DeckJoyDaemon/SetupUSBKeyboard" + DeckJoyDaemon_SetupUSBMouse_FullMethodName = "/deckjoy.DeckJoyDaemon/SetupUSBMouse" + DeckJoyDaemon_InstallSudoers_FullMethodName = "/deckjoy.DeckJoyDaemon/InstallSudoers" + DeckJoyDaemon_InitBluetooth_FullMethodName = "/deckjoy.DeckJoyDaemon/InitBluetooth" + DeckJoyDaemon_WriteBluetoothHIDData_FullMethodName = "/deckjoy.DeckJoyDaemon/WriteBluetoothHIDData" +) + +// DeckJoyDaemonClient is the client API for DeckJoyDaemon service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type DeckJoyDaemonClient interface { + Stop(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + InitUSB(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + SetupUSBJoystick(ctx context.Context, in *SetupJoystickRequest, opts ...grpc.CallOption) (*SetupJoystickResponse, error) + SetupUSBKeyboard(ctx context.Context, in *SetupKeyboardRequest, opts ...grpc.CallOption) (*SetupKeyboardResponse, error) + SetupUSBMouse(ctx context.Context, in *SetupMouseRequest, opts ...grpc.CallOption) (*SetupMouseResponse, error) + InstallSudoers(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + InitBluetooth(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + WriteBluetoothHIDData(ctx context.Context, in *WriteBluetoothHIDDataRequest, opts ...grpc.CallOption) (*Empty, error) +} + +type deckJoyDaemonClient struct { + cc grpc.ClientConnInterface +} + +func NewDeckJoyDaemonClient(cc grpc.ClientConnInterface) DeckJoyDaemonClient { + return &deckJoyDaemonClient{cc} +} + +func (c *deckJoyDaemonClient) Stop(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, DeckJoyDaemon_Stop_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *deckJoyDaemonClient) InitUSB(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, DeckJoyDaemon_InitUSB_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *deckJoyDaemonClient) SetupUSBJoystick(ctx context.Context, in *SetupJoystickRequest, opts ...grpc.CallOption) (*SetupJoystickResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SetupJoystickResponse) + err := c.cc.Invoke(ctx, DeckJoyDaemon_SetupUSBJoystick_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *deckJoyDaemonClient) SetupUSBKeyboard(ctx context.Context, in *SetupKeyboardRequest, opts ...grpc.CallOption) (*SetupKeyboardResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SetupKeyboardResponse) + err := c.cc.Invoke(ctx, DeckJoyDaemon_SetupUSBKeyboard_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *deckJoyDaemonClient) SetupUSBMouse(ctx context.Context, in *SetupMouseRequest, opts ...grpc.CallOption) (*SetupMouseResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SetupMouseResponse) + err := c.cc.Invoke(ctx, DeckJoyDaemon_SetupUSBMouse_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *deckJoyDaemonClient) InstallSudoers(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, DeckJoyDaemon_InstallSudoers_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *deckJoyDaemonClient) InitBluetooth(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, DeckJoyDaemon_InitBluetooth_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *deckJoyDaemonClient) WriteBluetoothHIDData(ctx context.Context, in *WriteBluetoothHIDDataRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, DeckJoyDaemon_WriteBluetoothHIDData_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DeckJoyDaemonServer is the server API for DeckJoyDaemon service. +// All implementations must embed UnimplementedDeckJoyDaemonServer +// for forward compatibility. +type DeckJoyDaemonServer interface { + Stop(context.Context, *Empty) (*Empty, error) + InitUSB(context.Context, *Empty) (*Empty, error) + SetupUSBJoystick(context.Context, *SetupJoystickRequest) (*SetupJoystickResponse, error) + SetupUSBKeyboard(context.Context, *SetupKeyboardRequest) (*SetupKeyboardResponse, error) + SetupUSBMouse(context.Context, *SetupMouseRequest) (*SetupMouseResponse, error) + InstallSudoers(context.Context, *Empty) (*Empty, error) + InitBluetooth(context.Context, *Empty) (*Empty, error) + WriteBluetoothHIDData(context.Context, *WriteBluetoothHIDDataRequest) (*Empty, error) + mustEmbedUnimplementedDeckJoyDaemonServer() +} + +// UnimplementedDeckJoyDaemonServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedDeckJoyDaemonServer struct{} + +func (UnimplementedDeckJoyDaemonServer) Stop(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") +} +func (UnimplementedDeckJoyDaemonServer) InitUSB(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InitUSB not implemented") +} +func (UnimplementedDeckJoyDaemonServer) SetupUSBJoystick(context.Context, *SetupJoystickRequest) (*SetupJoystickResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetupUSBJoystick not implemented") +} +func (UnimplementedDeckJoyDaemonServer) SetupUSBKeyboard(context.Context, *SetupKeyboardRequest) (*SetupKeyboardResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetupUSBKeyboard not implemented") +} +func (UnimplementedDeckJoyDaemonServer) SetupUSBMouse(context.Context, *SetupMouseRequest) (*SetupMouseResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetupUSBMouse not implemented") +} +func (UnimplementedDeckJoyDaemonServer) InstallSudoers(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InstallSudoers not implemented") +} +func (UnimplementedDeckJoyDaemonServer) InitBluetooth(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method InitBluetooth not implemented") +} +func (UnimplementedDeckJoyDaemonServer) WriteBluetoothHIDData(context.Context, *WriteBluetoothHIDDataRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method WriteBluetoothHIDData not implemented") +} +func (UnimplementedDeckJoyDaemonServer) mustEmbedUnimplementedDeckJoyDaemonServer() {} +func (UnimplementedDeckJoyDaemonServer) testEmbeddedByValue() {} + +// UnsafeDeckJoyDaemonServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to DeckJoyDaemonServer will +// result in compilation errors. +type UnsafeDeckJoyDaemonServer interface { + mustEmbedUnimplementedDeckJoyDaemonServer() +} + +func RegisterDeckJoyDaemonServer(s grpc.ServiceRegistrar, srv DeckJoyDaemonServer) { + // If the following call pancis, it indicates UnimplementedDeckJoyDaemonServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&DeckJoyDaemon_ServiceDesc, srv) +} + +func _DeckJoyDaemon_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeckJoyDaemonServer).Stop(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeckJoyDaemon_Stop_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeckJoyDaemonServer).Stop(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _DeckJoyDaemon_InitUSB_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeckJoyDaemonServer).InitUSB(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeckJoyDaemon_InitUSB_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeckJoyDaemonServer).InitUSB(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _DeckJoyDaemon_SetupUSBJoystick_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetupJoystickRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeckJoyDaemonServer).SetupUSBJoystick(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeckJoyDaemon_SetupUSBJoystick_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeckJoyDaemonServer).SetupUSBJoystick(ctx, req.(*SetupJoystickRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DeckJoyDaemon_SetupUSBKeyboard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetupKeyboardRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeckJoyDaemonServer).SetupUSBKeyboard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeckJoyDaemon_SetupUSBKeyboard_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeckJoyDaemonServer).SetupUSBKeyboard(ctx, req.(*SetupKeyboardRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DeckJoyDaemon_SetupUSBMouse_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetupMouseRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeckJoyDaemonServer).SetupUSBMouse(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeckJoyDaemon_SetupUSBMouse_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeckJoyDaemonServer).SetupUSBMouse(ctx, req.(*SetupMouseRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _DeckJoyDaemon_InstallSudoers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeckJoyDaemonServer).InstallSudoers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeckJoyDaemon_InstallSudoers_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeckJoyDaemonServer).InstallSudoers(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _DeckJoyDaemon_InitBluetooth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeckJoyDaemonServer).InitBluetooth(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeckJoyDaemon_InitBluetooth_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeckJoyDaemonServer).InitBluetooth(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _DeckJoyDaemon_WriteBluetoothHIDData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WriteBluetoothHIDDataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeckJoyDaemonServer).WriteBluetoothHIDData(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DeckJoyDaemon_WriteBluetoothHIDData_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeckJoyDaemonServer).WriteBluetoothHIDData(ctx, req.(*WriteBluetoothHIDDataRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// DeckJoyDaemon_ServiceDesc is the grpc.ServiceDesc for DeckJoyDaemon service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var DeckJoyDaemon_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "deckjoy.DeckJoyDaemon", + HandlerType: (*DeckJoyDaemonServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Stop", + Handler: _DeckJoyDaemon_Stop_Handler, + }, + { + MethodName: "InitUSB", + Handler: _DeckJoyDaemon_InitUSB_Handler, + }, + { + MethodName: "SetupUSBJoystick", + Handler: _DeckJoyDaemon_SetupUSBJoystick_Handler, + }, + { + MethodName: "SetupUSBKeyboard", + Handler: _DeckJoyDaemon_SetupUSBKeyboard_Handler, + }, + { + MethodName: "SetupUSBMouse", + Handler: _DeckJoyDaemon_SetupUSBMouse_Handler, + }, + { + MethodName: "InstallSudoers", + Handler: _DeckJoyDaemon_InstallSudoers_Handler, + }, + { + MethodName: "InitBluetooth", + Handler: _DeckJoyDaemon_InitBluetooth_Handler, + }, + { + MethodName: "WriteBluetoothHIDData", + Handler: _DeckJoyDaemon_WriteBluetoothHIDData_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "daemon.proto", +} diff --git a/pkg/ipc/proto/daemon.proto b/pkg/ipc/proto/daemon.proto index 97f8f2d..c725e23 100644 --- a/pkg/ipc/proto/daemon.proto +++ b/pkg/ipc/proto/daemon.proto @@ -5,10 +5,13 @@ package deckjoy; service DeckJoyDaemon { rpc Stop (Empty) returns (Empty) {} - rpc Init (Empty) returns (Empty) {} - rpc SetupJoystick (SetupJoystickRequest) returns (SetupJoystickResponse) {} - rpc SetupKeyboard (SetupKeyboardRequest) returns (SetupKeyboardResponse) {} - rpc SetupMouse (SetupMouseRequest) returns (SetupMouseResponse) {} + rpc InitUSB (Empty) returns (Empty) {} + rpc SetupUSBJoystick (SetupJoystickRequest) returns (SetupJoystickResponse) {} + rpc SetupUSBKeyboard (SetupKeyboardRequest) returns (SetupKeyboardResponse) {} + rpc SetupUSBMouse (SetupMouseRequest) returns (SetupMouseResponse) {} + rpc InstallSudoers(Empty) returns (Empty) {} + rpc InitBluetooth (Empty) returns (Empty) {} + rpc WriteBluetoothHIDData (WriteBluetoothHIDDataRequest) returns (Empty) {} } message Empty { @@ -37,3 +40,7 @@ message SetupMouseRequest { message SetupMouseResponse { string path = 1; } + +message WriteBluetoothHIDDataRequest { + bytes data = 1; +} diff --git a/pkg/ipc/proto/generate.go b/pkg/ipc/proto/generate.go index bb76ddc..f721610 100644 --- a/pkg/ipc/proto/generate.go +++ b/pkg/ipc/proto/generate.go @@ -1,3 +1,3 @@ package ipc -//go:generate protoc --go_out=plugins=grpc:.. --go_opt=paths=source_relative daemon.proto +//go:generate protoc --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative daemon.proto diff --git a/pkg/service/deck.go b/pkg/service/deck.go index 1cead5b..988dba8 100644 --- a/pkg/service/deck.go +++ b/pkg/service/deck.go @@ -3,6 +3,7 @@ package service import ( "context" "fmt" + "github.com/lucaber/deckjoy/pkg/bluetooth" "github.com/lucaber/deckjoy/pkg/daemon" "github.com/lucaber/deckjoy/pkg/hid" "github.com/lucaber/deckjoy/pkg/ipc" @@ -30,7 +31,7 @@ func NewDeck() *Deck { return deck } -func (d *Deck) Run(ctx context.Context) { +func (d *Deck) RunUSB(ctx context.Context) { for { var err error connectCtx, connectCtxCancel := context.WithTimeout(ctx, time.Second) @@ -44,14 +45,19 @@ func (d *Deck) Run(ctx context.Context) { break } - _, err := d.Daemon.Init(ctx, &ipc.Empty{}) + _, err := d.Daemon.InstallSudoers(ctx, &ipc.Empty{}) + if err != nil { + log.WithError(err).Infof("failed to install sudoers") + } + + _, err = d.Daemon.InitUSB(ctx, &ipc.Empty{}) if err != nil { d.SetupErr = fmt.Errorf("usb init failed: %w", err) log.WithError(err).Infof("usb init failed") return } - joystickRes, err := d.Daemon.SetupJoystick(ctx, &ipc.SetupJoystickRequest{ + joystickRes, err := d.Daemon.SetupUSBJoystick(ctx, &ipc.SetupJoystickRequest{ UserPermissions: true, }) if err != nil { @@ -60,9 +66,9 @@ func (d *Deck) Run(ctx context.Context) { return } log.Infof("created joystick at %s", joystickRes.Path) - d.Joystick = hid.NewJoystick(joystickRes.Path) + d.Joystick = hid.NewJoystick(hid.NewFileDevice(joystickRes.Path)) - keyboardRes, err := d.Daemon.SetupKeyboard(ctx, &ipc.SetupKeyboardRequest{ + keyboardRes, err := d.Daemon.SetupUSBKeyboard(ctx, &ipc.SetupKeyboardRequest{ UserPermissions: true, }) if err != nil { @@ -71,9 +77,9 @@ func (d *Deck) Run(ctx context.Context) { return } log.Infof("created keyboard at %s", keyboardRes.Path) - d.Keyboard = hid.NewKeyboard(keyboardRes.Path) + d.Keyboard = hid.NewKeyboard(hid.NewFileDevice(keyboardRes.Path)) - mouseRes, err := d.Daemon.SetupMouse(ctx, &ipc.SetupMouseRequest{ + mouseRes, err := d.Daemon.SetupUSBMouse(ctx, &ipc.SetupMouseRequest{ UserPermissions: true, }) if err != nil { @@ -82,7 +88,41 @@ func (d *Deck) Run(ctx context.Context) { return } log.Infof("created mouse at %s", mouseRes.Path) - d.Mouse = hid.NewMouse(mouseRes.Path) + d.Mouse = hid.NewMouse(hid.NewFileDevice(mouseRes.Path)) + + d.RunJoystick() +} +func (d *Deck) RunBluetooth(ctx context.Context) { + for { + var err error + connectCtx, connectCtxCancel := context.WithTimeout(ctx, time.Second) + d.Daemon, err = daemon.NewClient(connectCtx, SocketPath) + connectCtxCancel() + if err != nil { + log.WithError(err).Infof("failed to connect to daemon") + time.Sleep(1 * time.Second) + continue + } + break + } + + _, err := d.Daemon.InstallSudoers(ctx, &ipc.Empty{}) + if err != nil { + log.WithError(err).Infof("failed to install sudoers") + } + + _, err = d.Daemon.InitBluetooth(ctx, &ipc.Empty{}) + if err != nil { + d.SetupErr = fmt.Errorf("bluetooth init failed: %w", err) + log.WithError(err).Infof("bluetooth init failed") + return + } + + bd := bluetooth.NewBluetoothDevice(d.Daemon) + d.Joystick = hid.NewJoystick(hid.NewReportIDDevice(bd, 1)) + //d.Joystick = hid.NewJoystick(hid.NewNullDevice()) + d.Keyboard = hid.NewKeyboard(hid.NewReportIDDevice(bd, 2)) + d.Mouse = hid.NewMouse(hid.NewReportIDDevice(bd, 3)) d.RunJoystick() } diff --git a/pkg/setup/install.go b/pkg/steamworks/install.go similarity index 98% rename from pkg/setup/install.go rename to pkg/steamworks/install.go index 7f8f757..7d9fa0d 100644 --- a/pkg/setup/install.go +++ b/pkg/steamworks/install.go @@ -1,4 +1,4 @@ -package setup +package steamworks import ( "fmt" diff --git a/pkg/setup/modprobe.go b/pkg/usb/modprobe.go similarity index 92% rename from pkg/setup/modprobe.go rename to pkg/usb/modprobe.go index 8f0c933..8d00048 100644 --- a/pkg/setup/modprobe.go +++ b/pkg/usb/modprobe.go @@ -1,4 +1,4 @@ -package setup +package usb import ( "context" diff --git a/pkg/setup/usb.go b/pkg/usb/usb.go similarity index 83% rename from pkg/setup/usb.go rename to pkg/usb/usb.go index b0fed50..2a6e91a 100644 --- a/pkg/setup/usb.go +++ b/pkg/usb/usb.go @@ -1,8 +1,8 @@ -package setup +package usb import ( "fmt" - "github.com/lucaber/deckjoy/pkg/deck" + "github.com/lucaber/deckjoy/pkg/config" "github.com/lucaber/deckjoy/pkg/hid" "github.com/lucaber/deckjoy/pkg/usbgadget" "github.com/pkg/errors" @@ -13,26 +13,26 @@ import ( type AfterEnableHookFunc func() error -type Deck struct { +type USB struct { gadget *usbgadget.Gadget conf *usbgadget.Config afterEnableHooks map[string]AfterEnableHookFunc } -func NewDeck() (*Deck, error) { - return &Deck{ +func NewUSB() (*USB, error) { + return &USB{ afterEnableHooks: map[string]AfterEnableHookFunc{}, }, nil } -func (d *Deck) SetupModules() error { +func (d *USB) SetupModules() error { return Modprobe("libcomposite") } // sometimes the usb controller gets detected with a wrong class and the wrong module get loaded for the device // - pci 0000:04:00.3: [1022:163a] type 00 class 0x0c0330 // + pci 0000:04:00.3: [1022:163a] type 00 class 0x0c03fe -func (d *Deck) SetupDeviceModules() error { +func (d *USB) SetupDeviceModules() error { // todo: remove hardcoded id device := []byte("0000:04:00.3") err := os.WriteFile(path.Join("/sys/bus/pci/drivers/xhci_hcd/unbind"), device, os.ModePerm) @@ -49,7 +49,7 @@ func (d *Deck) SetupDeviceModules() error { return nil } -func (d *Deck) setupGadget() error { +func (d *USB) setupGadget() error { gadget, err := usbgadget.CreateGadget("/sys/kernel/config/", "g.1") if err != nil { return err @@ -58,7 +58,7 @@ func (d *Deck) setupGadget() error { return nil } -func (d *Deck) SetupGadget() error { +func (d *USB) SetupGadget() error { err := d.setupGadget() if err != nil { return err @@ -71,7 +71,7 @@ func (d *Deck) SetupGadget() error { BCDUSB: 0x0200, }) - serial, err := deck.SerialNumber() + serial, err := config.SerialNumber() if err != nil || serial == "" { serial = "1" } @@ -106,7 +106,7 @@ func (d *Deck) SetupGadget() error { return nil } -func (d *Deck) enable() error { +func (d *USB) enable() error { if d.gadget == nil { return fmt.Errorf("gadget not setup") } @@ -127,19 +127,19 @@ func (d *Deck) enable() error { return nil } -func (d *Deck) SetupJoystick(userPermissions bool) (string, error) { +func (d *USB) SetupJoystick(userPermissions bool) (string, error) { return d.SetupHidDevice("hid.joystick", hid.JoystickReportDesc, userPermissions) } -func (d *Deck) SetupKeyboard(userPermissions bool) (string, error) { +func (d *USB) SetupKeyboard(userPermissions bool) (string, error) { return d.SetupHidDevice("hid.keyboard", hid.KeyboardReportDesc, userPermissions) } -func (d *Deck) SetupMouse(userPermissions bool) (string, error) { +func (d *USB) SetupMouse(userPermissions bool) (string, error) { return d.SetupHidDevice("hid.mouse", hid.MouseReportDesc, userPermissions) } -func (d *Deck) SetupHidDevice(name string, reportDesc []byte, userPermissions bool) (string, error) { +func (d *USB) SetupHidDevice(name string, reportDesc []byte, userPermissions bool) (string, error) { if d.conf == nil { return "", fmt.Errorf("gadget not setup") } @@ -190,7 +190,7 @@ func (d *Deck) SetupHidDevice(name string, reportDesc []byte, userPermissions bo return path, nil } -func (d *Deck) Destroy() error { +func (d *USB) Destroy() error { if d.gadget == nil { err := d.setupGadget() if err != nil { diff --git a/pkg/util/root.go b/pkg/util/root.go index f8d0f04..a298ca7 100644 --- a/pkg/util/root.go +++ b/pkg/util/root.go @@ -20,9 +20,13 @@ func ExecAsRoot(ctx context.Context, args ...string) error { time.Sleep(100 * time.Millisecond) - err := Exec(ctx, "pkexec", args...) + err := Exec(ctx, "sudo", args...) if err != nil { - return err + log.Infof("failed to start daemon with sudo, using pkexec: %v", err) + err := Exec(ctx, "pkexec", args...) + if err != nil { + return err + } } return nil