diff --git a/pkg/worker/caged/libretro/nanoarch/input.go b/pkg/worker/caged/libretro/nanoarch/input.go index 647e37b59..f0b99dab5 100644 --- a/pkg/worker/caged/libretro/nanoarch/input.go +++ b/pkg/worker/caged/libretro/nanoarch/input.go @@ -18,8 +18,9 @@ const ( // It consists of: // - uint16 button values // - int16 analog stick values +type InputState [maxPort]RetroPadState + type ( - InputState [maxPort]RetroPadState RetroPadState struct { keys uint32 axes [dpadAxes]int32 @@ -30,15 +31,13 @@ type ( mu sync.Mutex } MouseState struct { - x, y int16 - mp sync.Mutex - l bool - r bool - m bool - mb sync.Mutex + dx, dy atomic.Int32 + buttons atomic.Int32 } ) +type MouseBtnState int32 + type Device byte const ( @@ -52,6 +51,12 @@ const ( MouseButton ) +const ( + MouseLeft MouseBtnState = 1 << iota + MouseRight + MouseMiddle +) + const ( maxPort = 8 dpadAxes = 4 @@ -87,8 +92,6 @@ func NewKeyboardState() KeyboardState { // SetKey sets keyboard state. // -// data format -// // 0 1 2 3 4 5 6 // [ KEY ] P MOD // @@ -128,67 +131,30 @@ func (ks *KeyboardState) Pressed(key uint) C.int16_t { // ShiftPos sets mouse relative position state. // -// data format +// 0 1 2 3 +// [dx] [dy] // -// 0 1 2 3 -// [x] [y] -// -// x and y contain relative (to the previous values) -// X, Y positions as signed int. +// dx and dy are relative mouse coordinates func (ms *MouseState) ShiftPos(data []byte) { if len(data) != 4 { return } dx := int16(data[0])<<8 + int16(data[1]) dy := int16(data[2])<<8 + int16(data[3]) - ms.mp.Lock() - ms.x += dx - ms.y += dy - ms.mp.Unlock() -} - -func (ms *MouseState) PopX() C.int16_t { - var x int16 - ms.mp.Lock() - x = ms.x - ms.x = 0 - ms.mp.Unlock() - return C.int16_t(x) + ms.dx.Add(int32(dx)) + ms.dy.Add(int32(dy)) } -func (ms *MouseState) PopY() C.int16_t { - var y int16 - ms.mp.Lock() - y = ms.y - ms.y = 0 - ms.mp.Unlock() - return C.int16_t(y) -} +func (ms *MouseState) PopX() C.int16_t { return C.int16_t(ms.dx.Swap(0)) } +func (ms *MouseState) PopY() C.int16_t { return C.int16_t(ms.dy.Swap(0)) } -// SetButtons sets the state of mouse buttons. -// -// data format -// -// 0 1 2 -// L R M -// -// L is the left button, R is right, and M is the middle button. -func (ms *MouseState) SetButtons(data []byte) { - if len(data) != 3 { - return - } - ms.mb.Lock() - ms.l = data[0] == 1 - ms.r = data[1] == 1 - ms.m = data[2] == 1 - ms.mb.Unlock() -} +// SetButtons sets the state MouseBtnState of mouse buttons. +func (ms *MouseState) SetButtons(data byte) { ms.buttons.Store(int32(data)) } func (ms *MouseState) Buttons() (l, r, m bool) { - ms.mb.Lock() - l = ms.l - r = ms.r - m = ms.m - ms.mb.Unlock() + mbs := MouseBtnState(ms.buttons.Load()) + l = mbs&MouseLeft != 0 + r = mbs&MouseRight != 0 + m = mbs&MouseMiddle != 0 return } diff --git a/pkg/worker/caged/libretro/nanoarch/input_test.go b/pkg/worker/caged/libretro/nanoarch/input_test.go index d0c3a06ea..97015fd95 100644 --- a/pkg/worker/caged/libretro/nanoarch/input_test.go +++ b/pkg/worker/caged/libretro/nanoarch/input_test.go @@ -1,6 +1,7 @@ package nanoarch import ( + "encoding/binary" "math/rand" "sync" "testing" @@ -19,3 +20,54 @@ func TestConcurrentInput(t *testing.T) { } wg.Wait() } + +func TestMousePos(t *testing.T) { + data := []byte{0, 0, 0, 0} + + dx := 1111 + dy := 2222 + + binary.BigEndian.PutUint16(data, uint16(dx)) + binary.BigEndian.PutUint16(data[2:], uint16(dy)) + + ms := MouseState{} + ms.ShiftPos(data) + + x := int(ms.PopX()) + y := int(ms.PopY()) + + if x != dx || y != dy { + t.Errorf("invalid state, %v = %v, %v = %v", dx, x, dy, y) + } + + if ms.dx.Load() != 0 || ms.dy.Load() != 0 { + t.Errorf("coordinates weren't cleared") + } +} + +func TestMouseButtons(t *testing.T) { + tests := []struct { + name string + data byte + l bool + r bool + m bool + }{ + {name: "l+r+m+", data: 1 + 2 + 4, l: true, r: true, m: true}, + {name: "l-r-m-", data: 0}, + {name: "l-r+m-", data: 2, r: true}, + {name: "l+r-m+", data: 1 + 4, l: true, m: true}, + } + + ms := MouseState{} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ms.SetButtons(test.data) + l, r, m := ms.Buttons() + if l != test.l || r != test.r || m != test.m { + t.Errorf("wrong button state: %v -> %v, %v, %v", test.data, l, r, m) + } + }) + } +} diff --git a/pkg/worker/caged/libretro/nanoarch/nanoarch.go b/pkg/worker/caged/libretro/nanoarch/nanoarch.go index c69644628..02edf7882 100644 --- a/pkg/worker/caged/libretro/nanoarch/nanoarch.go +++ b/pkg/worker/caged/libretro/nanoarch/nanoarch.go @@ -402,7 +402,7 @@ func (n *Nanoarch) InputMouse(_ int, data []byte) { case MouseMove: n.mouse.ShiftPos(state) case MouseButton: - n.mouse.SetButtons(state) + n.mouse.SetButtons(state[0]) } } diff --git a/web/js/api/api.js b/web/js/api/api.js index 77d1de73e..a2cddf72f 100644 --- a/web/js/api/api.js +++ b/web/js/api/api.js @@ -80,36 +80,23 @@ const api = (() => { })(); const mousePress = (() => { - // 0 1 2 3 - // T L R M - const buffer = new ArrayBuffer(4); + // 0 1 + // T B + const buffer = new ArrayBuffer(2); const dv = new DataView(buffer); - // #define RETRO_DEVICE_ID_MOUSE_LEFT 2 - // #define RETRO_DEVICE_ID_MOUSE_RIGHT 3 - // #define RETRO_DEVICE_ID_MOUSE_WHEELUP 4 - // #define RETRO_DEVICE_ID_MOUSE_WHEELDOWN 5 - // #define RETRO_DEVICE_ID_MOUSE_MIDDLE 6 - // #define RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP 7 - // #define RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN 8 - // #define RETRO_DEVICE_ID_MOUSE_BUTTON_4 9 - // #define RETRO_DEVICE_ID_MOUSE_BUTTON_5 10 - // 0: Main button pressed, usually the left button or the un-initialized state // 1: Auxiliary button pressed, usually the wheel button or the middle button (if present) // 2: Secondary button pressed, usually the right button // 3: Fourth button, typically the Browser Back button // 4: Fifth button, typically the Browser Forward button - const b2r = [1, 3, 2, 9, 10] // browser mouse button to retro button + const b2r = [1, 4, 2, 0, 0] // browser mouse button to retro button + // assumed that only one button pressed / released - /* @param buttons contains pressed state of mouse buttons according to - * https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button - */ return (button = 0, pressed = false) => { - dv.setUint32(0, 0); dv.setUint8(0, mouse.BUTTONS); - dv.setUint8(b2r[button], +pressed); + dv.setUint8(1, pressed ? b2r[button] : 0); webrtc.mouse(buffer); } })();