canvas.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build wasm
  5. package window
  6. import (
  7. "fmt"
  8. "github.com/g3n/engine/core"
  9. "github.com/g3n/engine/gls"
  10. _ "image/png"
  11. "syscall/js"
  12. )
  13. // Keycodes
  14. const (
  15. KeyUnknown = Key(iota)
  16. KeySpace
  17. KeyApostrophe
  18. KeyComma
  19. KeyMinus
  20. KeyPeriod
  21. KeySlash
  22. Key0
  23. Key1
  24. Key2
  25. Key3
  26. Key4
  27. Key5
  28. Key6
  29. Key7
  30. Key8
  31. Key9
  32. KeySemicolon
  33. KeyEqual
  34. KeyA
  35. KeyB
  36. KeyC
  37. KeyD
  38. KeyE
  39. KeyF
  40. KeyG
  41. KeyH
  42. KeyI
  43. KeyJ
  44. KeyK
  45. KeyL
  46. KeyM
  47. KeyN
  48. KeyO
  49. KeyP
  50. KeyQ
  51. KeyR
  52. KeyS
  53. KeyT
  54. KeyU
  55. KeyV
  56. KeyW
  57. KeyX
  58. KeyY
  59. KeyZ
  60. KeyLeftBracket
  61. KeyBackslash
  62. KeyRightBracket
  63. KeyGraveAccent
  64. KeyWorld1
  65. KeyWorld2
  66. KeyEscape
  67. KeyEnter
  68. KeyTab
  69. KeyBackspace
  70. KeyInsert
  71. KeyDelete
  72. KeyRight
  73. KeyLeft
  74. KeyDown
  75. KeyUp
  76. KeyPageUp
  77. KeyPageDown
  78. KeyHome
  79. KeyEnd
  80. KeyCapsLock
  81. KeyScrollLock
  82. KeyNumLock
  83. KeyPrintScreen
  84. KeyPause
  85. KeyF1
  86. KeyF2
  87. KeyF3
  88. KeyF4
  89. KeyF5
  90. KeyF6
  91. KeyF7
  92. KeyF8
  93. KeyF9
  94. KeyF10
  95. KeyF11
  96. KeyF12
  97. KeyF13
  98. KeyF14
  99. KeyF15
  100. KeyF16
  101. KeyF17
  102. KeyF18
  103. KeyF19
  104. KeyF20
  105. KeyF21
  106. KeyF22
  107. KeyF23
  108. KeyF24
  109. KeyF25
  110. KeyKP0
  111. KeyKP1
  112. KeyKP2
  113. KeyKP3
  114. KeyKP4
  115. KeyKP5
  116. KeyKP6
  117. KeyKP7
  118. KeyKP8
  119. KeyKP9
  120. KeyKPDecimal
  121. KeyKPDivide
  122. KeyKPMultiply
  123. KeyKPSubtract
  124. KeyKPAdd
  125. KeyKPEnter
  126. KeyKPEqual
  127. KeyLeftShift
  128. KeyLeftControl
  129. KeyLeftAlt
  130. KeyLeftSuper // Meta in Javascript
  131. KeyRightShift
  132. KeyRightControl
  133. KeyRightAlt
  134. KeyRightSuper
  135. KeyMenu
  136. KeyLast
  137. )
  138. var keyMap = map[string]Key{
  139. //"KeyUnknown": KeyUnknown, TODO emit when key is not in map
  140. "Space": KeySpace,
  141. "Quote": KeyApostrophe,
  142. "Comma": KeyComma,
  143. "Minus": KeyMinus,
  144. "Period": KeyPeriod,
  145. "Slash": KeySlash,
  146. "Digit0": Key0,
  147. "Digit1": Key1,
  148. "Digit2": Key2,
  149. "Digit3": Key3,
  150. "Digit4": Key4,
  151. "Digit5": Key5,
  152. "Digit6": Key6,
  153. "Digit7": Key7,
  154. "Digit8": Key8,
  155. "Digit9": Key9,
  156. "Semicolon": KeySemicolon,
  157. "Equal": KeyEqual,
  158. "KeyA": KeyA,
  159. "KeyB": KeyB,
  160. "KeyC": KeyC,
  161. "KeyD": KeyD,
  162. "KeyE": KeyE,
  163. "KeyF": KeyF,
  164. "KeyG": KeyG,
  165. "KeyH": KeyH,
  166. "KeyI": KeyI,
  167. "KeyJ": KeyJ,
  168. "KeyK": KeyK,
  169. "KeyL": KeyL,
  170. "KeyM": KeyM,
  171. "KeyN": KeyN,
  172. "KeyO": KeyO,
  173. "KeyP": KeyP,
  174. "KeyQ": KeyQ,
  175. "KeyR": KeyR,
  176. "KeyS": KeyS,
  177. "KeyT": KeyT,
  178. "KeyU": KeyU,
  179. "KeyV": KeyV,
  180. "KeyW": KeyW,
  181. "KeyX": KeyX,
  182. "KeyY": KeyY,
  183. "KeyZ": KeyZ,
  184. "BracketLeft": KeyLeftBracket,
  185. "Backslash": KeyBackslash,
  186. "BracketRight": KeyRightBracket,
  187. "Backquote": KeyGraveAccent,
  188. //"KeyWorld1": KeyWorld1,
  189. //"KeyWorld2": KeyWorld2,
  190. "Escape": KeyEscape,
  191. "Enter": KeyEnter,
  192. "Tab": KeyTab,
  193. "Backspace": KeyBackspace,
  194. "Insert": KeyInsert,
  195. "Delete": KeyDelete,
  196. "ArrowRight": KeyRight,
  197. "ArrowLeft": KeyLeft,
  198. "ArrowDown": KeyDown,
  199. "ArrowUp": KeyUp,
  200. "PageUp": KeyPageUp,
  201. "PageDown": KeyPageDown,
  202. "Home": KeyHome,
  203. "End": KeyEnd,
  204. "CapsLock": KeyCapsLock,
  205. "ScrollLock": KeyScrollLock,
  206. "NumLock": KeyNumLock,
  207. "PrintScreen": KeyPrintScreen,
  208. "Pause": KeyPause,
  209. "F1": KeyF1,
  210. "F2": KeyF2,
  211. "F3": KeyF3,
  212. "F4": KeyF4,
  213. "F5": KeyF5,
  214. "F6": KeyF6,
  215. "F7": KeyF7,
  216. "F8": KeyF8,
  217. "F9": KeyF9,
  218. "F10": KeyF10,
  219. "F11": KeyF11,
  220. "F12": KeyF12,
  221. "F13": KeyF13,
  222. "F14": KeyF14,
  223. "F15": KeyF15,
  224. "F16": KeyF16,
  225. "F17": KeyF17,
  226. "F18": KeyF18,
  227. "F19": KeyF19,
  228. "F20": KeyF20,
  229. "F21": KeyF21,
  230. "F22": KeyF22,
  231. "F23": KeyF23,
  232. "F24": KeyF24,
  233. "F25": KeyF25,
  234. "Numpad0": KeyKP0,
  235. "Numpad1": KeyKP1,
  236. "Numpad2": KeyKP2,
  237. "Numpad3": KeyKP3,
  238. "Numpad4": KeyKP4,
  239. "Numpad5": KeyKP5,
  240. "Numpad6": KeyKP6,
  241. "Numpad7": KeyKP7,
  242. "Numpad8": KeyKP8,
  243. "Numpad9": KeyKP9,
  244. "NumpadDecimal": KeyKPDecimal,
  245. "NumpadDivide": KeyKPDivide,
  246. "NumpadMultiply": KeyKPMultiply,
  247. "NumpadSubtract": KeyKPSubtract,
  248. "NumpadAdd": KeyKPAdd,
  249. "NumpadEnter": KeyKPEnter,
  250. "NumpadEqual": KeyKPEqual,
  251. "ShiftLeft": KeyLeftShift,
  252. "ControlLeft": KeyLeftControl,
  253. "AltLeft": KeyLeftAlt,
  254. "MetaLeft": KeyLeftSuper,
  255. "ShitRight": KeyRightShift,
  256. "ControlRight": KeyRightControl,
  257. "AltRight": KeyRightAlt,
  258. "MetaRight": KeyRightSuper,
  259. "Menu": KeyMenu,
  260. }
  261. // Modifier keys
  262. const (
  263. ModShift = ModifierKey(1 << iota) // Bitmask
  264. ModControl
  265. ModAlt
  266. ModSuper // Meta in Javascript
  267. )
  268. // Mouse buttons
  269. const (
  270. //MouseButton1 = MouseButton(0)
  271. //MouseButton2 = MouseButton(0)
  272. //MouseButton3 = MouseButton(0)
  273. //MouseButton4 = MouseButton(0)
  274. //MouseButton5 = MouseButton(0)
  275. //MouseButton6 = MouseButton(0)
  276. //MouseButton7 = MouseButton(0)
  277. //MouseButton8 = MouseButton(0)
  278. //MouseButtonLast = MouseButton(0)
  279. MouseButtonLeft = MouseButton(0)
  280. MouseButtonRight = MouseButton(2)
  281. MouseButtonMiddle = MouseButton(1)
  282. )
  283. // Actions
  284. const (
  285. Release = Action(iota) // Release indicates that the key or mouse button was released
  286. Press // Press indicates that the key or mouse button was pressed
  287. Repeat // Repeat indicates that the key was held down until it repeated
  288. )
  289. // Input modes
  290. const (
  291. CursorInputMode = InputMode(iota) // See Cursor mode values
  292. StickyKeysInputMode // Value can be either 1 or 0
  293. StickyMouseButtonsInputMode // Value can be either 1 or 0
  294. )
  295. // Cursor mode values
  296. const (
  297. CursorNormal = CursorMode(iota)
  298. CursorHidden
  299. CursorDisabled
  300. )
  301. // WebGlCanvas is a browser-based WebGL canvas.
  302. type WebGlCanvas struct {
  303. core.Dispatcher // Embedded event dispatcher
  304. canvas js.Value // Associated WebGL canvas
  305. gls *gls.GLS // Associated WebGL state
  306. // Events
  307. keyEv KeyEvent
  308. charEv CharEvent
  309. mouseEv MouseEvent
  310. posEv PosEvent
  311. sizeEv SizeEvent
  312. cursorEv CursorEvent
  313. scrollEv ScrollEvent
  314. // Callbacks
  315. onCtxMenu js.Func
  316. keyDown js.Func
  317. keyUp js.Func
  318. mouseDown js.Func
  319. mouseUp js.Func
  320. mouseMove js.Func
  321. mouseWheel js.Func
  322. winResize js.Func
  323. }
  324. // Init initializes the WebGlCanvas singleton.
  325. // If canvasId is provided, the pre-existing WebGlCanvas with that id is used.
  326. // If canvasId is the empty string then it creates a new WebGL canvas.
  327. func Init(canvasId string) error {
  328. // Panic if already created
  329. if win != nil {
  330. panic(fmt.Errorf("can only call window.Init() once"))
  331. }
  332. // Create wrapper window with dispatcher
  333. w := new(WebGlCanvas)
  334. w.Dispatcher.Initialize()
  335. // Create or get WebGlCanvas
  336. doc := js.Global().Get("document")
  337. if canvasId == "" {
  338. w.canvas = doc.Call("createElement", "WebGlCanvas")
  339. } else {
  340. w.canvas = doc.Call("getElementById", canvasId)
  341. if w.canvas == js.Null() {
  342. panic(fmt.Sprintf("Cannot find canvas with provided id: %s", canvasId))
  343. }
  344. }
  345. // Get reference to WebGL context
  346. webglCtx := w.canvas.Call("getContext", "webgl2")
  347. if webglCtx == js.Undefined() {
  348. return fmt.Errorf("Browser doesn't support WebGL2")
  349. }
  350. // Create WebGL state
  351. gl, err := gls.New(webglCtx)
  352. if err != nil {
  353. return err
  354. }
  355. w.gls = gl
  356. // Disable right-click context menu on the canvas
  357. w.onCtxMenu = js.FuncOf(func(this js.Value, args []js.Value) interface{} { return false })
  358. w.canvas.Set("oncontextmenu", w.onCtxMenu)
  359. // TODO scaling/hidpi (device pixel ratio)
  360. // Set up key callbacks to dispatch events
  361. w.keyDown = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  362. event := args[0]
  363. eventCode := event.Get("code").String()
  364. w.keyEv.Keycode = Key(keyMap[eventCode])
  365. w.keyEv.Action = Action(Press)
  366. w.keyEv.Mods = getModifiers(event)
  367. w.Dispatch(OnKeyDown, &w.keyEv)
  368. return nil
  369. })
  370. js.Global().Call("addEventListener", "keydown", w.keyDown)
  371. w.keyUp = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  372. event := args[0]
  373. eventCode := event.Get("code").String()
  374. w.keyEv.Keycode = Key(keyMap[eventCode])
  375. w.keyEv.Action = Action(Release)
  376. w.keyEv.Mods = getModifiers(event)
  377. w.Dispatch(OnKeyUp, &w.keyEv)
  378. return nil
  379. })
  380. js.Global().Call("addEventListener", "keyup", w.keyUp)
  381. // Set up mouse button callbacks to dispatch events // TODO make these comments consistent
  382. w.mouseDown = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  383. event := args[0]
  384. button := MouseButton(event.Get("button").Int())
  385. xpos := event.Get("offsetX").Int()
  386. ypos := event.Get("offsetY").Int()
  387. w.mouseEv.Button = MouseButton(button)
  388. w.mouseEv.Action = Action(Press)
  389. w.mouseEv.Mods = getModifiers(event)
  390. w.mouseEv.Xpos = float32(xpos) //* float32(w.scaleX) TODO
  391. w.mouseEv.Ypos = float32(ypos) //* float32(w.scaleY)
  392. w.Dispatch(OnMouseDown, &w.mouseEv)
  393. return nil
  394. })
  395. w.canvas.Call("addEventListener", "mousedown", w.mouseDown)
  396. w.mouseUp = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  397. event := args[0]
  398. button := MouseButton(event.Get("button").Int())
  399. xpos := event.Get("offsetX").Float()
  400. ypos := event.Get("offsetY").Float()
  401. w.mouseEv.Button = MouseButton(button)
  402. w.mouseEv.Action = Action(Release)
  403. w.mouseEv.Mods = getModifiers(event)
  404. w.mouseEv.Xpos = float32(xpos) //* float32(w.scaleX) TODO
  405. w.mouseEv.Ypos = float32(ypos) //* float32(w.scaleY)
  406. w.Dispatch(OnMouseUp, &w.mouseEv)
  407. return nil
  408. })
  409. w.canvas.Call("addEventListener", "mouseup", w.mouseUp)
  410. w.mouseMove = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  411. event := args[0]
  412. xpos := event.Get("offsetX").Float()
  413. ypos := event.Get("offsetY").Float()
  414. w.cursorEv.Xpos = float32(xpos) //* float32(w.scaleX) TODO
  415. w.cursorEv.Ypos = float32(ypos) //* float32(w.scaleY)
  416. w.cursorEv.Mods = getModifiers(event)
  417. w.Dispatch(OnCursor, &w.cursorEv)
  418. return nil
  419. })
  420. w.canvas.Call("addEventListener", "mousemove", w.mouseMove)
  421. w.mouseWheel = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  422. event := args[0]
  423. event.Call("preventDefault")
  424. xoff := event.Get("deltaX").Float()
  425. yoff := event.Get("deltaY").Float()
  426. w.scrollEv.Xoffset = -float32(xoff) / 100.0
  427. w.scrollEv.Yoffset = -float32(yoff) / 100.0
  428. w.scrollEv.Mods = getModifiers(event)
  429. w.Dispatch(OnScroll, &w.scrollEv)
  430. return nil
  431. })
  432. w.canvas.Call("addEventListener", "wheel", w.mouseWheel)
  433. w.winResize = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  434. w.sizeEv.Width = w.canvas.Get("width").Int()
  435. w.sizeEv.Height = w.canvas.Get("height").Int()
  436. // TODO device pixel ratio
  437. //fbw, fbh := x.GetFramebufferSize()
  438. //w.scaleX = float64(fbw) / float64(width)
  439. //w.scaleY = float64(fbh) / float64(height)
  440. w.Dispatch(OnWindowSize, &w.sizeEv)
  441. return nil
  442. })
  443. js.Global().Get("window").Call("addEventListener", "resize", w.winResize)
  444. //// Set char callback TODO
  445. //w.SetCharModsCallback(func(x *glfw.Window, char rune, mods glfw.ModifierKey) { //
  446. // w.charEv.Char = char
  447. // w.charEv.Mods = ModifierKey(mods)
  448. // w.Dispatch(OnChar, &w.charEv)
  449. //})
  450. win = w // Set singleton
  451. return nil
  452. }
  453. // getModifiers extracts a ModifierKey bitmask from a Javascript event object.
  454. func getModifiers(event js.Value) ModifierKey {
  455. shiftKey := event.Get("shiftKey").Bool()
  456. ctrlKey := event.Get("ctrlKey").Bool()
  457. altKey := event.Get("altKey").Bool()
  458. metaKey := event.Get("metaKey").Bool()
  459. var mods ModifierKey
  460. if shiftKey {
  461. mods = mods | ModShift
  462. }
  463. if ctrlKey {
  464. mods = mods | ModControl
  465. }
  466. if altKey {
  467. mods = mods | ModAlt
  468. }
  469. if metaKey {
  470. mods = mods | ModSuper
  471. }
  472. return mods
  473. }
  474. // Canvas returns the associated WebGL WebGlCanvas.
  475. func (w *WebGlCanvas) Canvas() js.Value {
  476. return w.canvas
  477. }
  478. // Gls returns the associated OpenGL state
  479. func (w *WebGlCanvas) Gls() *gls.GLS {
  480. return w.gls
  481. }
  482. // FullScreen returns whether this canvas is fullscreen
  483. func (w *WebGlCanvas) FullScreen() bool {
  484. // TODO
  485. return false
  486. }
  487. // SetFullScreen sets this window full screen state for the primary monitor
  488. func (w *WebGlCanvas) SetFullScreen(full bool) {
  489. // TODO
  490. // Make it so that the first user interaction (e.g. click) should set the canvas as fullscreen.
  491. }
  492. // Destroy destroys the WebGL canvas and removes all event listeners.
  493. func (w *WebGlCanvas) Destroy() {
  494. // Remove event listeners
  495. w.canvas.Set("oncontextmenu", js.Null())
  496. js.Global().Call("removeEventListener", "keydown", w.keyDown)
  497. js.Global().Call("removeEventListener", "keyup", w.keyUp)
  498. w.canvas.Call("removeEventListener", "mousedown", w.mouseDown)
  499. w.canvas.Call("removeEventListener", "mouseup", w.mouseUp)
  500. w.canvas.Call("removeEventListener", "mousemove", w.mouseMove)
  501. w.canvas.Call("removeEventListener", "wheel", w.mouseWheel)
  502. js.Global().Get("window").Call("removeEventListener", "resize", w.winResize)
  503. // Release callbacks
  504. w.onCtxMenu.Release()
  505. w.keyDown.Release()
  506. w.keyUp.Release()
  507. w.mouseDown.Release()
  508. w.mouseUp.Release()
  509. w.mouseMove.Release()
  510. w.mouseWheel.Release()
  511. w.winResize.Release()
  512. }
  513. // GetFramebufferSize returns the framebuffer size.
  514. func (w *WebGlCanvas) GetFramebufferSize() (width int, height int) {
  515. // TODO device pixel ratio
  516. return w.canvas.Get("width").Int(), w.canvas.Get("height").Int()
  517. }
  518. // GetSize returns this window's size in screen coordinates.
  519. func (w *WebGlCanvas) GetSize() (width int, height int) {
  520. return w.canvas.Get("width").Int(), w.canvas.Get("height").Int()
  521. }
  522. // SetSize sets the size, in screen coordinates, of the canvas.
  523. func (w *WebGlCanvas) SetSize(width int, height int) {
  524. w.canvas.Set("width", width)
  525. w.canvas.Set("height", height)
  526. }
  527. // Scale returns this window's DPI scale factor (FramebufferSize / Size)
  528. func (w *WebGlCanvas) GetScale() (x float64, y float64) {
  529. // TODO device pixel ratio
  530. return 1, 1
  531. }
  532. // CreateCursor creates a new custom cursor and returns an int handle.
  533. func (w *WebGlCanvas) CreateCursor(imgFile string, xhot, yhot int) (Cursor, error) {
  534. // TODO
  535. return 0, nil
  536. }
  537. // SetCursor sets the window's cursor to a standard one
  538. func (w *WebGlCanvas) SetCursor(cursor Cursor) {
  539. // TODO
  540. }
  541. // DisposeAllCursors deletes all existing custom cursors.
  542. func (w *WebGlCanvas) DisposeAllCustomCursors() {
  543. // TODO
  544. }
  545. // SetInputMode changes specified input to specified state
  546. //func (w *WebGlCanvas) SetInputMode(mode InputMode, state int) {
  547. //
  548. // // TODO
  549. // // Hide cursor etc
  550. //}