diff --git a/gameforui.go b/gameforui.go index 1769ff09d..1f75dabcd 100644 --- a/gameforui.go +++ b/gameforui.go @@ -20,6 +20,7 @@ import ( "sync/atomic" "github.com/hajimehoshi/ebiten/v2/internal/atlas" + "github.com/hajimehoshi/ebiten/v2/internal/inputstate" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -94,7 +95,7 @@ func (g *gameForUI) Layout(outsideWidth, outsideHeight float64) (float64, float6 } func (g *gameForUI) UpdateInputState(fn func(*ui.InputState)) { - theInputState.update(fn) + inputstate.Get().Update(fn) } func (g *gameForUI) Update() error { diff --git a/genkeys.go b/genkeys.go index 59a9d6e1d..eea260a2f 100644 --- a/genkeys.go +++ b/genkeys.go @@ -468,10 +468,10 @@ type Key int // Keys. const ( {{range $index, $name := .EbitengineKeyNamesWithoutMods}}Key{{$name}} Key = Key(ui.Key{{$name}}) -{{end}} KeyAlt Key = Key(ui.KeyReserved0) - KeyControl Key = Key(ui.KeyReserved1) - KeyShift Key = Key(ui.KeyReserved2) - KeyMeta Key = Key(ui.KeyReserved3) +{{end}} KeyAlt Key = Key(ui.KeyAlt) + KeyControl Key = Key(ui.KeyControl) + KeyShift Key = Key(ui.KeyShift) + KeyMeta Key = Key(ui.KeyMeta) KeyMax Key = KeyMeta // Keys for backward compatibility. @@ -480,16 +480,6 @@ const ( {{end}} ) -func (k Key) isValid() bool { - switch k { - {{range $name := .EbitengineKeyNamesWithoutOld}}case Key{{$name}}: - return true - {{end}} - default: - return false - } -} - // String returns a string representing the key. // // If k is an undefined key, String returns an empty string. @@ -539,11 +529,11 @@ type Key int const ( {{range $index, $name := .UIKeyNames}}Key{{$name}}{{if eq $index 0}} Key = iota{{end}} -{{end}} KeyReserved0 - KeyReserved1 - KeyReserved2 - KeyReserved3 - KeyMax = KeyReserved3 +{{end}} KeyAlt + KeyControl + KeyShift + KeyMeta + KeyMax = KeyMeta ) func (k Key) String() string { diff --git a/input.go b/input.go index 3ad5d0456..3c7a0893e 100644 --- a/input.go +++ b/input.go @@ -15,11 +15,9 @@ package ebiten import ( - "io/fs" - "sync" - "github.com/hajimehoshi/ebiten/v2/internal/gamepad" "github.com/hajimehoshi/ebiten/v2/internal/gamepaddb" + "github.com/hajimehoshi/ebiten/v2/internal/inputstate" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -36,7 +34,7 @@ import ( // // On Android (ebitenmobile), EbitenView must be focusable to enable to handle keyboard keys. func AppendInputChars(runes []rune) []rune { - return theInputState.appendInputChars(runes) + return inputstate.Get().AppendInputChars(runes) } // InputChars return "printable" runes read from the keyboard at the time Update is called. @@ -58,7 +56,7 @@ func InputChars() []rune { // // On Android (ebitenmobile), EbitenView must be focusable to enable to handle keyboard keys. func IsKeyPressed(key Key) bool { - return theInputState.isKeyPressed(key) + return inputstate.Get().IsKeyPressed(ui.Key(key)) } // KeyName returns a key name for the current keyboard layout. @@ -83,7 +81,7 @@ func KeyName(key Key) string { // // CursorPosition is concurrent-safe. func CursorPosition() (x, y int) { - cx, cy := theInputState.cursorPosition() + cx, cy := inputstate.Get().CursorPosition() return int(cx), int(cy) } @@ -92,7 +90,7 @@ func CursorPosition() (x, y int) { // // Wheel is concurrent-safe. func Wheel() (xoff, yoff float64) { - return theInputState.wheel() + return inputstate.Get().Wheel() } // IsMouseButtonPressed returns a boolean indicating whether mouseButton is pressed. @@ -102,7 +100,7 @@ func Wheel() (xoff, yoff float64) { // // IsMouseButtonPressed is concurrent-safe. func IsMouseButtonPressed(mouseButton MouseButton) bool { - return theInputState.isMouseButtonPressed(mouseButton) + return inputstate.Get().IsMouseButtonPressed(ui.MouseButton(mouseButton)) } // GamepadID represents a gamepad identifier. @@ -350,7 +348,7 @@ func UpdateStandardGamepadLayoutMappings(mappings string) (bool, error) { } // TouchID represents a touch's identifier. -type TouchID int +type TouchID = ui.TouchID // AppendTouchIDs appends the current touch states to touches, and returns the extended buffer. // Giving a slice that already has enough capacity works efficiently. @@ -363,7 +361,7 @@ type TouchID int // // AppendTouchIDs is concurrent-safe. func AppendTouchIDs(touches []TouchID) []TouchID { - return theInputState.appendTouchIDs(touches) + return inputstate.Get().AppendTouchIDs(touches) } // TouchIDs returns the current touch states. @@ -379,99 +377,5 @@ func TouchIDs() []TouchID { // // TouchPosition is concurrent-safe. func TouchPosition(id TouchID) (int, int) { - return theInputState.touchPosition(id) -} - -var theInputState inputState - -type inputState struct { - state ui.InputState - m sync.Mutex -} - -func (i *inputState) update(fn func(*ui.InputState)) { - i.m.Lock() - defer i.m.Unlock() - fn(&i.state) -} - -func (i *inputState) appendInputChars(runes []rune) []rune { - i.m.Lock() - defer i.m.Unlock() - return append(runes, i.state.Runes...) -} - -func (i *inputState) isKeyPressed(key Key) bool { - if !key.isValid() { - return false - } - - i.m.Lock() - defer i.m.Unlock() - - switch key { - case KeyAlt: - return i.state.KeyPressed[ui.KeyAltLeft] || i.state.KeyPressed[ui.KeyAltRight] - case KeyControl: - return i.state.KeyPressed[ui.KeyControlLeft] || i.state.KeyPressed[ui.KeyControlRight] - case KeyShift: - return i.state.KeyPressed[ui.KeyShiftLeft] || i.state.KeyPressed[ui.KeyShiftRight] - case KeyMeta: - return i.state.KeyPressed[ui.KeyMetaLeft] || i.state.KeyPressed[ui.KeyMetaRight] - default: - return i.state.KeyPressed[key] - } -} - -func (i *inputState) cursorPosition() (float64, float64) { - i.m.Lock() - defer i.m.Unlock() - return i.state.CursorX, i.state.CursorY -} - -func (i *inputState) wheel() (float64, float64) { - i.m.Lock() - defer i.m.Unlock() - return i.state.WheelX, i.state.WheelY -} - -func (i *inputState) isMouseButtonPressed(mouseButton MouseButton) bool { - i.m.Lock() - defer i.m.Unlock() - return i.state.MouseButtonPressed[mouseButton] -} - -func (i *inputState) appendTouchIDs(touches []TouchID) []TouchID { - i.m.Lock() - defer i.m.Unlock() - - for _, t := range i.state.Touches { - touches = append(touches, TouchID(t.ID)) - } - return touches -} - -func (i *inputState) touchPosition(id TouchID) (int, int) { - i.m.Lock() - defer i.m.Unlock() - - for _, t := range i.state.Touches { - if id != TouchID(t.ID) { - continue - } - return t.X, t.Y - } - return 0, 0 -} - -func (i *inputState) windowBeingClosed() bool { - i.m.Lock() - defer i.m.Unlock() - return i.state.WindowBeingClosed -} - -func (i *inputState) droppedFiles() fs.FS { - i.m.Lock() - defer i.m.Unlock() - return i.state.DroppedFiles + return inputstate.Get().TouchPosition(ui.TouchID(id)) } diff --git a/internal/inputstate/inputstate.go b/internal/inputstate/inputstate.go new file mode 100644 index 000000000..43b4bbe79 --- /dev/null +++ b/internal/inputstate/inputstate.go @@ -0,0 +1,124 @@ +// Copyright 2025 The Ebitengine 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 inputstate provides APIs to access the input state for the current tick. +// This package is for the ebiten package and the inpututil package. +package inputstate + +import ( + "io/fs" + "sync" + + "github.com/hajimehoshi/ebiten/v2/internal/ui" +) + +var ( + theInputState inputState +) + +type inputState struct { + state ui.InputState + m sync.Mutex +} + +func Get() *inputState { + return &theInputState +} + +func (i *inputState) Update(fn func(*ui.InputState)) { + i.m.Lock() + defer i.m.Unlock() + fn(&i.state) +} + +func (i *inputState) AppendInputChars(runes []rune) []rune { + i.m.Lock() + defer i.m.Unlock() + return append(runes, i.state.Runes...) +} + +func (i *inputState) IsKeyPressed(key ui.Key) bool { + if key < 0 || key > ui.KeyMax { + return false + } + + i.m.Lock() + defer i.m.Unlock() + + switch key { + case ui.KeyAlt: + return i.state.KeyPressed[ui.KeyAltLeft] || i.state.KeyPressed[ui.KeyAltRight] + case ui.KeyControl: + return i.state.KeyPressed[ui.KeyControlLeft] || i.state.KeyPressed[ui.KeyControlRight] + case ui.KeyShift: + return i.state.KeyPressed[ui.KeyShiftLeft] || i.state.KeyPressed[ui.KeyShiftRight] + case ui.KeyMeta: + return i.state.KeyPressed[ui.KeyMetaLeft] || i.state.KeyPressed[ui.KeyMetaRight] + default: + return i.state.KeyPressed[key] + } +} + +func (i *inputState) CursorPosition() (float64, float64) { + i.m.Lock() + defer i.m.Unlock() + return i.state.CursorX, i.state.CursorY +} + +func (i *inputState) Wheel() (float64, float64) { + i.m.Lock() + defer i.m.Unlock() + return i.state.WheelX, i.state.WheelY +} + +func (i *inputState) IsMouseButtonPressed(mouseButton ui.MouseButton) bool { + i.m.Lock() + defer i.m.Unlock() + return i.state.MouseButtonPressed[mouseButton] +} + +func (i *inputState) AppendTouchIDs(touches []ui.TouchID) []ui.TouchID { + i.m.Lock() + defer i.m.Unlock() + + for _, t := range i.state.Touches { + touches = append(touches, ui.TouchID(t.ID)) + } + return touches +} + +func (i *inputState) TouchPosition(id ui.TouchID) (int, int) { + i.m.Lock() + defer i.m.Unlock() + + for _, t := range i.state.Touches { + if id != ui.TouchID(t.ID) { + continue + } + return t.X, t.Y + } + return 0, 0 +} + +func (i *inputState) WindowBeingClosed() bool { + i.m.Lock() + defer i.m.Unlock() + return i.state.WindowBeingClosed +} + +func (i *inputState) DroppedFiles() fs.FS { + i.m.Lock() + defer i.m.Unlock() + return i.state.DroppedFiles +} diff --git a/internal/ui/keys.go b/internal/ui/keys.go index 59bdcab77..a9efb900c 100644 --- a/internal/ui/keys.go +++ b/internal/ui/keys.go @@ -141,11 +141,11 @@ const ( KeySlash KeySpace KeyTab - KeyReserved0 - KeyReserved1 - KeyReserved2 - KeyReserved3 - KeyMax = KeyReserved3 + KeyAlt + KeyControl + KeyShift + KeyMeta + KeyMax = KeyMeta ) func (k Key) String() string { diff --git a/keys.go b/keys.go index e7a1fa82b..aed74a631 100644 --- a/keys.go +++ b/keys.go @@ -148,10 +148,10 @@ const ( KeySlash Key = Key(ui.KeySlash) KeySpace Key = Key(ui.KeySpace) KeyTab Key = Key(ui.KeyTab) - KeyAlt Key = Key(ui.KeyReserved0) - KeyControl Key = Key(ui.KeyReserved1) - KeyShift Key = Key(ui.KeyReserved2) - KeyMeta Key = Key(ui.KeyReserved3) + KeyAlt Key = Key(ui.KeyAlt) + KeyControl Key = Key(ui.KeyControl) + KeyShift Key = Key(ui.KeyShift) + KeyMeta Key = Key(ui.KeyMeta) KeyMax Key = KeyMeta // Keys for backward compatibility. @@ -194,258 +194,6 @@ const ( KeyUp Key = Key(ui.KeyArrowUp) ) -func (k Key) isValid() bool { - switch k { - case KeyA: - return true - case KeyB: - return true - case KeyC: - return true - case KeyD: - return true - case KeyE: - return true - case KeyF: - return true - case KeyG: - return true - case KeyH: - return true - case KeyI: - return true - case KeyJ: - return true - case KeyK: - return true - case KeyL: - return true - case KeyM: - return true - case KeyN: - return true - case KeyO: - return true - case KeyP: - return true - case KeyQ: - return true - case KeyR: - return true - case KeyS: - return true - case KeyT: - return true - case KeyU: - return true - case KeyV: - return true - case KeyW: - return true - case KeyX: - return true - case KeyY: - return true - case KeyZ: - return true - case KeyAlt: - return true - case KeyAltLeft: - return true - case KeyAltRight: - return true - case KeyArrowDown: - return true - case KeyArrowLeft: - return true - case KeyArrowRight: - return true - case KeyArrowUp: - return true - case KeyBackquote: - return true - case KeyBackslash: - return true - case KeyBackspace: - return true - case KeyBracketLeft: - return true - case KeyBracketRight: - return true - case KeyCapsLock: - return true - case KeyComma: - return true - case KeyContextMenu: - return true - case KeyControl: - return true - case KeyControlLeft: - return true - case KeyControlRight: - return true - case KeyDelete: - return true - case KeyDigit0: - return true - case KeyDigit1: - return true - case KeyDigit2: - return true - case KeyDigit3: - return true - case KeyDigit4: - return true - case KeyDigit5: - return true - case KeyDigit6: - return true - case KeyDigit7: - return true - case KeyDigit8: - return true - case KeyDigit9: - return true - case KeyEnd: - return true - case KeyEnter: - return true - case KeyEqual: - return true - case KeyEscape: - return true - case KeyF1: - return true - case KeyF2: - return true - case KeyF3: - return true - case KeyF4: - return true - case KeyF5: - return true - case KeyF6: - return true - case KeyF7: - return true - case KeyF8: - return true - case KeyF9: - return true - case KeyF10: - return true - case KeyF11: - return true - case KeyF12: - return true - case KeyF13: - return true - case KeyF14: - return true - case KeyF15: - return true - case KeyF16: - return true - case KeyF17: - return true - case KeyF18: - return true - case KeyF19: - return true - case KeyF20: - return true - case KeyF21: - return true - case KeyF22: - return true - case KeyF23: - return true - case KeyF24: - return true - case KeyHome: - return true - case KeyInsert: - return true - case KeyIntlBackslash: - return true - case KeyMeta: - return true - case KeyMetaLeft: - return true - case KeyMetaRight: - return true - case KeyMinus: - return true - case KeyNumLock: - return true - case KeyNumpad0: - return true - case KeyNumpad1: - return true - case KeyNumpad2: - return true - case KeyNumpad3: - return true - case KeyNumpad4: - return true - case KeyNumpad5: - return true - case KeyNumpad6: - return true - case KeyNumpad7: - return true - case KeyNumpad8: - return true - case KeyNumpad9: - return true - case KeyNumpadAdd: - return true - case KeyNumpadDecimal: - return true - case KeyNumpadDivide: - return true - case KeyNumpadEnter: - return true - case KeyNumpadEqual: - return true - case KeyNumpadMultiply: - return true - case KeyNumpadSubtract: - return true - case KeyPageDown: - return true - case KeyPageUp: - return true - case KeyPause: - return true - case KeyPeriod: - return true - case KeyPrintScreen: - return true - case KeyQuote: - return true - case KeyScrollLock: - return true - case KeySemicolon: - return true - case KeyShift: - return true - case KeyShiftLeft: - return true - case KeyShiftRight: - return true - case KeySlash: - return true - case KeySpace: - return true - case KeyTab: - return true - - default: - return false - } -} - // String returns a string representing the key. // // If k is an undefined key, String returns an empty string. diff --git a/run.go b/run.go index f75872249..b912873fc 100644 --- a/run.go +++ b/run.go @@ -23,6 +23,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/clock" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/inputstate" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -754,7 +755,7 @@ func toUIRunOptions(options *RunGameOptions) *ui.RunOptions { // // DroppedFiles is concurrent-safe. func DroppedFiles() fs.FS { - return theInputState.droppedFiles() + return inputstate.Get().DroppedFiles() } // Tick returns the current tick count. diff --git a/window.go b/window.go index 0802c117f..8b2928d8c 100644 --- a/window.go +++ b/window.go @@ -18,6 +18,7 @@ import ( "image" "sync/atomic" + "github.com/hajimehoshi/ebiten/v2/internal/inputstate" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -293,7 +294,7 @@ func RestoreWindow() { // // IsWindowBeingClosed is concurrent-safe. func IsWindowBeingClosed() bool { - return theInputState.windowBeingClosed() + return inputstate.Get().WindowBeingClosed() } // SetWindowClosingHandled sets whether the window closing is handled or not on desktops. The default state is false.