mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 02:38:53 +01:00
parent
e6346c01d2
commit
dd7e125d9c
@ -17,15 +17,18 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
_ "image/png"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/hajimehoshi/bitmapfont/v2"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/keyboard/keyboard"
|
||||
rkeyboard "github.com/hajimehoshi/ebiten/v2/examples/resources/images/keyboard"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"github.com/hajimehoshi/ebiten/v2/text"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -78,11 +81,17 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
screen.DrawImage(keyboardImage.SubImage(r).(*ebiten.Image), op)
|
||||
}
|
||||
|
||||
keyStrs := []string{}
|
||||
var keyStrs []string
|
||||
var keyNames []string
|
||||
for _, k := range g.keys {
|
||||
keyStrs = append(keyStrs, k.String())
|
||||
if name := ebiten.KeyName(k); name != "" {
|
||||
keyNames = append(keyNames, name)
|
||||
}
|
||||
}
|
||||
ebitenutil.DebugPrint(screen, strings.Join(keyStrs, ", "))
|
||||
|
||||
// Use bitmapfont.Face instead of ebitenutil.DebugPrint, since some key names might not be printed with DebugPrint.
|
||||
text.Draw(screen, strings.Join(keyStrs, ", ")+"\n"+strings.Join(keyNames, ", "), bitmapfont.Face, 8, 12, color.White)
|
||||
}
|
||||
|
||||
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
|
13
input.go
13
input.go
@ -69,6 +69,19 @@ func IsKeyPressed(key Key) bool {
|
||||
return theInputState.isKeyPressed(key)
|
||||
}
|
||||
|
||||
// KeyName returns a key name for the current keyboard layout.
|
||||
// For example, KeyName(KeyQ) returns 'q' for a QWERTY keyboard, and returns 'a' for an AZERTY keyboard.
|
||||
//
|
||||
// KeyName returns an empty string if 1) the key doesn't have a phisical key name, 2) the platform doesn't support KeyName,
|
||||
// or 3) the main loop doesn't start yet.
|
||||
//
|
||||
// KeyName is supported by desktops and browsers.
|
||||
//
|
||||
// KeyName is concurrent-safe.
|
||||
func KeyName(key Key) string {
|
||||
return ui.KeyName(ui.Key(key))
|
||||
}
|
||||
|
||||
// CursorPosition returns a position of a mouse cursor relative to the game screen (window). The cursor position is
|
||||
// 'logical' position and this considers the scale of the screen.
|
||||
//
|
||||
|
@ -273,6 +273,10 @@ func CreateWindow(width, height int, title string, monitor *Monitor, share *Wind
|
||||
return theWindows.add(w), nil
|
||||
}
|
||||
|
||||
func GetKeyName(key Key, scancode int) string {
|
||||
return glfw.GetKeyName(glfw.Key(key), scancode)
|
||||
}
|
||||
|
||||
func GetMonitors() []*Monitor {
|
||||
ms := []*Monitor{}
|
||||
for _, m := range glfw.GetMonitors() {
|
||||
|
@ -306,6 +306,14 @@ func CreateWindow(width, height int, title string, monitor *Monitor, share *Wind
|
||||
return (*Window)(w), err
|
||||
}
|
||||
|
||||
func GetKeyName(key Key, scancode int) string {
|
||||
name, err := glfwwin.GetKeyName(glfwwin.Key(key), scancode)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func GetMonitors() []*Monitor {
|
||||
ms, err := glfwwin.GetMonitors()
|
||||
if err != nil {
|
||||
|
@ -845,6 +845,7 @@ var (
|
||||
procSetWindowTextW = user32.NewProc("SetWindowTextW")
|
||||
procShowWindow = user32.NewProc("ShowWindow")
|
||||
procSystemParametersInfoW = user32.NewProc("SystemParametersInfoW")
|
||||
procToUnicode = user32.NewProc("ToUnicode")
|
||||
procTranslateMessage = user32.NewProc("TranslateMessage")
|
||||
procTrackMouseEvent = user32.NewProc("TrackMouseEvent")
|
||||
procUnregisterClassW = user32.NewProc("UnregisterClassW")
|
||||
@ -1718,6 +1719,24 @@ func _TlsSetValue(dwTlsIndex uint32, lpTlsValue uintptr) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func _ToUnicode(wVirtualKey uint32, wScanCode uint32, keyState []byte, buff []uint16, cchBuff int32, wFlags uint32) int32 {
|
||||
var lpKeyState *byte
|
||||
if len(keyState) > 0 {
|
||||
lpKeyState = &keyState[0]
|
||||
}
|
||||
var pwszBuff *uint16
|
||||
if len(buff) > 0 {
|
||||
pwszBuff = &buff[0]
|
||||
}
|
||||
|
||||
r, _, _ := procToUnicode.Call(uintptr(wVirtualKey), uintptr(wScanCode), uintptr(unsafe.Pointer(lpKeyState)),
|
||||
uintptr(unsafe.Pointer(pwszBuff)), uintptr(cchBuff), uintptr(wFlags))
|
||||
runtime.KeepAlive(lpKeyState)
|
||||
runtime.KeepAlive(pwszBuff)
|
||||
|
||||
return int32(r)
|
||||
}
|
||||
|
||||
func _TranslateMessage(lpMsg *_MSG) bool {
|
||||
r, _, _ := procTranslateMessage.Call(uintptr(unsafe.Pointer(lpMsg)))
|
||||
return int32(r) != 0
|
||||
|
@ -240,7 +240,20 @@ func RawMouseMotionSupported() (bool, error) {
|
||||
return platformRawMouseMotionSupported(), nil
|
||||
}
|
||||
|
||||
// GetKeyName is not implemented.
|
||||
func GetKeyName(key Key, scancode int) (string, error) {
|
||||
if !_glfw.initialized {
|
||||
return "", NotInitialized
|
||||
}
|
||||
|
||||
if key != KeyUnknown {
|
||||
if key != KeyKPEqual && (key < KeyKP0 || key > KeyKPAdd) && (key < KeyApostrophe || key > KeyWorld2) {
|
||||
return "", nil
|
||||
}
|
||||
scancode = platformGetKeyScancode(key)
|
||||
}
|
||||
|
||||
return platformGetScancodeName(scancode)
|
||||
}
|
||||
|
||||
func GetKeyScancode(key Key) (int, error) {
|
||||
if !_glfw.initialized {
|
||||
|
@ -261,6 +261,7 @@ type library struct {
|
||||
clipboardString string
|
||||
keycodes [512]Key
|
||||
scancodes [KeyLast + 1]int
|
||||
keynames [KeyLast + 1]string
|
||||
|
||||
// Where to place the cursor when re-enabled
|
||||
restoreCursorPosX float64
|
||||
|
@ -151,6 +151,47 @@ func createKeyTables() {
|
||||
}
|
||||
}
|
||||
|
||||
func updateKeyNamesWin32() {
|
||||
for i := range _glfw.win32.keynames {
|
||||
_glfw.win32.keynames[i] = ""
|
||||
}
|
||||
|
||||
var state [256]byte
|
||||
|
||||
for key := KeySpace; key <= KeyLast; key++ {
|
||||
scancode := _glfw.win32.scancodes[key]
|
||||
if scancode == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
var vk uint32
|
||||
if key >= KeyKP0 && key <= KeyKPAdd {
|
||||
vks := []uint32{
|
||||
_VK_NUMPAD0, _VK_NUMPAD1, _VK_NUMPAD2, _VK_NUMPAD3,
|
||||
_VK_NUMPAD4, _VK_NUMPAD5, _VK_NUMPAD6, _VK_NUMPAD7,
|
||||
_VK_NUMPAD8, _VK_NUMPAD9, _VK_DECIMAL, _VK_DIVIDE,
|
||||
_VK_MULTIPLY, _VK_SUBTRACT, _VK_ADD,
|
||||
}
|
||||
vk = vks[key-KeyKP0]
|
||||
} else {
|
||||
vk = _MapVirtualKeyW(uint32(scancode), _MAPVK_VSC_TO_VK)
|
||||
}
|
||||
|
||||
var chars [16]uint16
|
||||
length := _ToUnicode(vk, uint32(scancode), state[:], chars[:], int32(len(chars)), 0)
|
||||
if length == -1 {
|
||||
// This is a dead key, so we need a second simulated key press
|
||||
// to make it output its own character (usually a diacritic)
|
||||
length = _ToUnicode(vk, uint32(scancode), state[:], chars[:], int32(len(chars)), 0)
|
||||
}
|
||||
if length < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
_glfw.win32.keynames[key] = windows.UTF16ToString(chars[:length])
|
||||
}
|
||||
}
|
||||
|
||||
func createHelperWindow() error {
|
||||
h, err := _CreateWindowExW(_WS_EX_OVERLAPPEDWINDOW, _GLFW_WNDCLASSNAME, "GLFW message window", _WS_CLIPSIBLINGS|_WS_CLIPCHILDREN, 0, 0, 1, 1, 0, 0, _glfw.win32.instance, nil)
|
||||
if err != nil {
|
||||
@ -238,6 +279,7 @@ func platformInit() error {
|
||||
_glfw.win32.instance = _HINSTANCE(m)
|
||||
|
||||
createKeyTables()
|
||||
updateKeyNamesWin32()
|
||||
|
||||
if isWindows10CreatorsUpdateOrGreaterWin32() {
|
||||
if !microsoftgdk.IsXbox() {
|
||||
|
@ -690,7 +690,8 @@ func windowProc(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM)
|
||||
return 0
|
||||
|
||||
case _WM_INPUTLANGCHANGE:
|
||||
// Do nothing
|
||||
updateKeyNamesWin32()
|
||||
return 0
|
||||
|
||||
case _WM_CHAR, _WM_SYSCHAR:
|
||||
if wParam >= 0xd800 && wParam <= 0xdbff {
|
||||
@ -2204,6 +2205,13 @@ func (w *Window) platformSetCursorMode(mode int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func platformGetScancodeName(scancode int) (string, error) {
|
||||
if scancode < 0 || scancode > (_KF_EXTENDED|0xff) || _glfw.win32.keycodes[scancode] == KeyUnknown {
|
||||
return "", fmt.Errorf("glwfwin: invalid scancode %d: %w", scancode, InvalidValue)
|
||||
}
|
||||
return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]], nil
|
||||
}
|
||||
|
||||
func platformGetKeyScancode(key Key) int {
|
||||
return _glfw.win32.scancodes[key]
|
||||
}
|
||||
|
@ -76,3 +76,24 @@ func (u *userInterfaceImpl) updateInputState() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func KeyName(key Key) string {
|
||||
return theUI.keyName(key)
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) keyName(key Key) string {
|
||||
if !u.isRunning() {
|
||||
return ""
|
||||
}
|
||||
|
||||
gk, ok := uiKeyToGLFWKey[key]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
var name string
|
||||
u.t.Call(func() {
|
||||
name = glfw.GetKeyName(gk, 0)
|
||||
})
|
||||
return name
|
||||
}
|
||||
|
@ -167,3 +167,51 @@ func isKeyString(str string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
jsKeyboard = js.Global().Get("navigator").Get("keyboard")
|
||||
jsKeyboardGetLayoutMap js.Value
|
||||
jsKeyboardGetLayoutMapCh chan js.Value
|
||||
jsKeyboardGetLayoutMapCallback js.Func
|
||||
)
|
||||
|
||||
func init() {
|
||||
if !jsKeyboard.Truthy() {
|
||||
return
|
||||
}
|
||||
|
||||
jsKeyboardGetLayoutMap = jsKeyboard.Get("getLayoutMap").Call("bind", jsKeyboard)
|
||||
jsKeyboardGetLayoutMapCh = make(chan js.Value, 1)
|
||||
jsKeyboardGetLayoutMapCallback = js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
jsKeyboardGetLayoutMapCh <- args[0]
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func KeyName(key Key) string {
|
||||
return theUI.keyName(key)
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) keyName(key Key) string {
|
||||
if !u.running {
|
||||
return ""
|
||||
}
|
||||
|
||||
// keyboardLayoutMap is reset every tick.
|
||||
if u.keyboardLayoutMap.IsUndefined() {
|
||||
if !jsKeyboard.Truthy() {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Invoke getLayoutMap every tick to detect the keyboard change.
|
||||
// TODO: Calling this every tick might be inefficient. Is there a way to detect a keyboard change?
|
||||
jsKeyboardGetLayoutMap.Invoke().Call("then", jsKeyboardGetLayoutMapCallback)
|
||||
u.keyboardLayoutMap = <-jsKeyboardGetLayoutMapCh
|
||||
}
|
||||
|
||||
n := u.keyboardLayoutMap.Call("get", uiKeyToJSKey[key])
|
||||
if n.IsUndefined() {
|
||||
return ""
|
||||
}
|
||||
return n.String()
|
||||
}
|
||||
|
@ -51,3 +51,8 @@ func (u *userInterfaceImpl) updateInputState(keys map[Key]struct{}, runes []rune
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func KeyName(key Key) string {
|
||||
// TODO: Implement this.
|
||||
return ""
|
||||
}
|
||||
|
@ -37,3 +37,7 @@ func (u *userInterfaceImpl) updateInputState() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func KeyName(key Key) string {
|
||||
return ""
|
||||
}
|
||||
|
@ -91,6 +91,8 @@ type userInterfaceImpl struct {
|
||||
origCursorX int
|
||||
origCursorY int
|
||||
|
||||
keyboardLayoutMap js.Value
|
||||
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
@ -686,6 +688,7 @@ func (u *userInterfaceImpl) readInputState(inputState *InputState) {
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) resetForTick() {
|
||||
u.keyboardLayoutMap = js.Value{}
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) Window() Window {
|
||||
|
Loading…
Reference in New Issue
Block a user