repo_description: A dead simple 2D game engine for Go
repo_topics: ebiten ebitengine game game-2d game-development game-engine game-engine-2d game-library gamedev go golang golang-library
code: // 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
  
  // The key name convention follows the Web standard: https://www.w3.org/TR/uievents-code/#code-value-tables
  
  package main
  
  import (
  	"bufio"
  	"log"
  	"os"
  	"path/filepath"
  	"sort"
  	"strconv"
  	"strings"
  	"text/template"
  )
  
  var (
  	glfwKeyNameToGLFWKey            map[string]int
  	uiKeyNameToGLFWKeyName          map[string]string
  	androidKeyToUIKeyName           map[int]string
  	iosKeyToUIKeyName               map[int]string
  	uiKeyNameToJSCode               map[string]string
  	oldEbitengineKeyNameToUIKeyName map[string]string
  )
  
  func init() {
  	glfwKeyNameToGLFWKey = map[string]int{
  		"Unknown":      -1,
  		"Space":        32,
  		"Apostrophe":   39,
  		"Comma":        44,
  		"Minus":        45,
  		"Period":       46,
  		"Slash":        47,
  		"Semicolon":    59,
  		"Equal":        61,
  		"LeftBracket":  91,
  		"Backslash":    92,
  		"RightBracket": 93,
  		"GraveAccent":  96,
  		"World1":       161,
  		"World2":       162,
  		"Escape":       256,
  		"Enter":        257,
  		"Tab":          258,
  		"Backspace":    259,
  		"Insert":       260,
  		"Delete":       261,
  		"Right":        262,
  		"Left":         263,
  		"Down":         264,
  		"Up":           265,
  		"PageUp":       266,
  		"PageDown":     267,
  		"Home":         268,
  		"End":          269,
  		"CapsLock":     280,
  		"ScrollLock":   281,
  		"NumLock":      282,
  		"PrintScreen":  283,
  		"Pause":        284,
  		"LeftShift":    340,
  		"LeftControl":  341,
  		"LeftAlt":      342,
  		"LeftSuper":    343,
  		"RightShift":   344,
  		"RightControl": 345,
  		"RightAlt":     346,
  		"RightSuper":   347,
  		"Menu":         348,
  		"KPDecimal":    330,
  		"KPDivide":     331,
  		"KPMultiply":   332,
  		"KPSubtract":   333,
  		"KPAdd":        334,
  		"KPEnter":      335,
  		"KPEqual":      336,
  		"Last":         348,
  	}
  
  	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",
  		"IntlBackslash":  "World1",
  	}
  
  	// https://developer.android.com/reference/android/view/KeyEvent
  	//
  	// Android doesn't distinguish these keys:
  	// - a US backslash key (HID: 0x31),
  	// - an international pound/tilde key (HID: 0x32), and
  	// - an international backslash key (HID: 0x64).
  	// These are mapped to the same key code KEYCODE_BACKSLASH (73).
  	// See https://source.android.com/docs/core/interaction/input/keyboard-devices
  	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",
  	}
  
  	// https://developer.apple.com/documentation/uikit/uikeyboardhidusage?language=objc
  	iosKeyToUIKeyName = map[int]string{
  		0xE2: "AltLeft",
  		0xE6: "AltRight",
  		0x51: "ArrowDown",
  		0x50: "ArrowLeft",
  		0x4F: "ArrowRight",
  		0x52: "ArrowUp",
  		0x35: "Backquote",
  
  		// These three keys are:
  		// - US backslash-pipe key, and
  		// - non-US hashmark key (bottom left of return; on German layout, this is the #' key).
  		// On US layout configurations, they all map to the same characters - the backslash.
  		//
  		// See also: https://www.w3.org/TR/uievents-code/#keyboard-102
  		0x31: "Backslash", // UIKeyboardHIDUsageKeyboardBackslash
  		0x32: "Backslash", // UIKeyboardHIDUsageKeyboardNonUSPound
  
  		0x64: "IntlBackslash", // UIKeyboardHIDUsageKeyboardNonUSBackslash
  
  		0x2A: "Backspace",
  		0x2F: "BracketLeft",
  		0x30: "BracketRight",
  
  		// Caps Lock can either be a normal key or a hardware toggle.
  		0x39: "CapsLock", // UIKeyboardHIDUsageKeyboardCapsLock
  		0x82: "CapsLock", // UIKeyboardHIDUsageKeyboardLockingCapsLock
  
  		0x36: "Comma",
  		0xE0: "ControlLeft",
  		0xE4: "ControlRight",
  		0x4C: "Delete",
  		0x4D: "End",
  		0x28: "Enter",
  		0x2E: "Equal",
  		0x29: "Escape",
  		0x4A: "Home",
  		0x49: "Insert",
  		0x76: "ContextMenu",
  		0xE3: "MetaLeft",
  		0xE7: "MetaRight",
  		0x2D: "Minus",
  
  		// Num Lock can either be a normal key or a hardware toggle.
  		0x53: "NumLock", // UIKeyboardHIDUsageKeyboardNumLock
  		0x83: "NumLock", // UIKeyboardHIDUsageKeyboardLockingNumLock
  
  		0x57: "NumpadAdd",
  
  		// Some keyboard layouts have a comma, some a period on the numeric pad.
  		// They are the same key, though.
  		0x63: "NumpadDecimal", // UIKeyboardHIDUsageKeypadPeriod
  		0x85: "NumpadDecimal", // UIKeyboardHIDUsageKeypadComma
  
  		0x54: "NumpadDivide",
  		0x58: "NumpadEnter",
  
  		// Some numeric keypads also have an equals sign.
  		// There appear to be two separate keycodes for that.
  		0x67: "NumpadEqual", // UIKeyboardHIDUsageKeypadEqualSign
  		0x86: "NumpadEqual", // UIKeyboardHIDUsageKeypadEqualSignAS400
  
  		0x55: "NumpadMultiply",
  		0x56: "NumpadSubtract",
  		0x4E: "PageDown",
  		0x4B: "PageUp",
  		0x48: "Pause",
  		0x37: "Period",
  		0x46: "PrintScreen",
  		0x34: "Quote",
  
  		// Scroll Lock can either be a normal key or a hardware toggle.
  		0x47: "ScrollLock", // UIKeyboardHIDUsageKeyboardScrollLock
  		0x84: "ScrollLock", // UIKeyboardHIDUsageKeyboardLockingScrollLock
  
  		0x33: "Semicolon",
  		0xE1: "ShiftLeft",
  		0xE5: "ShiftRight",
  		0x38: "Slash",
  		0x2C: "Space",
  		0x2B: "Tab",
  	}
  
  	// The UI key and JS key are almost same but very slightly different (e.g., 'A' vs 'KeyA').
  	uiKeyNameToJSCode = 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",
  		"IntlBackslash":  "IntlBackslash",
  	}
  
  	const (
  		glfwKey0   = 48
  		glfwKeyA   = 65
  		glfwKeyF1  = 290
  		glfwKeyKP0 = 320
  	)
  
  	// ASCII: 0 - 9
  	for c := '0'; c <= '9'; c++ {
  		glfwKeyNameToGLFWKey[string(c)] = int(glfwKey0 + 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. Same for iOS.
  		if c == '0' {
  			iosKeyToUIKeyName[0x27] = name
  		} else {
  			iosKeyToUIKeyName[0x1E+int(c)-'1'] = name
  		}
  		uiKeyNameToJSCode[name] = name
  
  	}
  	// ASCII: A - Z
  	for c := 'A'; c <= 'Z'; c++ {
  		glfwKeyNameToGLFWKey[string(c)] = int(glfwKeyA + c - 'A')
  		uiKeyNameToGLFWKeyName[string(c)] = string(c)
  		androidKeyToUIKeyName[29+int(c)-'A'] = string(c)
  		iosKeyToUIKeyName[0x04+int(c)-'A'] = string(c)
  		uiKeyNameToJSCode[string(c)] = "Key" + string(c)
  	}
  	// Function keys
  	for i := 1; i <= 24; i++ {
  		name := "F" + strconv.Itoa(i)
  		glfwKeyNameToGLFWKey[name] = glfwKeyF1 + i - 1
  		uiKeyNameToGLFWKeyName[name] = name
  		// Android doesn't support F13 and more as constants of KeyEvent:
  		// https://developer.android.com/reference/android/view/KeyEvent
  		//
  		// Note that F13 might be avilable if HID devices are available directly:
  		// https://source.android.com/docs/core/interaction/input/keyboard-devices
  		if i <= 12 {
  			androidKeyToUIKeyName[131+i-1] = name
  		}
  		if i <= 12 {
  			iosKeyToUIKeyName[0x3A+i-1] = name
  		} else {
  			iosKeyToUIKeyName[0x68+i-13] = name
  		}
  		uiKeyNameToJSCode[name] = name
  	}
  	// 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)] = int(glfwKeyKP0 + 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. Same for iOS.
  		if c == '0' {
  			iosKeyToUIKeyName[0x62] = name
  		} else {
  			iosKeyToUIKeyName[0x59+int(c)-'1'] = name
  		}
  		uiKeyNameToJSCode[name] = name
  	}
  
  	// Keys for backward compatibility
  	oldEbitengineKeyNameToUIKeyName = 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",
  	}
  }
  
  const ebitengineKeysTmpl = `{{.License}}
  
  {{.DoNotEdit}}
  
  package ebiten
  
  import (
  	"fmt"
  	"strings"
  
  	"github.com/hajimehoshi/ebiten/v2/internal/ui"
  )
  
  // A Key represents a keyboard key.
  // These keys represent physical 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 := .EbitengineKeyNamesWithoutMods}}Key{{$name}} Key = Key(ui.Key{{$name}})
  {{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.
  	// Deprecated: as of v2.1.
  {{range $old, $new := .OldEbitengineKeyNameToUIKeyName}}Key{{$old}} Key = Key(ui.Key{{$new}})
  {{end}}
  )
  
  // 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 := .EbitengineKeyNamesWithoutOld}}case Key{{$name}}:
  		return {{$name | printf "%q"}}
  	{{end}}}
  	return ""
  }
  
  func keyNameToKeyCode(name string) (Key, bool) {
  	switch strings.ToLower(name) {
  	{{range $name := .EbitengineKeyNames}}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}}	KeyAlt
  	KeyControl
  	KeyShift
  	KeyMeta
  	KeyMax = KeyMeta
  )
  
  func (k Key) String() string {
  	switch k {
  	{{range $index, $name := .UIKeyNames}}case Key{{$name}}:
  		return {{$name | printf "Key%s" | printf "%q"}}
  	{{end}}}
  	return fmt.Sprintf("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}}
  
  {{.BuildConstraints}}
  
  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}}
  }
  
  var glfwKeyToUIKey = map[glfw.Key]Key{
  {{range $dname, $gname := .UIKeyNameToGLFWKeyName}}glfw.Key{{$gname}}: Key{{$dname}},
  {{end}}
  }
  `
  
  const uiJSKeysTmpl = `{{.License}}
  
  {{.DoNotEdit}}
  
  {{.BuildConstraints}}
  
  package ui
  
  import (
  	"syscall/js"
  )
  
  var uiKeyToJSCode = map[Key]js.Value{
  {{range $name, $code := .UIKeyNameToJSCode}}Key{{$name}}: js.ValueOf({{$code | printf "%q"}}),
  {{end}}
  }
  `
  
  const glfwKeysTmpl = `{{.License}}
  
  {{.DoNotEdit}}
  
  {{.BuildConstraints}}
  
  package glfw
  
  const (
  {{range $name, $key := .GLFWKeyNameToGLFWKey}}Key{{$name}} = Key({{$key}})
  {{end}}
  )
  `
  
  const mobileAndroidKeysTmpl = `{{.License}}
  
  {{.DoNotEdit}}
  
  {{.BuildConstraints}}
  
  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 mobileIOSKeysTmpl = `{{.License}}
  
  {{.DoNotEdit}}
  
  {{.BuildConstraints}}
  
  package ebitenmobileview
  
  import (
  	"github.com/hajimehoshi/ebiten/v2/internal/ui"
  )
  
  var iosKeyToUIKey = map[int]ui.Key{
  {{range $key, $name := .IOSKeyToUIKeyName}}{{$key}}: ui.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://pkg.go.dev/cmd/go#hdr-Generate_Go_files_by_processing_source).
  	doNotEdit := "// Code generated by genkeys.go using 'go generate'. DO NOT EDIT."
  
  	ebitengineKeyNames := []string{}
  	ebitengineKeyNamesWithoutOld := []string{}
  	ebitengineKeyNamesWithoutMods := []string{}
  	uiKeyNames := []string{}
  
  	for name := range uiKeyNameToJSCode {
  		uiKeyNames = append(uiKeyNames, name)
  		ebitengineKeyNames = append(ebitengineKeyNames, name)
  		ebitengineKeyNamesWithoutOld = append(ebitengineKeyNamesWithoutOld, name)
  		ebitengineKeyNamesWithoutMods = append(ebitengineKeyNamesWithoutMods, name)
  	}
  	for old := range oldEbitengineKeyNameToUIKeyName {
  		ebitengineKeyNames = append(ebitengineKeyNames, old)
  	}
  	// Keys for modifiers
  	ebitengineKeyNames = append(ebitengineKeyNames, "Alt", "Control", "Shift", "Meta")
  	ebitengineKeyNamesWithoutOld = append(ebitengineKeyNamesWithoutOld, "Alt", "Control", "Shift", "Meta")
  
  	sort.Slice(ebitengineKeyNames, keyNamesLess(ebitengineKeyNames))
  	sort.Slice(ebitengineKeyNamesWithoutOld, keyNamesLess(ebitengineKeyNamesWithoutOld))
  	sort.Slice(ebitengineKeyNamesWithoutMods, keyNamesLess(ebitengineKeyNamesWithoutMods))
  	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_js.go"):                  uiJSKeysTmpl,
  		filepath.Join("keys.go"):                                       ebitengineKeysTmpl,
  		filepath.Join("mobile", "ebitenmobileview", "keys_android.go"): mobileAndroidKeysTmpl,
  		filepath.Join("mobile", "ebitenmobileview", "keys_ios.go"):     mobileIOSKeysTmpl,
  	} {
  		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.
  		buildConstraints := ""
  		switch path {
  		case filepath.Join("internal", "glfw", "keys.go"):
  			buildConstraints = "//go:build darwin || freebsd || linux || netbsd || openbsd || windows"
  		case filepath.Join("internal", "ui", "keys_mobile.go"):
  			buildConstraints = "//go:build android || ios"
  		case filepath.Join("internal", "ui", "keys_glfw.go"):
  			buildConstraints = "//go:build !android && !ios && !js && !nintendosdk && !playstation5"
  		}
  		// NOTE: According to godoc, maps are automatically sorted by key.
  		w := bufio.NewWriter(f)
  		if err := tmpl.Execute(w, struct {
  			License                         string
  			DoNotEdit                       string
  			BuildConstraints                string
  			UIKeyNameToJSCode               map[string]string
  			EbitengineKeyNames              []string
  			EbitengineKeyNamesWithoutOld    []string
  			EbitengineKeyNamesWithoutMods   []string
  			GLFWKeyNameToGLFWKey            map[string]int
  			UIKeyNames                      []string
  			UIKeyNameToGLFWKeyName          map[string]string
  			AndroidKeyToUIKeyName           map[int]string
  			IOSKeyToUIKeyName               map[int]string
  			OldEbitengineKeyNameToUIKeyName map[string]string
  		}{
  			License:                         license,
  			DoNotEdit:                       doNotEdit,
  			BuildConstraints:                buildConstraints,
  			UIKeyNameToJSCode:               uiKeyNameToJSCode,
  			EbitengineKeyNames:              ebitengineKeyNames,
  			EbitengineKeyNamesWithoutOld:    ebitengineKeyNamesWithoutOld,
  			EbitengineKeyNamesWithoutMods:   ebitengineKeyNamesWithoutMods,
  			GLFWKeyNameToGLFWKey:            glfwKeyNameToGLFWKey,
  			UIKeyNames:                      uiKeyNames,
  			UIKeyNameToGLFWKeyName:          uiKeyNameToGLFWKeyName,
  			AndroidKeyToUIKeyName:           androidKeyToUIKeyName,
  			IOSKeyToUIKeyName:               iosKeyToUIKeyName,
  			OldEbitengineKeyNameToUIKeyName: oldEbitengineKeyNameToUIKeyName,
  		}); err != nil {
  			log.Fatal(err)
  		}
  
  		if err := w.Flush(); err != nil {
  			log.Fatal(err)
  		}
  	}
  }
  