mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-20 05:40:49 -04:00
26bb7ae69d
fixed overflow of paddle resistance for stelladaptor
483 lines
15 KiB
Go
483 lines
15 KiB
Go
// This file is part of Gopher2600.
|
|
//
|
|
// Gopher2600 is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Gopher2600 is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package userinput
|
|
|
|
import (
|
|
"github.com/jetsetilly/gopher2600/hardware/riot/ports"
|
|
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
|
|
)
|
|
|
|
// quite a large deadzone for the thumbstick
|
|
const ThumbstickDeadzone = 10000
|
|
|
|
// Controllers keeps track of hardware userinput options.
|
|
type Controllers struct {
|
|
inputHandler HandleInput
|
|
swapped bool
|
|
}
|
|
|
|
// Controllers is the preferred method of initialisation for the Controllers type.
|
|
func NewControllers(h HandleInput) *Controllers {
|
|
return &Controllers{inputHandler: h}
|
|
}
|
|
|
|
// Swap exchanges which controls affects which player port. For example, the
|
|
// cursor keys on the keyboard will normally control the left player. However,
|
|
// when swapped, the cursor keys will control the right player
|
|
//
|
|
// Returns true if the ports are now swapped and false if the ports are in the
|
|
// original state
|
|
func (c *Controllers) Swap() bool {
|
|
c.swapped = !c.swapped
|
|
return c.swapped
|
|
}
|
|
|
|
// handleSwap returns the required PortID relative to the supplied PortID if the
|
|
// controls have been swapped
|
|
func (c Controllers) handleSwap(port plugging.PortID) plugging.PortID {
|
|
if c.swapped {
|
|
switch port {
|
|
case plugging.PortLeft:
|
|
port = plugging.PortRight
|
|
case plugging.PortRight:
|
|
port = plugging.PortLeft
|
|
}
|
|
}
|
|
return port
|
|
}
|
|
|
|
// handleEvents sends the port/event information to each HandleInput
|
|
// implementation.
|
|
//
|
|
// returns True if event has been handled/recognised by at least one of the
|
|
// registered input handlers.
|
|
func (c *Controllers) handleEvents(id plugging.PortID, ev ports.Event, d ports.EventData) (bool, error) {
|
|
handled, err := c.inputHandler.HandleInputEvent(ports.InputEvent{Port: id, Ev: ev, D: d})
|
|
if err != nil {
|
|
return handled, err
|
|
}
|
|
return handled, nil
|
|
}
|
|
|
|
func (c *Controllers) mouseMotion(ev EventMouseMotion) (bool, error) {
|
|
// mix y-axis with x-axis. in this scenario the absolute value of the y-axis
|
|
// is given the same sign as the x-axis
|
|
motion := ev.X
|
|
|
|
// absolute value of y
|
|
y := ev.Y
|
|
if y < 0 {
|
|
y *= -1
|
|
}
|
|
|
|
// add/subtract y-value to x-axis (according to sign of x-axis)
|
|
if ev.X < 0 {
|
|
motion -= y
|
|
} else if ev.X > 0 {
|
|
motion += y
|
|
}
|
|
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.PaddleSet, ports.EventDataPaddle{
|
|
A: motion,
|
|
Relative: true,
|
|
})
|
|
}
|
|
|
|
func (c *Controllers) mouseButton(ev EventMouseButton) (bool, error) {
|
|
switch ev.Button {
|
|
case MouseButtonLeft:
|
|
if ev.Down {
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Fire, true)
|
|
} else {
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Fire, false)
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// differentiateKeyboard is called by the keyboard() function in order to
|
|
// disambiguate physical key presses and sending the correct event depending on
|
|
// the attached peripheral.
|
|
//
|
|
// like the handleEvent() function if sends the port/event information to each
|
|
// HandleInput implemtation, when appropriate.
|
|
//
|
|
// returns True if event has been handled/recognised by at least one of the
|
|
// registered input handlers.
|
|
func (c *Controllers) differentiateKeyboard(key string, down bool) (bool, error) {
|
|
var handled bool
|
|
|
|
var ev ports.Event
|
|
var d ports.EventData
|
|
|
|
var portID plugging.PortID
|
|
var keyEv ports.Event
|
|
var stickEvData ports.EventData
|
|
var fireEvData ports.EventData
|
|
|
|
if down {
|
|
keyEv = ports.KeypadDown
|
|
stickEvData = ports.DataStickTrue
|
|
fireEvData = true
|
|
} else {
|
|
keyEv = ports.KeypadUp
|
|
stickEvData = ports.DataStickFalse
|
|
fireEvData = false
|
|
}
|
|
|
|
switch key {
|
|
case "Y":
|
|
portID = c.handleSwap(plugging.PortRight)
|
|
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
|
|
ev = keyEv
|
|
d = '6'
|
|
} else {
|
|
ev = ports.Up
|
|
d = stickEvData
|
|
}
|
|
case "F":
|
|
portID = c.handleSwap(plugging.PortRight)
|
|
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
|
|
ev = keyEv
|
|
d = '7'
|
|
} else {
|
|
ev = ports.Fire
|
|
d = fireEvData
|
|
}
|
|
case "G":
|
|
portID = c.handleSwap(plugging.PortRight)
|
|
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
|
|
ev = keyEv
|
|
d = '8'
|
|
} else {
|
|
ev = ports.Left
|
|
d = stickEvData
|
|
}
|
|
case "H":
|
|
portID = c.handleSwap(plugging.PortRight)
|
|
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
|
|
ev = keyEv
|
|
d = '9'
|
|
} else {
|
|
ev = ports.Down
|
|
d = stickEvData
|
|
}
|
|
case "B":
|
|
portID = c.handleSwap(plugging.PortRight)
|
|
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
|
|
ev = keyEv
|
|
d = '0'
|
|
} else {
|
|
portID = c.handleSwap(plugging.PortLeft)
|
|
ev = ports.SecondFire
|
|
d = fireEvData
|
|
}
|
|
case "6":
|
|
portID = c.handleSwap(plugging.PortRight)
|
|
if c.inputHandler.PeripheralID(portID) == plugging.PeriphKeypad {
|
|
ev = keyEv
|
|
d = '6'
|
|
} else {
|
|
ev = ports.SecondFire
|
|
d = fireEvData
|
|
}
|
|
default:
|
|
}
|
|
|
|
// all differentiated keyboard events go to the right player port
|
|
v, err := c.inputHandler.HandleInputEvent(ports.InputEvent{Port: portID, Ev: ev, D: d})
|
|
if err != nil {
|
|
return handled, err
|
|
}
|
|
handled = handled || v
|
|
|
|
return handled, nil
|
|
}
|
|
|
|
func (c *Controllers) keyboard(ev EventKeyboard) (bool, error) {
|
|
if ev.Down && ev.Mod == KeyModNone {
|
|
switch ev.Key {
|
|
// panel
|
|
case "F1":
|
|
return c.handleEvents(plugging.PortPanel, ports.PanelSelect, true)
|
|
case "F2":
|
|
return c.handleEvents(plugging.PortPanel, ports.PanelReset, true)
|
|
case "F3":
|
|
return c.handleEvents(plugging.PortPanel, ports.PanelToggleColor, nil)
|
|
case "F4":
|
|
return c.handleEvents(plugging.PortPanel, ports.PanelTogglePlayer0Pro, nil)
|
|
case "F5":
|
|
return c.handleEvents(plugging.PortPanel, ports.PanelTogglePlayer1Pro, nil)
|
|
|
|
// joystick (left player)
|
|
case "Left":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Left, ports.DataStickTrue)
|
|
case "Right":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Right, ports.DataStickTrue)
|
|
case "Up":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Up, ports.DataStickTrue)
|
|
case "Down":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Down, ports.DataStickTrue)
|
|
case "Space":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Fire, true)
|
|
|
|
// joystick (right player)
|
|
// * keypad and joystick share some keys (see below for other inputs)
|
|
case "J":
|
|
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.Right, ports.DataStickTrue)
|
|
|
|
// keypad (left player)
|
|
case "1", "2", "3":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, rune(ev.Key[0]))
|
|
case "Q":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '4')
|
|
case "W":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '5')
|
|
case "E":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '6')
|
|
case "A":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '7')
|
|
case "S":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '8')
|
|
case "D":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '9')
|
|
case "Z":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '*')
|
|
case "X":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '0')
|
|
case "C":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadDown, '#')
|
|
|
|
// keypad (right player)
|
|
// * keypad and joystick share some keys (see below for other inputs)
|
|
case "4":
|
|
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '1')
|
|
case "5":
|
|
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '2')
|
|
case "R":
|
|
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '4')
|
|
case "T":
|
|
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '5')
|
|
case "V":
|
|
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '*')
|
|
case "N":
|
|
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadDown, '#')
|
|
|
|
default:
|
|
// keypad (right player) *OR* joystick (right player)
|
|
// * keypad and joystick share some keys (see above for other inputs)
|
|
return c.differentiateKeyboard(ev.Key, true)
|
|
}
|
|
} else {
|
|
switch ev.Key {
|
|
// panel
|
|
case "F1":
|
|
return c.handleEvents(plugging.PortPanel, ports.PanelSelect, false)
|
|
case "F2":
|
|
return c.handleEvents(plugging.PortPanel, ports.PanelReset, false)
|
|
|
|
// josytick (left player)
|
|
case "Left":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Left, ports.DataStickFalse)
|
|
case "Right":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Right, ports.DataStickFalse)
|
|
case "Up":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Up, ports.DataStickFalse)
|
|
case "Down":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Down, ports.DataStickFalse)
|
|
case "Space":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.Fire, false)
|
|
|
|
// joystick (right player)
|
|
// * keypad and joystick share some keys (see below for other inputs)
|
|
case "J":
|
|
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.Right, ports.DataStickFalse)
|
|
|
|
// keyboard (left player)
|
|
case "1", "2", "3", "Q", "W", "E", "A", "S", "D", "Z", "X", "C":
|
|
return c.handleEvents(c.handleSwap(plugging.PortLeft), ports.KeypadUp, nil)
|
|
|
|
// keyboard (right player)
|
|
// * keypad and joystick share some keys (see below for other inputs)
|
|
case "4", "5", "R", "T", "V", "N":
|
|
return c.handleEvents(c.handleSwap(plugging.PortRight), ports.KeypadUp, nil)
|
|
|
|
default:
|
|
// keypad (right player) *OR* joystick (right player)
|
|
// * keypad and joystick share some keys (see above for other inputs)
|
|
return c.differentiateKeyboard(ev.Key, false)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Controllers) gamepadDPad(ev EventGamepadDPad) (bool, error) {
|
|
switch ev.Direction {
|
|
case DPadCentre:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Centre, nil)
|
|
|
|
case DPadUp:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Up, ports.DataStickSet)
|
|
|
|
case DPadDown:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Down, ports.DataStickSet)
|
|
|
|
case DPadLeft:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Left, ports.DataStickSet)
|
|
|
|
case DPadRight:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Right, ports.DataStickSet)
|
|
|
|
case DPadLeftUp:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftUp, ports.DataStickSet)
|
|
|
|
case DPadLeftDown:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftDown, ports.DataStickSet)
|
|
|
|
case DPadRightUp:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.RightUp, ports.DataStickSet)
|
|
|
|
case DPadRightDown:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.RightDown, ports.DataStickSet)
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
func (c *Controllers) gamepadButton(ev EventGamepadButton) (bool, error) {
|
|
switch ev.Button {
|
|
case GamepadButtonStart:
|
|
return c.handleEvents(plugging.PortPanel, ports.PanelReset, ev.Down)
|
|
case GamepadButtonA:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Fire, ev.Down)
|
|
case GamepadButtonB:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.SecondFire, ev.Down)
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (c *Controllers) gamepadThumbstick(ev EventGamepadThumbstick) (bool, error) {
|
|
if ev.Thumbstick != GamepadThumbstickLeft {
|
|
return false, nil
|
|
}
|
|
|
|
if ev.Horiz > ThumbstickDeadzone {
|
|
if ev.Vert > ThumbstickDeadzone {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.RightDown, ports.DataStickSet)
|
|
} else if ev.Vert < -ThumbstickDeadzone {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.RightUp, ports.DataStickSet)
|
|
}
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Right, ports.DataStickSet)
|
|
} else if ev.Horiz < -ThumbstickDeadzone {
|
|
if ev.Vert > ThumbstickDeadzone {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftDown, ports.DataStickSet)
|
|
} else if ev.Vert < -ThumbstickDeadzone {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftUp, ports.DataStickSet)
|
|
}
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Left, ports.DataStickSet)
|
|
} else if ev.Vert > ThumbstickDeadzone {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Down, ports.DataStickSet)
|
|
} else if ev.Vert < -ThumbstickDeadzone {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Up, ports.DataStickSet)
|
|
}
|
|
|
|
// never report that the centre event has been handled by the emulated
|
|
// machine.
|
|
//
|
|
// for example, it prevents deadzone signals causing the emulation to unpause
|
|
//
|
|
// this might be wrong behaviour in some situations.
|
|
_, err := c.handleEvents(c.handleSwap(ev.ID), ports.Centre, nil)
|
|
return false, err
|
|
}
|
|
|
|
func (c *Controllers) stelladaptor(ev EventStelladaptor) (bool, error) {
|
|
switch c.inputHandler.PeripheralID(ev.ID) {
|
|
case plugging.PeriphStick:
|
|
// boundary value is compared again incoming axes values
|
|
//
|
|
// a better strategy might be just to switch on the values that we know
|
|
// represent the state of a digital stick (ie. up/down, left/right, at rest)
|
|
// but I'm not yet confident in the precise values thar are sent by the
|
|
// stelladapter
|
|
//
|
|
// suspected values currently:
|
|
// 0x007f = at rest
|
|
// 0x7fff = down (vert axis) right (horiz axis)
|
|
// 0x8000 = up (vert axis) left (horiz axis)
|
|
const boundaryValue = 255
|
|
|
|
if ev.Horiz > boundaryValue {
|
|
if ev.Vert > boundaryValue {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.RightDown, ports.DataStickSet)
|
|
} else if ev.Vert < -boundaryValue {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.RightUp, ports.DataStickSet)
|
|
}
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Right, ports.DataStickSet)
|
|
} else if ev.Horiz < -boundaryValue {
|
|
if ev.Vert > boundaryValue {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftDown, ports.DataStickSet)
|
|
} else if ev.Vert < -boundaryValue {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.LeftUp, ports.DataStickSet)
|
|
}
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Left, ports.DataStickSet)
|
|
} else if ev.Vert > boundaryValue {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Down, ports.DataStickSet)
|
|
} else if ev.Vert < -boundaryValue {
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Up, ports.DataStickSet)
|
|
}
|
|
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.Centre, nil)
|
|
|
|
case plugging.PeriphPaddles:
|
|
return c.handleEvents(c.handleSwap(ev.ID), ports.PaddleSet, ports.EventDataPaddle{
|
|
A: -ev.Horiz - 1,
|
|
B: -ev.Vert - 1,
|
|
})
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// HandleUserInput deciphers the Event and forwards the input to the Atari 2600
|
|
// player ports.
|
|
//
|
|
// Returns True if event has been handled/recognised by at least one of the
|
|
// registered input handlers.
|
|
func (c *Controllers) HandleUserInput(ev Event) (bool, error) {
|
|
switch ev := ev.(type) {
|
|
case EventKeyboard:
|
|
return c.keyboard(ev)
|
|
case EventMouseButton:
|
|
return c.mouseButton(ev)
|
|
case EventMouseMotion:
|
|
return c.mouseMotion(ev)
|
|
case EventGamepadDPad:
|
|
return c.gamepadDPad(ev)
|
|
case EventGamepadButton:
|
|
return c.gamepadButton(ev)
|
|
case EventGamepadThumbstick:
|
|
return c.gamepadThumbstick(ev)
|
|
case EventGamepadTrigger:
|
|
// not using trigger
|
|
case EventStelladaptor:
|
|
return c.stelladaptor(ev)
|
|
default:
|
|
}
|
|
|
|
return false, nil
|
|
}
|