canvas.go 14 KB

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