mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
91e1c0ea29
This change adds these new cursor shapes: * CursorShapeNESWResize * CursorShapeNWSEResize * CursorShapeMove * CursorShapeNotAllowed Closes #2476
505 lines
11 KiB
Go
505 lines
11 KiB
Go
// SPDX-License-Identifier: Zlib
|
|
// SPDX-FileCopyrightText: 2002-2006 Marcus Geelnard
|
|
// SPDX-FileCopyrightText: 2006-2019 Camilla Löwy
|
|
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
|
|
|
//go:build darwin || windows
|
|
|
|
package goglfw
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
const stick = 3
|
|
|
|
func (w *Window) inputKey(key Key, scancode int, action Action, mods ModifierKey) {
|
|
if key >= 0 && key <= KeyLast {
|
|
var repeated bool
|
|
|
|
if action == Release && w.keys[key] == Release {
|
|
return
|
|
}
|
|
|
|
if action == Press && w.keys[key] == Press {
|
|
repeated = true
|
|
}
|
|
|
|
if action == Release && w.stickyKeys {
|
|
w.keys[key] = stick
|
|
} else {
|
|
w.keys[key] = action
|
|
}
|
|
|
|
if repeated {
|
|
action = Repeat
|
|
}
|
|
}
|
|
|
|
if !w.lockKeyMods {
|
|
mods &^= ModCapsLock | ModNumLock
|
|
}
|
|
|
|
if w.callbacks.key != nil {
|
|
w.callbacks.key(w, key, scancode, action, mods)
|
|
}
|
|
}
|
|
|
|
func (w *Window) inputChar(codepoint rune, mods ModifierKey, plain bool) {
|
|
if codepoint < 32 || (codepoint > 126 && codepoint < 160) {
|
|
return
|
|
}
|
|
|
|
if !w.lockKeyMods {
|
|
mods &^= ModCapsLock | ModNumLock
|
|
}
|
|
|
|
if w.callbacks.charmods != nil {
|
|
w.callbacks.charmods(w, codepoint, mods)
|
|
}
|
|
|
|
if plain {
|
|
if w.callbacks.character != nil {
|
|
w.callbacks.character(w, codepoint)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *Window) inputScroll(xoffset, yoffset float64) {
|
|
if w.callbacks.scroll != nil {
|
|
w.callbacks.scroll(w, xoffset, yoffset)
|
|
}
|
|
}
|
|
|
|
func (w *Window) inputMouseClick(button MouseButton, action Action, mods ModifierKey) {
|
|
if button < 0 || button > MouseButtonLast {
|
|
return
|
|
}
|
|
|
|
if !w.lockKeyMods {
|
|
mods &^= ModCapsLock | ModNumLock
|
|
}
|
|
|
|
if action == Release && w.stickyMouseButtons {
|
|
w.mouseButtons[button] = stick
|
|
} else {
|
|
w.mouseButtons[button] = action
|
|
}
|
|
|
|
if w.callbacks.mouseButton != nil {
|
|
w.callbacks.mouseButton(w, button, action, mods)
|
|
}
|
|
}
|
|
|
|
func (w *Window) inputCursorPos(xpos float64, ypos float64) {
|
|
if w.virtualCursorPosX == xpos && w.virtualCursorPosY == ypos {
|
|
return
|
|
}
|
|
|
|
w.virtualCursorPosX = xpos
|
|
w.virtualCursorPosY = ypos
|
|
|
|
if w.callbacks.cursorPos != nil {
|
|
w.callbacks.cursorPos(w, xpos, ypos)
|
|
}
|
|
}
|
|
|
|
func (w *Window) inputCursorEnter(entered bool) {
|
|
if w.callbacks.cursorEnter != nil {
|
|
w.callbacks.cursorEnter(w, entered)
|
|
}
|
|
}
|
|
|
|
func (w *Window) inputDrop(paths []string) {
|
|
if w.callbacks.drop != nil {
|
|
w.callbacks.drop(w, paths)
|
|
}
|
|
}
|
|
|
|
func (w *Window) centerCursorInContentArea() error {
|
|
width, height, err := w.platformGetWindowSize()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := w.platformSetCursorPos(float64(width/2), float64(height/2)); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *Window) GetInputMode(mode InputMode) (int, error) {
|
|
if !_glfw.initialized {
|
|
return 0, NotInitialized
|
|
}
|
|
switch mode {
|
|
case CursorMode:
|
|
return w.cursorMode, nil
|
|
case StickyKeysMode:
|
|
return boolToInt(w.stickyKeys), nil
|
|
case StickyMouseButtonsMode:
|
|
return boolToInt(w.stickyMouseButtons), nil
|
|
case LockKeyMods:
|
|
return boolToInt(w.lockKeyMods), nil
|
|
case RawMouseMotion:
|
|
return boolToInt(w.rawMouseMotion), nil
|
|
default:
|
|
return 0, fmt.Errorf("goglfw: invalid input mode 0x%08X: %w", mode, InvalidEnum)
|
|
}
|
|
}
|
|
|
|
func (w *Window) SetInputMode(mode InputMode, value int) error {
|
|
if !_glfw.initialized {
|
|
return NotInitialized
|
|
}
|
|
|
|
switch mode {
|
|
case CursorMode:
|
|
if value != CursorNormal && value != CursorHidden && value != CursorDisabled {
|
|
return fmt.Errorf("goglfw: invalid cursor mode 0x%08X: %w", value, InvalidEnum)
|
|
}
|
|
|
|
if w.cursorMode == value {
|
|
return nil
|
|
}
|
|
|
|
w.cursorMode = value
|
|
|
|
x, y, err := w.platformGetCursorPos()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.virtualCursorPosX = x
|
|
w.virtualCursorPosY = y
|
|
|
|
if err := w.platformSetCursorMode(value); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
|
|
case StickyKeysMode:
|
|
if w.stickyKeys == intToBool(value) {
|
|
return nil
|
|
}
|
|
|
|
if !intToBool(value) {
|
|
// Release all sticky keys
|
|
for i := Key(0); i <= KeyLast; i++ {
|
|
if w.keys[i] == stick {
|
|
w.keys[i] = Release
|
|
}
|
|
}
|
|
}
|
|
|
|
w.stickyKeys = intToBool(value)
|
|
return nil
|
|
|
|
case StickyMouseButtonsMode:
|
|
if w.stickyMouseButtons == intToBool(value) {
|
|
return nil
|
|
}
|
|
|
|
if !intToBool(value) {
|
|
// Release all sticky mouse buttons
|
|
for i := MouseButton(0); i <= MouseButtonLast; i++ {
|
|
if w.mouseButtons[i] == stick {
|
|
w.mouseButtons[i] = Release
|
|
}
|
|
}
|
|
}
|
|
|
|
w.stickyMouseButtons = intToBool(value)
|
|
return nil
|
|
|
|
case LockKeyMods:
|
|
w.lockKeyMods = intToBool(value)
|
|
return nil
|
|
|
|
case RawMouseMotion:
|
|
if !platformRawMouseMotionSupported() {
|
|
return fmt.Errorf("goglfw: raw mouse motion is not supported on this system: %w", PlatformError)
|
|
}
|
|
|
|
if w.rawMouseMotion == intToBool(value) {
|
|
return nil
|
|
}
|
|
|
|
w.rawMouseMotion = intToBool(value)
|
|
if err := w.platformSetRawMouseMotion(intToBool(value)); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
|
|
default:
|
|
return fmt.Errorf("goglfw: invalid input mode 0x%08X: %w", mode, InvalidEnum)
|
|
}
|
|
}
|
|
|
|
func RawMouseMotionSupported() (bool, error) {
|
|
if !_glfw.initialized {
|
|
return false, NotInitialized
|
|
}
|
|
return platformRawMouseMotionSupported(), nil
|
|
}
|
|
|
|
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 {
|
|
return 0, NotInitialized
|
|
}
|
|
|
|
if key < KeySpace || key > KeyLast {
|
|
return 0, fmt.Errorf("goglfw: invalid key %d: %w", key, InvalidEnum)
|
|
}
|
|
|
|
return platformGetKeyScancode(key), nil
|
|
}
|
|
|
|
func (w *Window) GetKey(key Key) (Action, error) {
|
|
if !_glfw.initialized {
|
|
return 0, NotInitialized
|
|
}
|
|
|
|
if key < KeySpace || key > KeyLast {
|
|
return 0, fmt.Errorf("goglfw: invalid key %d: %w", key, InvalidEnum)
|
|
}
|
|
|
|
if w.keys[key] == stick {
|
|
// Sticky mode: release key now
|
|
w.keys[key] = Release
|
|
return Press, nil
|
|
}
|
|
|
|
return w.keys[key], nil
|
|
}
|
|
|
|
func (w *Window) GetMouseButton(button MouseButton) (Action, error) {
|
|
if !_glfw.initialized {
|
|
return 0, NotInitialized
|
|
}
|
|
|
|
if button < MouseButton1 || button > MouseButtonLast {
|
|
return 0, fmt.Errorf("goglfw: invalid mouse button %d: %w", button, InvalidEnum)
|
|
}
|
|
|
|
if w.mouseButtons[button] == stick {
|
|
// Sticky mode: release mouse button now
|
|
w.mouseButtons[button] = Release
|
|
return Press, nil
|
|
}
|
|
|
|
return w.mouseButtons[button], nil
|
|
}
|
|
|
|
func (w *Window) GetCursorPos() (xpos, ypos float64, err error) {
|
|
if !_glfw.initialized {
|
|
return 0, 0, NotInitialized
|
|
}
|
|
|
|
if w.cursorMode == CursorDisabled {
|
|
return w.virtualCursorPosX, w.virtualCursorPosY, nil
|
|
} else {
|
|
return w.platformGetCursorPos()
|
|
}
|
|
}
|
|
|
|
func (w *Window) SetCursorPos(xpos, ypos float64) error {
|
|
if !_glfw.initialized {
|
|
return NotInitialized
|
|
}
|
|
|
|
if xpos != xpos || xpos < -math.MaxFloat64 || xpos > math.MaxFloat64 || ypos != ypos || ypos < -math.MaxFloat64 || ypos > math.MaxFloat64 {
|
|
return fmt.Errorf("goglfw: invalid cursor position %f %f: %w", xpos, ypos, InvalidValue)
|
|
}
|
|
|
|
if !w.platformWindowFocused() {
|
|
return nil
|
|
}
|
|
|
|
if w.cursorMode == CursorDisabled {
|
|
// Only update the accumulated position if the cursor is disabled
|
|
w.virtualCursorPosX = xpos
|
|
w.virtualCursorPosY = ypos
|
|
return nil
|
|
} else {
|
|
// Update system cursor position
|
|
return w.platformSetCursorPos(xpos, ypos)
|
|
}
|
|
}
|
|
|
|
func CreateStandardCursor(shape StandardCursor) (*Cursor, error) {
|
|
if !_glfw.initialized {
|
|
return nil, NotInitialized
|
|
}
|
|
|
|
if shape != ArrowCursor &&
|
|
shape != IBeamCursor &&
|
|
shape != CrosshairCursor &&
|
|
shape != HandCursor &&
|
|
shape != HResizeCursor &&
|
|
shape != VResizeCursor &&
|
|
shape != ResizeNWSECursor &&
|
|
shape != ResizeNESWCursor &&
|
|
shape != ResizeAllCursor &&
|
|
shape != NotAllowedCursor {
|
|
return nil, fmt.Errorf("goglfw: invalid standard cursor 0x%08X: %w", shape, InvalidEnum)
|
|
}
|
|
|
|
cursor := &Cursor{}
|
|
_glfw.cursors = append(_glfw.cursors, cursor)
|
|
|
|
if err := cursor.platformCreateStandardCursor(shape); err != nil {
|
|
_ = cursor.Destroy()
|
|
return nil, err
|
|
}
|
|
|
|
return cursor, nil
|
|
}
|
|
|
|
func (c *Cursor) Destroy() error {
|
|
if !_glfw.initialized {
|
|
return NotInitialized
|
|
}
|
|
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
// Make sure the cursor is not being used by any window
|
|
for _, window := range _glfw.windows {
|
|
if window.cursor == c {
|
|
if err := window.SetCursor(nil); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := c.platformDestroyCursor(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Unlink cursor from global linked list
|
|
for i, cursor := range _glfw.cursors {
|
|
if cursor == c {
|
|
copy(_glfw.cursors[i:], _glfw.cursors[i+1:])
|
|
_glfw.cursors = _glfw.cursors[:len(_glfw.cursors)-1]
|
|
break
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (w *Window) SetCursor(cursor *Cursor) error {
|
|
if !_glfw.initialized {
|
|
return NotInitialized
|
|
}
|
|
|
|
w.cursor = cursor
|
|
|
|
if err := w.platformSetCursor(cursor); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *Window) SetKeyCallback(cbfun KeyCallback) (KeyCallback, error) {
|
|
if !_glfw.initialized {
|
|
return nil, NotInitialized
|
|
}
|
|
old := w.callbacks.key
|
|
w.callbacks.key = cbfun
|
|
return old, nil
|
|
}
|
|
|
|
func (w *Window) SetCharCallback(cbfun CharCallback) (CharCallback, error) {
|
|
if !_glfw.initialized {
|
|
return nil, NotInitialized
|
|
}
|
|
old := w.callbacks.character
|
|
w.callbacks.character = cbfun
|
|
return old, nil
|
|
}
|
|
|
|
func (w *Window) SetCharModsCallback(cbfun CharModsCallback) (CharModsCallback, error) {
|
|
if !_glfw.initialized {
|
|
return nil, NotInitialized
|
|
}
|
|
old := w.callbacks.charmods
|
|
w.callbacks.charmods = cbfun
|
|
return old, nil
|
|
}
|
|
|
|
func (w *Window) SetMouseButtonCallback(cbfun MouseButtonCallback) (MouseButtonCallback, error) {
|
|
if !_glfw.initialized {
|
|
return nil, NotInitialized
|
|
}
|
|
old := w.callbacks.mouseButton
|
|
w.callbacks.mouseButton = cbfun
|
|
return old, nil
|
|
}
|
|
|
|
func (w *Window) SetCursorPosCallback(cbfun CursorPosCallback) (CursorPosCallback, error) {
|
|
if !_glfw.initialized {
|
|
return nil, NotInitialized
|
|
}
|
|
old := w.callbacks.cursorPos
|
|
w.callbacks.cursorPos = cbfun
|
|
return old, nil
|
|
}
|
|
|
|
func (w *Window) SetCursorEnterCallback(cbfun CursorEnterCallback) (CursorEnterCallback, error) {
|
|
if !_glfw.initialized {
|
|
return nil, NotInitialized
|
|
}
|
|
old := w.callbacks.cursorEnter
|
|
w.callbacks.cursorEnter = cbfun
|
|
return old, nil
|
|
}
|
|
|
|
func (w *Window) SetScrollCallback(cbfun ScrollCallback) (ScrollCallback, error) {
|
|
if !_glfw.initialized {
|
|
return nil, NotInitialized
|
|
}
|
|
old := w.callbacks.scroll
|
|
w.callbacks.scroll = cbfun
|
|
return old, nil
|
|
}
|
|
|
|
func (w *Window) SetDropCallback(cbfun DropCallback) (DropCallback, error) {
|
|
if !_glfw.initialized {
|
|
return nil, NotInitialized
|
|
}
|
|
old := w.callbacks.drop
|
|
w.callbacks.drop = cbfun
|
|
return old, nil
|
|
}
|
|
|
|
func (w *Window) SetClipboardString(str string) error {
|
|
if !_glfw.initialized {
|
|
return NotInitialized
|
|
}
|
|
return platformSetClipboardString(str)
|
|
}
|
|
|
|
func GetClipboardString() (string, error) {
|
|
if !_glfw.initialized {
|
|
return "", NotInitialized
|
|
}
|
|
return platformGetClipboardString()
|
|
}
|