ebiten/genkeys.go

641 lines
16 KiB
Go
Raw Normal View History

2015-01-06 18:25:26 +01:00
// Copyright 2015 Hajime Hoshi
//
// 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 ignore
// Note:
// * Respect GLFW key names
// * https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode
2015-01-06 20:27:57 +01:00
// * It is best to replace keyCode with code, but many browsers don't implement it.
2015-01-06 18:25:26 +01:00
package main
import (
"log"
"os"
2019-09-10 18:08:48 +02:00
"path/filepath"
2015-01-06 18:25:26 +01:00
"sort"
"strconv"
"strings"
2015-01-06 18:25:26 +01:00
"text/template"
2019-11-25 15:13:44 +01:00
"github.com/go-gl/glfw/v3.3/glfw"
2015-01-06 18:25:26 +01:00
)
var (
2020-02-19 02:42:42 +01:00
nameToGLFWKey map[string]glfw.Key
nameToAndroidKey map[string]int
2020-02-19 02:42:42 +01:00
nameToJSKey map[string]string
edgeKeyCodeToName map[int]string
)
func init() {
2020-02-19 02:42:42 +01:00
nameToGLFWKey = map[string]glfw.Key{
"Unknown": glfw.KeyUnknown,
"Space": glfw.KeySpace,
"Apostrophe": glfw.KeyApostrophe,
"Comma": glfw.KeyComma,
"Minus": glfw.KeyMinus,
"Period": glfw.KeyPeriod,
"Slash": glfw.KeySlash,
"Semicolon": glfw.KeySemicolon,
"Equal": glfw.KeyEqual,
"LeftBracket": glfw.KeyLeftBracket,
"Backslash": glfw.KeyBackslash,
"RightBracket": glfw.KeyRightBracket,
"GraveAccent": glfw.KeyGraveAccent,
"World1": glfw.KeyWorld1,
"World2": glfw.KeyWorld2,
"Escape": glfw.KeyEscape,
"Enter": glfw.KeyEnter,
"Tab": glfw.KeyTab,
"Backspace": glfw.KeyBackspace,
"Insert": glfw.KeyInsert,
"Delete": glfw.KeyDelete,
"Right": glfw.KeyRight,
"Left": glfw.KeyLeft,
"Down": glfw.KeyDown,
"Up": glfw.KeyUp,
"PageUp": glfw.KeyPageUp,
"PageDown": glfw.KeyPageDown,
"Home": glfw.KeyHome,
"End": glfw.KeyEnd,
"CapsLock": glfw.KeyCapsLock,
"ScrollLock": glfw.KeyScrollLock,
"NumLock": glfw.KeyNumLock,
"PrintScreen": glfw.KeyPrintScreen,
"Pause": glfw.KeyPause,
"LeftShift": glfw.KeyLeftShift,
"LeftControl": glfw.KeyLeftControl,
"LeftAlt": glfw.KeyLeftAlt,
"LeftSuper": glfw.KeyLeftSuper,
"RightShift": glfw.KeyRightShift,
"RightControl": glfw.KeyRightControl,
"RightAlt": glfw.KeyRightAlt,
"RightSuper": glfw.KeyRightSuper,
"Menu": glfw.KeyMenu,
2020-02-19 16:18:40 +01:00
"KPDecimal": glfw.KeyKPDecimal,
"KPDivide": glfw.KeyKPDivide,
"KPMultiply": glfw.KeyKPMultiply,
"KPSubtract": glfw.KeyKPSubtract,
"KPAdd": glfw.KeyKPAdd,
"KPEnter": glfw.KeyKPEnter,
"KPEqual": glfw.KeyKPEqual,
"Last": glfw.KeyLast,
}
// https://developer.android.com/reference/android/view/KeyEvent
nameToAndroidKey = map[string]int{
"Comma": 55,
"Period": 56,
"LeftAlt": 57,
"RightAlt": 58,
"CapsLock": 115,
"LeftControl": 113,
"RightControl": 114,
"LeftShift": 59,
"RightShift": 60,
"Enter": 66,
"Space": 62,
"Tab": 61,
"Delete": 112, // KEYCODE_FORWARD_DEL
"End": 123,
"Home": 122,
"Insert": 124,
"PageDown": 93,
"PageUp": 92,
"Down": 20,
"Left": 21,
"Right": 22,
"Up": 19,
"Escape": 111,
"Backspace": 67, // KEYCODE_DEL
"Apostrophe": 75,
"Minus": 69,
"Slash": 76,
"Semicolon": 74,
"Equal": 70,
"LeftBracket": 71,
"Backslash": 73,
"RightBracket": 72,
"GraveAccent": 68,
"NumLock": 143,
"Pause": 121, // KEYCODE_BREAK
"PrintScreen": 120, // KEYCODE_SYSRQ
"ScrollLock": 116,
"Menu": 82,
"KPDecimal": 158,
"KPDivide": 154,
"KPMultiply": 155,
"KPSubtract": 156,
"KPAdd": 157,
"KPEnter": 160,
"KPEqual": 161,
}
2020-02-19 02:42:42 +01:00
nameToJSKey = map[string]string{
"Comma": "Comma",
"Period": "Period",
"LeftAlt": "AltLeft",
"RightAlt": "AltRight",
"CapsLock": "CapsLock",
"LeftControl": "ControlLeft",
"RightControl": "ControlRight",
"LeftShift": "ShiftLeft",
"RightShift": "ShiftRight",
"Enter": "Enter",
"Space": "Space",
"Tab": "Tab",
"Delete": "Delete",
"End": "End",
"Home": "Home",
"Insert": "Insert",
"PageDown": "PageDown",
"PageUp": "PageUp",
"Down": "ArrowDown",
"Left": "ArrowLeft",
"Right": "ArrowRight",
"Up": "ArrowUp",
"Escape": "Escape",
"Backspace": "Backspace",
"Apostrophe": "Quote",
"Minus": "Minus",
"Slash": "Slash",
"Semicolon": "Semicolon",
"Equal": "Equal",
"LeftBracket": "BracketLeft",
"Backslash": "Backslash",
"RightBracket": "BracketRight",
"GraveAccent": "Backquote",
"NumLock": "NumLock",
"Pause": "Pause",
"PrintScreen": "PrintScreen",
"ScrollLock": "ScrollLock",
"Menu": "ContextMenu",
2020-02-19 16:18:40 +01:00
"KPDecimal": "NumpadDecimal",
"KPDivide": "NumpadDivide",
"KPMultiply": "NumpadMultiply",
"KPSubtract": "NumpadSubtract",
"KPAdd": "NumpadAdd",
"KPEnter": "NumpadEnter",
"KPEqual": "NumpadEqual",
}
// ASCII: 0 - 9
for c := '0'; c <= '9'; c++ {
2020-02-19 02:42:42 +01:00
nameToGLFWKey[string(c)] = glfw.Key0 + glfw.Key(c) - '0'
nameToAndroidKey[string(c)] = int(7 + c - '0')
2020-02-19 02:42:42 +01:00
nameToJSKey[string(c)] = "Digit" + string(c)
}
// ASCII: A - Z
for c := 'A'; c <= 'Z'; c++ {
2020-02-19 02:42:42 +01:00
nameToGLFWKey[string(c)] = glfw.KeyA + glfw.Key(c) - 'A'
nameToAndroidKey[string(c)] = int(29 + c - 'A')
2020-02-19 02:42:42 +01:00
nameToJSKey[string(c)] = "Key" + string(c)
}
// Function keys
for i := 1; i <= 12; i++ {
name := "F" + strconv.Itoa(i)
2020-02-19 02:42:42 +01:00
nameToGLFWKey[name] = glfw.KeyF1 + glfw.Key(i) - 1
nameToAndroidKey[name] = 131 + i - 1
2020-02-19 02:42:42 +01:00
nameToJSKey[name] = name
}
// Numpad
// https://www.w3.org/TR/uievents-code/#key-numpad-section
for c := '0'; c <= '9'; c++ {
name := "KP" + string(c)
2020-02-19 02:42:42 +01:00
nameToGLFWKey[name] = glfw.KeyKP0 + glfw.Key(c) - '0'
nameToAndroidKey[name] = int(144 + c - '0')
2020-02-19 02:42:42 +01:00
nameToJSKey[name] = "Numpad" + string(c)
}
}
2015-01-06 18:25:26 +01:00
func init() {
// TODO: How should we treat modifier keys? Now 'left' modifier keys are available.
2020-02-19 02:42:42 +01:00
edgeKeyCodeToName = map[int]string{
2017-04-13 20:09:00 +02:00
0xbc: "Comma",
0xbe: "Period",
0x12: "LeftAlt",
0x14: "CapsLock",
0x11: "LeftControl",
0x10: "LeftShift",
0x0D: "Enter",
0x20: "Space",
0x09: "Tab",
0x2E: "Delete",
0x23: "End",
0x24: "Home",
0x2D: "Insert",
0x22: "PageDown",
0x21: "PageUp",
0x28: "Down",
0x25: "Left",
0x27: "Right",
0x26: "Up",
0x1B: "Escape",
2017-04-13 20:09:00 +02:00
0xde: "Apostrophe",
0xbd: "Minus",
0xbf: "Slash",
0xba: "Semicolon",
0xbb: "Equal",
0xdb: "LeftBracket",
0xdc: "Backslash",
0xdd: "RightBracket",
0xc0: "GraveAccent",
0x08: "Backspace",
2018-04-13 19:29:18 +02:00
0x90: "NumLock",
0x6e: "KPDecimal",
0x6f: "KPDivide",
0x6a: "KPMultiply",
0x6d: "KPSubtract",
0x6b: "KPAdd",
0x13: "Pause",
0x91: "ScrollLock",
0x5d: "Menu",
2018-04-21 18:39:28 +02:00
// On Edge, this key does not work. PrintScreen works only on keyup event.
// 0x2C: "PrintScreen",
2018-04-13 19:29:18 +02:00
2018-04-21 18:39:28 +02:00
// On Edge, it is impossible to tell KPEnter and Enter / KPEqual and Equal.
2018-04-13 19:29:18 +02:00
// 0x0d: "KPEnter",
// 0x0c: "KPEqual",
2015-01-06 18:25:26 +01:00
}
// ASCII: 0 - 9
for c := '0'; c <= '9'; c++ {
2020-02-19 02:42:42 +01:00
edgeKeyCodeToName[int(c)] = string(c)
2015-01-06 18:25:26 +01:00
}
// ASCII: A - Z
for c := 'A'; c <= 'Z'; c++ {
2020-02-19 02:42:42 +01:00
edgeKeyCodeToName[int(c)] = string(c)
2015-01-06 18:25:26 +01:00
}
// Function keys
for i := 1; i <= 12; i++ {
2020-02-19 02:42:42 +01:00
edgeKeyCodeToName[0x70+i-1] = "F" + strconv.Itoa(i)
2015-01-06 18:25:26 +01:00
}
2018-04-13 19:29:18 +02:00
// Numpad keys
for c := '0'; c <= '9'; c++ {
2020-02-19 02:42:42 +01:00
edgeKeyCodeToName[0x60+int(c-'0')] = "KP" + string(c)
2018-04-13 19:29:18 +02:00
}
2015-01-06 18:25:26 +01:00
}
const ebitenKeysTmpl = `{{.License}}
{{.DoNotEdit}}
2015-01-07 15:21:52 +01:00
2015-01-06 18:25:26 +01:00
package ebiten
import (
"strings"
2019-03-30 15:28:07 +01:00
"github.com/hajimehoshi/ebiten/internal/driver"
2015-01-06 18:25:26 +01:00
)
// A Key represents a keyboard key.
2017-04-10 03:45:04 +02:00
// These keys represent pysical keys of US keyboard.
2017-07-23 15:49:04 +02:00
// For example, KeyQ represents Q key on US keyboards and ' (quote) key on Dvorak keyboards.
2015-01-06 18:25:26 +01:00
type Key int
2018-04-13 19:29:18 +02:00
// Keys.
2015-01-06 18:25:26 +01:00
const (
{{range $index, $name := .EbitenKeyNamesWithoutMods}}Key{{$name}} Key = Key(driver.Key{{$name}})
{{end}} KeyAlt Key = Key(driver.KeyReserved0)
KeyControl Key = Key(driver.KeyReserved1)
KeyShift Key = Key(driver.KeyReserved2)
KeyMax Key = KeyShift
2015-01-06 18:25:26 +01:00
)
func (k Key) isValid() bool {
switch k {
{{range $name := .EbitenKeyNames}}case Key{{$name}}:
return true
{{end}}
default:
return false
}
}
2018-04-13 20:20:43 +02:00
// String returns a string representing the key.
//
// If k is an undefined key, String returns an empty string.
func (k Key) String() string {
switch k {
2019-09-01 14:12:29 +02:00
{{range $name := .EbitenKeyNames}}case Key{{$name}}:
2018-04-13 20:20:43 +02:00
return {{$name | printf "%q"}}
{{end}}}
return ""
}
2020-02-19 02:42:42 +01:00
func keyNameToKeyCode(name string) (Key, bool) {
switch strings.ToLower(name) {
2019-09-01 14:12:29 +02:00
{{range $name := .EbitenKeyNames}}case {{$name | printf "%q" | ToLower}}:
return Key{{$name}}, true
{{end}}}
return 0, false
}
2015-01-06 18:25:26 +01:00
`
2019-03-30 15:28:07 +01:00
const driverKeysTmpl = `{{.License}}
2015-01-06 18:25:26 +01:00
{{.DoNotEdit}}
2015-01-07 15:21:52 +01:00
2019-03-30 15:28:07 +01:00
package driver
2015-01-06 18:25:26 +01:00
type Key int
const (
{{range $index, $name := .DriverKeyNames}}Key{{$name}}{{if eq $index 0}} Key = iota{{end}}
{{end}} KeyReserved0
KeyReserved1
KeyReserved2
2015-01-06 18:25:26 +01:00
)
`
const eventKeysTmpl = `{{.License}}
{{.DoNotEdit}}
package event
import (
"github.com/hajimehoshi/ebiten/internal/driver"
)
type Key = driver.Key
const (
{{range $index, $name := .DriverKeyNames}}Key{{$name}} = driver.Key{{$name}}
{{end}}
)
`
const uidriverGlfwKeysTmpl = `{{.License}}
2015-01-06 18:25:26 +01:00
{{.DoNotEdit}}
2015-01-07 15:21:52 +01:00
{{.BuildTag}}
2015-01-06 18:25:26 +01:00
package glfw
2015-01-06 18:25:26 +01:00
import (
2019-03-30 15:28:07 +01:00
"github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/glfw"
2015-01-06 18:25:26 +01:00
)
2020-02-19 02:42:42 +01:00
var glfwKeyToDriverKey = map[glfw.Key]driver.Key{
{{range $index, $name := .DriverKeyNames}}glfw.Key{{$name}}: driver.Key{{$name}},
2015-01-06 18:25:26 +01:00
{{end}}
}
2020-02-19 02:42:42 +01:00
var driverKeyToGLFWKey = map[driver.Key]glfw.Key{
{{range $index, $name := .DriverKeyNames}}driver.Key{{$name}}: glfw.Key{{$name}},
{{end}}
}
2015-01-06 18:25:26 +01:00
`
const uidriverJsKeysTmpl = `{{.License}}
2015-01-06 18:25:26 +01:00
{{.DoNotEdit}}
2015-01-07 15:21:52 +01:00
{{.BuildTag}}
2015-01-06 18:25:26 +01:00
package js
2015-01-06 18:25:26 +01:00
import (
"github.com/hajimehoshi/ebiten/internal/driver"
)
2020-02-19 02:42:42 +01:00
var driverKeyToJSKey = map[driver.Key]string{
{{range $name, $code := .NameToJSKey}}driver.Key{{$name}}: {{$code | printf "%q"}},
{{end}}
}
2020-02-19 02:42:42 +01:00
var edgeKeyCodeToDriverKey = map[int]driver.Key{
{{range $code, $name := .EdgeKeyCodeToName}}{{$code}}: driver.Key{{$name}},
2015-01-06 18:25:26 +01:00
{{end}}
}
`
const glfwKeysTmpl = `{{.License}}
{{.DoNotEdit}}
{{.BuildTag}}
package glfw
const (
2020-02-19 02:42:42 +01:00
{{range $name, $key := .NameToGLFWKey}}Key{{$name}} = Key({{$key}})
{{end}}
)
`
const mobileAndroidKeysTmpl = `{{.License}}
{{.DoNotEdit}}
{{.BuildTag}}
package ebitenmobileview
import (
"github.com/hajimehoshi/ebiten/internal/driver"
)
var androidKeyToDriverKey = map[int]driver.Key{
{{range $name, $code := .NameToAndroidKey}}{{$code}}: driver.Key{{$name}},
{{end}}
}
`
2019-09-01 11:07:11 +02:00
func digitKey(name string) int {
2015-01-07 03:00:15 +01:00
if len(name) != 1 {
return -1
}
c := name[0]
if c < '0' || '9' < c {
return -1
}
return int(c - '0')
}
2019-09-01 11:07:11 +02:00
func alphabetKey(name string) rune {
2015-01-07 03:00:15 +01:00
if len(name) != 1 {
return -1
}
c := rune(name[0])
if c < 'A' || 'Z' < c {
return -1
}
return c
}
2019-09-01 11:07:11 +02:00
func functionKey(name string) int {
2015-01-07 03:00:15 +01:00
if len(name) < 2 {
return -1
}
if name[0] != 'F' {
return -1
}
i, err := strconv.Atoi(name[1:])
if err != nil {
return -1
}
return i
}
2019-09-01 11:07:11 +02:00
func keyNamesLess(k []string) func(i, j int) bool {
return func(i, j int) bool {
k0, k1 := k[i], k[j]
d0, d1 := digitKey(k0), digitKey(k1)
a0, a1 := alphabetKey(k0), alphabetKey(k1)
f0, f1 := functionKey(k0), functionKey(k1)
if d0 != -1 {
if d1 != -1 {
return d0 < d1
}
return true
}
if a0 != -1 {
if d1 != -1 {
return false
}
if a1 != -1 {
return a0 < a1
}
return true
2015-01-07 03:00:15 +01:00
}
if d1 != -1 {
return false
}
if a1 != -1 {
2019-09-01 11:07:11 +02:00
return false
2015-01-07 03:00:15 +01:00
}
2019-09-01 11:07:11 +02:00
if f0 != -1 && f1 != -1 {
return f0 < f1
}
return k0 < k1
2015-01-07 03:00:15 +01:00
}
}
const license = `// Copyright 2013 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.
`
2015-01-06 18:25:26 +01:00
func main() {
// Follow the standard comment rule (https://golang.org/s/generatedcode).
doNotEdit := "// Code generated by genkeys.go using 'go generate'. DO NOT EDIT."
2015-01-07 15:21:52 +01:00
2019-09-01 14:12:29 +02:00
ebitenKeyNames := []string{}
ebitenKeyNamesWithoutMods := []string{}
driverKeyNames := []string{}
2020-02-19 02:42:42 +01:00
for name := range nameToJSKey {
driverKeyNames = append(driverKeyNames, name)
if !strings.HasSuffix(name, "Alt") && !strings.HasSuffix(name, "Control") && !strings.HasSuffix(name, "Shift") {
ebitenKeyNames = append(ebitenKeyNames, name)
2019-09-01 14:12:29 +02:00
ebitenKeyNamesWithoutMods = append(ebitenKeyNamesWithoutMods, name)
continue
}
if name == "LeftAlt" {
ebitenKeyNames = append(ebitenKeyNames, "Alt")
continue
}
if name == "LeftControl" {
ebitenKeyNames = append(ebitenKeyNames, "Control")
continue
}
if name == "LeftShift" {
ebitenKeyNames = append(ebitenKeyNames, "Shift")
continue
2015-01-07 15:18:40 +01:00
}
2015-01-06 18:25:26 +01:00
}
2019-09-01 14:12:29 +02:00
sort.Slice(ebitenKeyNames, keyNamesLess(ebitenKeyNames))
sort.Slice(ebitenKeyNamesWithoutMods, keyNamesLess(ebitenKeyNamesWithoutMods))
sort.Slice(driverKeyNames, keyNamesLess(driverKeyNames))
2015-01-06 18:25:26 +01:00
for path, tmpl := range map[string]string{
filepath.Join("event", "keys.go"): eventKeysTmpl,
filepath.Join("internal", "driver", "keys.go"): driverKeysTmpl,
filepath.Join("internal", "glfw", "keys.go"): glfwKeysTmpl,
filepath.Join("internal", "uidriver", "glfw", "keys.go"): uidriverGlfwKeysTmpl,
filepath.Join("internal", "uidriver", "js", "keys.go"): uidriverJsKeysTmpl,
filepath.Join("keys.go"): ebitenKeysTmpl,
filepath.Join("mobile", "ebitenmobileview", "keys_android.go"): mobileAndroidKeysTmpl,
2015-01-06 18:25:26 +01:00
} {
f, err := os.Create(path)
if err != nil {
log.Fatal(err)
}
defer f.Close()
funcs := template.FuncMap{
"ToLower": strings.ToLower,
}
tmpl, err := template.New(path).Funcs(funcs).Parse(tmpl)
2015-01-06 18:25:26 +01:00
if err != nil {
log.Fatal(err)
}
// The build tag can't be included in the templates because of `go vet`.
// Pass the build tag and extract this in the template to make `go vet` happy.
buildTag := ""
switch path {
case "internal/uidriver/glfw/keys.go":
2017-05-11 12:09:13 +02:00
buildTag = "// +build darwin freebsd linux windows" +
2016-06-27 19:51:50 +02:00
"\n// +build !js" +
"\n// +build !android" +
"\n// +build !ios"
case "internal/uidriver/js/keys.go":
buildTag = "// +build js"
}
2015-01-06 18:25:26 +01:00
// NOTE: According to godoc, maps are automatically sorted by key.
2019-09-01 11:07:11 +02:00
if err := tmpl.Execute(f, struct {
2019-09-01 14:12:29 +02:00
License string
DoNotEdit string
BuildTag string
2020-02-19 02:42:42 +01:00
NameToJSKey map[string]string
EdgeKeyCodeToName map[int]string
2019-09-01 14:12:29 +02:00
EbitenKeyNames []string
EbitenKeyNamesWithoutMods []string
DriverKeyNames []string
2020-02-19 02:42:42 +01:00
NameToGLFWKey map[string]glfw.Key
NameToAndroidKey map[string]int
2019-09-01 11:07:11 +02:00
}{
2019-09-01 14:12:29 +02:00
License: license,
DoNotEdit: doNotEdit,
BuildTag: buildTag,
2020-02-19 02:42:42 +01:00
NameToJSKey: nameToJSKey,
EdgeKeyCodeToName: edgeKeyCodeToName,
2019-09-01 14:12:29 +02:00
EbitenKeyNames: ebitenKeyNames,
EbitenKeyNamesWithoutMods: ebitenKeyNamesWithoutMods,
DriverKeyNames: driverKeyNames,
2020-02-19 02:42:42 +01:00
NameToGLFWKey: nameToGLFWKey,
NameToAndroidKey: nameToAndroidKey,
2015-01-18 13:11:03 +01:00
}); err != nil {
log.Fatal(err)
}
2015-01-06 18:25:26 +01:00
}
}