From 649632796d2d5753d85e267074412e2a16cf3e9f Mon Sep 17 00:00:00 2001 From: Sergey Stepanov Date: Sat, 9 Mar 2024 22:41:20 +0300 Subject: [PATCH] Add ugly way of keyboard and mouse support notify --- pkg/api/user.go | 5 ++-- pkg/api/worker.go | 5 ++-- pkg/config/config.yaml | 1 + pkg/config/emulator.go | 1 + pkg/coordinator/userapi.go | 4 +-- pkg/coordinator/userhandlers.go | 2 +- pkg/worker/caged/app/app.go | 1 + pkg/worker/caged/libretro/caged.go | 1 + pkg/worker/caged/libretro/frontend.go | 2 ++ .../caged/libretro/nanoarch/nanoarch.go | 2 ++ pkg/worker/coordinatorhandlers.go | 6 ++++- web/index.html | 6 ++--- web/js/controller.js | 5 ++-- web/js/event/event.js | 1 + web/js/input/keyboard.js | 27 ++++++++++--------- web/js/stream/stream.js | 19 ++++++++----- 16 files changed, 56 insertions(+), 32 deletions(-) diff --git a/pkg/api/user.go b/pkg/api/user.go index aef4305dc..189b61fc9 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -12,8 +12,9 @@ type ( PlayerIndex int `json:"player_index"` } GameStartUserResponse struct { - RoomId string `json:"roomId"` - Av *AppVideoInfo `json:"av"` + RoomId string `json:"roomId"` + Av *AppVideoInfo `json:"av"` + KbMouse bool `json:"kb_mouse"` } IceServer struct { Urls string `json:"urls,omitempty"` diff --git a/pkg/api/worker.go b/pkg/api/worker.go index a4078f5e2..cd9284346 100644 --- a/pkg/api/worker.go +++ b/pkg/api/worker.go @@ -33,8 +33,9 @@ type ( } StartGameResponse struct { Room - AV *AppVideoInfo `json:"av"` - Record bool + AV *AppVideoInfo `json:"av"` + Record bool `json:"record"` + KbMouse bool `json:"kb_mouse"` } RecordGameRequest[T Id] struct { StatefulRoom[T] diff --git a/pkg/config/config.yaml b/pkg/config/config.yaml index 39b695ea8..a0fc99fd5 100644 --- a/pkg/config/config.yaml +++ b/pkg/config/config.yaml @@ -186,6 +186,7 @@ emulator: # A list of device IDs to bind to the input ports. # Some cores allow binding multiple devices to a single port (DosBox), but typically, # you should bind just one device to one port. + # - kbMouseSupport (bool) -- (temp) a flag if the core needs the keyboard and mouse on the client # - vfr (bool) # (experimental) # Enable variable frame rate only for cores that can't produce a constant frame rate. diff --git a/pkg/config/emulator.go b/pkg/config/emulator.go index 49bf6713e..d06bea2c9 100644 --- a/pkg/config/emulator.go +++ b/pkg/config/emulator.go @@ -47,6 +47,7 @@ type LibretroCoreConfig struct { Height int Hid map[int][]int IsGlAllowed bool + KbMouseSupport bool Lib string Options map[string]string Roms []string diff --git a/pkg/coordinator/userapi.go b/pkg/coordinator/userapi.go index ed1ebcead..047fe1d13 100644 --- a/pkg/coordinator/userapi.go +++ b/pkg/coordinator/userapi.go @@ -37,6 +37,6 @@ func (u *User) SendWebrtcOffer(sdp string) { u.Notify(api.WebrtcOffer, sdp) } func (u *User) SendWebrtcIceCandidate(candidate string) { u.Notify(api.WebrtcIce, candidate) } // StartGame signals the user that everything is ready to start a game. -func (u *User) StartGame(av *api.AppVideoInfo) { - u.Notify(api.StartGame, api.GameStartUserResponse{RoomId: u.w.RoomId, Av: av}) +func (u *User) StartGame(av *api.AppVideoInfo, kbMouse bool) { + u.Notify(api.StartGame, api.GameStartUserResponse{RoomId: u.w.RoomId, Av: av, KbMouse: kbMouse}) } diff --git a/pkg/coordinator/userhandlers.go b/pkg/coordinator/userhandlers.go index 240f9dbe0..cf62d65ba 100644 --- a/pkg/coordinator/userhandlers.go +++ b/pkg/coordinator/userhandlers.go @@ -56,7 +56,7 @@ func (u *User) HandleStartGame(rq api.GameStartUserRequest, launcher games.Launc return } u.log.Info().Str("id", startGameResp.Rid).Msg("Received room response from worker") - u.StartGame(startGameResp.AV) + u.StartGame(startGameResp.AV, startGameResp.KbMouse) // send back recording status if conf.Recording.Enabled && rq.Record { diff --git a/pkg/worker/caged/app/app.go b/pkg/worker/caged/app/app.go index b665c48dd..f341ea32e 100644 --- a/pkg/worker/caged/app/app.go +++ b/pkg/worker/caged/app/app.go @@ -16,6 +16,7 @@ type App interface { InputGamepad(port int, data []byte) InputKeyboard(port int, data []byte) InputMouse(port int, data []byte) + KbMouseSupport() bool } type Audio struct { diff --git a/pkg/worker/caged/libretro/caged.go b/pkg/worker/caged/libretro/caged.go index 979868e12..347640b2e 100644 --- a/pkg/worker/caged/libretro/caged.go +++ b/pkg/worker/caged/libretro/caged.go @@ -89,6 +89,7 @@ func (c *Caged) Scale() float64 { return c.Emulator.Scale() func (c *Caged) InputGamepad(port int, data []byte) { c.base.Input(port, RetroPad, data) } func (c *Caged) InputKeyboard(port int, data []byte) { c.base.Input(port, Keyboard, data) } func (c *Caged) InputMouse(port int, data []byte) { c.base.Input(port, Mouse, data) } +func (c *Caged) KbMouseSupport() bool { return c.base.KbMouseSupport() } func (c *Caged) Start() { go c.Emulator.Start() } func (c *Caged) SetSaveOnClose(v bool) { c.base.SaveOnClose = v } func (c *Caged) SetSessionId(name string) { c.base.SetSessionId(name) } diff --git a/pkg/worker/caged/libretro/frontend.go b/pkg/worker/caged/libretro/frontend.go index d296c820c..3cd5425a1 100644 --- a/pkg/worker/caged/libretro/frontend.go +++ b/pkg/worker/caged/libretro/frontend.go @@ -152,6 +152,7 @@ func (f *Frontend) LoadCore(emu string) { Options: conf.Options, UsesLibCo: conf.UsesLibCo, CoreAspectRatio: conf.CoreAspectRatio, + KbMouseSupport: conf.KbMouseSupport, } f.mu.Lock() scale := 1.0 @@ -279,6 +280,7 @@ func (f *Frontend) FrameSize() (int, int) { return f.nano.BaseWidth(), f func (f *Frontend) HasSave() bool { return os.Exists(f.HashPath()) } func (f *Frontend) HashPath() string { return f.storage.GetSavePath() } func (f *Frontend) IsPortrait() bool { return f.nano.IsPortrait() } +func (f *Frontend) KbMouseSupport() bool { return f.nano.KbMouseSupport() } func (f *Frontend) LoadGame(path string) error { return f.nano.LoadGame(path) } func (f *Frontend) PixFormat() uint32 { return f.nano.Video.PixFmt.C } func (f *Frontend) RestoreGameState() error { return f.Load() } diff --git a/pkg/worker/caged/libretro/nanoarch/nanoarch.go b/pkg/worker/caged/libretro/nanoarch/nanoarch.go index 1e63b37ff..0d04e8ae0 100644 --- a/pkg/worker/caged/libretro/nanoarch/nanoarch.go +++ b/pkg/worker/caged/libretro/nanoarch/nanoarch.go @@ -96,6 +96,7 @@ type Metadata struct { Hacks []string Hid map[int][]int CoreAspectRatio bool + KbMouseSupport bool } type PixFmt struct { @@ -143,6 +144,7 @@ func (n *Nanoarch) AspectRatio() float32 { return float32(n.sys.av.g func (n *Nanoarch) AudioSampleRate() int { return int(n.sys.av.timing.sample_rate) } func (n *Nanoarch) VideoFramerate() int { return int(n.sys.av.timing.fps) } func (n *Nanoarch) IsPortrait() bool { return 90 == n.Rot%180 } +func (n *Nanoarch) KbMouseSupport() bool { return n.meta.KbMouseSupport } func (n *Nanoarch) BaseWidth() int { return int(n.sys.av.geometry.base_width) } func (n *Nanoarch) BaseHeight() int { return int(n.sys.av.geometry.base_height) } func (n *Nanoarch) WaitReady() { <-n.reserved } diff --git a/pkg/worker/coordinatorhandlers.go b/pkg/worker/coordinatorhandlers.go index 9e551fbff..121f4a7a6 100644 --- a/pkg/worker/coordinatorhandlers.go +++ b/pkg/worker/coordinatorhandlers.go @@ -179,7 +179,11 @@ func (c *coordinator) HandleGameStart(rq api.StartGameRequest[com.Uid], w *Worke c.RegisterRoom(r.Id()) - response := api.StartGameResponse{Room: api.Room{Rid: r.Id()}, Record: w.conf.Recording.Enabled} + response := api.StartGameResponse{ + Room: api.Room{Rid: r.Id()}, + Record: w.conf.Recording.Enabled, + KbMouse: r.App().KbMouseSupport(), + } if r.App().AspectEnabled() { ww, hh := r.App().ViewportSize() response.AV = &api.AppVideoInfo{W: ww, H: hh, A: r.App().AspectRatio(), S: int(r.App().Scale())} diff --git a/web/index.html b/web/index.html index 471afe3bb..e17279fce 100644 --- a/web/index.html +++ b/web/index.html @@ -135,9 +135,9 @@

Options

- - - + + + diff --git a/web/js/controller.js b/web/js/controller.js index 5ce6c157c..780a185b0 100644 --- a/web/js/controller.js +++ b/web/js/controller.js @@ -160,9 +160,8 @@ event.pub(WEBRTC_ICE_CANDIDATE_RECEIVED, {candidate: payload}); break; case api.endpoint.GAME_START: - if (payload.av) { - event.pub(APP_VIDEO_CHANGED, payload.av) - } + payload.av && event.pub(APP_VIDEO_CHANGED, payload.av) + payload.kb_mouse && event.pub(KB_MOUSE_FLAG); event.pub(GAME_ROOM_AVAILABLE, {roomId: payload.roomId}); break; case api.endpoint.GAME_SAVE: diff --git a/web/js/event/event.js b/web/js/event/event.js index 9bc57ea82..ace101d9b 100644 --- a/web/js/event/event.js +++ b/web/js/event/event.js @@ -110,3 +110,4 @@ const RECORDING_TOGGLED = 'recordingToggle' const RECORDING_STATUS_CHANGED = 'recordingStatusChanged' const APP_VIDEO_CHANGED = 'appVideoChanged' +const KB_MOUSE_FLAG = 'kbMouseFlag' diff --git a/web/js/input/keyboard.js b/web/js/input/keyboard.js index e67458165..3659da46e 100644 --- a/web/js/input/keyboard.js +++ b/web/js/input/keyboard.js @@ -100,18 +100,21 @@ const keyboard = (() => { event.sub(DPAD_TOGGLE, (data) => onDpadToggle(data.checked)); - const supportsKeyboardLock = - ('keyboard' in navigator) && ('lock' in navigator.keyboard); - - if (supportsKeyboardLock) { - event.sub(FULLSCREEN_CHANGE, async (fullscreenEl) => { - enabled = !fullscreenEl; - enabled ? navigator.keyboard.unlock() : await navigator.keyboard.lock(); - log.debug(`Keyboard lock: ${!enabled}`); - }) - } else { - log.warn('Browser doesn\'t support keyboard lock!'); - } + event.sub(KB_MOUSE_FLAG, () => { + const supportsKeyboardLock = + ('keyboard' in navigator) && ('lock' in navigator.keyboard); + + if (supportsKeyboardLock) { + event.sub(FULLSCREEN_CHANGE, async (fullscreenEl) => { + enabled = !fullscreenEl; + enabled ? navigator.keyboard.unlock() : await navigator.keyboard.lock(); + log.debug(`Keyboard lock: ${!enabled}`); + }) + } else { + log.warn('Browser doesn\'t support keyboard lock!'); + } + }) + return { init: () => { diff --git a/web/js/stream/stream.js b/web/js/stream/stream.js index 5486e8a0c..98c3abfc4 100644 --- a/web/js/stream/stream.js +++ b/web/js/stream/stream.js @@ -16,6 +16,7 @@ const stream = (() => { state = { screen: screen, fullscreen: false, + kbmLock: false, timerId: null, w: 0, h: 0, @@ -191,6 +192,8 @@ const stream = (() => { screen.blur(); + if (!state.kbmLock) return; + if (state.fullscreen && !pointerLocked) { // event.pub(POINTER_LOCK_CHANGE, screen); await screen.requestPointerLock( @@ -230,12 +233,6 @@ const stream = (() => { event.pub(MOUSE_MOVED, scaleCursorPos(e.movementX, e.movementY)); } - event.sub(POINTER_LOCK_CHANGE, (lockedEl) => { - pointerLocked = lockedEl === screen; - screen.onpointermove = pointerLocked ? handlePointerMove : null; - log.debug(`Pointer lock: ${pointerLocked}`); - }); - const fit = 'contain' event.sub(APP_VIDEO_CHANGED, (payload) => { @@ -257,6 +254,16 @@ const stream = (() => { state.screen.style.aspectRatio = '' + state.aspect }) + event.sub(KB_MOUSE_FLAG, () => { + console.info('Keyboard and mouse will be locked in fullscreen'); + state.kbmLock = true; + event.sub(POINTER_LOCK_CHANGE, (lockedEl) => { + pointerLocked = lockedEl === screen; + screen.onpointermove = pointerLocked ? handlePointerMove : null; + log.debug(`Pointer lock: ${pointerLocked}`); + }); + }) + return { audio: {mute}, video: {toggleFullscreen, el: getVideoEl},