begin refactoring shell/glfw-gamepad.sml. (Most functions inside that file are pure now, and I think a 'modal' approach to the face buttons would be better/more ergonomic.)
This commit is contained in:
@@ -129,7 +129,6 @@ struct
|
|||||||
loop (0, DrawMailbox.getMessagesAndClear (), shellState)
|
loop (0, DrawMailbox.getMessagesAndClear (), shellState)
|
||||||
end
|
end
|
||||||
|
|
||||||
local
|
|
||||||
fun updateLoop (pos, msgVec, app) =
|
fun updateLoop (pos, msgVec, app) =
|
||||||
if pos = Vector.length msgVec then
|
if pos = Vector.length msgVec then
|
||||||
app
|
app
|
||||||
@@ -140,13 +139,12 @@ struct
|
|||||||
in
|
in
|
||||||
updateLoop (pos + 1, msgVec, app)
|
updateLoop (pos + 1, msgVec, app)
|
||||||
end
|
end
|
||||||
in
|
|
||||||
fun update app =
|
fun update app =
|
||||||
updateLoop (0, InputMailbox.getMessagesAndClear (), app)
|
updateLoop (0, InputMailbox.getMessagesAndClear (), app)
|
||||||
end
|
|
||||||
|
|
||||||
fun helpLoop (app, shellState as {window, ...}: t) =
|
fun helpLoop (app, shellState: t, gamepad) =
|
||||||
case Glfw.windowShouldClose window of
|
case Glfw.windowShouldClose (#window shellState) of
|
||||||
false =>
|
false =>
|
||||||
let
|
let
|
||||||
val shellState = consumeDrawEvents shellState
|
val shellState = consumeDrawEvents shellState
|
||||||
@@ -154,19 +152,34 @@ struct
|
|||||||
val _ = Gles3.clearColor (0.89, 0.89, 0.89, 1.0)
|
val _ = Gles3.clearColor (0.89, 0.89, 0.89, 1.0)
|
||||||
val _ = Gles3.clear ()
|
val _ = Gles3.clear ()
|
||||||
|
|
||||||
val () = GlfwGamepad.query ()
|
(* one update reacting to gamepad events *)
|
||||||
|
val (gamepad, actions) = GlfwGamepad.query gamepad
|
||||||
|
val app = updateLoop (0, Vector.fromList actions, app)
|
||||||
|
|
||||||
|
(* one update reacting to keyboard events *)
|
||||||
val app = update app
|
val app = update app
|
||||||
val _ = draw shellState
|
val _ = draw shellState
|
||||||
|
|
||||||
val _ = Glfw.swapBuffers window
|
val _ = Glfw.swapBuffers (#window shellState)
|
||||||
val _ = Glfw.waitEvents ()
|
val _ = Glfw.waitEvents ()
|
||||||
in
|
in
|
||||||
helpLoop (app, shellState)
|
helpLoop (app, shellState, gamepad)
|
||||||
end
|
end
|
||||||
| true => Glfw.terminate ()
|
| true => Glfw.terminate ()
|
||||||
|
|
||||||
fun loop (app, window) =
|
fun loop (app, window) =
|
||||||
let val shellState = create window
|
let
|
||||||
in helpLoop (app, shellState)
|
val shellState = create window
|
||||||
|
|
||||||
|
val gamepad: GlfwGamepad.gamepad_state =
|
||||||
|
{ mode = GlfwGamepad.PENDING
|
||||||
|
, shiftChr = false
|
||||||
|
, trianglePressed = false
|
||||||
|
, circlePressed = false
|
||||||
|
, crossPressed = false
|
||||||
|
, squarePressed = false
|
||||||
|
}
|
||||||
|
in
|
||||||
|
helpLoop (app, shellState, gamepad)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,33 +1,127 @@
|
|||||||
structure GlfwGamepad =
|
structure GlfwGamepad =
|
||||||
struct
|
struct
|
||||||
datatype joystick_dir = UP | LEFT | DOWN | RIGHT | CENTRE | L2 | R2
|
datatype mode =
|
||||||
|
PENDING
|
||||||
|
| TRIANGLE
|
||||||
|
| TRIANGLE_CIRCLE
|
||||||
|
| CIRCLE
|
||||||
|
| CIRCLE_CROSS
|
||||||
|
| CROSS
|
||||||
|
| CROSS_SQUARE
|
||||||
|
| SQUARE
|
||||||
|
(* maybe SQUARE_TRIANGLE for numbers and symbols? *)
|
||||||
|
| SQUARE_TRIANGLE
|
||||||
|
|
||||||
fun isInDeadZone (x, y) =
|
structure IM = InputMsg
|
||||||
x < 0.1 andalso x > ~0.1 andalso y < 0.1 andalso y > ~0.1
|
|
||||||
|
|
||||||
fun axisToDir (x, y, l2, r2) =
|
type gamepad_state =
|
||||||
if isInDeadZone (x, y) then
|
{ mode: mode
|
||||||
(* analogue is in dead zone, so only query L2 and R2 *)
|
, shiftChr: bool
|
||||||
if r2 < 0.3 andalso l2 < 0.3 then CENTRE
|
, trianglePressed: bool
|
||||||
else if abs r2 > abs l2 then R2
|
, circlePressed: bool
|
||||||
else L2
|
, crossPressed: bool
|
||||||
else
|
, squarePressed: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPendingMode
|
||||||
|
( state: gamepad_state
|
||||||
|
, trianglePressed
|
||||||
|
, circlePressed
|
||||||
|
, crossPressed
|
||||||
|
, squarePressed
|
||||||
|
, actions: IM.t list
|
||||||
|
) =
|
||||||
|
if
|
||||||
|
trianglePressed orelse circlePressed orelse crossPressed
|
||||||
|
orelse squarePressed
|
||||||
|
then
|
||||||
|
(* some button is being pressed,
|
||||||
|
* so we record that in the returned state,
|
||||||
|
* in addition to whatever buttons were previously pressed *)
|
||||||
let
|
let
|
||||||
val ax = abs x
|
val trianglePressed = #trianglePressed state orelse trianglePressed
|
||||||
val ay = abs y
|
val circlePressed = #circlePressed state orelse circlePressed
|
||||||
val al2 = abs l2
|
val crossPressed = #crossPressed state orelse crossPressed
|
||||||
val ar2 = abs r2
|
val squarePressed = #squarePressed state orelse squarePressed
|
||||||
|
|
||||||
|
val newState =
|
||||||
|
{ mode = #mode state
|
||||||
|
, shiftChr = #shiftChr state
|
||||||
|
, trianglePressed = trianglePressed
|
||||||
|
, circlePressed = circlePressed
|
||||||
|
, crossPressed = crossPressed
|
||||||
|
, squarePressed = squarePressed
|
||||||
|
}
|
||||||
in
|
in
|
||||||
if ax > ay andalso ax > al2 andalso ax > ar2 then
|
(newState, actions)
|
||||||
if x > 0.0 then RIGHT else LEFT
|
end
|
||||||
else if ay > ax andalso ay > al2 andalso ay > ar2 then
|
|
||||||
if y > 0.0 then DOWN else UP
|
|
||||||
else if al2 > ax andalso al2 > ay andalso al2 > ar2 then
|
|
||||||
L2
|
|
||||||
else
|
else
|
||||||
R2
|
(* nothing is currently pressed,
|
||||||
|
* so we check if there is a valid mode indicated by the state
|
||||||
|
* and change the mode if so *)
|
||||||
|
let
|
||||||
|
val
|
||||||
|
{ trianglePressed
|
||||||
|
, circlePressed
|
||||||
|
, crossPressed
|
||||||
|
, squarePressed
|
||||||
|
, shiftChr
|
||||||
|
, mode = _
|
||||||
|
} = state
|
||||||
|
val newMode =
|
||||||
|
if trianglePressed andalso not (crossPressed orelse squarePressed) then
|
||||||
|
if not circlePressed then TRIANGLE else TRIANGLE_CIRCLE
|
||||||
|
else if circlePressed andalso not squarePressed then
|
||||||
|
if not crossPressed then CIRCLE else CIRCLE_CROSS
|
||||||
|
else if crossPressed then
|
||||||
|
if not squarePressed then CROSS else CROSS_SQUARE
|
||||||
|
else if squarePressed then
|
||||||
|
if not trianglePressed then SQUARE else SQUARE_TRIANGLE
|
||||||
|
else
|
||||||
|
(* some buttons are being pressed,
|
||||||
|
* but not a valid combination to switch
|
||||||
|
* to another mode, so we are still on PENDING *)
|
||||||
|
PENDING
|
||||||
|
|
||||||
|
val newState =
|
||||||
|
{ mode = newMode
|
||||||
|
, shiftChr = shiftChr
|
||||||
|
, trianglePressed = false
|
||||||
|
, circlePressed = false
|
||||||
|
, crossPressed = false
|
||||||
|
, squarePressed = false
|
||||||
|
}
|
||||||
|
in
|
||||||
|
(newState, actions)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
fun handleButtons
|
||||||
|
( gamepadState
|
||||||
|
, trianglePressed
|
||||||
|
, circlePressed
|
||||||
|
, crossPressed
|
||||||
|
, squarePressed
|
||||||
|
, l1Pressed
|
||||||
|
, r1Pressed
|
||||||
|
) =
|
||||||
|
let
|
||||||
|
val actions = if l1Pressed then [IM.KEY_BACKSPACE] else []
|
||||||
|
val actions =
|
||||||
|
if r1Pressed then (IM.CHAR_EVENT #" ") :: actions else actions
|
||||||
|
in
|
||||||
|
case #mode gamepadState of
|
||||||
|
PENDING =>
|
||||||
|
onPendingMode
|
||||||
|
( gamepadState
|
||||||
|
, trianglePressed
|
||||||
|
, circlePressed
|
||||||
|
, crossPressed
|
||||||
|
, squarePressed
|
||||||
|
, actions
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
(* impure functions below *)
|
||||||
fun getGamepadState () =
|
fun getGamepadState () =
|
||||||
Input.getGamepadState 0 <> 0 orelse Input.getGamepadState 1 <> 0
|
Input.getGamepadState 0 <> 0 orelse Input.getGamepadState 1 <> 0
|
||||||
orelse Input.getGamepadState 2 <> 0 orelse Input.getGamepadState 3 <> 0
|
orelse Input.getGamepadState 2 <> 0 orelse Input.getGamepadState 3 <> 0
|
||||||
@@ -38,203 +132,27 @@ struct
|
|||||||
orelse Input.getGamepadState 12 <> 0 orelse Input.getGamepadState 13 <> 0
|
orelse Input.getGamepadState 12 <> 0 orelse Input.getGamepadState 13 <> 0
|
||||||
orelse Input.getGamepadState 14 <> 0 orelse Input.getGamepadState 15 <> 0
|
orelse Input.getGamepadState 14 <> 0 orelse Input.getGamepadState 15 <> 0
|
||||||
|
|
||||||
local
|
fun query gamepadState =
|
||||||
val state =
|
if getGamepadState () then
|
||||||
{ crossPressed = ref false
|
|
||||||
, circlePressed = ref false
|
|
||||||
, squarePressed = ref false
|
|
||||||
, trianglePressed = ref false
|
|
||||||
, r1Pressed = ref false
|
|
||||||
, l1Pressed = ref false
|
|
||||||
, shiftChr = ref false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun appendChr chr =
|
|
||||||
(InputMailbox.append (InputMsg.CHAR_EVENT chr); #shiftChr state := false)
|
|
||||||
|
|
||||||
fun handleTrianglePressed (x, y, l2, r2) =
|
|
||||||
if !(#trianglePressed state) then
|
|
||||||
()
|
|
||||||
else
|
|
||||||
let
|
let
|
||||||
val () = #trianglePressed state := true
|
val trianglePressed = Input.isTriangleButtonPressed () <> 0
|
||||||
val chr =
|
val circlePressed = Input.isCircleButtonPressed () <> 0
|
||||||
if !(#shiftChr state) then
|
val crossPressed = Input.isCrossButtonPressed () <> 0
|
||||||
case axisToDir (x, y, l2, r2) of
|
val squarePressed = Input.isSquareButtonPressed () <> 0
|
||||||
CENTRE => #"A"
|
val l1Pressed = Input.isL1ButtonPressed () <> 0
|
||||||
| UP => #"E"
|
val r1Pressed = Input.isL1ButtonPressed () <> 0
|
||||||
| RIGHT => #"I"
|
|
||||||
| DOWN => #"M"
|
|
||||||
| LEFT => #"Q"
|
|
||||||
| L2 => #"U"
|
|
||||||
| R2 => #"Y"
|
|
||||||
else
|
|
||||||
case axisToDir (x, y, l2, r2) of
|
|
||||||
CENTRE => #"a"
|
|
||||||
| UP => #"e"
|
|
||||||
| RIGHT => #"i"
|
|
||||||
| DOWN => #"m"
|
|
||||||
| LEFT => #"q"
|
|
||||||
| L2 => #"u"
|
|
||||||
| R2 => #"y"
|
|
||||||
in
|
in
|
||||||
appendChr chr
|
handleButtons
|
||||||
|
( gamepadState
|
||||||
|
, trianglePressed
|
||||||
|
, circlePressed
|
||||||
|
, crossPressed
|
||||||
|
, squarePressed
|
||||||
|
, l1Pressed
|
||||||
|
, r1Pressed
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
fun handleCirclePressed (x, y, l2, r2) =
|
|
||||||
if !(#circlePressed state) then
|
|
||||||
()
|
|
||||||
else
|
else
|
||||||
let
|
(* nothing to do, so return same state *)
|
||||||
val () = #circlePressed state := true
|
(gamepadState, [])
|
||||||
val chr =
|
|
||||||
if !(#shiftChr state) then
|
|
||||||
case axisToDir (x, y, l2, r2) of
|
|
||||||
CENTRE => #"B"
|
|
||||||
| UP => #"F"
|
|
||||||
| RIGHT => #"J"
|
|
||||||
| DOWN => #"N"
|
|
||||||
| LEFT => #"R"
|
|
||||||
| L2 => #"V"
|
|
||||||
| R2 => #"Z"
|
|
||||||
else
|
|
||||||
case axisToDir (x, y, l2, r2) of
|
|
||||||
CENTRE => #"b"
|
|
||||||
| UP => #"f"
|
|
||||||
| RIGHT => #"j"
|
|
||||||
| DOWN => #"n"
|
|
||||||
| LEFT => #"r"
|
|
||||||
| L2 => #"v"
|
|
||||||
| R2 => #"z"
|
|
||||||
in
|
|
||||||
appendChr chr
|
|
||||||
end
|
|
||||||
|
|
||||||
fun handleCrossPressed (x, y, l2, r2) =
|
|
||||||
if !(#crossPressed state) then
|
|
||||||
()
|
|
||||||
else
|
|
||||||
let
|
|
||||||
val () = #crossPressed state := true
|
|
||||||
in
|
|
||||||
if !(#shiftChr state) then
|
|
||||||
case axisToDir (x, y, l2, r2) of
|
|
||||||
CENTRE => appendChr #"U"
|
|
||||||
| UP => appendChr #"U"
|
|
||||||
| RIGHT => appendChr #"U"
|
|
||||||
| DOWN => appendChr #"U"
|
|
||||||
| LEFT => appendChr #"U"
|
|
||||||
| L2 => appendChr #"U"
|
|
||||||
| R2 => InputMailbox.append InputMsg.KEY_ENTER
|
|
||||||
else
|
|
||||||
case axisToDir (x, y, l2, r2) of
|
|
||||||
CENTRE => appendChr #"c"
|
|
||||||
| UP => appendChr #"g"
|
|
||||||
| RIGHT => appendChr #"k"
|
|
||||||
| DOWN => appendChr #"o"
|
|
||||||
| LEFT => appendChr #"s"
|
|
||||||
| L2 => appendChr #"w"
|
|
||||||
| R2 => InputMailbox.append InputMsg.KEY_ENTER
|
|
||||||
end
|
|
||||||
|
|
||||||
fun handleSquarePressed (x, y, l2, r2) =
|
|
||||||
if !(#squarePressed state) then
|
|
||||||
()
|
|
||||||
else
|
|
||||||
let
|
|
||||||
val () = #squarePressed state := true
|
|
||||||
in
|
|
||||||
if !(#shiftChr state) then
|
|
||||||
case axisToDir (x, y, l2, r2) of
|
|
||||||
CENTRE => appendChr #"U"
|
|
||||||
| UP => appendChr #"U"
|
|
||||||
| RIGHT => appendChr #"U"
|
|
||||||
| DOWN => appendChr #"U"
|
|
||||||
| LEFT => appendChr #"U"
|
|
||||||
| L2 => appendChr #"U"
|
|
||||||
| R2 => #shiftChr state := true
|
|
||||||
else
|
|
||||||
case axisToDir (x, y, l2, r2) of
|
|
||||||
CENTRE => appendChr #"d"
|
|
||||||
| UP => appendChr #"h"
|
|
||||||
| RIGHT => appendChr #"l"
|
|
||||||
| DOWN => appendChr #"p"
|
|
||||||
| LEFT => appendChr #"t"
|
|
||||||
| L2 => appendChr #"x"
|
|
||||||
| R2 => #shiftChr state := true
|
|
||||||
end
|
|
||||||
in
|
|
||||||
fun handleIfJoystickIsPresent () =
|
|
||||||
let
|
|
||||||
open InputMsg
|
|
||||||
|
|
||||||
(* query possible events for information *)
|
|
||||||
val xAxis = Input.getLeftJoystickXAxisState ()
|
|
||||||
val yAxis = Input.getLeftJoystickYAxisState ()
|
|
||||||
|
|
||||||
val r2 = (Input.getR2State () + 1.0) / 2.0
|
|
||||||
val l2 = (Input.getL2State () + 1.0) / 2.0
|
|
||||||
|
|
||||||
val crossPressed = Input.isCrossButtonPressed ()
|
|
||||||
val circlePressed = Input.isCircleButtonPressed ()
|
|
||||||
val squarePressed = Input.isSquareButtonPressed ()
|
|
||||||
val trianglePressed = Input.isTriangleButtonPressed ()
|
|
||||||
|
|
||||||
val r1Pressed = Input.isR1ButtonPressed ()
|
|
||||||
val l1Pressed = Input.isL1ButtonPressed ()
|
|
||||||
|
|
||||||
(* handle button events *)
|
|
||||||
val () =
|
|
||||||
if crossPressed = 0 then #crossPressed state := false
|
|
||||||
else if !(#crossPressed state) then ()
|
|
||||||
else handleCrossPressed (xAxis, yAxis, l2, r2)
|
|
||||||
|
|
||||||
val () =
|
|
||||||
if circlePressed = 0 then #circlePressed state := false
|
|
||||||
else if !(#circlePressed state) then ()
|
|
||||||
else handleCirclePressed (xAxis, yAxis, l2, r2)
|
|
||||||
|
|
||||||
val () =
|
|
||||||
if squarePressed = 0 then #squarePressed state := false
|
|
||||||
else handleSquarePressed (xAxis, yAxis, l2, r2)
|
|
||||||
|
|
||||||
val () =
|
|
||||||
if trianglePressed = 0 then #trianglePressed state := false
|
|
||||||
else if !(#trianglePressed state) then ()
|
|
||||||
else handleTrianglePressed (xAxis, yAxis, l2, r2)
|
|
||||||
|
|
||||||
val () =
|
|
||||||
if r1Pressed = 0 then
|
|
||||||
#r1Pressed state := false
|
|
||||||
else if !(#r1Pressed state) then
|
|
||||||
()
|
|
||||||
else
|
|
||||||
let val () = InputMailbox.append (InputMsg.CHAR_EVENT #" ")
|
|
||||||
in #r1Pressed state := true
|
|
||||||
end
|
|
||||||
|
|
||||||
val () =
|
|
||||||
if l1Pressed = 0 then
|
|
||||||
#l1Pressed state := false
|
|
||||||
else if !(#l1Pressed state) then
|
|
||||||
()
|
|
||||||
else
|
|
||||||
let val () = InputMailbox.append InputMsg.KEY_BACKSPACE
|
|
||||||
in #l1Pressed state := true
|
|
||||||
end
|
|
||||||
|
|
||||||
val () =
|
|
||||||
(* if l2 and r2 are both pressed, then send escape event *)
|
|
||||||
if l2 > 0.3 andalso r2 > 0.3 then
|
|
||||||
InputMailbox.append InputMsg.KEY_ESC
|
|
||||||
else
|
|
||||||
()
|
|
||||||
in
|
|
||||||
()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
fun query () =
|
|
||||||
if getGamepadState () then handleIfJoystickIsPresent ()
|
|
||||||
else (* nothing to do if no gamepad is present *) ()
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user