ebiten/genkeys.go

1080 lines
30 KiB
Go

// 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.
//go:build ignore
// +build ignore
// The key name convention follows the Web standard: https://www.w3.org/TR/uievents-code/#keyboard-key-codes
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"text/template"
"github.com/go-gl/glfw/v3.3/glfw"
"golang.org/x/mobile/event/key"
)
var (
glfwKeyNameToGLFWKey map[string]glfw.Key
uiKeyNameToGLFWKeyName map[string]string
androidKeyToUIKeyName map[int]string
gbuildKeyToUIKeyName map[key.Code]string
uiKeyNameToJSKey map[string]string
uiKeyNameToCGKey map[string]string
edgeKeyCodeToName map[int]string
oldEbitenKeyNameToUIKeyName map[string]string
)
func init() {
glfwKeyNameToGLFWKey = 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,
"KPDecimal": glfw.KeyKPDecimal,
"KPDivide": glfw.KeyKPDivide,
"KPMultiply": glfw.KeyKPMultiply,
"KPSubtract": glfw.KeyKPSubtract,
"KPAdd": glfw.KeyKPAdd,
"KPEnter": glfw.KeyKPEnter,
"KPEqual": glfw.KeyKPEqual,
"Last": glfw.KeyLast,
}
uiKeyNameToGLFWKeyName = map[string]string{
"Space": "Space",
"Quote": "Apostrophe",
"Comma": "Comma",
"Minus": "Minus",
"Period": "Period",
"Slash": "Slash",
"Semicolon": "Semicolon",
"Equal": "Equal",
"BracketLeft": "LeftBracket",
"Backslash": "Backslash",
"BracketRight": "RightBracket",
"Backquote": "GraveAccent",
"Escape": "Escape",
"Enter": "Enter",
"Tab": "Tab",
"Backspace": "Backspace",
"Insert": "Insert",
"Delete": "Delete",
"ArrowRight": "Right",
"ArrowLeft": "Left",
"ArrowDown": "Down",
"ArrowUp": "Up",
"PageUp": "PageUp",
"PageDown": "PageDown",
"Home": "Home",
"End": "End",
"CapsLock": "CapsLock",
"ScrollLock": "ScrollLock",
"NumLock": "NumLock",
"PrintScreen": "PrintScreen",
"Pause": "Pause",
"ShiftLeft": "LeftShift",
"ControlLeft": "LeftControl",
"AltLeft": "LeftAlt",
"MetaLeft": "LeftSuper",
"ShiftRight": "RightShift",
"ControlRight": "RightControl",
"AltRight": "RightAlt",
"MetaRight": "RightSuper",
"ContextMenu": "Menu",
"NumpadAdd": "KPAdd",
"NumpadDecimal": "KPDecimal",
"NumpadDivide": "KPDivide",
"NumpadMultiply": "KPMultiply",
"NumpadSubtract": "KPSubtract",
"NumpadEnter": "KPEnter",
"NumpadEqual": "KPEqual",
}
// https://developer.android.com/reference/android/view/KeyEvent
androidKeyToUIKeyName = map[int]string{
55: "Comma",
56: "Period",
57: "AltLeft",
58: "AltRight",
115: "CapsLock",
113: "ControlLeft",
114: "ControlRight",
59: "ShiftLeft",
60: "ShiftRight",
66: "Enter",
62: "Space",
61: "Tab",
112: "Delete", // KEYCODE_FORWARD_DEL
123: "End",
122: "Home",
124: "Insert",
93: "PageDown",
92: "PageUp",
20: "ArrowDown",
21: "ArrowLeft",
22: "ArrowRight",
19: "ArrowUp",
111: "Escape",
67: "Backspace", // KEYCODE_DEL
75: "Quote",
69: "Minus",
76: "Slash",
74: "Semicolon",
70: "Equal",
71: "BracketLeft",
73: "Backslash",
72: "BracketRight",
68: "Backquote",
143: "NumLock",
121: "Pause", // KEYCODE_BREAK
120: "PrintScreen", // KEYCODE_SYSRQ
116: "ScrollLock",
82: "ContextMenu",
157: "NumpadAdd",
158: "NumpadDecimal",
154: "NumpadDivide",
155: "NumpadMultiply",
156: "NumpadSubtract",
160: "NumpadEnter",
161: "NumpadEqual",
117: "MetaLeft",
118: "MetaRight",
}
gbuildKeyToUIKeyName = map[key.Code]string{
key.CodeComma: "Comma",
key.CodeFullStop: "Period",
key.CodeLeftAlt: "AltLeft",
key.CodeRightAlt: "AltRight",
key.CodeCapsLock: "CapsLock",
key.CodeLeftControl: "ControlLeft",
key.CodeRightControl: "ControlRight",
key.CodeLeftShift: "ShiftLeft",
key.CodeRightShift: "ShiftRight",
key.CodeReturnEnter: "Enter",
key.CodeSpacebar: "Space",
key.CodeTab: "Tab",
key.CodeDeleteForward: "Delete",
key.CodeEnd: "End",
key.CodeHome: "Home",
key.CodeInsert: "Insert",
key.CodePageDown: "PageDown",
key.CodePageUp: "PageUp",
key.CodeDownArrow: "ArrowDown",
key.CodeLeftArrow: "ArrowLeft",
key.CodeRightArrow: "ArrowRight",
key.CodeUpArrow: "ArrowUp",
key.CodeEscape: "Escape",
key.CodeDeleteBackspace: "Backspace",
key.CodeApostrophe: "Quote",
key.CodeHyphenMinus: "Minus",
key.CodeSlash: "Slash",
key.CodeSemicolon: "Semicolon",
key.CodeEqualSign: "Equal",
key.CodeLeftSquareBracket: "BracketLeft",
key.CodeBackslash: "Backslash",
key.CodeRightSquareBracket: "BracketRight",
key.CodeGraveAccent: "Backquote",
key.CodeKeypadNumLock: "NumLock",
key.CodePause: "Pause",
key.CodeKeypadPlusSign: "NumpadAdd",
key.CodeKeypadFullStop: "NumpadDecimal",
key.CodeKeypadSlash: "NumpadDivide",
key.CodeKeypadAsterisk: "NumpadMultiply",
key.CodeKeypadHyphenMinus: "NumpadSubtract",
key.CodeKeypadEnter: "NumpadEnter",
key.CodeKeypadEqualSign: "NumpadEqual",
key.CodeLeftGUI: "MetaLeft",
key.CodeRightGUI: "MetaRight",
// Missing keys:
// ui.KeyPrintScreen
// ui.KeyScrollLock
// ui.KeyMenu
}
// The UI key and JS key are almost same but very slightly different (e.g., 'A' vs 'KeyA').
uiKeyNameToJSKey = map[string]string{
"Comma": "Comma",
"Period": "Period",
"AltLeft": "AltLeft",
"AltRight": "AltRight",
"CapsLock": "CapsLock",
"ControlLeft": "ControlLeft",
"ControlRight": "ControlRight",
"ShiftLeft": "ShiftLeft",
"ShiftRight": "ShiftRight",
"Enter": "Enter",
"Space": "Space",
"Tab": "Tab",
"Delete": "Delete",
"End": "End",
"Home": "Home",
"Insert": "Insert",
"PageDown": "PageDown",
"PageUp": "PageUp",
"ArrowDown": "ArrowDown",
"ArrowLeft": "ArrowLeft",
"ArrowRight": "ArrowRight",
"ArrowUp": "ArrowUp",
"Escape": "Escape",
"Backspace": "Backspace",
"Quote": "Quote",
"Minus": "Minus",
"Slash": "Slash",
"Semicolon": "Semicolon",
"Equal": "Equal",
"BracketLeft": "BracketLeft",
"Backslash": "Backslash",
"BracketRight": "BracketRight",
"Backquote": "Backquote",
"NumLock": "NumLock",
"Pause": "Pause",
"PrintScreen": "PrintScreen",
"ScrollLock": "ScrollLock",
"ContextMenu": "ContextMenu",
"NumpadAdd": "NumpadAdd",
"NumpadDecimal": "NumpadDecimal",
"NumpadDivide": "NumpadDivide",
"NumpadMultiply": "NumpadMultiply",
"NumpadSubtract": "NumpadSubtract",
"NumpadEnter": "NumpadEnter",
"NumpadEqual": "NumpadEqual",
"MetaLeft": "MetaLeft",
"MetaRight": "MetaRight",
}
// https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values
// Missing keys:
//
// - kVK_Function
// - kVK_VolumeUp
// - kVK_VolumeDown
// - kVK_Mute
// - kVK_Help
// - kVK_ISO_Section ("IntlBackslash")
// - kVK_JIS_Yen ("IntlYen")
// - kVK_JIS_Underscore ("IntlRo")
// - kVK_JIS_KeypadComma ("NumpadComma")
// - kVK_JIS_Eisu
// - kVK_JIS_Kana
uiKeyNameToCGKey = map[string]string{
"Equal": "kVK_ANSI_Equal",
"Minus": "kVK_ANSI_Minus",
"BracketRight": "kVK_ANSI_RightBracket",
"BracketLeft": "kVK_ANSI_LeftBracket",
"Quote": "kVK_ANSI_Quote",
"Semicolon": "kVK_ANSI_Semicolon",
"Backslash": "kVK_ANSI_Backslash",
"Comma": "kVK_ANSI_Comma",
"Slash": "kVK_ANSI_Slash",
"Period": "kVK_ANSI_Period",
"Backquote": "kVK_ANSI_Grave",
"NumpadDecimal": "kVK_ANSI_KeypadDecimal",
"NumpadMultiply": "kVK_ANSI_KeypadMultiply",
"NumpadAdd": "kVK_ANSI_KeypadPlus",
"NumLock": "kVK_ANSI_KeypadClear",
"NumpadDivide": "kVK_ANSI_KeypadDivide",
"NumpadEnter": "kVK_ANSI_KeypadEnter",
"NumpadSubtract": "kVK_ANSI_KeypadMinus",
"NumpadEqual": "kVK_ANSI_KeypadEquals",
"Enter": "kVK_Return",
"Tab": "kVK_Tab",
"Space": "kVK_Space",
"Backspace": "kVK_Delete",
"Escape": "kVK_Escape",
"MetaLeft": "kVK_Command",
"ShiftLeft": "kVK_Shift",
"CapsLock": "kVK_CapsLock",
"AltLeft": "kVK_Option",
"ControlLeft": "kVK_Control",
"ShiftRight": "kVK_RightShift",
"AltRight": "kVK_RightOption",
"ControlRight": "kVK_RightControl",
"Home": "kVK_Home",
"PageUp": "kVK_PageUp",
"Delete": "kVK_ForwardDelete",
"End": "kVK_End",
"PageDown": "kVK_PageDown",
"ArrowLeft": "kVK_LeftArrow",
"ArrowRight": "kVK_RightArrow",
"ArrowDown": "kVK_DownArrow",
"ArrowUp": "kVK_UpArrow",
}
// ASCII: 0 - 9
for c := '0'; c <= '9'; c++ {
glfwKeyNameToGLFWKey[string(c)] = glfw.Key0 + glfw.Key(c) - '0'
name := "Digit" + string(c)
uiKeyNameToGLFWKeyName[name] = string(c)
androidKeyToUIKeyName[7+int(c)-'0'] = name
// Gomobile's key code (= USB HID key codes) has successive key codes for 1, 2, ..., 9, 0
// in this order.
if c == '0' {
gbuildKeyToUIKeyName[key.Code0] = name
} else {
gbuildKeyToUIKeyName[key.Code1+key.Code(c)-'1'] = name
}
uiKeyNameToJSKey[name] = name
uiKeyNameToCGKey[name] = fmt.Sprintf("kVK_ANSI_%d", c-'0')
}
// ASCII: A - Z
for c := 'A'; c <= 'Z'; c++ {
glfwKeyNameToGLFWKey[string(c)] = glfw.KeyA + glfw.Key(c) - 'A'
uiKeyNameToGLFWKeyName[string(c)] = string(c)
androidKeyToUIKeyName[29+int(c)-'A'] = string(c)
gbuildKeyToUIKeyName[key.CodeA+key.Code(c)-'A'] = string(c)
uiKeyNameToJSKey[string(c)] = "Key" + string(c)
uiKeyNameToCGKey[string(c)] = "kVK_ANSI_" + string(c)
}
// Function keys
for i := 1; i <= 12; i++ {
name := "F" + strconv.Itoa(i)
glfwKeyNameToGLFWKey[name] = glfw.KeyF1 + glfw.Key(i) - 1
uiKeyNameToGLFWKeyName[name] = name
androidKeyToUIKeyName[131+i-1] = name
gbuildKeyToUIKeyName[key.CodeF1+key.Code(i)-1] = name
uiKeyNameToJSKey[name] = name
uiKeyNameToCGKey[name] = fmt.Sprintf("kVK_F%d", i)
}
// Numpad
// https://www.w3.org/TR/uievents-code/#key-numpad-section
for c := '0'; c <= '9'; c++ {
name := "Numpad" + string(c)
glfwKeyNameToGLFWKey["KP"+string(c)] = glfw.KeyKP0 + glfw.Key(c) - '0'
uiKeyNameToGLFWKeyName[name] = "KP" + string(c)
androidKeyToUIKeyName[144+int(c)-'0'] = name
// Gomobile's key code (= USB HID key codes) has successive key codes for 1, 2, ..., 9, 0
// in this order.
if c == '0' {
gbuildKeyToUIKeyName[key.CodeKeypad0] = name
} else {
gbuildKeyToUIKeyName[key.CodeKeypad1+key.Code(c)-'1'] = name
}
uiKeyNameToJSKey[name] = name
uiKeyNameToCGKey[name] = fmt.Sprintf("kVK_ANSI_Keypad%d", c-'0')
}
// Keys for backward compatibility
oldEbitenKeyNameToUIKeyName = map[string]string{
"0": "Digit0",
"1": "Digit1",
"2": "Digit2",
"3": "Digit3",
"4": "Digit4",
"5": "Digit5",
"6": "Digit6",
"7": "Digit7",
"8": "Digit8",
"9": "Digit9",
"Apostrophe": "Quote",
"Down": "ArrowDown",
"GraveAccent": "Backquote",
"KP0": "Numpad0",
"KP1": "Numpad1",
"KP2": "Numpad2",
"KP3": "Numpad3",
"KP4": "Numpad4",
"KP5": "Numpad5",
"KP6": "Numpad6",
"KP7": "Numpad7",
"KP8": "Numpad8",
"KP9": "Numpad9",
"KPAdd": "NumpadAdd",
"KPDecimal": "NumpadDecimal",
"KPDivide": "NumpadDivide",
"KPMultiply": "NumpadMultiply",
"KPSubtract": "NumpadSubtract",
"KPEnter": "NumpadEnter",
"KPEqual": "NumpadEqual",
"Left": "ArrowLeft",
"LeftBracket": "BracketLeft",
"Menu": "ContextMenu",
"Right": "ArrowRight",
"RightBracket": "BracketRight",
"Up": "ArrowUp",
}
}
func init() {
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
// TODO: How should we treat modifier keys? Now 'left' modifier keys are available.
edgeKeyCodeToName = map[int]string{
0xbc: "Comma",
0xbe: "Period",
0x12: "AltLeft",
0x14: "CapsLock",
0x11: "ControlLeft",
0x10: "ShiftLeft",
0x0D: "Enter",
0x20: "Space",
0x09: "Tab",
0x2E: "Delete",
0x23: "End",
0x24: "Home",
0x2D: "Insert",
0x22: "PageDown",
0x21: "PageUp",
0x28: "ArrowDown",
0x25: "ArrowLeft",
0x27: "ArrowRight",
0x26: "ArrowUp",
0x1B: "Escape",
0xde: "Quote",
0xbd: "Minus",
0xbf: "Slash",
0xba: "Semicolon",
0xbb: "Equal",
0xdb: "BracketLeft",
0xdc: "Backslash",
0xdd: "BracketRight",
0xc0: "Backquote",
0x08: "Backspace",
0x90: "NumLock",
0x6b: "NumpadAdd",
0x6e: "NumpadDecimal",
0x6f: "NumpadDivide",
0x6a: "NumpadMultiply",
0x6d: "NumpadSubtract",
0x13: "Pause",
0x91: "ScrollLock",
0x5d: "ContextMenu",
0x5b: "MetaLeft",
0x5c: "MetaRight",
// On Edge, this key does not work. PrintScreen works only on keyup event.
// 0x2C: "PrintScreen",
// On Edge, it is impossible to tell NumpadEnter and Enter / NumpadEqual and Equal.
// 0x0d: "NumpadEnter",
// 0x0c: "NumpadEqual",
}
// ASCII: 0 - 9
for c := '0'; c <= '9'; c++ {
edgeKeyCodeToName[int(c)] = "Digit" + string(c)
}
// ASCII: A - Z
for c := 'A'; c <= 'Z'; c++ {
edgeKeyCodeToName[int(c)] = string(c)
}
// Function keys
for i := 1; i <= 12; i++ {
edgeKeyCodeToName[0x70+i-1] = "F" + strconv.Itoa(i)
}
// Numpad keys
for c := '0'; c <= '9'; c++ {
edgeKeyCodeToName[0x60+int(c-'0')] = "Numpad" + string(c)
}
}
const ebitenKeysTmpl = `{{.License}}
{{.DoNotEdit}}
package ebiten
import (
"fmt"
"strings"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
// A Key represents a keyboard key.
// These keys represent pysical keys of US keyboard.
// For example, KeyQ represents Q key on US keyboards and ' (quote) key on Dvorak keyboards.
type Key int
// Keys.
const (
{{range $index, $name := .EbitenKeyNamesWithoutMods}}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)
KeyMax Key = KeyMeta
// Keys for backward compatibility.
// Deprecated: as of v2.1.
{{range $old, $new := .OldEbitenKeyNameToUIKeyName}}Key{{$old}} Key = Key(ui.Key{{$new}})
{{end}}
)
func (k Key) isValid() bool {
switch k {
{{range $name := .EbitenKeyNamesWithoutOld}}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.
func (k Key) String() string {
switch k {
{{range $name := .EbitenKeyNamesWithoutOld}}case Key{{$name}}:
return {{$name | printf "%q"}}
{{end}}}
return ""
}
func keyNameToKeyCode(name string) (Key, bool) {
switch strings.ToLower(name) {
{{range $name := .EbitenKeyNames}}case {{$name | printf "%q" | ToLower}}:
return Key{{$name}}, true
{{end}}}
return 0, false
}
// MarshalText implements encoding.TextMarshaler.
func (k Key) MarshalText() ([]byte, error) {
return []byte(k.String()), nil
}
// UnmarshalText implements encoding.TextUnmarshaler
func (k *Key) UnmarshalText(text []byte) error {
key, ok := keyNameToKeyCode(string(text))
if !ok {
return fmt.Errorf("ebiten: unexpected key name: %s", string(text))
}
*k = key
return nil
}
`
const uiKeysTmpl = `{{.License}}
{{.DoNotEdit}}
package ui
import (
"fmt"
)
type Key int
const (
{{range $index, $name := .UIKeyNames}}Key{{$name}}{{if eq $index 0}} Key = iota{{end}}
{{end}} KeyReserved0
KeyReserved1
KeyReserved2
KeyReserved3
)
func (k Key) String() string {
switch k {
{{range $index, $name := .UIKeyNames}}case Key{{$name}}:
return {{$name | printf "Key%s" | printf "%q"}}
{{end}}}
panic(fmt.Sprintf("ui: invalid key: %d", k))
}
`
const eventKeysTmpl = `{{.License}}
{{.DoNotEdit}}
package event
import (
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
type Key = ui.Key
const (
{{range $index, $name := .UIKeyNames}}Key{{$name}} = ui.Key{{$name}}
{{end}}
)
`
const uiGLFWKeysTmpl = `{{.License}}
{{.DoNotEdit}}
{{.BuildTag}}
package ui
import (
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
)
var uiKeyToGLFWKey = map[Key]glfw.Key{
{{range $dname, $gname := .UIKeyNameToGLFWKeyName}}Key{{$dname}}: glfw.Key{{$gname}},
{{end}}
}
`
const uiJSKeysTmpl = `{{.License}}
{{.DoNotEdit}}
{{.BuildTag}}
package ui
import (
"syscall/js"
)
var uiKeyToJSKey = map[Key]js.Value{
{{range $name, $code := .UIKeyNameToJSKey}}Key{{$name}}: js.ValueOf({{$code | printf "%q"}}),
{{end}}
}
var edgeKeyCodeToUIKey = map[int]Key{
{{range $code, $name := .EdgeKeyCodeToName}}{{$code}}: Key{{$name}},
{{end}}
}
`
const uiDarwinKeysTmpl = `{{.License}}
{{.DoNotEdit}}
package ui
const (
kVK_ANSI_A = 0x00
kVK_ANSI_S = 0x01
kVK_ANSI_D = 0x02
kVK_ANSI_F = 0x03
kVK_ANSI_H = 0x04
kVK_ANSI_G = 0x05
kVK_ANSI_Z = 0x06
kVK_ANSI_X = 0x07
kVK_ANSI_C = 0x08
kVK_ANSI_V = 0x09
kVK_ANSI_B = 0x0B
kVK_ANSI_Q = 0x0C
kVK_ANSI_W = 0x0D
kVK_ANSI_E = 0x0E
kVK_ANSI_R = 0x0F
kVK_ANSI_Y = 0x10
kVK_ANSI_T = 0x11
kVK_ANSI_1 = 0x12
kVK_ANSI_2 = 0x13
kVK_ANSI_3 = 0x14
kVK_ANSI_4 = 0x15
kVK_ANSI_6 = 0x16
kVK_ANSI_5 = 0x17
kVK_ANSI_Equal = 0x18
kVK_ANSI_9 = 0x19
kVK_ANSI_7 = 0x1A
kVK_ANSI_Minus = 0x1B
kVK_ANSI_8 = 0x1C
kVK_ANSI_0 = 0x1D
kVK_ANSI_RightBracket = 0x1E
kVK_ANSI_O = 0x1F
kVK_ANSI_U = 0x20
kVK_ANSI_LeftBracket = 0x21
kVK_ANSI_I = 0x22
kVK_ANSI_P = 0x23
kVK_ANSI_L = 0x25
kVK_ANSI_J = 0x26
kVK_ANSI_Quote = 0x27
kVK_ANSI_K = 0x28
kVK_ANSI_Semicolon = 0x29
kVK_ANSI_Backslash = 0x2A
kVK_ANSI_Comma = 0x2B
kVK_ANSI_Slash = 0x2C
kVK_ANSI_N = 0x2D
kVK_ANSI_M = 0x2E
kVK_ANSI_Period = 0x2F
kVK_ANSI_Grave = 0x32
kVK_ANSI_KeypadDecimal = 0x41
kVK_ANSI_KeypadMultiply = 0x43
kVK_ANSI_KeypadPlus = 0x45
kVK_ANSI_KeypadClear = 0x47
kVK_ANSI_KeypadDivide = 0x4B
kVK_ANSI_KeypadEnter = 0x4C
kVK_ANSI_KeypadMinus = 0x4E
kVK_ANSI_KeypadEquals = 0x51
kVK_ANSI_Keypad0 = 0x52
kVK_ANSI_Keypad1 = 0x53
kVK_ANSI_Keypad2 = 0x54
kVK_ANSI_Keypad3 = 0x55
kVK_ANSI_Keypad4 = 0x56
kVK_ANSI_Keypad5 = 0x57
kVK_ANSI_Keypad6 = 0x58
kVK_ANSI_Keypad7 = 0x59
kVK_ANSI_Keypad8 = 0x5B
kVK_ANSI_Keypad9 = 0x5C
// keycodes for keys that are independent of keyboard layout
kVK_Return = 0x24
kVK_Tab = 0x30
kVK_Space = 0x31
kVK_Delete = 0x33
kVK_Escape = 0x35
kVK_Command = 0x37
kVK_Shift = 0x38
kVK_CapsLock = 0x39
kVK_Option = 0x3A
kVK_Control = 0x3B
kVK_RightShift = 0x3C
kVK_RightOption = 0x3D
kVK_RightControl = 0x3E
kVK_Function = 0x3F
kVK_F17 = 0x40
kVK_VolumeUp = 0x48
kVK_VolumeDown = 0x49
kVK_Mute = 0x4A
kVK_F18 = 0x4F
kVK_F19 = 0x50
kVK_F20 = 0x5A
kVK_F5 = 0x60
kVK_F6 = 0x61
kVK_F7 = 0x62
kVK_F3 = 0x63
kVK_F8 = 0x64
kVK_F9 = 0x65
kVK_F11 = 0x67
kVK_F13 = 0x69
kVK_F16 = 0x6A
kVK_F14 = 0x6B
kVK_F10 = 0x6D
kVK_F12 = 0x6F
kVK_F15 = 0x71
kVK_Help = 0x72
kVK_Home = 0x73
kVK_PageUp = 0x74
kVK_ForwardDelete = 0x75
kVK_F4 = 0x76
kVK_End = 0x77
kVK_F2 = 0x78
kVK_PageDown = 0x79
kVK_F1 = 0x7A
kVK_LeftArrow = 0x7B
kVK_RightArrow = 0x7C
kVK_DownArrow = 0x7D
kVK_UpArrow = 0x7E
// ISO keyboards only
kVK_ISO_Section = 0x0A
// JIS keyboards only
kVK_JIS_Yen = 0x5D
kVK_JIS_Underscore = 0x5E
kVK_JIS_KeypadComma = 0x5F
kVK_JIS_Eisu = 0x66
kVK_JIS_Kana = 0x68
)
var uiKeyToCGKey = map[Key]int{
{{range $uname, $cname := .UIKeyNameToCGKey}}Key{{$uname}}: {{$cname}},
{{end}}
}
`
const glfwKeysTmpl = `{{.License}}
{{.DoNotEdit}}
{{.BuildTag}}
package glfw
const (
{{range $name, $key := .GLFWKeyNameToGLFWKey}}Key{{$name}} = Key({{$key}})
{{end}}
)
`
const mobileAndroidKeysTmpl = `{{.License}}
{{.DoNotEdit}}
{{.BuildTag}}
package ebitenmobileview
import (
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
var androidKeyToUIKey = map[int]ui.Key{
{{range $key, $name := .AndroidKeyToUIKeyName}}{{$key}}: ui.Key{{$name}},
{{end}}
}
`
const uiMobileKeysTmpl = `{{.License}}
{{.DoNotEdit}}
{{.BuildTag}}
package ui
import (
"golang.org/x/mobile/event/key"
)
var gbuildKeyToUIKey = map[key.Code]Key{
{{range $key, $name := .GBuildKeyToUIKeyName}}key.{{$key}}: Key{{$name}},
{{end}}
}
`
func digitKey(name string) int {
if len(name) != 1 {
return -1
}
c := name[0]
if c < '0' || '9' < c {
return -1
}
return int(c - '0')
}
func alphabetKey(name string) rune {
if len(name) != 1 {
return -1
}
c := rune(name[0])
if c < 'A' || 'Z' < c {
return -1
}
return c
}
func functionKey(name string) int {
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
}
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
}
if d1 != -1 {
return false
}
if a1 != -1 {
return false
}
if f0 != -1 && f1 != -1 {
return f0 < f1
}
return k0 < k1
}
}
const license = `// Copyright 2013 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.
`
func main() {
// Follow the standard comment rule (https://golang.org/s/generatedcode).
doNotEdit := "// Code generated by genkeys.go using 'go generate'. DO NOT EDIT."
ebitenKeyNames := []string{}
ebitenKeyNamesWithoutOld := []string{}
ebitenKeyNamesWithoutMods := []string{}
uiKeyNames := []string{}
for name := range uiKeyNameToJSKey {
uiKeyNames = append(uiKeyNames, name)
ebitenKeyNames = append(ebitenKeyNames, name)
ebitenKeyNamesWithoutOld = append(ebitenKeyNamesWithoutOld, name)
ebitenKeyNamesWithoutMods = append(ebitenKeyNamesWithoutMods, name)
}
for old := range oldEbitenKeyNameToUIKeyName {
ebitenKeyNames = append(ebitenKeyNames, old)
}
// Keys for modifiers
ebitenKeyNames = append(ebitenKeyNames, "Alt", "Control", "Shift", "Meta")
ebitenKeyNamesWithoutOld = append(ebitenKeyNamesWithoutOld, "Alt", "Control", "Shift", "Meta")
sort.Slice(ebitenKeyNames, keyNamesLess(ebitenKeyNames))
sort.Slice(ebitenKeyNamesWithoutOld, keyNamesLess(ebitenKeyNamesWithoutOld))
sort.Slice(ebitenKeyNamesWithoutMods, keyNamesLess(ebitenKeyNamesWithoutMods))
sort.Slice(uiKeyNames, keyNamesLess(uiKeyNames))
// TODO: Add this line for event package (#926).
//
// filepath.Join("event", "keys.go"): eventKeysTmpl,
for path, tmpl := range map[string]string{
filepath.Join("internal", "glfw", "keys.go"): glfwKeysTmpl,
filepath.Join("internal", "ui", "keys.go"): uiKeysTmpl,
filepath.Join("internal", "ui", "keys_glfw.go"): uiGLFWKeysTmpl,
filepath.Join("internal", "ui", "keys_mobile.go"): uiMobileKeysTmpl,
filepath.Join("internal", "ui", "keys_js.go"): uiJSKeysTmpl,
filepath.Join("internal", "ui", "keys_darwin.go"): uiDarwinKeysTmpl,
filepath.Join("keys.go"): ebitenKeysTmpl,
filepath.Join("mobile", "ebitenmobileview", "keys_android.go"): mobileAndroidKeysTmpl,
} {
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)
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 filepath.Join("internal", "glfw", "keys.go"):
buildTag = "//go:build !js" +
"\n// +build !js"
case filepath.Join("internal", "ui", "keys_mobile.go"):
buildTag = "//go:build (android || ios) && !ebitenginecbackend && !ebitencbackend" +
"\n// +build android ios" +
"\n// +build !ebitenginecbackend && !ebitencbackend"
case filepath.Join("internal", "ui", "keys_glfw.go"):
buildTag = "//go:build !android && !ios && !js && !ebitenginecbackend && !ebitencbackend" +
"\n// +build !android,!ios,!js,!ebitenginecbackend && !ebitencbackend"
case filepath.Join("internal", "ui", "keys_darwin.go"):
buildTag = "//go:build !ios" +
"\n// +build !ios"
}
// NOTE: According to godoc, maps are automatically sorted by key.
if err := tmpl.Execute(f, struct {
License string
DoNotEdit string
BuildTag string
UIKeyNameToJSKey map[string]string
UIKeyNameToCGKey map[string]string
EdgeKeyCodeToName map[int]string
EbitenKeyNames []string
EbitenKeyNamesWithoutOld []string
EbitenKeyNamesWithoutMods []string
GLFWKeyNameToGLFWKey map[string]glfw.Key
UIKeyNames []string
UIKeyNameToGLFWKeyName map[string]string
AndroidKeyToUIKeyName map[int]string
GBuildKeyToUIKeyName map[key.Code]string
OldEbitenKeyNameToUIKeyName map[string]string
}{
License: license,
DoNotEdit: doNotEdit,
BuildTag: buildTag,
UIKeyNameToJSKey: uiKeyNameToJSKey,
UIKeyNameToCGKey: uiKeyNameToCGKey,
EdgeKeyCodeToName: edgeKeyCodeToName,
EbitenKeyNames: ebitenKeyNames,
EbitenKeyNamesWithoutOld: ebitenKeyNamesWithoutOld,
EbitenKeyNamesWithoutMods: ebitenKeyNamesWithoutMods,
GLFWKeyNameToGLFWKey: glfwKeyNameToGLFWKey,
UIKeyNames: uiKeyNames,
UIKeyNameToGLFWKeyName: uiKeyNameToGLFWKeyName,
AndroidKeyToUIKeyName: androidKeyToUIKeyName,
GBuildKeyToUIKeyName: gbuildKeyToUIKeyName,
OldEbitenKeyNameToUIKeyName: oldEbitenKeyNameToUIKeyName,
}); err != nil {
log.Fatal(err)
}
}
}