canvas.go 14 KB

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