ebiten/inpututil/inpututil.go

331 lines
9.3 KiB
Go
Raw Normal View History

2018-02-04 09:51:03 +01:00
// Copyright 2018 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package inpututil provides utility functions of input like keyboard or mouse.
//
// Note: This package is experimental and API might be changed.
package inpututil
import (
"sort"
"sync"
2018-02-04 09:51:03 +01:00
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal/hooks"
)
type inputState struct {
2018-04-29 19:19:53 +02:00
keyDurations map[ebiten.Key]int
prevKeyDurations map[ebiten.Key]int
2018-04-29 19:19:53 +02:00
mouseButtonDurations map[ebiten.MouseButton]int
prevMouseButtonDurations map[ebiten.MouseButton]int
gamepadIDs map[int]struct{}
prevGamepadIDs map[int]struct{}
2018-04-29 19:19:53 +02:00
gamepadButtonDurations map[int]map[ebiten.GamepadButton]int
prevGamepadButtonDurations map[int]map[ebiten.GamepadButton]int
2018-04-29 19:19:53 +02:00
touchDurations map[int]int
prevTouchDurations map[int]int
2018-02-04 09:51:03 +01:00
m sync.RWMutex
}
var theInputState = &inputState{
2018-04-29 19:19:53 +02:00
keyDurations: map[ebiten.Key]int{},
prevKeyDurations: map[ebiten.Key]int{},
2018-04-29 19:19:53 +02:00
mouseButtonDurations: map[ebiten.MouseButton]int{},
prevMouseButtonDurations: map[ebiten.MouseButton]int{},
gamepadIDs: map[int]struct{}{},
prevGamepadIDs: map[int]struct{}{},
2018-04-29 19:19:53 +02:00
gamepadButtonDurations: map[int]map[ebiten.GamepadButton]int{},
prevGamepadButtonDurations: map[int]map[ebiten.GamepadButton]int{},
2018-04-29 19:19:53 +02:00
touchDurations: map[int]int{},
prevTouchDurations: map[int]int{},
2018-02-04 09:51:03 +01:00
}
func init() {
hooks.AppendHookOnBeforeUpdate(func() error {
2018-02-04 09:51:03 +01:00
theInputState.update()
return nil
})
}
func (i *inputState) update() {
i.m.Lock()
defer i.m.Unlock()
// Keyboard
for k := ebiten.Key(0); k <= ebiten.KeyMax; k++ {
2018-04-29 19:19:53 +02:00
i.prevKeyDurations[k] = i.keyDurations[k]
2018-02-04 09:51:03 +01:00
if ebiten.IsKeyPressed(k) {
2018-04-29 19:19:53 +02:00
i.keyDurations[k]++
2018-02-04 09:51:03 +01:00
} else {
2018-04-29 19:19:53 +02:00
i.keyDurations[k] = 0
2018-02-04 09:51:03 +01:00
}
}
// Mouse
2018-02-04 09:51:03 +01:00
for _, b := range []ebiten.MouseButton{
ebiten.MouseButtonLeft,
ebiten.MouseButtonRight,
ebiten.MouseButtonMiddle,
} {
2018-04-29 19:19:53 +02:00
i.prevMouseButtonDurations[b] = i.mouseButtonDurations[b]
2018-02-04 09:51:03 +01:00
if ebiten.IsMouseButtonPressed(b) {
2018-04-29 19:19:53 +02:00
i.mouseButtonDurations[b]++
2018-02-04 09:51:03 +01:00
} else {
2018-04-29 19:19:53 +02:00
i.mouseButtonDurations[b] = 0
2018-02-04 09:51:03 +01:00
}
}
// Gamepads
// Copy the gamepad IDs.
i.prevGamepadIDs = map[int]struct{}{}
for id := range i.gamepadIDs {
i.prevGamepadIDs[id] = struct{}{}
}
// Copy the gamepad button durations.
i.prevGamepadButtonDurations = map[int]map[ebiten.GamepadButton]int{}
for id, ds := range i.gamepadButtonDurations {
i.prevGamepadButtonDurations[id] = map[ebiten.GamepadButton]int{}
for b, d := range ds {
i.prevGamepadButtonDurations[id][b] = d
}
}
i.gamepadIDs = map[int]struct{}{}
2018-02-04 09:51:03 +01:00
for _, id := range ebiten.GamepadIDs() {
i.gamepadIDs[id] = struct{}{}
2018-04-29 19:19:53 +02:00
if _, ok := i.gamepadButtonDurations[id]; !ok {
i.gamepadButtonDurations[id] = map[ebiten.GamepadButton]int{}
2018-02-04 09:51:03 +01:00
}
n := ebiten.GamepadButtonNum(id)
for b := ebiten.GamepadButton(0); b < ebiten.GamepadButton(n); b++ {
if ebiten.IsGamepadButtonPressed(id, b) {
2018-04-29 19:19:53 +02:00
i.gamepadButtonDurations[id][b]++
2018-02-04 09:51:03 +01:00
} else {
2018-04-29 19:19:53 +02:00
i.gamepadButtonDurations[id][b] = 0
2018-02-04 09:51:03 +01:00
}
}
}
idsToDelete := []int{}
2018-04-29 19:19:53 +02:00
for id := range i.gamepadButtonDurations {
if _, ok := i.gamepadIDs[id]; !ok {
idsToDelete = append(idsToDelete, id)
}
}
for _, id := range idsToDelete {
2018-04-29 19:19:53 +02:00
delete(i.gamepadButtonDurations, id)
}
2018-02-04 09:51:03 +01:00
// Touches
ids := map[int]struct{}{}
// Copy the touch durations.
i.prevTouchDurations = map[int]int{}
for id := range i.touchDurations {
i.prevTouchDurations[id] = i.touchDurations[id]
}
for _, id := range ebiten.TouchIDs() {
ids[id] = struct{}{}
i.touchDurations[id]++
2018-02-04 09:51:03 +01:00
}
idsToDelete = []int{}
2018-04-29 19:19:53 +02:00
for id := range i.touchDurations {
2018-02-04 09:51:03 +01:00
if _, ok := ids[id]; !ok {
idsToDelete = append(idsToDelete, id)
}
}
for _, id := range idsToDelete {
2018-04-29 19:19:53 +02:00
delete(i.touchDurations, id)
2018-02-04 09:51:03 +01:00
}
}
// IsKeyJustPressed returns a boolean value indicating
// whether the given key is pressed just in the current frame.
//
// IsKeyJustPressed is concurrent safe.
2018-02-04 09:51:03 +01:00
func IsKeyJustPressed(key ebiten.Key) bool {
return KeyPressDuration(key) == 1
}
// IsKeyJustReleased returns a boolean value indicating
// whether the given key is released just in the current frame.
//
// IsKeyJustReleased is concurrent safe.
func IsKeyJustReleased(key ebiten.Key) bool {
theInputState.m.RLock()
2018-04-29 19:19:53 +02:00
r := theInputState.keyDurations[key] == 0 && theInputState.prevKeyDurations[key] > 0
theInputState.m.RUnlock()
return r
}
2018-02-04 09:51:03 +01:00
// KeyPressDuration returns how long the key is pressed in frames.
//
// KeyPressDuration is concurrent safe.
2018-02-04 09:51:03 +01:00
func KeyPressDuration(key ebiten.Key) int {
theInputState.m.RLock()
2018-04-29 19:19:53 +02:00
s := theInputState.keyDurations[key]
2018-02-04 09:51:03 +01:00
theInputState.m.RUnlock()
return s
}
// IsMouseButtonJustPressed returns a boolean value indicating
// whether the given mouse button is pressed just in the current frame.
//
// IsMouseButtonJustPressed is concurrent safe.
2018-02-04 09:51:03 +01:00
func IsMouseButtonJustPressed(button ebiten.MouseButton) bool {
return MouseButtonPressDuration(button) == 1
}
// IsMouseButtonJustReleased returns a boolean value indicating
// whether the given mouse button is released just in the current frame.
//
// IsMouseButtonJustReleased is concurrent safe.
func IsMouseButtonJustReleased(button ebiten.MouseButton) bool {
theInputState.m.RLock()
2018-04-29 19:19:53 +02:00
r := theInputState.mouseButtonDurations[button] == 0 &&
theInputState.prevMouseButtonDurations[button] > 0
theInputState.m.RUnlock()
return r
}
2018-02-04 09:51:03 +01:00
// MouseButtonPressDuration returns how long the mouse button is pressed in frames.
//
// MouseButtonPressDuration is concurrent safe.
2018-02-04 09:51:03 +01:00
func MouseButtonPressDuration(button ebiten.MouseButton) int {
theInputState.m.RLock()
2018-04-29 19:19:53 +02:00
s := theInputState.mouseButtonDurations[button]
2018-02-04 09:51:03 +01:00
theInputState.m.RUnlock()
return s
}
2018-04-29 19:52:56 +02:00
// JustConnectedGamepadIDs returns gamepad IDs that are connected just in the current frame.
//
// JustConnectedGamepadIDs might return nil when there is no connected gamepad.
//
2018-04-29 19:52:56 +02:00
// JustConnectedGamepadIDs is concurrent safe.
func JustConnectedGamepadIDs() []int {
var ids []int
theInputState.m.RLock()
for id := range theInputState.gamepadIDs {
if _, ok := theInputState.prevGamepadIDs[id]; !ok {
ids = append(ids, id)
}
}
theInputState.m.RUnlock()
sort.Ints(ids)
return ids
}
// IsGamepadJustDisconnected returns a boolean value indicating
// whether the gamepad of the given id is released just in the current frame.
2018-04-29 19:52:56 +02:00
//
// IsGamepadJustDisconnected is concurrent safe.
func IsGamepadJustDisconnected(id int) bool {
theInputState.m.RLock()
_, prev := theInputState.prevGamepadIDs[id]
_, current := theInputState.gamepadIDs[id]
theInputState.m.RUnlock()
return prev && !current
}
2018-02-04 09:51:03 +01:00
// IsGamepadButtonJustPressed returns a boolean value indicating
// whether the given gamepad button of the gamepad id is pressed just in the current frame.
//
// IsGamepadButtonJustPressed is concurrent safe.
2018-02-04 09:51:03 +01:00
func IsGamepadButtonJustPressed(id int, button ebiten.GamepadButton) bool {
return GamepadButtonPressDuration(id, button) == 1
}
// IsGamepadButtonJustReleased returns a boolean value indicating
// whether the given gamepad button of the gamepad id is released just in the current frame.
//
// IsGamepadButtonJustReleased is concurrent safe.
func IsGamepadButtonJustReleased(id int, button ebiten.GamepadButton) bool {
theInputState.m.RLock()
prev := 0
2018-04-29 19:19:53 +02:00
if _, ok := theInputState.prevGamepadButtonDurations[id]; ok {
prev = theInputState.prevGamepadButtonDurations[id][button]
}
current := 0
2018-04-29 19:19:53 +02:00
if _, ok := theInputState.gamepadButtonDurations[id]; ok {
current = theInputState.gamepadButtonDurations[id][button]
}
theInputState.m.RUnlock()
return current == 0 && prev > 0
}
2018-02-04 09:51:03 +01:00
// GamepadButtonPressDuration returns how long the gamepad button of the gamepad id is pressed in frames.
//
// GamepadButtonPressDuration is concurrent safe.
2018-02-04 09:51:03 +01:00
func GamepadButtonPressDuration(id int, button ebiten.GamepadButton) int {
theInputState.m.RLock()
s := 0
2018-04-29 19:19:53 +02:00
if _, ok := theInputState.gamepadButtonDurations[id]; ok {
s = theInputState.gamepadButtonDurations[id][button]
2018-02-04 09:51:03 +01:00
}
theInputState.m.RUnlock()
return s
}
// JustPressedTouchIDs returns touch IDs that are created just in the current frame.
//
// JustPressedTouchIDs might return nil when there is not touch.
//
// JustPressedTouchIDs is concurrent safe.
func JustPressedTouchIDs() []int {
var ids []int
theInputState.m.RLock()
2018-04-29 19:19:53 +02:00
for id, s := range theInputState.touchDurations {
if s == 1 {
ids = append(ids, id)
}
}
theInputState.m.RUnlock()
sort.Ints(ids)
return ids
2018-02-04 09:51:03 +01:00
}
// IsTouchJustReleased returns a boolean value indicating
// whether the given touch is released just in the current frame.
//
// IsTouchJustReleased is concurrent safe.
func IsTouchJustReleased(id int) bool {
theInputState.m.RLock()
2018-04-29 19:19:53 +02:00
r := theInputState.touchDurations[id] == 0 && theInputState.prevTouchDurations[id] > 0
theInputState.m.RUnlock()
return r
}
// TouchPressDuration returns how long the touch remains in frames.
//
// TouchPressDuration is concurrent safe.
func TouchPressDuration(id int) int {
2018-02-04 09:51:03 +01:00
theInputState.m.RLock()
2018-04-29 19:19:53 +02:00
s := theInputState.touchDurations[id]
2018-02-04 09:51:03 +01:00
theInputState.m.RUnlock()
return s
}