mirror of
https://github.com/libretro/ludo.git
synced 2026-11-07 12:15:57 -10:00
Support analog inputs (#416)
This commit is contained in:
1 parent
3f9e9e5a36
commit
d201e5321d
6 files changed
+99
-59
No files matched your search
+67
-27
@@ -26,8 +26,11 @@ type bind struct {
|
||||
threshold float32
|
||||
}
|
||||
|
||||
// States is the state of inputs for all players
|
||||
type States [MaxPlayers][ActionLast]bool
|
||||
// States can store the state of inputs for all players
|
||||
type States [MaxPlayers][ActionLast]int16
|
||||
|
||||
// AnalogStates can store the state of analog inputs for all players
|
||||
type AnalogStates [MaxPlayers][2]int16
|
||||
|
||||
// Input state for all the players
|
||||
var (
|
||||
@@ -35,6 +38,8 @@ var (
|
||||
OldState States // input state for the previous frame
|
||||
Released States // keys just released during this frame
|
||||
Pressed States // keys just pressed during this frame
|
||||
|
||||
NewAnalogState AnalogStates // analog input state for the current frame
|
||||
)
|
||||
|
||||
// Hot keys
|
||||
@@ -75,8 +80,12 @@ func Init(v *video.Video) {
|
||||
glfw.SetJoystickCallback(joystickCallback)
|
||||
}
|
||||
|
||||
func floatToAnalog(v float32) int16 {
|
||||
return int16(v * 32767.0)
|
||||
}
|
||||
|
||||
// pollJoypads process joypads of all players
|
||||
func pollJoypads(state States) States {
|
||||
func pollJoypads(state States, analogState AnalogStates) (States, AnalogStates) {
|
||||
for p := range state {
|
||||
buttonState := glfw.Joystick.GetButtons(glfw.Joystick(p))
|
||||
axisState := glfw.Joystick.GetAxes(glfw.Joystick(p))
|
||||
@@ -88,41 +97,47 @@ func pollJoypads(state States) States {
|
||||
case btn:
|
||||
if int(k.index) < len(buttonState) &&
|
||||
glfw.Action(buttonState[k.index]) == glfw.Press {
|
||||
state[p][v] = true
|
||||
state[p][v] = 1
|
||||
}
|
||||
case axis:
|
||||
if int(k.index) < len(axisState) &&
|
||||
k.direction*axisState[k.index] > k.threshold*k.direction {
|
||||
state[p][v] = true
|
||||
state[p][v] = 1
|
||||
}
|
||||
}
|
||||
|
||||
if !settings.Current.MapAxisToDPad {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case axisState[0] < -0.5:
|
||||
state[p][libretro.DeviceIDJoypadLeft] = true
|
||||
case axisState[0] > 0.5:
|
||||
state[p][libretro.DeviceIDJoypadRight] = true
|
||||
}
|
||||
switch {
|
||||
case axisState[1] > 0.5:
|
||||
state[p][libretro.DeviceIDJoypadDown] = true
|
||||
case axisState[1] < -0.5:
|
||||
state[p][libretro.DeviceIDJoypadUp] = true
|
||||
if settings.Current.MapAxisToDPad {
|
||||
if axisState[0] < -0.5 {
|
||||
state[p][libretro.DeviceIDJoypadLeft] = 1
|
||||
} else if axisState[0] > 0.5 {
|
||||
state[p][libretro.DeviceIDJoypadRight] = 1
|
||||
}
|
||||
if axisState[1] > 0.5 {
|
||||
state[p][libretro.DeviceIDJoypadDown] = 1
|
||||
} else if axisState[1] < -0.5 {
|
||||
state[p][libretro.DeviceIDJoypadUp] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return state
|
||||
|
||||
for p := range analogState {
|
||||
axisState := glfw.Joystick.GetAxes(glfw.Joystick(p))
|
||||
if len(axisState) >= 1 {
|
||||
analogState[p][0] = floatToAnalog(axisState[0])
|
||||
analogState[p][1] = floatToAnalog(axisState[1])
|
||||
}
|
||||
}
|
||||
|
||||
return state, analogState
|
||||
}
|
||||
|
||||
// pollKeyboard processes keyboard keys
|
||||
func pollKeyboard(state States) States {
|
||||
for k, v := range keyBinds {
|
||||
if vid.Window.GetKey(k) == glfw.Press {
|
||||
state[0][v] = true
|
||||
state[0][v] = 1
|
||||
}
|
||||
}
|
||||
return state
|
||||
@@ -132,8 +147,16 @@ func pollKeyboard(state States) States {
|
||||
func getPressedReleased(new States, old States) (States, States) {
|
||||
for p := range new {
|
||||
for k := range new[p] {
|
||||
Pressed[p][k] = new[p][k] && !old[p][k]
|
||||
Released[p][k] = !new[p][k] && old[p][k]
|
||||
if new[p][k] == 1 && old[p][k] == 0 {
|
||||
Pressed[p][k] = 1
|
||||
} else {
|
||||
Pressed[p][k] = 0
|
||||
}
|
||||
if new[p][k] == 0 && old[p][k] == 1 {
|
||||
Released[p][k] = 1
|
||||
} else {
|
||||
Released[p][k] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pressed, Released
|
||||
@@ -142,7 +165,7 @@ func getPressedReleased(new States, old States) (States, States) {
|
||||
// Poll calculates the input state. It is meant to be called for each frame.
|
||||
func Poll() {
|
||||
NewState = States{}
|
||||
NewState = pollJoypads(NewState)
|
||||
NewState, NewAnalogState = pollJoypads(NewState, NewAnalogState)
|
||||
NewState = pollKeyboard(NewState)
|
||||
Pressed, Released = getPressedReleased(NewState, OldState)
|
||||
|
||||
@@ -153,13 +176,30 @@ func Poll() {
|
||||
// State is a callback passed to core.SetInputState
|
||||
// It returns 1 if the button corresponding to the parameters is pressed
|
||||
func State(port uint, device uint32, index uint, id uint) int16 {
|
||||
if id >= 255 || index > 0 || port >= MaxPlayers || device&libretro.DeviceJoypad != 1 || id > uint(libretro.DeviceIDJoypadR3) {
|
||||
if port >= MaxPlayers {
|
||||
return 0
|
||||
}
|
||||
|
||||
if NewState[port][id] {
|
||||
return 1
|
||||
if device == libretro.DeviceJoypad {
|
||||
if id >= uint(ActionLast) || index > 0 {
|
||||
return 0
|
||||
}
|
||||
return NewState[port][id]
|
||||
}
|
||||
if device == libretro.DeviceAnalog {
|
||||
if id > uint(libretro.DeviceIDAnalogY) {
|
||||
// invalid
|
||||
return 0
|
||||
}
|
||||
|
||||
switch uint32(index) {
|
||||
case libretro.DeviceIndexAnalogLeft:
|
||||
return NewAnalogState[port][id]
|
||||
case libretro.DeviceIndexAnalogRight:
|
||||
return NewAnalogState[port][id]
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -7,14 +7,14 @@ import (
|
||||
func Test_getPressedReleased(t *testing.T) {
|
||||
t.Run("works", func(t *testing.T) {
|
||||
var old = States{
|
||||
{false, true, false, false},
|
||||
{0, 1, 0, 0},
|
||||
}
|
||||
var new = States{
|
||||
{false, false, false, true},
|
||||
{0, 0, 0, 1},
|
||||
}
|
||||
pressed, released := getPressedReleased(new, old)
|
||||
wantpressed := States{{false, false, false, true}}
|
||||
wantreleased := States{{false, true, false, false}}
|
||||
wantpressed := States{{0, 0, 0, 1}}
|
||||
wantreleased := States{{0, 1, 0, 0}}
|
||||
if pressed != wantpressed {
|
||||
t.Errorf("got = %v, want %v", pressed, wantpressed)
|
||||
}
|
||||
|
||||
+14
-14
@@ -66,7 +66,7 @@ func withRepeat() func(dt float32, pressed bool, f func()) {
|
||||
// vertically in entry lists, and also respond to presses on OK and Cancel.
|
||||
func genericInput(list *entry, dt float32) {
|
||||
// Down
|
||||
repeatDown(dt, input.NewState[0][libretro.DeviceIDJoypadDown], func() {
|
||||
repeatDown(dt, input.NewState[0][libretro.DeviceIDJoypadDown] == 1, func() {
|
||||
list.ptr++
|
||||
if list.ptr >= len(list.children) {
|
||||
list.ptr = 0
|
||||
@@ -76,7 +76,7 @@ func genericInput(list *entry, dt float32) {
|
||||
})
|
||||
|
||||
// Up
|
||||
repeatUp(dt, input.NewState[0][libretro.DeviceIDJoypadUp], func() {
|
||||
repeatUp(dt, input.NewState[0][libretro.DeviceIDJoypadUp] == 1, func() {
|
||||
list.ptr--
|
||||
if list.ptr < 0 {
|
||||
list.ptr = len(list.children) - 1
|
||||
@@ -86,7 +86,7 @@ func genericInput(list *entry, dt float32) {
|
||||
})
|
||||
|
||||
// OK
|
||||
if input.Released[0][libretro.DeviceIDJoypadA] {
|
||||
if input.Released[0][libretro.DeviceIDJoypadA] == 1 {
|
||||
if list.children[list.ptr].callbackOK != nil {
|
||||
audio.PlayEffect(audio.Effects["ok"])
|
||||
list.children[list.ptr].callbackOK()
|
||||
@@ -94,7 +94,7 @@ func genericInput(list *entry, dt float32) {
|
||||
}
|
||||
|
||||
// Right
|
||||
if input.Released[0][libretro.DeviceIDJoypadRight] {
|
||||
if input.Released[0][libretro.DeviceIDJoypadRight] == 1 {
|
||||
if list.children[list.ptr].incr != nil {
|
||||
audio.PlayEffect(audio.Effects["up"])
|
||||
list.children[list.ptr].incr(1)
|
||||
@@ -102,7 +102,7 @@ func genericInput(list *entry, dt float32) {
|
||||
}
|
||||
|
||||
// Left
|
||||
if input.Released[0][libretro.DeviceIDJoypadLeft] {
|
||||
if input.Released[0][libretro.DeviceIDJoypadLeft] == 1 {
|
||||
if list.children[list.ptr].incr != nil {
|
||||
audio.PlayEffect(audio.Effects["down"])
|
||||
list.children[list.ptr].incr(-1)
|
||||
@@ -110,7 +110,7 @@ func genericInput(list *entry, dt float32) {
|
||||
}
|
||||
|
||||
// Cancel
|
||||
if input.Released[0][libretro.DeviceIDJoypadB] {
|
||||
if input.Released[0][libretro.DeviceIDJoypadB] == 1 {
|
||||
if len(menu.stack) > 1 {
|
||||
audio.PlayEffect(audio.Effects["cancel"])
|
||||
menu.stack[len(menu.stack)-2].segueBack()
|
||||
@@ -119,14 +119,14 @@ func genericInput(list *entry, dt float32) {
|
||||
}
|
||||
|
||||
// Jump to next letter
|
||||
if input.Released[0][libretro.DeviceIDJoypadR] && len(list.indexes) > 0 {
|
||||
if input.Released[0][libretro.DeviceIDJoypadR] == 1 && len(list.indexes) > 0 {
|
||||
list.ptr = indexed(list, +1)
|
||||
audio.PlayEffect(audio.Effects["down"])
|
||||
genericAnimate(list)
|
||||
}
|
||||
|
||||
// Jump to previous letter
|
||||
if input.Released[0][libretro.DeviceIDJoypadL] && len(list.indexes) > 0 {
|
||||
if input.Released[0][libretro.DeviceIDJoypadL] == 1 && len(list.indexes) > 0 {
|
||||
list.ptr = indexed(list, -1)
|
||||
audio.PlayEffect(audio.Effects["up"])
|
||||
genericAnimate(list)
|
||||
@@ -159,10 +159,10 @@ func (m *Menu) ProcessHotkeys() {
|
||||
}
|
||||
|
||||
// Toggle the menu if ActionMenuToggle or the combo L3+R3 is pressed
|
||||
combo := input.NewState[0][libretro.DeviceIDJoypadL3] && input.Pressed[0][libretro.DeviceIDJoypadR3]
|
||||
combo = combo || input.Pressed[0][libretro.DeviceIDJoypadL3] && input.NewState[0][libretro.DeviceIDJoypadR3]
|
||||
combo := input.NewState[0][libretro.DeviceIDJoypadL3] == 1 && input.Pressed[0][libretro.DeviceIDJoypadR3] == 1
|
||||
combo = combo || input.Pressed[0][libretro.DeviceIDJoypadL3] == 1 && input.NewState[0][libretro.DeviceIDJoypadR3] == 1
|
||||
|
||||
if (input.Pressed[0][input.ActionMenuToggle] || combo) && state.Global.CoreRunning {
|
||||
if (input.Pressed[0][input.ActionMenuToggle] == 1 || combo) && state.Global.CoreRunning {
|
||||
state.Global.MenuActive = !state.Global.MenuActive
|
||||
state.Global.FastForward = false
|
||||
if state.Global.MenuActive {
|
||||
@@ -173,7 +173,7 @@ func (m *Menu) ProcessHotkeys() {
|
||||
}
|
||||
|
||||
// Toggle fullscreen if ActionFullscreenToggle is pressed
|
||||
if input.Pressed[0][input.ActionFullscreenToggle] {
|
||||
if input.Pressed[0][input.ActionFullscreenToggle] == 1 {
|
||||
settings.Current.VideoFullscreen = !settings.Current.VideoFullscreen
|
||||
vid.Reconfigure(settings.Current.VideoFullscreen)
|
||||
m.ContextReset()
|
||||
@@ -183,7 +183,7 @@ func (m *Menu) ProcessHotkeys() {
|
||||
}
|
||||
}
|
||||
|
||||
if input.Pressed[0][input.ActionFastForwardToggle] && !state.Global.MenuActive {
|
||||
if input.Pressed[0][input.ActionFastForwardToggle] == 1 && !state.Global.MenuActive {
|
||||
state.Global.FastForward = !state.Global.FastForward
|
||||
if state.Global.FastForward {
|
||||
ntf.DisplayAndLog(ntf.Info, "Menu", "Fast forward ON")
|
||||
@@ -194,7 +194,7 @@ func (m *Menu) ProcessHotkeys() {
|
||||
|
||||
// Close if ActionShouldClose is pressed, but display a confirmation dialog
|
||||
// in case a game is running
|
||||
if input.Pressed[0][input.ActionShouldClose] {
|
||||
if input.Pressed[0][input.ActionShouldClose] == 1 {
|
||||
askConfirmation(func() {
|
||||
vid.Window.SetShouldClose(true)
|
||||
})
|
||||
|
||||
@@ -33,13 +33,13 @@ func (s *sceneDialog) segueBack() {
|
||||
|
||||
func (s *sceneDialog) update(dt float32) {
|
||||
// OK
|
||||
if input.Released[0][libretro.DeviceIDJoypadA] {
|
||||
if input.Released[0][libretro.DeviceIDJoypadA] == 1 {
|
||||
audio.PlayEffect(audio.Effects["ok"])
|
||||
s.callbackOK()
|
||||
}
|
||||
|
||||
// Cancel
|
||||
if input.Released[0][libretro.DeviceIDJoypadB] {
|
||||
if input.Released[0][libretro.DeviceIDJoypadB] == 1 {
|
||||
audio.PlayEffect(audio.Effects["cancel"])
|
||||
menu.stack[len(menu.stack)-2].segueBack()
|
||||
menu.stack = menu.stack[:len(menu.stack)-1]
|
||||
|
||||
@@ -70,7 +70,7 @@ func (s *sceneKeyboard) segueBack() {
|
||||
|
||||
func (s *sceneKeyboard) update(dt float32) {
|
||||
// Right
|
||||
repeatRight(dt, input.NewState[0][libretro.DeviceIDJoypadRight], func() {
|
||||
repeatRight(dt, input.NewState[0][libretro.DeviceIDJoypadRight] == 1, func() {
|
||||
audio.PlayEffect(audio.Effects["up"])
|
||||
if (s.index+1)%10 == 0 {
|
||||
s.index -= 9
|
||||
@@ -80,7 +80,7 @@ func (s *sceneKeyboard) update(dt float32) {
|
||||
})
|
||||
|
||||
// Left
|
||||
repeatLeft(dt, input.NewState[0][libretro.DeviceIDJoypadLeft], func() {
|
||||
repeatLeft(dt, input.NewState[0][libretro.DeviceIDJoypadLeft] == 1, func() {
|
||||
audio.PlayEffect(audio.Effects["down"])
|
||||
if s.index%10 == 0 {
|
||||
s.index += 9
|
||||
@@ -90,7 +90,7 @@ func (s *sceneKeyboard) update(dt float32) {
|
||||
})
|
||||
|
||||
// Up
|
||||
repeatUp(dt, input.NewState[0][libretro.DeviceIDJoypadUp], func() {
|
||||
repeatUp(dt, input.NewState[0][libretro.DeviceIDJoypadUp] == 1, func() {
|
||||
audio.PlayEffect(audio.Effects["up"])
|
||||
if s.index < 10 {
|
||||
s.index += len(layouts[s.layout]) - 10
|
||||
@@ -100,7 +100,7 @@ func (s *sceneKeyboard) update(dt float32) {
|
||||
})
|
||||
|
||||
// Down
|
||||
repeatDown(dt, input.NewState[0][libretro.DeviceIDJoypadDown], func() {
|
||||
repeatDown(dt, input.NewState[0][libretro.DeviceIDJoypadDown] == 1, func() {
|
||||
audio.PlayEffect(audio.Effects["down"])
|
||||
if s.index >= len(layouts[s.layout])-10 {
|
||||
s.index -= len(layouts[s.layout]) - 10
|
||||
@@ -110,13 +110,13 @@ func (s *sceneKeyboard) update(dt float32) {
|
||||
})
|
||||
|
||||
// OK
|
||||
if input.Released[0][libretro.DeviceIDJoypadA] {
|
||||
if input.Released[0][libretro.DeviceIDJoypadA] == 1 {
|
||||
audio.PlayEffect(audio.Effects["ok"])
|
||||
s.value += layouts[s.layout][s.index]
|
||||
}
|
||||
|
||||
// Switch layout
|
||||
if input.Released[0][libretro.DeviceIDJoypadX] {
|
||||
if input.Released[0][libretro.DeviceIDJoypadX] == 1 {
|
||||
audio.PlayEffect(audio.Effects["ok"])
|
||||
s.layout++
|
||||
if s.layout >= len(layouts) {
|
||||
@@ -125,7 +125,7 @@ func (s *sceneKeyboard) update(dt float32) {
|
||||
}
|
||||
|
||||
// Delete character
|
||||
repeatY(dt, input.NewState[0][libretro.DeviceIDJoypadY], func() {
|
||||
repeatY(dt, input.NewState[0][libretro.DeviceIDJoypadY] == 1, func() {
|
||||
if len(s.value) > 0 {
|
||||
audio.PlayEffect(audio.Effects["cancel"])
|
||||
s.value = s.value[:len(s.value)-1]
|
||||
@@ -133,14 +133,14 @@ func (s *sceneKeyboard) update(dt float32) {
|
||||
})
|
||||
|
||||
// Cancel
|
||||
if input.Released[0][libretro.DeviceIDJoypadB] && len(menu.stack) > 1 {
|
||||
if input.Released[0][libretro.DeviceIDJoypadB] == 1 && len(menu.stack) > 1 {
|
||||
audio.PlayEffect(audio.Effects["cancel"])
|
||||
menu.stack[len(menu.stack)-2].segueBack()
|
||||
menu.stack = menu.stack[:len(menu.stack)-1]
|
||||
}
|
||||
|
||||
// Done
|
||||
if input.Released[0][libretro.DeviceIDJoypadStart] && s.value != "" {
|
||||
if input.Released[0][libretro.DeviceIDJoypadStart] == 1 && s.value != "" {
|
||||
audio.PlayEffect(audio.Effects["notice"])
|
||||
s.callbackDone(s.value)
|
||||
menu.stack[len(menu.stack)-2].segueBack()
|
||||
|
||||
+3
-3
@@ -227,7 +227,7 @@ func (tabs *sceneTabs) segueNext() {
|
||||
|
||||
func (tabs *sceneTabs) update(dt float32) {
|
||||
// Right
|
||||
repeatRight(dt, input.NewState[0][libretro.DeviceIDJoypadRight], func() {
|
||||
repeatRight(dt, input.NewState[0][libretro.DeviceIDJoypadRight] == 1, func() {
|
||||
tabs.ptr++
|
||||
if tabs.ptr >= len(tabs.children) {
|
||||
tabs.ptr = 0
|
||||
@@ -237,7 +237,7 @@ func (tabs *sceneTabs) update(dt float32) {
|
||||
})
|
||||
|
||||
// Left
|
||||
repeatLeft(dt, input.NewState[0][libretro.DeviceIDJoypadLeft], func() {
|
||||
repeatLeft(dt, input.NewState[0][libretro.DeviceIDJoypadLeft] == 1, func() {
|
||||
tabs.ptr--
|
||||
if tabs.ptr < 0 {
|
||||
tabs.ptr = len(tabs.children) - 1
|
||||
@@ -247,7 +247,7 @@ func (tabs *sceneTabs) update(dt float32) {
|
||||
})
|
||||
|
||||
// OK
|
||||
if input.Released[0][libretro.DeviceIDJoypadA] {
|
||||
if input.Released[0][libretro.DeviceIDJoypadA] == 1 {
|
||||
if tabs.children[tabs.ptr].callbackOK != nil {
|
||||
audio.PlayEffect(audio.Effects["ok"])
|
||||
tabs.segueNext()
|
||||
|
||||
Reference in new issue
Block a user