Add input package

This commit is contained in:
Hajime Hoshi 2018-04-01 23:20:45 +09:00
parent 861c737495
commit 4de2dc0240
19 changed files with 346 additions and 299 deletions

View File

@ -15,7 +15,7 @@
package ebiten package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/ui" "github.com/hajimehoshi/ebiten/internal/input"
) )
// A GamepadButton represents a gamepad button. // A GamepadButton represents a gamepad button.
@ -23,37 +23,37 @@ type GamepadButton int
// GamepadButtons // GamepadButtons
const ( const (
GamepadButton0 GamepadButton = GamepadButton(ui.GamepadButton0) GamepadButton0 GamepadButton = GamepadButton(input.GamepadButton0)
GamepadButton1 GamepadButton = GamepadButton(ui.GamepadButton1) GamepadButton1 GamepadButton = GamepadButton(input.GamepadButton1)
GamepadButton2 GamepadButton = GamepadButton(ui.GamepadButton2) GamepadButton2 GamepadButton = GamepadButton(input.GamepadButton2)
GamepadButton3 GamepadButton = GamepadButton(ui.GamepadButton3) GamepadButton3 GamepadButton = GamepadButton(input.GamepadButton3)
GamepadButton4 GamepadButton = GamepadButton(ui.GamepadButton4) GamepadButton4 GamepadButton = GamepadButton(input.GamepadButton4)
GamepadButton5 GamepadButton = GamepadButton(ui.GamepadButton5) GamepadButton5 GamepadButton = GamepadButton(input.GamepadButton5)
GamepadButton6 GamepadButton = GamepadButton(ui.GamepadButton6) GamepadButton6 GamepadButton = GamepadButton(input.GamepadButton6)
GamepadButton7 GamepadButton = GamepadButton(ui.GamepadButton7) GamepadButton7 GamepadButton = GamepadButton(input.GamepadButton7)
GamepadButton8 GamepadButton = GamepadButton(ui.GamepadButton8) GamepadButton8 GamepadButton = GamepadButton(input.GamepadButton8)
GamepadButton9 GamepadButton = GamepadButton(ui.GamepadButton9) GamepadButton9 GamepadButton = GamepadButton(input.GamepadButton9)
GamepadButton10 GamepadButton = GamepadButton(ui.GamepadButton10) GamepadButton10 GamepadButton = GamepadButton(input.GamepadButton10)
GamepadButton11 GamepadButton = GamepadButton(ui.GamepadButton11) GamepadButton11 GamepadButton = GamepadButton(input.GamepadButton11)
GamepadButton12 GamepadButton = GamepadButton(ui.GamepadButton12) GamepadButton12 GamepadButton = GamepadButton(input.GamepadButton12)
GamepadButton13 GamepadButton = GamepadButton(ui.GamepadButton13) GamepadButton13 GamepadButton = GamepadButton(input.GamepadButton13)
GamepadButton14 GamepadButton = GamepadButton(ui.GamepadButton14) GamepadButton14 GamepadButton = GamepadButton(input.GamepadButton14)
GamepadButton15 GamepadButton = GamepadButton(ui.GamepadButton15) GamepadButton15 GamepadButton = GamepadButton(input.GamepadButton15)
GamepadButton16 GamepadButton = GamepadButton(ui.GamepadButton16) GamepadButton16 GamepadButton = GamepadButton(input.GamepadButton16)
GamepadButton17 GamepadButton = GamepadButton(ui.GamepadButton17) GamepadButton17 GamepadButton = GamepadButton(input.GamepadButton17)
GamepadButton18 GamepadButton = GamepadButton(ui.GamepadButton18) GamepadButton18 GamepadButton = GamepadButton(input.GamepadButton18)
GamepadButton19 GamepadButton = GamepadButton(ui.GamepadButton19) GamepadButton19 GamepadButton = GamepadButton(input.GamepadButton19)
GamepadButton20 GamepadButton = GamepadButton(ui.GamepadButton20) GamepadButton20 GamepadButton = GamepadButton(input.GamepadButton20)
GamepadButton21 GamepadButton = GamepadButton(ui.GamepadButton21) GamepadButton21 GamepadButton = GamepadButton(input.GamepadButton21)
GamepadButton22 GamepadButton = GamepadButton(ui.GamepadButton22) GamepadButton22 GamepadButton = GamepadButton(input.GamepadButton22)
GamepadButton23 GamepadButton = GamepadButton(ui.GamepadButton23) GamepadButton23 GamepadButton = GamepadButton(input.GamepadButton23)
GamepadButton24 GamepadButton = GamepadButton(ui.GamepadButton24) GamepadButton24 GamepadButton = GamepadButton(input.GamepadButton24)
GamepadButton25 GamepadButton = GamepadButton(ui.GamepadButton25) GamepadButton25 GamepadButton = GamepadButton(input.GamepadButton25)
GamepadButton26 GamepadButton = GamepadButton(ui.GamepadButton26) GamepadButton26 GamepadButton = GamepadButton(input.GamepadButton26)
GamepadButton27 GamepadButton = GamepadButton(ui.GamepadButton27) GamepadButton27 GamepadButton = GamepadButton(input.GamepadButton27)
GamepadButton28 GamepadButton = GamepadButton(ui.GamepadButton28) GamepadButton28 GamepadButton = GamepadButton(input.GamepadButton28)
GamepadButton29 GamepadButton = GamepadButton(ui.GamepadButton29) GamepadButton29 GamepadButton = GamepadButton(input.GamepadButton29)
GamepadButton30 GamepadButton = GamepadButton(ui.GamepadButton30) GamepadButton30 GamepadButton = GamepadButton(input.GamepadButton30)
GamepadButton31 GamepadButton = GamepadButton(ui.GamepadButton31) GamepadButton31 GamepadButton = GamepadButton(input.GamepadButton31)
GamepadButtonMax GamepadButton = GamepadButton31 GamepadButtonMax GamepadButton = GamepadButton31
) )

View File

@ -137,7 +137,7 @@ const ebitenKeysTmpl = `{{.License}}
package ebiten package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/ui" "github.com/hajimehoshi/ebiten/internal/input"
) )
// A Key represents a keyboard key. // A Key represents a keyboard key.
@ -147,16 +147,16 @@ type Key int
// Keys // Keys
const ( const (
{{range $index, $name := .KeyNames}}Key{{$name}} Key = Key(ui.Key{{$name}}) {{range $index, $name := .KeyNames}}Key{{$name}} Key = Key(input.Key{{$name}})
{{end}} KeyMax Key = Key{{.LastKeyName}} {{end}} KeyMax Key = Key{{.LastKeyName}}
) )
` `
const uiKeysTmpl = `{{.License}} const inputKeysTmpl = `{{.License}}
{{.DoNotEdit}} {{.DoNotEdit}}
package ui package input
type Key int type Key int
@ -166,13 +166,13 @@ const (
) )
` `
const uiKeysGlfwTmpl = `{{.License}} const inputKeysGlfwTmpl = `{{.License}}
{{.DoNotEdit}} {{.DoNotEdit}}
{{.BuildTag}} {{.BuildTag}}
package ui package input
import ( import (
glfw "github.com/go-gl/glfw/v3.2/glfw" glfw "github.com/go-gl/glfw/v3.2/glfw"
@ -190,13 +190,13 @@ var glfwKeyCodeToKey = map[glfw.Key]Key{
} }
` `
const uiKeysJSTmpl = `{{.License}} const inputKeysJSTmpl = `{{.License}}
{{.DoNotEdit}} {{.DoNotEdit}}
{{.BuildTag}} {{.BuildTag}}
package ui package input
var keyToCodes = map[Key][]string{ var keyToCodes = map[Key][]string{
{{range $name, $codes := .NameToCodes}}Key{{$name}}: []string{ {{range $name, $codes := .NameToCodes}}Key{{$name}}: []string{
@ -322,10 +322,10 @@ func main() {
sort.Strings(codes) sort.Strings(codes)
for path, tmpl := range map[string]string{ for path, tmpl := range map[string]string{
"keys.go": ebitenKeysTmpl, "keys.go": ebitenKeysTmpl,
"internal/ui/keys.go": uiKeysTmpl, "internal/input/keys.go": inputKeysTmpl,
"internal/ui/keys_glfw.go": uiKeysGlfwTmpl, "internal/input/keys_glfw.go": inputKeysGlfwTmpl,
"internal/ui/keys_js.go": uiKeysJSTmpl, "internal/input/keys_js.go": inputKeysJSTmpl,
} { } {
f, err := os.Create(path) f, err := os.Create(path)
if err != nil { if err != nil {
@ -340,12 +340,12 @@ func main() {
// Pass the build tag and extract this in the template to make `go vet` happy. // Pass the build tag and extract this in the template to make `go vet` happy.
buildTag := "" buildTag := ""
switch path { switch path {
case "internal/ui/keys_glfw.go": case "internal/input/keys_glfw.go":
buildTag = "// +build darwin freebsd linux windows" + buildTag = "// +build darwin freebsd linux windows" +
"\n// +build !js" + "\n// +build !js" +
"\n// +build !android" + "\n// +build !android" +
"\n// +build !ios" "\n// +build !ios"
case "internal/ui/keys_js.go": case "internal/input/keys_js.go":
buildTag = "// +build js" buildTag = "// +build js"
} }
// NOTE: According to godoc, maps are automatically sorted by key. // NOTE: According to godoc, maps are automatically sorted by key.

View File

@ -15,6 +15,7 @@
package ebiten package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/ui" "github.com/hajimehoshi/ebiten/internal/ui"
) )
@ -28,7 +29,7 @@ import (
// //
// This function is concurrent-safe. // This function is concurrent-safe.
func InputChars() []rune { func InputChars() []rune {
rb := ui.CurrentInput().RuneBuffer() rb := input.Get().RuneBuffer()
return append(make([]rune, 0, len(rb)), rb...) return append(make([]rune, 0, len(rb)), rb...)
} }
@ -36,14 +37,14 @@ func InputChars() []rune {
// //
// This function is concurrent-safe. // This function is concurrent-safe.
func IsKeyPressed(key Key) bool { func IsKeyPressed(key Key) bool {
return ui.CurrentInput().IsKeyPressed(ui.Key(key)) return input.Get().IsKeyPressed(input.Key(key))
} }
// CursorPosition returns a position of a mouse cursor. // CursorPosition returns a position of a mouse cursor.
// //
// This function is concurrent-safe. // This function is concurrent-safe.
func CursorPosition() (x, y int) { func CursorPosition() (x, y int) {
return ui.CurrentInput().CursorPosition() return ui.AdjustedCursorPosition()
} }
// IsMouseButtonPressed returns a boolean indicating whether mouseButton is pressed. // IsMouseButtonPressed returns a boolean indicating whether mouseButton is pressed.
@ -53,7 +54,7 @@ func CursorPosition() (x, y int) {
// Note that touch events not longer affect this function's result as of 1.4.0-alpha. // Note that touch events not longer affect this function's result as of 1.4.0-alpha.
// Use Touches instead. // Use Touches instead.
func IsMouseButtonPressed(mouseButton MouseButton) bool { func IsMouseButtonPressed(mouseButton MouseButton) bool {
return ui.CurrentInput().IsMouseButtonPressed(ui.MouseButton(mouseButton)) return input.Get().IsMouseButtonPressed(input.MouseButton(mouseButton))
} }
// GamepadIDs returns a slice indicating available gamepad IDs. // GamepadIDs returns a slice indicating available gamepad IDs.
@ -62,7 +63,7 @@ func IsMouseButtonPressed(mouseButton MouseButton) bool {
// //
// This function always returns an empty slice on mobiles. // This function always returns an empty slice on mobiles.
func GamepadIDs() []int { func GamepadIDs() []int {
return ui.CurrentInput().GamepadIDs() return input.Get().GamepadIDs()
} }
// GamepadAxisNum returns the number of axes of the gamepad (id). // GamepadAxisNum returns the number of axes of the gamepad (id).
@ -71,7 +72,7 @@ func GamepadIDs() []int {
// //
// This function always returns 0 on mobiles. // This function always returns 0 on mobiles.
func GamepadAxisNum(id int) int { func GamepadAxisNum(id int) int {
return ui.CurrentInput().GamepadAxisNum(id) return input.Get().GamepadAxisNum(id)
} }
// GamepadAxis returns the float value [-1.0 - 1.0] of the given gamepad (id)'s axis (axis). // GamepadAxis returns the float value [-1.0 - 1.0] of the given gamepad (id)'s axis (axis).
@ -80,7 +81,7 @@ func GamepadAxisNum(id int) int {
// //
// This function always returns 0 on mobiles. // This function always returns 0 on mobiles.
func GamepadAxis(id int, axis int) float64 { func GamepadAxis(id int, axis int) float64 {
return ui.CurrentInput().GamepadAxis(id, axis) return input.Get().GamepadAxis(id, axis)
} }
// GamepadButtonNum returns the number of the buttons of the given gamepad (id). // GamepadButtonNum returns the number of the buttons of the given gamepad (id).
@ -89,7 +90,7 @@ func GamepadAxis(id int, axis int) float64 {
// //
// This function always returns 0 on mobiles. // This function always returns 0 on mobiles.
func GamepadButtonNum(id int) int { func GamepadButtonNum(id int) int {
return ui.CurrentInput().GamepadButtonNum(id) return input.Get().GamepadButtonNum(id)
} }
// IsGamepadButtonPressed returns the boolean indicating the given button of the gamepad (id) is pressed or not. // IsGamepadButtonPressed returns the boolean indicating the given button of the gamepad (id) is pressed or not.
@ -102,7 +103,7 @@ func GamepadButtonNum(id int) int {
// //
// This function always returns false on mobiles. // This function always returns false on mobiles.
func IsGamepadButtonPressed(id int, button GamepadButton) bool { func IsGamepadButtonPressed(id int, button GamepadButton) bool {
return ui.CurrentInput().IsGamepadButtonPressed(id, ui.GamepadButton(button)) return input.Get().IsGamepadButtonPressed(id, input.GamepadButton(button))
} }
// Touch represents a touch state. // Touch represents a touch state.
@ -118,7 +119,7 @@ type Touch interface {
// //
// Touches always returns nil on desktops. // Touches always returns nil on desktops.
func Touches() []Touch { func Touches() []Touch {
t := ui.CurrentInput().Touches() t := input.Get().Touches()
tt := make([]Touch, len(t)) tt := make([]Touch, len(t))
for i := 0; i < len(tt); i++ { for i := 0; i < len(tt); i++ {
tt[i] = t[i] tt[i] = t[i]

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package ui package input
type GamepadButton int type GamepadButton int

View File

@ -12,23 +12,18 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package ui package input
var currentInput = &Input{} var theInput = &Input{}
type Touch interface { func Get() *Input {
ID() int return theInput
Position() (x, y int)
}
func CurrentInput() *Input {
return currentInput
} }
func (i *Input) CursorPosition() (x, y int) { func (i *Input) CursorPosition() (x, y int) {
i.m.RLock() i.m.RLock()
defer i.m.RUnlock() defer i.m.RUnlock()
return adjustCursorPosition(i.cursorX, i.cursorY) return i.cursorX, i.cursorY
} }
var emptyIDs = []int{} var emptyIDs = []int{}
@ -86,9 +81,9 @@ func (i *Input) IsGamepadButtonPressed(id int, button GamepadButton) bool {
return i.gamepads[id].buttonPressed[button] return i.gamepads[id].buttonPressed[button]
} }
var emptyTouches = []Touch{} var emptyTouches = []*Touch{}
func (in *Input) Touches() []Touch { func (in *Input) Touches() []*Touch {
in.m.RLock() in.m.RLock()
defer in.m.RUnlock() defer in.m.RUnlock()
@ -98,9 +93,9 @@ func (in *Input) Touches() []Touch {
return emptyTouches return emptyTouches
} }
t := make([]Touch, len(in.touches)) t := make([]*Touch, len(in.touches))
for i := 0; i < len(t); i++ { for i := 0; i < len(t); i++ {
t[i] = &in.touches[i] t[i] = in.touches[i]
} }
return t return t
} }
@ -113,16 +108,24 @@ type gamePad struct {
buttonPressed [256]bool buttonPressed [256]bool
} }
type touch struct { type Touch struct {
id int id int
x int x int
y int y int
} }
func (t *touch) ID() int { func NewTouch(id int, x, y int) *Touch {
return &Touch{
id: id,
x: x,
y: y,
}
}
func (t *Touch) ID() int {
return t.id return t.id
} }
func (t *touch) Position() (x, y int) { func (t *Touch) Position() (x, y int) {
return t.x, t.y return t.x, t.y
} }

View File

@ -17,7 +17,7 @@
// +build !android // +build !android
// +build !ios // +build !ios
package ui package input
import ( import (
"sync" "sync"
@ -32,7 +32,7 @@ type Input struct {
cursorX int cursorX int
cursorY int cursorY int
gamepads [16]gamePad gamepads [16]gamePad
touches []touch // This is not updated until GLFW 3.3 is available (#417) touches []*Touch // This is not updated until GLFW 3.3 is available (#417)
runeBuffer []rune runeBuffer []rune
m sync.RWMutex m sync.RWMutex
} }
@ -43,6 +43,12 @@ func (i *Input) RuneBuffer() []rune {
return i.runeBuffer return i.runeBuffer
} }
func (i *Input) ClearRuneBuffer() {
i.m.RLock()
defer i.m.RUnlock()
i.runeBuffer = i.runeBuffer[:0]
}
func (i *Input) IsKeyPressed(key Key) bool { func (i *Input) IsKeyPressed(key Key) bool {
i.m.RLock() i.m.RLock()
defer i.m.RUnlock() defer i.m.RUnlock()
@ -83,7 +89,7 @@ var glfwMouseButtonToMouseButton = map[glfw.MouseButton]MouseButton{
glfw.MouseButtonMiddle: MouseButtonMiddle, glfw.MouseButtonMiddle: MouseButtonMiddle,
} }
func (i *Input) update(window *glfw.Window, scale float64) { func (i *Input) Update(window *glfw.Window, scale float64) {
i.m.Lock() i.m.Lock()
defer i.m.Unlock() defer i.m.Unlock()
if i.runeBuffer == nil { if i.runeBuffer == nil {

View File

@ -14,9 +14,11 @@
// +build js // +build js
package ui package input
import ( import (
"unicode"
"github.com/gopherjs/gopherjs/js" "github.com/gopherjs/gopherjs/js"
) )
@ -34,7 +36,7 @@ type Input struct {
cursorX int cursorX int
cursorY int cursorY int
gamepads [16]gamePad gamepads [16]gamePad
touches []touch touches []*Touch
runeBuffer []rune runeBuffer []rune
m mockRWLock m mockRWLock
} }
@ -43,6 +45,10 @@ func (i *Input) RuneBuffer() []rune {
return i.runeBuffer return i.runeBuffer
} }
func (i *Input) ClearRuneBuffer() {
i.runeBuffer = nil
}
func (i *Input) IsKeyPressed(key Key) bool { func (i *Input) IsKeyPressed(key Key) bool {
if i.keyPressed != nil { if i.keyPressed != nil {
for _, c := range keyToCodes[key] { for _, c := range keyToCodes[key] {
@ -131,7 +137,7 @@ func (i *Input) setMouseCursor(x, y int) {
i.cursorX, i.cursorY = x, y i.cursorX, i.cursorY = x, y
} }
func (i *Input) updateGamepads() { func (i *Input) UpdateGamepads() {
nav := js.Global.Get("navigator") nav := js.Global.Get("navigator")
if nav.Get("getGamepads") == js.Undefined { if nav.Get("getGamepads") == js.Undefined {
return return
@ -170,7 +176,111 @@ func (i *Input) updateGamepads() {
} }
} }
func (i *Input) updateTouches(t []touch) { func (i *Input) updateTouches(t []*Touch) {
i.touches = make([]touch, len(t)) i.touches = make([]*Touch, len(t))
copy(i.touches, t) copy(i.touches, t)
} }
func OnKeyDown(e *js.Object) {
c := e.Get("code")
if c == js.Undefined {
code := e.Get("keyCode").Int()
if keyCodeToKeyEdge[code] == KeyUp ||
keyCodeToKeyEdge[code] == KeyDown ||
keyCodeToKeyEdge[code] == KeyLeft ||
keyCodeToKeyEdge[code] == KeyRight ||
keyCodeToKeyEdge[code] == KeyBackspace ||
keyCodeToKeyEdge[code] == KeyTab {
e.Call("preventDefault")
}
theInput.keyDownEdge(code)
return
}
cs := c.String()
if cs == keyToCodes[KeyUp][0] ||
cs == keyToCodes[KeyDown][0] ||
cs == keyToCodes[KeyLeft][0] ||
cs == keyToCodes[KeyRight][0] ||
cs == keyToCodes[KeyBackspace][0] ||
cs == keyToCodes[KeyTab][0] {
e.Call("preventDefault")
}
theInput.keyDown(cs)
}
func OnKeyPress(e *js.Object) {
e.Call("preventDefault")
if r := rune(e.Get("charCode").Int()); unicode.IsPrint(r) {
theInput.runeBuffer = append(theInput.runeBuffer, r)
}
}
func OnKeyUp(e *js.Object) {
e.Call("preventDefault")
if e.Get("code") == js.Undefined {
// Assume that UA is Edge.
code := e.Get("keyCode").Int()
theInput.keyUpEdge(code)
return
}
code := e.Get("code").String()
theInput.keyUp(code)
}
func OnMouseDown(e *js.Object, scale float64, left, top int) {
e.Call("preventDefault")
button := e.Get("button").Int()
theInput.mouseDown(button)
setMouseCursorFromEvent(e, scale, left, top)
}
func OnMouseUp(e *js.Object, scale float64, left, top int) {
e.Call("preventDefault")
button := e.Get("button").Int()
theInput.mouseUp(button)
setMouseCursorFromEvent(e, scale, left, top)
}
func OnMouseMove(e *js.Object, scale float64, left, top int) {
e.Call("preventDefault")
setMouseCursorFromEvent(e, scale, left, top)
}
func OnTouchStart(e *js.Object, scale float64, left, top int) {
e.Call("preventDefault")
theInput.updateTouches(touchEventToTouches(e, scale, left, top))
}
func OnTouchEnd(e *js.Object, scale float64, left, top int) {
e.Call("preventDefault")
theInput.updateTouches(touchEventToTouches(e, scale, left, top))
}
func OnTouchMove(e *js.Object, scale float64, left, top int) {
e.Call("preventDefault")
theInput.updateTouches(touchEventToTouches(e, scale, left, top))
}
func setMouseCursorFromEvent(e *js.Object, scale float64, left, top int) {
x, y := e.Get("clientX").Int(), e.Get("clientY").Int()
x -= left
y -= top
theInput.setMouseCursor(int(float64(x)/scale), int(float64(y)/scale))
}
func touchEventToTouches(e *js.Object, scale float64, left, top int) []*Touch {
j := e.Get("targetTouches")
t := make([]*Touch, j.Get("length").Int())
for i := 0; i < len(t); i++ {
jj := j.Call("item", i)
id := jj.Get("identifier").Int()
x := int(float64(jj.Get("clientX").Int()-left) / scale)
y := int(float64(jj.Get("clientY").Int()-top) / scale)
t[i] = &Touch{
id: id,
x: x,
y: y,
}
}
return t
}

View File

@ -14,7 +14,7 @@
// +build android ios // +build android ios
package ui package input
import ( import (
"sync" "sync"
@ -24,7 +24,7 @@ type Input struct {
cursorX int cursorX int
cursorY int cursorY int
gamepads [16]gamePad gamepads [16]gamePad
touches []touch touches []*Touch
m sync.RWMutex m sync.RWMutex
} }
@ -40,13 +40,16 @@ func (i *Input) IsMouseButtonPressed(key MouseButton) bool {
return false return false
} }
func (i *Input) updateTouches(touches []Touch, dx, dy int) { func (i *Input) UpdateTouches(touches []*Touch, dx, dy int) {
i.m.Lock() i.m.Lock()
ts := make([]touch, len(touches)) ts := make([]*Touch, len(touches))
for i := 0; i < len(ts); i++ { for i := 0; i < len(ts); i++ {
ts[i].id = touches[i].ID()
x, y := touches[i].Position() x, y := touches[i].Position()
ts[i].x, ts[i].y = x+dx, y+dy ts[i] = &Touch{
id: touches[i].id,
x: x + dx,
y: y + dy,
}
} }
i.touches = ts i.touches = ts
i.m.Unlock() i.m.Unlock()

View File

@ -14,7 +14,7 @@
// Code generated by genkeys.go using 'go generate'. DO NOT EDIT. // Code generated by genkeys.go using 'go generate'. DO NOT EDIT.
package ui package input
type Key int type Key int

View File

@ -19,7 +19,7 @@
// +build !android // +build !android
// +build !ios // +build !ios
package ui package input
import ( import (
glfw "github.com/go-gl/glfw/v3.2/glfw" glfw "github.com/go-gl/glfw/v3.2/glfw"

View File

@ -16,7 +16,7 @@
// +build js // +build js
package ui package input
var keyToCodes = map[Key][]string{ var keyToCodes = map[Key][]string{
Key0: { Key0: {

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package ui package input
type MouseButton int type MouseButton int

View File

@ -22,9 +22,10 @@ import (
"golang.org/x/mobile/event/lifecycle" "golang.org/x/mobile/event/lifecycle"
"golang.org/x/mobile/event/paint" "golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size" "golang.org/x/mobile/event/size"
mtouch "golang.org/x/mobile/event/touch" "golang.org/x/mobile/event/touch"
"golang.org/x/mobile/gl" "golang.org/x/mobile/gl"
"github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
) )
@ -34,7 +35,7 @@ var (
func appMain(a app.App) { func appMain(a app.App) {
var glctx gl.Context var glctx gl.Context
touches := map[mtouch.Sequence]*touch{} touches := map[touch.Sequence]*input.Touch{}
for e := range a.Events() { for e := range a.Events() {
switch e := a.Filter(e).(type) { switch e := a.Filter(e).(type) {
case lifecycle.Event: case lifecycle.Event:
@ -61,20 +62,17 @@ func appMain(a app.App) {
<-chRenderEnd <-chRenderEnd
a.Publish() a.Publish()
a.Send(paint.Event{}) a.Send(paint.Event{})
case mtouch.Event: case touch.Event:
switch e.Type { switch e.Type {
case mtouch.TypeBegin, mtouch.TypeMove: case touch.TypeBegin, touch.TypeMove:
s := float32(actualScale()) s := float32(actualScale())
t := &touch{ // TODO: Is it ok to cast from int64 to int here?
id: int(e.Sequence), // TODO: Is it ok to cast from int64 to int here? t := input.NewTouch(int(e.Sequence), int(e.X/s), int(e.Y/s))
x: int(e.X / s),
y: int(e.Y / s),
}
touches[e.Sequence] = t touches[e.Sequence] = t
case mtouch.TypeEnd: case touch.TypeEnd:
delete(touches, e.Sequence) delete(touches, e.Sequence)
} }
ts := []Touch{} ts := []*input.Touch{}
for _, t := range touches { for _, t := range touches {
ts = append(ts, t) ts = append(ts, t)
} }

View File

@ -29,6 +29,7 @@ import (
"github.com/go-gl/glfw/v3.2/glfw" "github.com/go-gl/glfw/v3.2/glfw"
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
"github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
) )
@ -347,6 +348,10 @@ func ScreenPadding() (x0, y0, x1, y1 float64) {
return ox, oy, ox, oy return ox, oy, ox, oy
} }
func AdjustedCursorPosition() (x, y int) {
return adjustCursorPosition(input.Get().CursorPosition())
}
func adjustCursorPosition(x, y int) (int, int) { func adjustCursorPosition(x, y int) (int, int) {
u := currentUI u := currentUI
if !u.isRunning() { if !u.isRunning() {
@ -486,7 +491,7 @@ func (u *userInterface) actualScreenScale() float64 {
// pollEvents must be called from the main thread. // pollEvents must be called from the main thread.
func (u *userInterface) pollEvents() { func (u *userInterface) pollEvents() {
glfw.PollEvents() glfw.PollEvents()
currentInput.update(u.window, u.getScale()*glfwScale()) input.Get().Update(u.window, u.getScale()*glfwScale())
} }
func (u *userInterface) updateGraphicsContext(g GraphicsContext) { func (u *userInterface) updateGraphicsContext(g GraphicsContext) {
@ -542,7 +547,7 @@ func (u *userInterface) update(g GraphicsContext) error {
return nil return nil
}) })
if err := g.Update(func() { if err := g.Update(func() {
currentInput.runeBuffer = currentInput.runeBuffer[:0] input.Get().ClearRuneBuffer()
// The offscreens must be updated every frame (#490). // The offscreens must be updated every frame (#490).
u.updateGraphicsContext(g) u.updateGraphicsContext(g)
}); err != nil { }); err != nil {

View File

@ -19,11 +19,11 @@ package ui
import ( import (
"image" "image"
"strconv" "strconv"
"unicode"
"github.com/gopherjs/gopherjs/js" "github.com/gopherjs/gopherjs/js"
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
"github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
) )
@ -77,8 +77,8 @@ func ScreenPadding() (x0, y0, x1, y1 float64) {
return 0, 0, 0, 0 return 0, 0, 0, 0
} }
func adjustCursorPosition(x, y int) (int, int) { func AdjustedCursorPosition() (x, y int) {
return x, y return input.Get().CursorPosition()
} }
func IsCursorVisible() bool { func IsCursorVisible() bool {
@ -151,10 +151,10 @@ func (u *userInterface) update(g GraphicsContext) error {
return nil return nil
} }
currentInput.updateGamepads() input.Get().UpdateGamepads()
u.updateGraphicsContext(g) u.updateGraphicsContext(g)
if err := g.Update(func() { if err := g.Update(func() {
currentInput.runeBuffer = nil input.Get().ClearRuneBuffer()
// The offscreens must be updated every frame (#490). // The offscreens must be updated every frame (#490).
u.updateGraphicsContext(g) u.updateGraphicsContext(g)
}); err != nil { }); err != nil {
@ -180,21 +180,6 @@ func (u *userInterface) loop(g GraphicsContext) error {
return <-ch return <-ch
} }
func touchEventToTouches(e *js.Object) []touch {
scale := currentUI.getScale()
j := e.Get("targetTouches")
rect := canvas.Call("getBoundingClientRect")
left, top := rect.Get("left").Int(), rect.Get("top").Int()
t := make([]touch, j.Get("length").Int())
for i := 0; i < len(t); i++ {
jj := j.Call("item", i)
t[i].id = jj.Get("identifier").Int()
t[i].x = int(float64(jj.Get("clientX").Int()-left) / scale)
t[i].y = int(float64(jj.Get("clientY").Int()-top) / scale)
}
return t
}
func init() { func init() {
if err := initialize(); err != nil { if err := initialize(); err != nil {
panic(err) panic(err)
@ -263,66 +248,22 @@ func initialize() error {
canvas.Get("style").Set("outline", "none") canvas.Get("style").Set("outline", "none")
// Keyboard // Keyboard
canvas.Call("addEventListener", "keydown", func(e *js.Object) { canvas.Call("addEventListener", "keydown", input.OnKeyDown)
c := e.Get("code") canvas.Call("addEventListener", "keypress", input.OnKeyPress)
if c == js.Undefined { canvas.Call("addEventListener", "keyup", input.OnKeyUp)
code := e.Get("keyCode").Int()
if keyCodeToKeyEdge[code] == KeyUp ||
keyCodeToKeyEdge[code] == KeyDown ||
keyCodeToKeyEdge[code] == KeyLeft ||
keyCodeToKeyEdge[code] == KeyRight ||
keyCodeToKeyEdge[code] == KeyBackspace ||
keyCodeToKeyEdge[code] == KeyTab {
e.Call("preventDefault")
}
currentInput.keyDownEdge(code)
return
}
cs := c.String()
if cs == keyToCodes[KeyUp][0] ||
cs == keyToCodes[KeyDown][0] ||
cs == keyToCodes[KeyLeft][0] ||
cs == keyToCodes[KeyRight][0] ||
cs == keyToCodes[KeyBackspace][0] ||
cs == keyToCodes[KeyTab][0] {
e.Call("preventDefault")
}
currentInput.keyDown(cs)
})
canvas.Call("addEventListener", "keypress", func(e *js.Object) {
e.Call("preventDefault")
if r := rune(e.Get("charCode").Int()); unicode.IsPrint(r) {
currentInput.runeBuffer = append(currentInput.runeBuffer, r)
}
})
canvas.Call("addEventListener", "keyup", func(e *js.Object) {
e.Call("preventDefault")
if e.Get("code") == js.Undefined {
// Assume that UA is Edge.
code := e.Get("keyCode").Int()
currentInput.keyUpEdge(code)
return
}
code := e.Get("code").String()
currentInput.keyUp(code)
})
// Mouse // Mouse
canvas.Call("addEventListener", "mousedown", func(e *js.Object) { canvas.Call("addEventListener", "mousedown", func(e *js.Object) {
e.Call("preventDefault") rect := canvas.Call("getBoundingClientRect")
button := e.Get("button").Int() input.OnMouseDown(e, currentUI.getScale(), rect.Get("left").Int(), rect.Get("top").Int())
currentInput.mouseDown(button)
setMouseCursorFromEvent(e)
}) })
canvas.Call("addEventListener", "mouseup", func(e *js.Object) { canvas.Call("addEventListener", "mouseup", func(e *js.Object) {
e.Call("preventDefault") rect := canvas.Call("getBoundingClientRect")
button := e.Get("button").Int() input.OnMouseUp(e, currentUI.getScale(), rect.Get("left").Int(), rect.Get("top").Int())
currentInput.mouseUp(button)
setMouseCursorFromEvent(e)
}) })
canvas.Call("addEventListener", "mousemove", func(e *js.Object) { canvas.Call("addEventListener", "mousemove", func(e *js.Object) {
e.Call("preventDefault") rect := canvas.Call("getBoundingClientRect")
setMouseCursorFromEvent(e) input.OnMouseMove(e, currentUI.getScale(), rect.Get("left").Int(), rect.Get("top").Int())
}) })
canvas.Call("addEventListener", "contextmenu", func(e *js.Object) { canvas.Call("addEventListener", "contextmenu", func(e *js.Object) {
e.Call("preventDefault") e.Call("preventDefault")
@ -330,16 +271,16 @@ func initialize() error {
// Touch // Touch
canvas.Call("addEventListener", "touchstart", func(e *js.Object) { canvas.Call("addEventListener", "touchstart", func(e *js.Object) {
e.Call("preventDefault") rect := canvas.Call("getBoundingClientRect")
currentInput.updateTouches(touchEventToTouches(e)) input.OnTouchStart(e, currentUI.getScale(), rect.Get("left").Int(), rect.Get("top").Int())
}) })
canvas.Call("addEventListener", "touchend", func(e *js.Object) { canvas.Call("addEventListener", "touchend", func(e *js.Object) {
e.Call("preventDefault") rect := canvas.Call("getBoundingClientRect")
currentInput.updateTouches(touchEventToTouches(e)) input.OnTouchEnd(e, currentUI.getScale(), rect.Get("left").Int(), rect.Get("top").Int())
}) })
canvas.Call("addEventListener", "touchmove", func(e *js.Object) { canvas.Call("addEventListener", "touchmove", func(e *js.Object) {
e.Call("preventDefault") rect := canvas.Call("getBoundingClientRect")
currentInput.updateTouches(touchEventToTouches(e)) input.OnTouchMove(e, currentUI.getScale(), rect.Get("left").Int(), rect.Get("top").Int())
}) })
// Gamepad // Gamepad
@ -357,15 +298,6 @@ func initialize() error {
return nil return nil
} }
func setMouseCursorFromEvent(e *js.Object) {
scale := currentUI.getScale()
rect := canvas.Call("getBoundingClientRect")
x, y := e.Get("clientX").Int(), e.Get("clientY").Int()
x -= rect.Get("left").Int()
y -= rect.Get("top").Int()
currentInput.setMouseCursor(int(float64(x)/scale), int(float64(y)/scale))
}
func RunMainThreadLoop(ch <-chan error) error { func RunMainThreadLoop(ch <-chan error) error {
return <-ch return <-ch
} }

View File

@ -24,6 +24,7 @@ import (
"time" "time"
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
"github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
) )
@ -237,8 +238,8 @@ func (u *userInterface) screenPaddingImpl() (x0, y0, x1, y1 float64) {
return ox, oy, ox, oy return ox, oy, ox, oy
} }
func adjustCursorPosition(x, y int) (int, int) { func AdjustedCursorPosition() (x, y int) {
return currentUI.adjustCursorPosition(x, y) return currentUI.adjustCursorPosition(input.Get().CursorPosition())
} }
func (u *userInterface) adjustCursorPosition(x, y int) (int, int) { func (u *userInterface) adjustCursorPosition(x, y int) (int, int) {
@ -285,10 +286,10 @@ func SetWindowDecorated(decorated bool) {
// Do nothing // Do nothing
} }
func UpdateTouches(touches []Touch) { func UpdateTouches(touches []*input.Touch) {
currentUI.m.Lock() currentUI.m.Lock()
ox, oy, _, _ := currentUI.screenPaddingImpl() ox, oy, _, _ := currentUI.screenPaddingImpl()
s := currentUI.actualScaleImpl() s := currentUI.actualScaleImpl()
currentUI.m.Unlock() currentUI.m.Unlock()
currentInput.updateTouches(touches, -int(ox/s), -int(oy/s)) input.Get().UpdateTouches(touches, -int(ox/s), -int(oy/s))
} }

158
keys.go
View File

@ -17,7 +17,7 @@
package ebiten package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/ui" "github.com/hajimehoshi/ebiten/internal/input"
) )
// A Key represents a keyboard key. // A Key represents a keyboard key.
@ -27,83 +27,83 @@ type Key int
// Keys // Keys
const ( const (
Key0 Key = Key(ui.Key0) Key0 Key = Key(input.Key0)
Key1 Key = Key(ui.Key1) Key1 Key = Key(input.Key1)
Key2 Key = Key(ui.Key2) Key2 Key = Key(input.Key2)
Key3 Key = Key(ui.Key3) Key3 Key = Key(input.Key3)
Key4 Key = Key(ui.Key4) Key4 Key = Key(input.Key4)
Key5 Key = Key(ui.Key5) Key5 Key = Key(input.Key5)
Key6 Key = Key(ui.Key6) Key6 Key = Key(input.Key6)
Key7 Key = Key(ui.Key7) Key7 Key = Key(input.Key7)
Key8 Key = Key(ui.Key8) Key8 Key = Key(input.Key8)
Key9 Key = Key(ui.Key9) Key9 Key = Key(input.Key9)
KeyA Key = Key(ui.KeyA) KeyA Key = Key(input.KeyA)
KeyB Key = Key(ui.KeyB) KeyB Key = Key(input.KeyB)
KeyC Key = Key(ui.KeyC) KeyC Key = Key(input.KeyC)
KeyD Key = Key(ui.KeyD) KeyD Key = Key(input.KeyD)
KeyE Key = Key(ui.KeyE) KeyE Key = Key(input.KeyE)
KeyF Key = Key(ui.KeyF) KeyF Key = Key(input.KeyF)
KeyG Key = Key(ui.KeyG) KeyG Key = Key(input.KeyG)
KeyH Key = Key(ui.KeyH) KeyH Key = Key(input.KeyH)
KeyI Key = Key(ui.KeyI) KeyI Key = Key(input.KeyI)
KeyJ Key = Key(ui.KeyJ) KeyJ Key = Key(input.KeyJ)
KeyK Key = Key(ui.KeyK) KeyK Key = Key(input.KeyK)
KeyL Key = Key(ui.KeyL) KeyL Key = Key(input.KeyL)
KeyM Key = Key(ui.KeyM) KeyM Key = Key(input.KeyM)
KeyN Key = Key(ui.KeyN) KeyN Key = Key(input.KeyN)
KeyO Key = Key(ui.KeyO) KeyO Key = Key(input.KeyO)
KeyP Key = Key(ui.KeyP) KeyP Key = Key(input.KeyP)
KeyQ Key = Key(ui.KeyQ) KeyQ Key = Key(input.KeyQ)
KeyR Key = Key(ui.KeyR) KeyR Key = Key(input.KeyR)
KeyS Key = Key(ui.KeyS) KeyS Key = Key(input.KeyS)
KeyT Key = Key(ui.KeyT) KeyT Key = Key(input.KeyT)
KeyU Key = Key(ui.KeyU) KeyU Key = Key(input.KeyU)
KeyV Key = Key(ui.KeyV) KeyV Key = Key(input.KeyV)
KeyW Key = Key(ui.KeyW) KeyW Key = Key(input.KeyW)
KeyX Key = Key(ui.KeyX) KeyX Key = Key(input.KeyX)
KeyY Key = Key(ui.KeyY) KeyY Key = Key(input.KeyY)
KeyZ Key = Key(ui.KeyZ) KeyZ Key = Key(input.KeyZ)
KeyAlt Key = Key(ui.KeyAlt) KeyAlt Key = Key(input.KeyAlt)
KeyApostrophe Key = Key(ui.KeyApostrophe) KeyApostrophe Key = Key(input.KeyApostrophe)
KeyBackslash Key = Key(ui.KeyBackslash) KeyBackslash Key = Key(input.KeyBackslash)
KeyBackspace Key = Key(ui.KeyBackspace) KeyBackspace Key = Key(input.KeyBackspace)
KeyCapsLock Key = Key(ui.KeyCapsLock) KeyCapsLock Key = Key(input.KeyCapsLock)
KeyComma Key = Key(ui.KeyComma) KeyComma Key = Key(input.KeyComma)
KeyControl Key = Key(ui.KeyControl) KeyControl Key = Key(input.KeyControl)
KeyDelete Key = Key(ui.KeyDelete) KeyDelete Key = Key(input.KeyDelete)
KeyDown Key = Key(ui.KeyDown) KeyDown Key = Key(input.KeyDown)
KeyEnd Key = Key(ui.KeyEnd) KeyEnd Key = Key(input.KeyEnd)
KeyEnter Key = Key(ui.KeyEnter) KeyEnter Key = Key(input.KeyEnter)
KeyEqual Key = Key(ui.KeyEqual) KeyEqual Key = Key(input.KeyEqual)
KeyEscape Key = Key(ui.KeyEscape) KeyEscape Key = Key(input.KeyEscape)
KeyF1 Key = Key(ui.KeyF1) KeyF1 Key = Key(input.KeyF1)
KeyF2 Key = Key(ui.KeyF2) KeyF2 Key = Key(input.KeyF2)
KeyF3 Key = Key(ui.KeyF3) KeyF3 Key = Key(input.KeyF3)
KeyF4 Key = Key(ui.KeyF4) KeyF4 Key = Key(input.KeyF4)
KeyF5 Key = Key(ui.KeyF5) KeyF5 Key = Key(input.KeyF5)
KeyF6 Key = Key(ui.KeyF6) KeyF6 Key = Key(input.KeyF6)
KeyF7 Key = Key(ui.KeyF7) KeyF7 Key = Key(input.KeyF7)
KeyF8 Key = Key(ui.KeyF8) KeyF8 Key = Key(input.KeyF8)
KeyF9 Key = Key(ui.KeyF9) KeyF9 Key = Key(input.KeyF9)
KeyF10 Key = Key(ui.KeyF10) KeyF10 Key = Key(input.KeyF10)
KeyF11 Key = Key(ui.KeyF11) KeyF11 Key = Key(input.KeyF11)
KeyF12 Key = Key(ui.KeyF12) KeyF12 Key = Key(input.KeyF12)
KeyGraveAccent Key = Key(ui.KeyGraveAccent) KeyGraveAccent Key = Key(input.KeyGraveAccent)
KeyHome Key = Key(ui.KeyHome) KeyHome Key = Key(input.KeyHome)
KeyInsert Key = Key(ui.KeyInsert) KeyInsert Key = Key(input.KeyInsert)
KeyLeft Key = Key(ui.KeyLeft) KeyLeft Key = Key(input.KeyLeft)
KeyLeftBracket Key = Key(ui.KeyLeftBracket) KeyLeftBracket Key = Key(input.KeyLeftBracket)
KeyMinus Key = Key(ui.KeyMinus) KeyMinus Key = Key(input.KeyMinus)
KeyPageDown Key = Key(ui.KeyPageDown) KeyPageDown Key = Key(input.KeyPageDown)
KeyPageUp Key = Key(ui.KeyPageUp) KeyPageUp Key = Key(input.KeyPageUp)
KeyPeriod Key = Key(ui.KeyPeriod) KeyPeriod Key = Key(input.KeyPeriod)
KeyRight Key = Key(ui.KeyRight) KeyRight Key = Key(input.KeyRight)
KeyRightBracket Key = Key(ui.KeyRightBracket) KeyRightBracket Key = Key(input.KeyRightBracket)
KeySemicolon Key = Key(ui.KeySemicolon) KeySemicolon Key = Key(input.KeySemicolon)
KeyShift Key = Key(ui.KeyShift) KeyShift Key = Key(input.KeyShift)
KeySlash Key = Key(ui.KeySlash) KeySlash Key = Key(input.KeySlash)
KeySpace Key = Key(ui.KeySpace) KeySpace Key = Key(input.KeySpace)
KeyTab Key = Key(ui.KeyTab) KeyTab Key = Key(input.KeyTab)
KeyUp Key = Key(ui.KeyUp) KeyUp Key = Key(input.KeyUp)
KeyMax Key = KeyUp KeyMax Key = KeyUp
) )

View File

@ -17,6 +17,7 @@
package mobile package mobile
import ( import (
"github.com/hajimehoshi/ebiten/internal/input"
"github.com/hajimehoshi/ebiten/internal/ui" "github.com/hajimehoshi/ebiten/internal/ui"
) )
@ -29,26 +30,13 @@ var (
touches = map[int]position{} touches = map[int]position{}
) )
// touch implements ui.Touch.
type touch struct {
id int
position position
}
func (t touch) ID() int {
return t.id
}
func (t touch) Position() (int, int) {
// TODO: Is this OK to adjust the position here?
return int(float64(t.position.x) / ui.ScreenScale()),
int(float64(t.position.y) / ui.ScreenScale())
}
func updateTouches() { func updateTouches() {
ts := []ui.Touch{} ts := []*input.Touch{}
for id, position := range touches { for id, position := range touches {
ts = append(ts, touch{id, position}) // TODO: Is this OK to adjust the position here?
x := int(float64(position.x) / ui.ScreenScale())
y := int(float64(position.y) / ui.ScreenScale())
ts = append(ts, input.NewTouch(id, x, y))
} }
ui.UpdateTouches(ts) ui.UpdateTouches(ts)
} }

View File

@ -15,7 +15,7 @@
package ebiten package ebiten
import ( import (
"github.com/hajimehoshi/ebiten/internal/ui" "github.com/hajimehoshi/ebiten/internal/input"
) )
// A MouseButton represents a mouse button. // A MouseButton represents a mouse button.
@ -23,7 +23,7 @@ type MouseButton int
// MouseButtons // MouseButtons
const ( const (
MouseButtonLeft MouseButton = MouseButton(ui.MouseButtonLeft) MouseButtonLeft MouseButton = MouseButton(input.MouseButtonLeft)
MouseButtonRight MouseButton = MouseButton(ui.MouseButtonRight) MouseButtonRight MouseButton = MouseButton(input.MouseButtonRight)
MouseButtonMiddle MouseButton = MouseButton(ui.MouseButtonMiddle) MouseButtonMiddle MouseButton = MouseButton(input.MouseButtonMiddle)
) )