zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

input.go (10445B)


      1 // Copyright 2015 Hajime Hoshi
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 //go:build (darwin || freebsd || linux || windows) && !android && !ios
     16 // +build darwin freebsd linux windows
     17 // +build !android
     18 // +build !ios
     19 
     20 //go:generate curl --location --remote-name https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt
     21 //go:generate file2byteslice -package glfw -input=./gamecontrollerdb.txt -output=./gamecontrollerdb.txt.go -var=gamecontrollerdbTxt
     22 
     23 package glfw
     24 
     25 import (
     26 	"math"
     27 	"sync"
     28 	"unicode"
     29 
     30 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     31 	"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
     32 	"github.com/hajimehoshi/ebiten/v2/internal/glfw"
     33 )
     34 
     35 type gamepad struct {
     36 	valid         bool
     37 	guid          string
     38 	name          string
     39 	axisNum       int
     40 	axes          [16]float64
     41 	buttonNum     int
     42 	buttonPressed [256]bool
     43 	hatsNum       int
     44 	hats          [16]int
     45 }
     46 
     47 type Input struct {
     48 	keyPressed         map[glfw.Key]bool
     49 	mouseButtonPressed map[glfw.MouseButton]bool
     50 	onceCallback       sync.Once
     51 	scrollX            float64
     52 	scrollY            float64
     53 	cursorX            int
     54 	cursorY            int
     55 	gamepads           [16]gamepad
     56 	touches            map[driver.TouchID]pos // TODO: Implement this (#417)
     57 	runeBuffer         []rune
     58 	ui                 *UserInterface
     59 }
     60 
     61 type pos struct {
     62 	X int
     63 	Y int
     64 }
     65 
     66 func (i *Input) CursorPosition() (x, y int) {
     67 	if !i.ui.isRunning() {
     68 		return 0, 0
     69 	}
     70 
     71 	i.ui.m.RLock()
     72 	defer i.ui.m.RUnlock()
     73 	return i.cursorX, i.cursorY
     74 }
     75 
     76 func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID {
     77 	if !i.ui.isRunning() {
     78 		return nil
     79 	}
     80 
     81 	i.ui.m.RLock()
     82 	defer i.ui.m.RUnlock()
     83 	for id, g := range i.gamepads {
     84 		if g.valid {
     85 			gamepadIDs = append(gamepadIDs, driver.GamepadID(id))
     86 		}
     87 	}
     88 	return gamepadIDs
     89 }
     90 
     91 func (i *Input) GamepadSDLID(id driver.GamepadID) string {
     92 	if !i.ui.isRunning() {
     93 		return ""
     94 	}
     95 
     96 	i.ui.m.RLock()
     97 	defer i.ui.m.RUnlock()
     98 	if len(i.gamepads) <= int(id) {
     99 		return ""
    100 	}
    101 	return i.gamepads[id].guid
    102 }
    103 
    104 func (i *Input) GamepadName(id driver.GamepadID) string {
    105 	if !i.ui.isRunning() {
    106 		return ""
    107 	}
    108 
    109 	i.ui.m.RLock()
    110 	defer i.ui.m.RUnlock()
    111 	if len(i.gamepads) <= int(id) {
    112 		return ""
    113 	}
    114 	return i.gamepads[id].name
    115 }
    116 
    117 func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
    118 	if !i.ui.isRunning() {
    119 		return 0
    120 	}
    121 
    122 	i.ui.m.RLock()
    123 	defer i.ui.m.RUnlock()
    124 	if len(i.gamepads) <= int(id) {
    125 		return 0
    126 	}
    127 	return i.gamepads[id].axisNum
    128 }
    129 
    130 func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
    131 	if !i.ui.isRunning() {
    132 		return 0
    133 	}
    134 
    135 	i.ui.m.RLock()
    136 	defer i.ui.m.RUnlock()
    137 	if len(i.gamepads) <= int(id) {
    138 		return 0
    139 	}
    140 	return i.gamepads[id].axes[axis]
    141 }
    142 
    143 func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
    144 	if !i.ui.isRunning() {
    145 		return 0
    146 	}
    147 
    148 	i.ui.m.RLock()
    149 	defer i.ui.m.RUnlock()
    150 	if len(i.gamepads) <= int(id) {
    151 		return 0
    152 	}
    153 	return i.gamepads[id].buttonNum
    154 }
    155 
    156 func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool {
    157 	if !i.ui.isRunning() {
    158 		return false
    159 	}
    160 
    161 	i.ui.m.RLock()
    162 	defer i.ui.m.RUnlock()
    163 	if len(i.gamepads) <= int(id) {
    164 		return false
    165 	}
    166 	return i.gamepads[id].buttonPressed[button]
    167 }
    168 
    169 func (i *Input) AppendTouchIDs(touchIDs []driver.TouchID) []driver.TouchID {
    170 	if !i.ui.isRunning() {
    171 		return nil
    172 	}
    173 
    174 	i.ui.m.RLock()
    175 	defer i.ui.m.RUnlock()
    176 	for id := range i.touches {
    177 		touchIDs = append(touchIDs, id)
    178 	}
    179 	return touchIDs
    180 }
    181 
    182 func (i *Input) TouchPosition(id driver.TouchID) (x, y int) {
    183 	if !i.ui.isRunning() {
    184 		return 0, 0
    185 	}
    186 
    187 	i.ui.m.RLock()
    188 	defer i.ui.m.RUnlock()
    189 	for tid, pos := range i.touches {
    190 		if id == tid {
    191 			return pos.X, pos.Y
    192 		}
    193 	}
    194 	return 0, 0
    195 }
    196 
    197 func (i *Input) AppendInputChars(runes []rune) []rune {
    198 	if !i.ui.isRunning() {
    199 		return nil
    200 	}
    201 
    202 	i.ui.m.RLock()
    203 	defer i.ui.m.RUnlock()
    204 	return append(runes, i.runeBuffer...)
    205 }
    206 
    207 func (i *Input) resetForFrame() {
    208 	if !i.ui.isRunning() {
    209 		return
    210 	}
    211 
    212 	i.ui.m.Lock()
    213 	defer i.ui.m.Unlock()
    214 	i.runeBuffer = i.runeBuffer[:0]
    215 	i.scrollX, i.scrollY = 0, 0
    216 }
    217 
    218 func (i *Input) IsKeyPressed(key driver.Key) bool {
    219 	if !i.ui.isRunning() {
    220 		return false
    221 	}
    222 
    223 	i.ui.m.Lock()
    224 	defer i.ui.m.Unlock()
    225 	if i.keyPressed == nil {
    226 		i.keyPressed = map[glfw.Key]bool{}
    227 	}
    228 	gk, ok := driverKeyToGLFWKey[key]
    229 	return ok && i.keyPressed[gk]
    230 }
    231 
    232 func (i *Input) IsMouseButtonPressed(button driver.MouseButton) bool {
    233 	if !i.ui.isRunning() {
    234 		return false
    235 	}
    236 
    237 	i.ui.m.Lock()
    238 	defer i.ui.m.Unlock()
    239 	if i.mouseButtonPressed == nil {
    240 		i.mouseButtonPressed = map[glfw.MouseButton]bool{}
    241 	}
    242 	for gb, b := range glfwMouseButtonToMouseButton {
    243 		if b != button {
    244 			continue
    245 		}
    246 		if i.mouseButtonPressed[gb] {
    247 			return true
    248 		}
    249 	}
    250 	return false
    251 }
    252 
    253 func (i *Input) Wheel() (xoff, yoff float64) {
    254 	if !i.ui.isRunning() {
    255 		return 0, 0
    256 	}
    257 
    258 	i.ui.m.RLock()
    259 	defer i.ui.m.RUnlock()
    260 	return i.scrollX, i.scrollY
    261 }
    262 
    263 var glfwMouseButtonToMouseButton = map[glfw.MouseButton]driver.MouseButton{
    264 	glfw.MouseButtonLeft:   driver.MouseButtonLeft,
    265 	glfw.MouseButtonRight:  driver.MouseButtonRight,
    266 	glfw.MouseButtonMiddle: driver.MouseButtonMiddle,
    267 }
    268 
    269 // update must be called from the main thread.
    270 func (i *Input) update(window *glfw.Window, context driver.UIContext) {
    271 	i.ui.m.Lock()
    272 	defer i.ui.m.Unlock()
    273 
    274 	i.onceCallback.Do(func() {
    275 		window.SetCharModsCallback(glfw.ToCharModsCallback(func(w *glfw.Window, char rune, mods glfw.ModifierKey) {
    276 			// As this function is called from GLFW callbacks, the current thread is main.
    277 			if !unicode.IsPrint(char) {
    278 				return
    279 			}
    280 
    281 			i.ui.m.Lock()
    282 			defer i.ui.m.Unlock()
    283 			i.runeBuffer = append(i.runeBuffer, char)
    284 		}))
    285 		window.SetScrollCallback(glfw.ToScrollCallback(func(w *glfw.Window, xoff float64, yoff float64) {
    286 			// As this function is called from GLFW callbacks, the current thread is main.
    287 			i.ui.m.Lock()
    288 			defer i.ui.m.Unlock()
    289 			i.scrollX = xoff
    290 			i.scrollY = yoff
    291 		}))
    292 	})
    293 	if i.keyPressed == nil {
    294 		i.keyPressed = map[glfw.Key]bool{}
    295 	}
    296 	for gk := range glfwKeyToDriverKey {
    297 		i.keyPressed[gk] = window.GetKey(gk) == glfw.Press
    298 	}
    299 	if i.mouseButtonPressed == nil {
    300 		i.mouseButtonPressed = map[glfw.MouseButton]bool{}
    301 	}
    302 	for gb := range glfwMouseButtonToMouseButton {
    303 		i.mouseButtonPressed[gb] = window.GetMouseButton(gb) == glfw.Press
    304 	}
    305 	cx, cy := window.GetCursorPos()
    306 	// TODO: This is tricky. Rename the function?
    307 	s := i.ui.deviceScaleFactor()
    308 	cx = i.ui.fromGLFWPixel(cx)
    309 	cy = i.ui.fromGLFWPixel(cy)
    310 	cx, cy = context.AdjustPosition(cx, cy, s)
    311 
    312 	// AdjustPosition can return NaN at the initialization.
    313 	if !math.IsNaN(cx) && !math.IsNaN(cy) {
    314 		i.cursorX, i.cursorY = int(cx), int(cy)
    315 	}
    316 
    317 	for id := glfw.Joystick(0); id < glfw.Joystick(len(i.gamepads)); id++ {
    318 		i.gamepads[id].valid = false
    319 		if !id.Present() {
    320 			continue
    321 		}
    322 
    323 		buttons := id.GetButtons()
    324 
    325 		// A gamepad can be detected even though there are not. Apparently, some special devices are
    326 		// recognized as gamepads by GLFW. In this case, the number of the 'buttons' can exceeds the
    327 		// maximum. Skip such devices as a tentative solution (#1173).
    328 		if len(buttons) > driver.GamepadButtonNum {
    329 			continue
    330 		}
    331 
    332 		i.gamepads[id].valid = true
    333 
    334 		i.gamepads[id].buttonNum = len(buttons)
    335 		for b := 0; b < len(i.gamepads[id].buttonPressed); b++ {
    336 			if len(buttons) <= b {
    337 				i.gamepads[id].buttonPressed[b] = false
    338 				continue
    339 			}
    340 			i.gamepads[id].buttonPressed[b] = glfw.Action(buttons[b]) == glfw.Press
    341 		}
    342 
    343 		axes32 := id.GetAxes()
    344 		i.gamepads[id].axisNum = len(axes32)
    345 		for a := 0; a < len(i.gamepads[id].axes); a++ {
    346 			if len(axes32) <= a {
    347 				i.gamepads[id].axes[a] = 0
    348 				continue
    349 			}
    350 			i.gamepads[id].axes[a] = float64(axes32[a])
    351 		}
    352 
    353 		hats := id.GetHats()
    354 		i.gamepads[id].hatsNum = len(hats)
    355 		for h := 0; h < len(i.gamepads[id].hats); h++ {
    356 			if len(hats) <= h {
    357 				i.gamepads[id].hats[h] = 0
    358 				continue
    359 			}
    360 			i.gamepads[id].hats[h] = int(hats[h])
    361 		}
    362 
    363 		// Note that GLFW's gamepad GUID follows SDL's GUID.
    364 		i.gamepads[id].guid = id.GetGUID()
    365 		i.gamepads[id].name = id.GetName()
    366 	}
    367 }
    368 
    369 func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
    370 	i.ui.m.Lock()
    371 	defer i.ui.m.Unlock()
    372 
    373 	if len(i.gamepads) <= int(id) {
    374 		return false
    375 	}
    376 	g := i.gamepads[int(id)]
    377 	return gamepaddb.HasStandardLayoutMapping(g.guid)
    378 }
    379 
    380 func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
    381 	i.ui.m.Lock()
    382 	defer i.ui.m.Unlock()
    383 
    384 	if len(i.gamepads) <= int(id) {
    385 		return 0
    386 	}
    387 	g := i.gamepads[int(id)]
    388 	return gamepaddb.AxisValue(g.guid, axis, gamepadState{&g})
    389 }
    390 
    391 func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 {
    392 	i.ui.m.Lock()
    393 	defer i.ui.m.Unlock()
    394 
    395 	if len(i.gamepads) <= int(id) {
    396 		return 0
    397 	}
    398 	g := i.gamepads[int(id)]
    399 	return gamepaddb.ButtonValue(g.guid, button, gamepadState{&g})
    400 }
    401 
    402 func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
    403 	i.ui.m.Lock()
    404 	defer i.ui.m.Unlock()
    405 
    406 	if len(i.gamepads) <= int(id) {
    407 		return false
    408 	}
    409 	g := i.gamepads[int(id)]
    410 	return gamepaddb.IsButtonPressed(g.guid, button, gamepadState{&g})
    411 }
    412 
    413 func init() {
    414 	// Confirm that all the hat state values are the same.
    415 	if gamepaddb.HatUp != glfw.HatUp {
    416 		panic("glfw: gamepaddb.HatUp must equal to glfw.HatUp but not")
    417 	}
    418 	if gamepaddb.HatRight != glfw.HatRight {
    419 		panic("glfw: gamepaddb.HatRight must equal to glfw.HatRight but not")
    420 	}
    421 	if gamepaddb.HatDown != glfw.HatDown {
    422 		panic("glfw: gamepaddb.HatDown must equal to glfw.HatDown but not")
    423 	}
    424 	if gamepaddb.HatLeft != glfw.HatLeft {
    425 		panic("glfw: gamepaddb.HatLeft must equal to glfw.HatLeft but not")
    426 	}
    427 }
    428 
    429 type gamepadState struct {
    430 	g *gamepad
    431 }
    432 
    433 func (s gamepadState) Axis(index int) float64 {
    434 	return s.g.axes[index]
    435 }
    436 
    437 func (s gamepadState) Button(index int) bool {
    438 	return s.g.buttonPressed[index]
    439 }
    440 
    441 func (s gamepadState) Hat(index int) int {
    442 	return s.g.hats[index]
    443 }