diff --git a/backends/internal/input_handler.go b/backends/internal/input_handler.go new file mode 100644 index 0000000..a5fc1b2 --- /dev/null +++ b/backends/internal/input_handler.go @@ -0,0 +1,78 @@ +package internal + +import "github.com/gopxl/pixel/v2" + +type InputState struct { + Mouse pixel.Vec + Buttons [pixel.NumButtons]bool + Repeat [pixel.NumButtons]bool + Scroll pixel.Vec + Typed string +} + +type InputHandler struct { + Prev, Curr, temp InputState + + PressEvents, tempPressEvents [pixel.NumButtons]bool + ReleaseEvents, tempReleaseEvents [pixel.NumButtons]bool + + MouseInsideWindow bool +} + +// SetMousePosition overrides the mouse position +// Called when the mouse is set to a point in the backend Window +func (ih *InputHandler) SetMousePosition(pos pixel.Vec) { + ih.Prev.Mouse = pos + ih.Curr.Mouse = pos + ih.temp.Mouse = pos +} + +// ButtonEvent sets the action state of a button for the next update +func (ih *InputHandler) ButtonEvent(button pixel.Button, action pixel.Action) { + switch action { + case pixel.Press: + ih.tempPressEvents[button] = true + ih.temp.Buttons[button] = true + case pixel.Release: + ih.tempReleaseEvents[button] = true + ih.temp.Buttons[button] = false + case pixel.Repeat: + ih.temp.Repeat[button] = true + } +} + +// MouseMoveEvent sets the mouse position for the next update +func (ih *InputHandler) MouseMoveEvent(pos pixel.Vec) { + ih.temp.Mouse = pos +} + +// MouseScrollEvent adds to the scroll offset for the next update +func (ih *InputHandler) MouseScrollEvent(x, y float64) { + ih.temp.Scroll.X += x + ih.temp.Scroll.Y += y +} + +// MouseEnteredEvent is called when the mouse enters or leaves the window +func (ih *InputHandler) MouseEnteredEvent(entered bool) { + ih.MouseInsideWindow = entered +} + +// CharEvent adds to the typed string for the next update +func (ih *InputHandler) CharEvent(r rune) { + ih.temp.Typed += string(r) +} + +func (ih *InputHandler) Update() { + ih.Prev = ih.Curr + ih.Curr = ih.temp + + ih.PressEvents = ih.tempPressEvents + ih.ReleaseEvents = ih.tempReleaseEvents + + // Clear last frame's temporary status + ih.tempPressEvents = [pixel.NumButtons]bool{} + ih.tempReleaseEvents = [pixel.NumButtons]bool{} + ih.temp.Repeat = [pixel.NumButtons]bool{} + ih.temp.Scroll = pixel.ZV + ih.temp.Typed = "" +} diff --git a/backends/internal/joystick_state.go b/backends/internal/joystick_state.go new file mode 100644 index 0000000..9b40ff0 --- /dev/null +++ b/backends/internal/joystick_state.go @@ -0,0 +1,28 @@ +package internal + +import "github.com/gopxl/pixel/v2" + +type JoystickState struct { + Connected [pixel.NumJoysticks]bool + Name [pixel.NumJoysticks]string + Buttons [pixel.NumJoysticks][]pixel.Action + Axis [pixel.NumJoysticks][]float32 +} + +// Returns if a button on a joystick is down, returning false if the button or joystick is invalid. +func (js *JoystickState) GetButton(joystick pixel.Joystick, button pixel.GamepadButton) bool { + // Check that the joystick and button is valid, return false by default + if js.Buttons[joystick] == nil || int(button) >= len(js.Buttons[joystick]) || button < 0 { + return false + } + return js.Buttons[joystick][button] == pixel.Press +} + +// Returns the value of a joystick axis, returning 0 if the button or joystick is invalid. +func (js *JoystickState) GetAxis(joystick pixel.Joystick, axis pixel.GamepadAxis) float64 { + // Check that the joystick and axis is valid, return 0 by default. + if js.Axis[joystick] == nil || int(axis) >= len(js.Axis[joystick]) || axis < 0 { + return 0 + } + return float64(js.Axis[joystick][axis]) +} diff --git a/backends/opengl/input.go b/backends/opengl/input.go index 2bc5fdf..5e971f3 100644 --- a/backends/opengl/input.go +++ b/backends/opengl/input.go @@ -10,34 +10,34 @@ import ( // Pressed returns whether the Button is currently pressed down. func (w *Window) Pressed(button pixel.Button) bool { - return w.input.Pressed(button) + return w.input.Curr.Buttons[button] } // JustPressed returns whether the Button has been pressed in the last frame. func (w *Window) JustPressed(button pixel.Button) bool { - return w.input.JustPressed(button) + return w.input.PressEvents[button] } // JustReleased returns whether the Button has been released in the last frame. func (w *Window) JustReleased(button pixel.Button) bool { - return w.input.JustReleased(button) + return w.input.ReleaseEvents[button] } // Repeated returns whether a repeat event has been triggered on button. // // Repeat event occurs repeatedly when a button is held down for some time. func (w *Window) Repeated(button pixel.Button) bool { - return w.input.Repeated(button) + return w.input.Curr.Repeat[button] } // MousePosition returns the current mouse position in the Window's Bounds. func (w *Window) MousePosition() pixel.Vec { - return w.input.MousePosition() + return w.input.Curr.Mouse } // MousePreviousPosition returns the previous mouse position in the Window's Bounds. func (w *Window) MousePreviousPosition() pixel.Vec { - return w.input.MousePreviousPosition() + return w.input.Prev.Mouse } // SetMousePosition positions the mouse cursor anywhere within the Window's Bounds. @@ -56,21 +56,21 @@ func (w *Window) SetMousePosition(v pixel.Vec) { // MouseInsideWindow returns true if the mouse position is within the Window's Bounds. func (w *Window) MouseInsideWindow() bool { - return w.input.MouseInsideWindow() + return w.input.MouseInsideWindow } // MouseScroll returns the mouse scroll amount (in both axes) since the last call to Window.Update. func (w *Window) MouseScroll() pixel.Vec { - return w.input.MouseScroll() + return w.input.Curr.Mouse } func (w *Window) MousePreviousScroll() pixel.Vec { - return w.input.MousePreviousScroll() + return w.input.Prev.Mouse } // Typed returns the text typed on the keyboard since the last call to Window.Update. func (w *Window) Typed() string { - return w.input.Typed() + return w.input.Curr.Typed } var actionMapping = map[glfw.Action]pixel.Action{ diff --git a/backends/opengl/joystick.go b/backends/opengl/joystick.go index 8a00ee2..8043dbc 100644 --- a/backends/opengl/joystick.go +++ b/backends/opengl/joystick.go @@ -56,7 +56,7 @@ var gamepadButtonMapping = map[glfw.GamepadButton]pixel.GamepadButton{ // // This API is experimental. func (w *Window) JoystickPresent(js pixel.Joystick) bool { - return w.currJoy.connected[js] + return w.currJoy.Connected[js] } // JoystickName returns the name of the joystick. A disconnected joystick will return an @@ -64,21 +64,21 @@ func (w *Window) JoystickPresent(js pixel.Joystick) bool { // // This API is experimental. func (w *Window) JoystickName(js pixel.Joystick) string { - return w.currJoy.name[js] + return w.currJoy.Name[js] } // JoystickButtonCount returns the number of buttons a connected joystick has. // // This API is experimental. func (w *Window) JoystickButtonCount(js pixel.Joystick) int { - return len(w.currJoy.buttons[js]) + return len(w.currJoy.Buttons[js]) } // JoystickAxisCount returns the number of axes a connected joystick has. // // This API is experimental. func (w *Window) JoystickAxisCount(js pixel.Joystick) int { - return len(w.currJoy.axis[js]) + return len(w.currJoy.Axis[js]) } // JoystickPressed returns whether the joystick Button is currently pressed down. @@ -86,7 +86,7 @@ func (w *Window) JoystickAxisCount(js pixel.Joystick) int { // // This API is experimental. func (w *Window) JoystickPressed(js pixel.Joystick, button pixel.GamepadButton) bool { - return w.currJoy.getButton(js, int(button)) + return w.currJoy.GetButton(js, button) } // JoystickJustPressed returns whether the joystick Button has just been pressed down. @@ -94,7 +94,7 @@ func (w *Window) JoystickPressed(js pixel.Joystick, button pixel.GamepadButton) // // This API is experimental. func (w *Window) JoystickJustPressed(js pixel.Joystick, button pixel.GamepadButton) bool { - return w.currJoy.getButton(js, int(button)) && !w.prevJoy.getButton(js, int(button)) + return w.currJoy.GetButton(js, button) && !w.prevJoy.GetButton(js, button) } // JoystickJustReleased returns whether the joystick Button has just been released up. @@ -102,7 +102,7 @@ func (w *Window) JoystickJustPressed(js pixel.Joystick, button pixel.GamepadButt // // This API is experimental. func (w *Window) JoystickJustReleased(js pixel.Joystick, button pixel.GamepadButton) bool { - return !w.currJoy.getButton(js, int(button)) && w.prevJoy.getButton(js, int(button)) + return !w.currJoy.GetButton(js, button) && w.prevJoy.GetButton(js, button) } // JoystickAxis returns the value of a joystick axis at the last call to Window.Update. @@ -110,7 +110,7 @@ func (w *Window) JoystickJustReleased(js pixel.Joystick, button pixel.GamepadBut // // This API is experimental. func (w *Window) JoystickAxis(js pixel.Joystick, axis pixel.GamepadAxis) float64 { - return w.currJoy.getAxis(js, int(axis)) + return w.currJoy.GetAxis(js, axis) } // Used internally during Window.UpdateInput to update the state of the joysticks. @@ -122,30 +122,30 @@ func (w *Window) updateJoystickInput() { } // Determine and store if the joystick was connected joystickPresent := joystick.Present() - w.tempJoy.connected[js] = joystickPresent + w.tempJoy.Connected[js] = joystickPresent if joystickPresent { if joystick.IsGamepad() { gamepadInputs := joystick.GetGamepadState() - w.tempJoy.buttons[js] = convertGamepadButtons(gamepadInputs.Buttons) - w.tempJoy.axis[js] = gamepadInputs.Axes[:] + w.tempJoy.Buttons[js] = convertGamepadButtons(gamepadInputs.Buttons) + w.tempJoy.Axis[js] = gamepadInputs.Axes[:] } else { - w.tempJoy.buttons[js] = convertJoystickButtons(joystick.GetButtons()) - w.tempJoy.axis[js] = joystick.GetAxes() + w.tempJoy.Buttons[js] = convertJoystickButtons(joystick.GetButtons()) + w.tempJoy.Axis[js] = joystick.GetAxes() } - if !w.currJoy.connected[js] { + if !w.currJoy.Connected[js] { // The joystick was recently connected, we get the name - w.tempJoy.name[js] = joystick.GetName() + w.tempJoy.Name[js] = joystick.GetName() } else { // Use the name from the previous one - w.tempJoy.name[js] = w.currJoy.name[js] + w.tempJoy.Name[js] = w.currJoy.Name[js] } } else { - w.tempJoy.buttons[js] = []pixel.Action{} - w.tempJoy.axis[js] = []float32{} - w.tempJoy.name[js] = "" + w.tempJoy.Buttons[js] = []pixel.Action{} + w.tempJoy.Axis[js] = []float32{} + w.tempJoy.Name[js] = "" } } @@ -153,31 +153,6 @@ func (w *Window) updateJoystickInput() { w.currJoy = w.tempJoy } -type joystickState struct { - connected [pixel.NumJoysticks]bool - name [pixel.NumJoysticks]string - buttons [pixel.NumJoysticks][]pixel.Action - axis [pixel.NumJoysticks][]float32 -} - -// Returns if a button on a joystick is down, returning false if the button or joystick is invalid. -func (js *joystickState) getButton(joystick pixel.Joystick, button int) bool { - // Check that the joystick and button is valid, return false by default - if js.buttons[joystick] == nil || button >= len(js.buttons[joystick]) || button < 0 { - return false - } - return js.buttons[joystick][byte(button)] == pixel.Press -} - -// Returns the value of a joystick axis, returning 0 if the button or joystick is invalid. -func (js *joystickState) getAxis(joystick pixel.Joystick, axis int) float64 { - // Check that the joystick and axis is valid, return 0 by default. - if js.axis[joystick] == nil || axis >= len(js.axis[joystick]) || axis < 0 { - return 0 - } - return float64(js.axis[joystick][axis]) -} - // Convert buttons from a GLFW gamepad mapping to pixel format func convertGamepadButtons(buttons [glfw.ButtonLast + 1]glfw.Action) []pixel.Action { pixelButtons := make([]pixel.Action, pixel.NumGamepadButtons) diff --git a/backends/opengl/window.go b/backends/opengl/window.go index 13dd7e7..3cb5af3 100644 --- a/backends/opengl/window.go +++ b/backends/opengl/window.go @@ -11,6 +11,7 @@ import ( "github.com/gopxl/glhf/v2" "github.com/gopxl/mainthread/v2" "github.com/gopxl/pixel/v2" + "github.com/gopxl/pixel/v2/backends/internal" "github.com/pkg/errors" ) @@ -96,8 +97,8 @@ type Window struct { xpos, ypos, width, height int } - input *pixel.InputHandler - prevJoy, currJoy, tempJoy joystickState + input internal.InputHandler + prevJoy, currJoy, tempJoy internal.JoystickState buttonCallback func(win *Window, button pixel.Button, action pixel.Action) charCallback func(win *Window, r rune) @@ -117,7 +118,7 @@ func NewWindow(cfg WindowConfig) (*Window, error) { false: glfw.False, } - w := &Window{bounds: cfg.Bounds, cursorVisible: true, input: &pixel.InputHandler{}} + w := &Window{bounds: cfg.Bounds, cursorVisible: true} flag := false for _, v := range []int{0, 2, 4, 8, 16} { diff --git a/input.go b/input.go index e2e4951..1f0cff6 100644 --- a/input.go +++ b/input.go @@ -1,130 +1,5 @@ package pixel -type InputHandler struct { - prevInp, currInp, tempInp struct { - mouse Vec - buttons [NumButtons]bool - repeat [NumButtons]bool - scroll Vec - typed string - } - - pressEvents, tempPressEvents [NumButtons]bool - releaseEvents, tempReleaseEvents [NumButtons]bool - - mouseInsideWindow bool -} - -// Pressed returns whether the Button is currently pressed down. -func (ih *InputHandler) Pressed(button Button) bool { - return ih.currInp.buttons[button] -} - -// JustPressed returns whether the Button has been pressed in the last frame. -func (ih *InputHandler) JustPressed(button Button) bool { - return ih.pressEvents[button] -} - -// JustReleased returns whether the Button has been released in the last frame. -func (ih *InputHandler) JustReleased(button Button) bool { - return ih.releaseEvents[button] -} - -// Repeated returns whether a repeat event has been triggered on button. -// -// Repeat event occurs repeatedly when a button is held down for some time. -func (ih *InputHandler) Repeated(button Button) bool { - return ih.currInp.repeat[button] -} - -// MousePosition returns the current mouse position in the Window's Bounds -func (ih *InputHandler) MousePosition() Vec { - return ih.currInp.mouse -} - -// MousePreviousPosition returns the previous mouse position in the Window's Bounds -func (ih *InputHandler) MousePreviousPosition() Vec { - return ih.prevInp.mouse -} - -// MouseScroll returns the mouse scroll amount (in both axes) since the last update -func (ih *InputHandler) MouseScroll() Vec { - return ih.currInp.scroll -} - -// MousePreviousScroll returns the previous mouse scroll amount (in both axes) -func (ih *InputHandler) MousePreviousScroll() Vec { - return ih.prevInp.scroll -} - -// MouseInsideWindow returns true if the mouse position is within the Window's Bounds -func (ih *InputHandler) MouseInsideWindow() bool { - return ih.mouseInsideWindow -} - -// Typed returns the text typed on the keyboard since the last update -func (ih *InputHandler) Typed() string { - return ih.currInp.typed -} - -// SetMousePosition overrides the mouse position -// Called when the mouse is set to a point in the backend Window -func (ih *InputHandler) SetMousePosition(pos Vec) { - ih.prevInp.mouse = pos - ih.currInp.mouse = pos - ih.tempInp.mouse = pos -} - -// ButtonEvent sets the action state of a button for the next update -func (ih *InputHandler) ButtonEvent(button Button, action Action) { - switch action { - case Press: - ih.tempPressEvents[button] = true - ih.tempInp.buttons[button] = true - case Release: - ih.tempReleaseEvents[button] = true - ih.tempInp.buttons[button] = false - case Repeat: - ih.tempInp.repeat[button] = true - } -} - -// MouseMoveEvent sets the mouse position for the next update -func (ih *InputHandler) MouseMoveEvent(pos Vec) { - ih.tempInp.mouse = pos -} - -// MouseScrollEvent adds to the scroll offset for the next update -func (ih *InputHandler) MouseScrollEvent(x, y float64) { - ih.tempInp.scroll.X += x - ih.tempInp.scroll.Y += y -} - -// MouseEnteredEvent is called when the mouse enters or leaves the window -func (ih *InputHandler) MouseEnteredEvent(entered bool) { - ih.mouseInsideWindow = entered -} - -// CharEvent adds to the typed string for the next update -func (ih *InputHandler) CharEvent(r rune) { - ih.tempInp.typed += string(r) -} - -func (ih *InputHandler) Update() { - ih.prevInp = ih.currInp - ih.currInp = ih.tempInp - - ih.pressEvents = ih.tempPressEvents - ih.releaseEvents = ih.tempReleaseEvents - - // Clear last frame's temporary status - ih.tempPressEvents = [NumButtons]bool{} - ih.tempReleaseEvents = [NumButtons]bool{} - ih.tempInp.repeat = [NumButtons]bool{} - ih.tempInp.scroll = ZV - ih.tempInp.typed = "" -} - type Action int // String returns a human-readable string describing the Button.