diff --git a/examples/runes/main.go b/examples/runes/main.go new file mode 100644 index 000000000..34058e006 --- /dev/null +++ b/examples/runes/main.go @@ -0,0 +1,51 @@ +// Copyright 2017 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. + +// +build example + +package main + +import ( + "log" + + "github.com/hajimehoshi/ebiten" + "github.com/hajimehoshi/ebiten/ebitenutil" +) + +var runes = append(make([]rune, 0, 1024), []rune("Type on the keyboard:\n")...) + +var buf = make([]rune, 1024) + +var counter int + +func update(screen *ebiten.Image) error { + runes = append(runes, ebiten.InputChars()...) + if ebiten.IsKeyPressed(ebiten.KeyEnter) { + if len(runes) > 0 && runes[len(runes)-1] != '\n' { + runes = append(runes, '\n') + } + } + counter++ + if ebiten.IsRunningSlowly() { + return nil + } + if counter%60 < 30 { + return ebitenutil.DebugPrint(screen, string(append(runes, '_'))) + } + return ebitenutil.DebugPrint(screen, string(runes)) +} + +func main() { + log.Fatal(ebiten.Run(update, 320, 240, 2.0, "Runes (Ebiten Demo)")) // ebiterm? +} diff --git a/input.go b/input.go index 66d151ef0..647275766 100644 --- a/input.go +++ b/input.go @@ -18,6 +18,20 @@ import ( "github.com/hajimehoshi/ebiten/internal/ui" ) +// InputChars return "printable" runes read from the keyboard at the time update is called. +// +// InputChars represents the environment's locale-dependent translation of keyboard +// input to Unicode characters. +// +// IsKeyPressed is based on a mapping of device codes to input device keys. +// "Control" and modifier keys should be handled with IsKeyPressed. +// +// This function is concurrent-safe. +func InputChars() []rune { + rb := ui.CurrentInput().RuneBuffer() + return append(make([]rune, 0, len(rb)), rb...) +} + // IsKeyPressed returns a boolean indicating whether key is pressed. // // This function is concurrent-safe. diff --git a/internal/ui/input_glfw.go b/internal/ui/input_glfw.go index 13e202366..fd36b0e68 100644 --- a/internal/ui/input_glfw.go +++ b/internal/ui/input_glfw.go @@ -21,6 +21,7 @@ package ui import ( "sync" + "unicode" glfw "github.com/go-gl/glfw/v3.2/glfw" ) @@ -32,9 +33,16 @@ type Input struct { cursorY int gamepads [16]gamePad touches []touch + runeBuffer []rune m sync.RWMutex } +func (i *Input) RuneBuffer() []rune { + i.m.RLock() + defer i.m.RUnlock() + return i.runeBuffer +} + func (i *Input) IsKeyPressed(key Key) bool { i.m.RLock() defer i.m.RUnlock() @@ -78,7 +86,16 @@ var glfwMouseButtonToMouseButton = map[glfw.MouseButton]MouseButton{ func (i *Input) update(window *glfw.Window, scale float64) { i.m.Lock() defer i.m.Unlock() - + if i.runeBuffer == nil { + i.runeBuffer = make([]rune, 0, 1024) + window.SetCharModsCallback(func(w *glfw.Window, char rune, mods glfw.ModifierKey) { + if unicode.IsPrint(char) { + i.m.Lock() + i.runeBuffer = append(i.runeBuffer, char) + i.m.Unlock() + } + }) + } if i.keyPressed == nil { i.keyPressed = map[glfw.Key]bool{} } diff --git a/internal/ui/input_js.go b/internal/ui/input_js.go index a94189967..8a2f39494 100644 --- a/internal/ui/input_js.go +++ b/internal/ui/input_js.go @@ -35,9 +35,14 @@ type Input struct { cursorY int gamepads [16]gamePad touches []touch + runeBuffer []rune m mockRWLock } +func (i *Input) RuneBuffer() []rune { + return i.runeBuffer +} + func (i *Input) IsKeyPressed(key Key) bool { if i.keyPressed != nil { for _, c := range keyToCodes[key] { diff --git a/internal/ui/input_mobile.go b/internal/ui/input_mobile.go index e68a58a30..c6a2a7c7e 100644 --- a/internal/ui/input_mobile.go +++ b/internal/ui/input_mobile.go @@ -28,6 +28,10 @@ type Input struct { m sync.RWMutex } +func (i *Input) RuneBuffer() []rune { + return nil +} + func (i *Input) IsKeyPressed(key Key) bool { return false } diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index df5cca906..ea335357e 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -445,6 +445,7 @@ func (u *userInterface) update(g GraphicsContext) error { if err := g.Update(); err != nil { return err } + currentInput.runeBuffer = currentInput.runeBuffer[:0] return nil } diff --git a/internal/ui/ui_js.go b/internal/ui/ui_js.go index 08a76065a..2d1357577 100644 --- a/internal/ui/ui_js.go +++ b/internal/ui/ui_js.go @@ -19,6 +19,7 @@ package ui import ( "strconv" "strings" + "unicode" "github.com/gopherjs/gopherjs/js" "github.com/hajimehoshi/ebiten/internal/opengl" @@ -134,6 +135,7 @@ func (u *userInterface) update(g GraphicsContext) error { if err := g.Update(); err != nil { return err } + currentInput.runeBuffer = nil return nil } @@ -238,7 +240,6 @@ func initialize() error { // Keyboard canvas.Call("addEventListener", "keydown", func(e *js.Object) { - e.Call("preventDefault") if e.Get("code") == js.Undefined { // Assume that UA is Safari. code := e.Get("keyCode").Int() @@ -248,6 +249,12 @@ func initialize() error { code := e.Get("code").String() currentInput.keyDown(code) }) + 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 {