1
0

inpututil.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. // Copyright 2018 The Ebiten Authors
  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. // Package inpututil provides utility functions of input like keyboard or mouse.
  15. package inpututil
  16. import (
  17. "slices"
  18. "sync"
  19. "github.com/hajimehoshi/ebiten/v2"
  20. "github.com/hajimehoshi/ebiten/v2/internal/hook"
  21. )
  22. type gamepadState struct {
  23. buttonDurations [ebiten.GamepadButtonMax + 1]int
  24. standardButtonDurations [ebiten.StandardGamepadButtonMax + 1]int
  25. }
  26. type touchState struct {
  27. duration int
  28. x int
  29. y int
  30. }
  31. type inputState struct {
  32. keyDurations [ebiten.KeyMax + 1]int
  33. prevKeyDurations [ebiten.KeyMax + 1]int
  34. mouseButtonDurations [ebiten.MouseButtonMax + 1]int
  35. prevMouseButtonDurations [ebiten.MouseButtonMax + 1]int
  36. gamepadStates map[ebiten.GamepadID]gamepadState
  37. prevGamepadStates map[ebiten.GamepadID]gamepadState
  38. touchStates map[ebiten.TouchID]touchState
  39. prevTouchStates map[ebiten.TouchID]touchState
  40. gamepadIDsBuf []ebiten.GamepadID
  41. touchIDsBuf []ebiten.TouchID
  42. m sync.RWMutex
  43. }
  44. var theInputState = &inputState{
  45. gamepadStates: map[ebiten.GamepadID]gamepadState{},
  46. prevGamepadStates: map[ebiten.GamepadID]gamepadState{},
  47. touchStates: map[ebiten.TouchID]touchState{},
  48. prevTouchStates: map[ebiten.TouchID]touchState{},
  49. }
  50. func init() {
  51. hook.AppendHookOnBeforeUpdate(func() error {
  52. theInputState.update()
  53. return nil
  54. })
  55. }
  56. func (i *inputState) update() {
  57. i.m.Lock()
  58. defer i.m.Unlock()
  59. // Keyboard
  60. copy(i.prevKeyDurations[:], i.keyDurations[:])
  61. for idx := range i.keyDurations {
  62. if ebiten.IsKeyPressed(ebiten.Key(idx)) {
  63. i.keyDurations[idx]++
  64. } else {
  65. i.keyDurations[idx] = 0
  66. }
  67. }
  68. // Mouse
  69. copy(i.prevMouseButtonDurations[:], i.mouseButtonDurations[:])
  70. for idx := range i.mouseButtonDurations {
  71. if ebiten.IsMouseButtonPressed(ebiten.MouseButton(idx)) {
  72. i.mouseButtonDurations[idx]++
  73. } else {
  74. i.mouseButtonDurations[idx] = 0
  75. }
  76. }
  77. // Gamepads
  78. // Copy the gamepad states.
  79. clear(i.prevGamepadStates)
  80. for id, s := range i.gamepadStates {
  81. i.prevGamepadStates[id] = s
  82. }
  83. i.gamepadIDsBuf = ebiten.AppendGamepadIDs(i.gamepadIDsBuf[:0])
  84. for _, id := range i.gamepadIDsBuf {
  85. state := i.gamepadStates[id]
  86. for b := range i.gamepadStates[id].buttonDurations {
  87. if ebiten.IsGamepadButtonPressed(id, ebiten.GamepadButton(b)) {
  88. state.buttonDurations[b]++
  89. } else {
  90. state.buttonDurations[b] = 0
  91. }
  92. }
  93. for b := range i.gamepadStates[id].standardButtonDurations {
  94. if ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButton(b)) {
  95. state.standardButtonDurations[b]++
  96. } else {
  97. state.standardButtonDurations[b] = 0
  98. }
  99. }
  100. i.gamepadStates[id] = state
  101. }
  102. // Remove disconnected gamepads.
  103. for id := range i.gamepadStates {
  104. if !slices.Contains(i.gamepadIDsBuf, id) {
  105. delete(i.gamepadStates, id)
  106. }
  107. }
  108. // Touches
  109. // Copy the touch durations and positions.
  110. clear(i.prevTouchStates)
  111. for id, state := range i.touchStates {
  112. i.prevTouchStates[id] = state
  113. }
  114. i.touchIDsBuf = ebiten.AppendTouchIDs(i.touchIDsBuf[:0])
  115. for _, id := range i.touchIDsBuf {
  116. state := i.touchStates[id]
  117. state.duration++
  118. state.x, state.y = ebiten.TouchPosition(id)
  119. i.touchStates[id] = state
  120. }
  121. // Remove released touches.
  122. for id := range i.touchStates {
  123. if !slices.Contains(i.touchIDsBuf, id) {
  124. delete(i.touchStates, id)
  125. }
  126. }
  127. }
  128. // AppendPressedKeys append currently pressed keyboard keys to keys and returns the extended buffer.
  129. // Giving a slice that already has enough capacity works efficiently.
  130. //
  131. // AppendPressedKeys must be called in a game's Update, not Draw.
  132. //
  133. // AppendPressedKeys is concurrent safe.
  134. func AppendPressedKeys(keys []ebiten.Key) []ebiten.Key {
  135. theInputState.m.RLock()
  136. defer theInputState.m.RUnlock()
  137. for i, d := range theInputState.keyDurations {
  138. if d == 0 {
  139. continue
  140. }
  141. keys = append(keys, ebiten.Key(i))
  142. }
  143. return keys
  144. }
  145. // PressedKeys returns a set of currently pressed keyboard keys.
  146. //
  147. // PressedKeys must be called in a game's Update, not Draw.
  148. //
  149. // Deprecated: as of v2.2. Use AppendPressedKeys instead.
  150. func PressedKeys() []ebiten.Key {
  151. return AppendPressedKeys(nil)
  152. }
  153. // AppendJustPressedKeys append just pressed keyboard keys to keys and returns the extended buffer.
  154. // Giving a slice that already has enough capacity works efficiently.
  155. //
  156. // AppendJustPressedKeys must be called in a game's Update, not Draw.
  157. //
  158. // AppendJustPressedKeys is concurrent safe.
  159. func AppendJustPressedKeys(keys []ebiten.Key) []ebiten.Key {
  160. theInputState.m.RLock()
  161. defer theInputState.m.RUnlock()
  162. for i, d := range theInputState.keyDurations {
  163. if d != 1 {
  164. continue
  165. }
  166. keys = append(keys, ebiten.Key(i))
  167. }
  168. return keys
  169. }
  170. // AppendJustReleasedKeys append just released keyboard keys to keys and returns the extended buffer.
  171. // Giving a slice that already has enough capacity works efficiently.
  172. //
  173. // AppendJustReleasedKeys must be called in a game's Update, not Draw.
  174. //
  175. // AppendJustReleasedKeys is concurrent safe.
  176. func AppendJustReleasedKeys(keys []ebiten.Key) []ebiten.Key {
  177. theInputState.m.RLock()
  178. defer theInputState.m.RUnlock()
  179. for i := range theInputState.keyDurations {
  180. if theInputState.keyDurations[i] != 0 {
  181. continue
  182. }
  183. if theInputState.prevKeyDurations[i] == 0 {
  184. continue
  185. }
  186. keys = append(keys, ebiten.Key(i))
  187. }
  188. return keys
  189. }
  190. // IsKeyJustPressed returns a boolean value indicating
  191. // whether the given key is pressed just in the current tick.
  192. //
  193. // IsKeyJustPressed must be called in a game's Update, not Draw.
  194. //
  195. // IsKeyJustPressed is concurrent safe.
  196. func IsKeyJustPressed(key ebiten.Key) bool {
  197. return KeyPressDuration(key) == 1
  198. }
  199. // IsKeyJustReleased returns a boolean value indicating
  200. // whether the given key is released just in the current tick.
  201. //
  202. // IsKeyJustReleased must be called in a game's Update, not Draw.
  203. //
  204. // IsKeyJustReleased is concurrent safe.
  205. func IsKeyJustReleased(key ebiten.Key) bool {
  206. theInputState.m.RLock()
  207. defer theInputState.m.RUnlock()
  208. return theInputState.keyDurations[key] == 0 && theInputState.prevKeyDurations[key] > 0
  209. }
  210. // KeyPressDuration returns how long the key is pressed in ticks (Update).
  211. //
  212. // KeyPressDuration must be called in a game's Update, not Draw.
  213. //
  214. // KeyPressDuration is concurrent safe.
  215. func KeyPressDuration(key ebiten.Key) int {
  216. theInputState.m.RLock()
  217. defer theInputState.m.RUnlock()
  218. return theInputState.keyDurations[key]
  219. }
  220. // IsMouseButtonJustPressed returns a boolean value indicating
  221. // whether the given mouse button is pressed just in the current tick.
  222. //
  223. // IsMouseButtonJustPressed must be called in a game's Update, not Draw.
  224. //
  225. // IsMouseButtonJustPressed is concurrent safe.
  226. func IsMouseButtonJustPressed(button ebiten.MouseButton) bool {
  227. return MouseButtonPressDuration(button) == 1
  228. }
  229. // IsMouseButtonJustReleased returns a boolean value indicating
  230. // whether the given mouse button is released just in the current tick.
  231. //
  232. // IsMouseButtonJustReleased must be called in a game's Update, not Draw.
  233. //
  234. // IsMouseButtonJustReleased is concurrent safe.
  235. func IsMouseButtonJustReleased(button ebiten.MouseButton) bool {
  236. theInputState.m.RLock()
  237. defer theInputState.m.RUnlock()
  238. return theInputState.mouseButtonDurations[button] == 0 && theInputState.prevMouseButtonDurations[button] > 0
  239. }
  240. // MouseButtonPressDuration returns how long the mouse button is pressed in ticks (Update).
  241. //
  242. // MouseButtonPressDuration must be called in a game's Update, not Draw.
  243. //
  244. // MouseButtonPressDuration is concurrent safe.
  245. func MouseButtonPressDuration(button ebiten.MouseButton) int {
  246. theInputState.m.RLock()
  247. defer theInputState.m.RUnlock()
  248. return theInputState.mouseButtonDurations[button]
  249. }
  250. // AppendJustConnectedGamepadIDs appends gamepad IDs that are connected just in the current tick to gamepadIDs,
  251. // and returns the extended buffer.
  252. // Giving a slice that already has enough capacity works efficiently.
  253. //
  254. // AppendJustConnectedGamepadIDs must be called in a game's Update, not Draw.
  255. //
  256. // AppendJustConnectedGamepadIDs is concurrent safe.
  257. func AppendJustConnectedGamepadIDs(gamepadIDs []ebiten.GamepadID) []ebiten.GamepadID {
  258. theInputState.m.RLock()
  259. defer theInputState.m.RUnlock()
  260. origLen := len(gamepadIDs)
  261. for id := range theInputState.gamepadStates {
  262. if _, ok := theInputState.prevGamepadStates[id]; !ok {
  263. gamepadIDs = append(gamepadIDs, id)
  264. }
  265. }
  266. slices.Sort(gamepadIDs[origLen:])
  267. return gamepadIDs
  268. }
  269. // JustConnectedGamepadIDs returns gamepad IDs that are connected just in the current tick.
  270. //
  271. // JustConnectedGamepadIDs must be called in a game's Update, not Draw.
  272. //
  273. // Deprecated: as of v2.2. Use AppendJustConnectedGamepadIDs instead.
  274. func JustConnectedGamepadIDs() []ebiten.GamepadID {
  275. return AppendJustConnectedGamepadIDs(nil)
  276. }
  277. // IsGamepadJustDisconnected returns a boolean value indicating
  278. // whether the gamepad of the given id is released just in the current tick.
  279. //
  280. // IsGamepadJustDisconnected must be called in a game's Update, not Draw.
  281. //
  282. // IsGamepadJustDisconnected is concurrent safe.
  283. func IsGamepadJustDisconnected(id ebiten.GamepadID) bool {
  284. theInputState.m.RLock()
  285. defer theInputState.m.RUnlock()
  286. _, current := theInputState.gamepadStates[id]
  287. _, prev := theInputState.prevGamepadStates[id]
  288. return !current && prev
  289. }
  290. // AppendPressedGamepadButtons append currently pressed gamepad buttons to buttons and returns the extended buffer.
  291. // Giving a slice that already has enough capacity works efficiently.
  292. //
  293. // AppendPressedGamepadButtons must be called in a game's Update, not Draw.
  294. //
  295. // AppendPressedGamepadButtons is concurrent safe.
  296. func AppendPressedGamepadButtons(id ebiten.GamepadID, buttons []ebiten.GamepadButton) []ebiten.GamepadButton {
  297. theInputState.m.RLock()
  298. defer theInputState.m.RUnlock()
  299. state, ok := theInputState.gamepadStates[id]
  300. if !ok {
  301. return buttons
  302. }
  303. for b, d := range state.buttonDurations {
  304. if d == 0 {
  305. continue
  306. }
  307. buttons = append(buttons, ebiten.GamepadButton(b))
  308. }
  309. return buttons
  310. }
  311. // AppendJustPressedGamepadButtons append just pressed gamepad buttons to buttons and returns the extended buffer.
  312. // Giving a slice that already has enough capacity works efficiently.
  313. //
  314. // AppendJustPressedGamepadButtons must be called in a game's Update, not Draw.
  315. //
  316. // AppendJustPressedGamepadButtons is concurrent safe.
  317. func AppendJustPressedGamepadButtons(id ebiten.GamepadID, buttons []ebiten.GamepadButton) []ebiten.GamepadButton {
  318. theInputState.m.RLock()
  319. defer theInputState.m.RUnlock()
  320. state, ok := theInputState.gamepadStates[id]
  321. if !ok {
  322. return buttons
  323. }
  324. for b, d := range state.buttonDurations {
  325. if d != 1 {
  326. continue
  327. }
  328. buttons = append(buttons, ebiten.GamepadButton(b))
  329. }
  330. return buttons
  331. }
  332. // AppendJustReleasedGamepadButtons append just released gamepad buttons to buttons and returns the extended buffer.
  333. // Giving a slice that already has enough capacity works efficiently.
  334. //
  335. // AppendJustReleasedGamepadButtons must be called in a game's Update, not Draw.
  336. //
  337. // AppendJustReleasedGamepadButtons is concurrent safe.
  338. func AppendJustReleasedGamepadButtons(id ebiten.GamepadID, buttons []ebiten.GamepadButton) []ebiten.GamepadButton {
  339. theInputState.m.RLock()
  340. defer theInputState.m.RUnlock()
  341. state, ok := theInputState.gamepadStates[id]
  342. if !ok {
  343. return buttons
  344. }
  345. prevState, ok := theInputState.prevGamepadStates[id]
  346. if !ok {
  347. return buttons
  348. }
  349. for b := range state.buttonDurations {
  350. if state.buttonDurations[b] != 0 {
  351. continue
  352. }
  353. if prevState.buttonDurations[b] == 0 {
  354. continue
  355. }
  356. buttons = append(buttons, ebiten.GamepadButton(b))
  357. }
  358. return buttons
  359. }
  360. // IsGamepadButtonJustPressed returns a boolean value indicating
  361. // whether the given gamepad button of the gamepad id is pressed just in the current tick.
  362. //
  363. // IsGamepadButtonJustPressed must be called in a game's Update, not Draw.
  364. //
  365. // IsGamepadButtonJustPressed is concurrent safe.
  366. func IsGamepadButtonJustPressed(id ebiten.GamepadID, button ebiten.GamepadButton) bool {
  367. return GamepadButtonPressDuration(id, button) == 1
  368. }
  369. // IsGamepadButtonJustReleased returns a boolean value indicating
  370. // whether the given gamepad button of the gamepad id is released just in the current tick.
  371. //
  372. // IsGamepadButtonJustReleased must be called in a game's Update, not Draw.
  373. //
  374. // IsGamepadButtonJustReleased is concurrent safe.
  375. func IsGamepadButtonJustReleased(id ebiten.GamepadID, button ebiten.GamepadButton) bool {
  376. theInputState.m.RLock()
  377. defer theInputState.m.RUnlock()
  378. state, ok := theInputState.gamepadStates[id]
  379. if !ok {
  380. return false
  381. }
  382. prevState, ok := theInputState.prevGamepadStates[id]
  383. if !ok {
  384. return false
  385. }
  386. return state.buttonDurations[button] == 0 && prevState.buttonDurations[button] > 0
  387. }
  388. // GamepadButtonPressDuration returns how long the gamepad button of the gamepad id is pressed in ticks (Update).
  389. //
  390. // GamepadButtonPressDuration must be called in a game's Update, not Draw.
  391. //
  392. // GamepadButtonPressDuration is concurrent safe.
  393. func GamepadButtonPressDuration(id ebiten.GamepadID, button ebiten.GamepadButton) int {
  394. theInputState.m.RLock()
  395. defer theInputState.m.RUnlock()
  396. state, ok := theInputState.gamepadStates[id]
  397. if !ok {
  398. return 0
  399. }
  400. return state.buttonDurations[button]
  401. }
  402. // AppendPressedStandardGamepadButtons append currently pressed standard gamepad buttons to buttons and returns the extended buffer.
  403. // Giving a slice that already has enough capacity works efficiently.
  404. //
  405. // AppendPressedStandardGamepadButtons must be called in a game's Update, not Draw.
  406. //
  407. // AppendPressedStandardGamepadButtons is concurrent safe.
  408. func AppendPressedStandardGamepadButtons(id ebiten.GamepadID, buttons []ebiten.StandardGamepadButton) []ebiten.StandardGamepadButton {
  409. theInputState.m.RLock()
  410. defer theInputState.m.RUnlock()
  411. state, ok := theInputState.gamepadStates[id]
  412. if !ok {
  413. return buttons
  414. }
  415. for i, d := range state.standardButtonDurations {
  416. if d == 0 {
  417. continue
  418. }
  419. buttons = append(buttons, ebiten.StandardGamepadButton(i))
  420. }
  421. return buttons
  422. }
  423. // AppendJustPressedStandardGamepadButtons append just pressed standard gamepad buttons to buttons and returns the extended buffer.
  424. // Giving a slice that already has enough capacity works efficiently.
  425. //
  426. // AppendJustPressedStandardGamepadButtons must be called in a game's Update, not Draw.
  427. //
  428. // AppendJustPressedStandardGamepadButtons is concurrent safe.
  429. func AppendJustPressedStandardGamepadButtons(id ebiten.GamepadID, buttons []ebiten.StandardGamepadButton) []ebiten.StandardGamepadButton {
  430. theInputState.m.RLock()
  431. defer theInputState.m.RUnlock()
  432. state, ok := theInputState.gamepadStates[id]
  433. if !ok {
  434. return buttons
  435. }
  436. for b, d := range state.standardButtonDurations {
  437. if d != 1 {
  438. continue
  439. }
  440. buttons = append(buttons, ebiten.StandardGamepadButton(b))
  441. }
  442. return buttons
  443. }
  444. // AppendJustReleasedStandardGamepadButtons append just released standard gamepad buttons to buttons and returns the extended buffer.
  445. // Giving a slice that already has enough capacity works efficiently.
  446. //
  447. // AppendJustReleasedStandardGamepadButtons must be called in a game's Update, not Draw.
  448. //
  449. // AppendJustReleasedStandardGamepadButtons is concurrent safe.
  450. func AppendJustReleasedStandardGamepadButtons(id ebiten.GamepadID, buttons []ebiten.StandardGamepadButton) []ebiten.StandardGamepadButton {
  451. theInputState.m.RLock()
  452. defer theInputState.m.RUnlock()
  453. state, ok := theInputState.gamepadStates[id]
  454. if !ok {
  455. return buttons
  456. }
  457. prevState, ok := theInputState.prevGamepadStates[id]
  458. if !ok {
  459. return buttons
  460. }
  461. for b := range state.standardButtonDurations {
  462. if state.standardButtonDurations[b] != 0 {
  463. continue
  464. }
  465. if prevState.standardButtonDurations[b] == 0 {
  466. continue
  467. }
  468. buttons = append(buttons, ebiten.StandardGamepadButton(b))
  469. }
  470. return buttons
  471. }
  472. // IsStandardGamepadButtonJustPressed returns a boolean value indicating
  473. // whether the given standard gamepad button of the gamepad id is pressed just in the current tick.
  474. //
  475. // IsStandardGamepadButtonJustPressed must be called in a game's Update, not Draw.
  476. //
  477. // IsStandardGamepadButtonJustPressed is concurrent safe.
  478. func IsStandardGamepadButtonJustPressed(id ebiten.GamepadID, button ebiten.StandardGamepadButton) bool {
  479. return StandardGamepadButtonPressDuration(id, button) == 1
  480. }
  481. // IsStandardGamepadButtonJustReleased returns a boolean value indicating
  482. // whether the given standard gamepad button of the gamepad id is released just in the current tick.
  483. //
  484. // IsStandardGamepadButtonJustReleased must be called in a game's Update, not Draw.
  485. //
  486. // IsStandardGamepadButtonJustReleased is concurrent safe.
  487. func IsStandardGamepadButtonJustReleased(id ebiten.GamepadID, button ebiten.StandardGamepadButton) bool {
  488. theInputState.m.RLock()
  489. defer theInputState.m.RUnlock()
  490. state, ok := theInputState.gamepadStates[id]
  491. if !ok {
  492. return false
  493. }
  494. prevState, ok := theInputState.prevGamepadStates[id]
  495. if !ok {
  496. return false
  497. }
  498. return state.standardButtonDurations[button] == 0 && prevState.standardButtonDurations[button] > 0
  499. }
  500. // StandardGamepadButtonPressDuration returns how long the standard gamepad button of the gamepad id is pressed in ticks (Update).
  501. //
  502. // StandardGamepadButtonPressDuration must be called in a game's Update, not Draw.
  503. //
  504. // StandardGamepadButtonPressDuration is concurrent safe.
  505. func StandardGamepadButtonPressDuration(id ebiten.GamepadID, button ebiten.StandardGamepadButton) int {
  506. theInputState.m.RLock()
  507. defer theInputState.m.RUnlock()
  508. state, ok := theInputState.gamepadStates[id]
  509. if !ok {
  510. return 0
  511. }
  512. return state.standardButtonDurations[button]
  513. }
  514. // AppendJustPressedTouchIDs append touch IDs that are created just in the current tick to touchIDs,
  515. // and returns the extended buffer.
  516. // Giving a slice that already has enough capacity works efficiently.
  517. //
  518. // AppendJustPressedTouchIDs must be called in a game's Update, not Draw.
  519. //
  520. // AppendJustPressedTouchIDs is concurrent safe.
  521. func AppendJustPressedTouchIDs(touchIDs []ebiten.TouchID) []ebiten.TouchID {
  522. theInputState.m.RLock()
  523. defer theInputState.m.RUnlock()
  524. origLen := len(touchIDs)
  525. for id, state := range theInputState.touchStates {
  526. if state.duration != 1 {
  527. continue
  528. }
  529. touchIDs = append(touchIDs, id)
  530. }
  531. slices.Sort(touchIDs[origLen:])
  532. return touchIDs
  533. }
  534. // JustPressedTouchIDs returns touch IDs that are created just in the current tick.
  535. //
  536. // JustPressedTouchIDs must be called in a game's Update, not Draw.
  537. //
  538. // Deprecated: as of v2.2. Use AppendJustPressedTouchIDs instead.
  539. func JustPressedTouchIDs() []ebiten.TouchID {
  540. return AppendJustPressedTouchIDs(nil)
  541. }
  542. // AppendJustReleasedTouchIDs append touch IDs that are released just in the current tick to touchIDs,
  543. // and returns the extended buffer.
  544. // Giving a slice that already has enough capacity works efficiently.
  545. //
  546. // AppendJustReleasedTouchIDs must be called in a game's Update, not Draw.
  547. //
  548. // AppendJustReleasedTouchIDs is concurrent safe.
  549. func AppendJustReleasedTouchIDs(touchIDs []ebiten.TouchID) []ebiten.TouchID {
  550. theInputState.m.RLock()
  551. defer theInputState.m.RUnlock()
  552. origLen := len(touchIDs)
  553. // Iterate prevTouchStates instead of touchStates since touchStates doesn't have released touches.
  554. for id, state := range theInputState.prevTouchStates {
  555. if state.duration == 0 {
  556. continue
  557. }
  558. if theInputState.touchStates[id].duration != 0 {
  559. continue
  560. }
  561. touchIDs = append(touchIDs, id)
  562. }
  563. slices.Sort(touchIDs[origLen:])
  564. return touchIDs
  565. }
  566. // IsTouchJustReleased returns a boolean value indicating
  567. // whether the given touch is released just in the current tick.
  568. //
  569. // IsTouchJustReleased must be called in a game's Update, not Draw.
  570. //
  571. // IsTouchJustReleased is concurrent safe.
  572. func IsTouchJustReleased(id ebiten.TouchID) bool {
  573. theInputState.m.RLock()
  574. defer theInputState.m.RUnlock()
  575. current := theInputState.touchStates[id]
  576. prev := theInputState.prevTouchStates[id]
  577. return current.duration == 0 && prev.duration > 0
  578. }
  579. // TouchPressDuration returns how long the touch remains in ticks (Update).
  580. //
  581. // TouchPressDuration must be called in a game's Update, not Draw.
  582. //
  583. // TouchPressDuration is concurrent safe.
  584. func TouchPressDuration(id ebiten.TouchID) int {
  585. theInputState.m.RLock()
  586. defer theInputState.m.RUnlock()
  587. return theInputState.touchStates[id].duration
  588. }
  589. // TouchPositionInPreviousTick returns the position in the previous tick.
  590. // If the touch is a just-released touch, TouchPositionInPreviousTick returns the last position of the touch.
  591. //
  592. // TouchPositionInPreviousTick must be called in a game's Update, not Draw.
  593. //
  594. // TouchJustReleasedPosition is concurrent safe.
  595. func TouchPositionInPreviousTick(id ebiten.TouchID) (int, int) {
  596. theInputState.m.RLock()
  597. defer theInputState.m.RUnlock()
  598. state := theInputState.prevTouchStates[id]
  599. return state.x, state.y
  600. }