Skip to content

Commit 7ee98c1

Browse files
committed
Add keyboard and mouse support
Keyboard and mouse controls will now work if you use the kbMouseSupport parameter in the config for Libretro cores. Be aware that capturing mouse and keyboard controls properly is only possible in fullscreen mode. Note: In the case of DOSBox, a virtual filesystem handler is not yet implemented, thus each game state will be shared between all rooms (DOS game instances) of CloudRetro.
1 parent af8569a commit 7ee98c1

38 files changed

+1565
-547
lines changed

pkg/api/user.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ type (
1212
PlayerIndex int `json:"player_index"`
1313
}
1414
GameStartUserResponse struct {
15-
RoomId string `json:"roomId"`
16-
Av *AppVideoInfo `json:"av"`
15+
RoomId string `json:"roomId"`
16+
Av *AppVideoInfo `json:"av"`
17+
KbMouse bool `json:"kb_mouse"`
1718
}
1819
IceServer struct {
1920
Urls string `json:"urls,omitempty"`

pkg/api/worker.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ type (
3333
}
3434
StartGameResponse struct {
3535
Room
36-
AV *AppVideoInfo `json:"av"`
37-
Record bool
36+
AV *AppVideoInfo `json:"av"`
37+
Record bool `json:"record"`
38+
KbMouse bool `json:"kb_mouse"`
3839
}
3940
RecordGameRequest[T Id] struct {
4041
StatefulRoom[T]

pkg/config/config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,13 @@ emulator:
185185
# - isGlAllowed (bool)
186186
# - usesLibCo (bool)
187187
# - hasMultitap (bool) -- (removed)
188-
# - coreAspectRatio (bool) -- correct the aspect ratio on the client with the info from the core.
188+
# - coreAspectRatio (bool) -- (deprecated) correct the aspect ratio on the client with the info from the core.
189189
# - hid (map[int][]int)
190190
# A list of device IDs to bind to the input ports.
191+
# Can be seen in human readable form in the console when worker.debug is enabled.
191192
# Some cores allow binding multiple devices to a single port (DosBox), but typically,
192193
# you should bind just one device to one port.
194+
# - kbMouseSupport (bool) -- (temp) a flag if the core needs the keyboard and mouse on the client
193195
# - vfr (bool)
194196
# (experimental)
195197
# Enable variable frame rate only for cores that can't produce a constant frame rate.
@@ -213,7 +215,6 @@ emulator:
213215
mgba_audio_low_pass_filter: enabled
214216
mgba_audio_low_pass_range: 40
215217
pcsx:
216-
coreAspectRatio: true
217218
lib: pcsx_rearmed_libretro
218219
roms: [ "cue", "chd" ]
219220
# example of folder override
@@ -227,7 +228,6 @@ emulator:
227228
# https://docs.libretro.com/library/fbneo/
228229
mame:
229230
lib: fbneo_libretro
230-
coreAspectRatio: true
231231
roms: [ "zip" ]
232232
nes:
233233
lib: nestopia_libretro
@@ -280,7 +280,7 @@ encoder:
280280
# see: https://trac.ffmpeg.org/wiki/Encode/H.264
281281
h264:
282282
# Constant Rate Factor (CRF) 0-51 (default: 23)
283-
crf: 26
283+
crf: 23
284284
# ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo
285285
preset: superfast
286286
# baseline, main, high, high10, high422, high444

pkg/config/emulator.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ type LibretroCoreConfig struct {
4848
Height int
4949
Hid map[int][]int
5050
IsGlAllowed bool
51+
KbMouseSupport bool
5152
Lib string
5253
Options map[string]string
5354
Options4rom map[string]map[string]string // <(^_^)>

pkg/coordinator/userapi.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@ func (u *User) SendWebrtcOffer(sdp string) { u.Notify(api.WebrtcOffer, sdp) }
3737
func (u *User) SendWebrtcIceCandidate(candidate string) { u.Notify(api.WebrtcIce, candidate) }
3838

3939
// StartGame signals the user that everything is ready to start a game.
40-
func (u *User) StartGame(av *api.AppVideoInfo) {
41-
u.Notify(api.StartGame, api.GameStartUserResponse{RoomId: u.w.RoomId, Av: av})
40+
func (u *User) StartGame(av *api.AppVideoInfo, kbMouse bool) {
41+
u.Notify(api.StartGame, api.GameStartUserResponse{RoomId: u.w.RoomId, Av: av, KbMouse: kbMouse})
4242
}

pkg/coordinator/userhandlers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func (u *User) HandleStartGame(rq api.GameStartUserRequest, launcher games.Launc
5656
return
5757
}
5858
u.log.Info().Str("id", startGameResp.Rid).Msg("Received room response from worker")
59-
u.StartGame(startGameResp.AV)
59+
u.StartGame(startGameResp.AV, startGameResp.KbMouse)
6060

6161
// send back recording status
6262
if conf.Recording.Enabled && rq.Record {

pkg/network/webrtc/webrtc.go

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,15 @@ func (p *Peer) NewCall(vCodec, aCodec string, onICECandidate func(ice any)) (sdp
8181
p.log.Debug().Msgf("Added [%s] track", audio.Codec().MimeType)
8282
p.a = audio
8383

84-
// plug in the [data] channel (in and out)
85-
if err = p.addDataChannel("data"); err != nil {
84+
err = p.AddChannel("data", func(data []byte) {
85+
if len(data) == 0 || p.OnMessage == nil {
86+
return
87+
}
88+
p.OnMessage(data)
89+
})
90+
if err != nil {
8691
return "", err
8792
}
88-
p.log.Debug().Msg("Added [data] chan")
8993

9094
p.conn.OnICEConnectionStateChange(p.handleICEState(func() { p.log.Info().Msg("Connected") }))
9195
// Stream provider supposes to send offer
@@ -221,6 +225,19 @@ func (p *Peer) AddCandidate(candidate string, decoder Decoder) error {
221225
return nil
222226
}
223227

228+
func (p *Peer) AddChannel(label string, onMessage func([]byte)) error {
229+
ch, err := p.addDataChannel(label)
230+
if err != nil {
231+
return err
232+
}
233+
if label == "data" {
234+
p.d = ch
235+
}
236+
ch.OnMessage(func(m webrtc.DataChannelMessage) { onMessage(m.Data) })
237+
p.log.Debug().Msgf("Added [%v] chan", label)
238+
return nil
239+
}
240+
224241
func (p *Peer) Disconnect() {
225242
if p.conn == nil {
226243
return
@@ -232,29 +249,19 @@ func (p *Peer) Disconnect() {
232249
p.log.Debug().Msg("WebRTC stop")
233250
}
234251

235-
// addDataChannel creates a new WebRTC data channel for user input.
252+
// addDataChannel creates new WebRTC data channel.
236253
// Default params -- ordered: true, negotiated: false.
237-
func (p *Peer) addDataChannel(label string) error {
254+
func (p *Peer) addDataChannel(label string) (*webrtc.DataChannel, error) {
238255
ch, err := p.conn.CreateDataChannel(label, nil)
239256
if err != nil {
240-
return err
257+
return nil, err
241258
}
242259
ch.OnOpen(func() {
243-
p.log.Debug().Str("label", ch.Label()).Uint16("id", *ch.ID()).
244-
Msg("Data channel [input] opened")
260+
p.log.Debug().Uint16("id", *ch.ID()).Msgf("Data channel [%v] opened", ch.Label())
245261
})
246262
ch.OnError(p.logx)
247-
ch.OnMessage(func(m webrtc.DataChannelMessage) {
248-
if len(m.Data) == 0 {
249-
return
250-
}
251-
if p.OnMessage != nil {
252-
p.OnMessage(m.Data)
253-
}
254-
})
255-
p.d = ch
256-
ch.OnClose(func() { p.log.Debug().Msg("Data channel [input] has been closed") })
257-
return nil
263+
ch.OnClose(func() { p.log.Debug().Msgf("Data channel [%v] has been closed", ch.Label()) })
264+
return ch, nil
258265
}
259266

260267
func (p *Peer) logx(err error) { p.log.Error().Err(err) }

pkg/worker/caged/app/app.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ type App interface {
1313
SetAudioCb(func(Audio))
1414
SetVideoCb(func(Video))
1515
SetDataCb(func([]byte))
16-
SendControl(port int, data []byte)
16+
Input(port int, device byte, data []byte)
17+
KbMouseSupport() bool
1718
}
1819

1920
type Audio struct {

pkg/worker/caged/caged.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ type Manager struct {
1515
log *logger.Logger
1616
}
1717

18+
const (
19+
RetroPad = libretro.RetroPad
20+
Keyboard = libretro.Keyboard
21+
Mouse = libretro.Mouse
22+
)
23+
1824
type ModName string
1925

2026
const Libretro ModName = "libretro"

pkg/worker/caged/libretro/caged.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,16 @@ func (c *Caged) EnableCloudStorage(uid string, storage cloud.Storage) {
7979
}
8080
}
8181

82-
func (c *Caged) AspectEnabled() bool { return c.base.nano.Aspect }
83-
func (c *Caged) AspectRatio() float32 { return c.base.AspectRatio() }
84-
func (c *Caged) PixFormat() uint32 { return c.Emulator.PixFormat() }
85-
func (c *Caged) Rotation() uint { return c.Emulator.Rotation() }
86-
func (c *Caged) AudioSampleRate() int { return c.Emulator.AudioSampleRate() }
87-
func (c *Caged) ViewportSize() (int, int) { return c.base.ViewportSize() }
88-
func (c *Caged) Scale() float64 { return c.Emulator.Scale() }
89-
func (c *Caged) SendControl(port int, data []byte) { c.base.Input(port, data) }
90-
func (c *Caged) Start() { go c.Emulator.Start() }
91-
func (c *Caged) SetSaveOnClose(v bool) { c.base.SaveOnClose = v }
92-
func (c *Caged) SetSessionId(name string) { c.base.SetSessionId(name) }
93-
func (c *Caged) Close() { c.Emulator.Close() }
82+
func (c *Caged) AspectEnabled() bool { return c.base.nano.Aspect }
83+
func (c *Caged) AspectRatio() float32 { return c.base.AspectRatio() }
84+
func (c *Caged) PixFormat() uint32 { return c.Emulator.PixFormat() }
85+
func (c *Caged) Rotation() uint { return c.Emulator.Rotation() }
86+
func (c *Caged) AudioSampleRate() int { return c.Emulator.AudioSampleRate() }
87+
func (c *Caged) ViewportSize() (int, int) { return c.base.ViewportSize() }
88+
func (c *Caged) Scale() float64 { return c.Emulator.Scale() }
89+
func (c *Caged) Input(p int, d byte, data []byte) { c.base.Input(p, d, data) }
90+
func (c *Caged) KbMouseSupport() bool { return c.base.KbMouseSupport() }
91+
func (c *Caged) Start() { go c.Emulator.Start() }
92+
func (c *Caged) SetSaveOnClose(v bool) { c.base.SaveOnClose = v }
93+
func (c *Caged) SetSessionId(name string) { c.base.SetSessionId(name) }
94+
func (c *Caged) Close() { c.Emulator.Close() }

0 commit comments

Comments
 (0)