canvas.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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. // Input modes
  284. const (
  285. CursorInputMode = InputMode(iota) // See Cursor mode values
  286. StickyKeysInputMode // Value can be either 1 or 0
  287. StickyMouseButtonsInputMode // Value can be either 1 or 0
  288. )
  289. // Cursor mode values
  290. const (
  291. CursorNormal = CursorMode(iota)
  292. CursorHidden
  293. CursorDisabled
  294. )
  295. // WebGlCanvas is a browser-based WebGL canvas.
  296. type WebGlCanvas struct {
  297. core.Dispatcher // Embedded event dispatcher
  298. canvas js.Value // Associated WebGL canvas
  299. gls *gls.GLS // Associated WebGL state
  300. // Events
  301. keyEv KeyEvent
  302. charEv CharEvent
  303. mouseEv MouseEvent
  304. posEv PosEvent
  305. sizeEv SizeEvent
  306. cursorEv CursorEvent
  307. scrollEv ScrollEvent
  308. focusEv FocusEvent
  309. // Callbacks
  310. onCtxMenu js.Func
  311. keyDown js.Func
  312. keyUp js.Func
  313. mouseDown js.Func
  314. mouseUp js.Func
  315. mouseMove js.Func
  316. mouseWheel js.Func
  317. winResize js.Func
  318. winFocus js.Func
  319. winBlur js.Func
  320. }
  321. // Init initializes the WebGlCanvas singleton.
  322. // If canvasId is provided, the pre-existing WebGlCanvas with that id is used.
  323. // If canvasId is the empty string then it creates a new WebGL canvas.
  324. func Init(canvasId string) error {
  325. // Panic if already created
  326. if win != nil {
  327. panic(fmt.Errorf("can only call window.Init() once"))
  328. }
  329. // Create wrapper window with dispatcher
  330. w := new(WebGlCanvas)
  331. w.Dispatcher.Initialize()
  332. // Create or get WebGlCanvas
  333. doc := js.Global().Get("document")
  334. if canvasId == "" {
  335. w.canvas = doc.Call("createElement", "WebGlCanvas")
  336. } else {
  337. w.canvas = doc.Call("getElementById", canvasId)
  338. if w.canvas == js.Null() {
  339. panic(fmt.Sprintf("Cannot find canvas with provided id: %s", canvasId))
  340. }
  341. }
  342. // Get reference to WebGL context
  343. webglCtx := w.canvas.Call("getContext", "webgl2")
  344. if webglCtx == js.Undefined() {
  345. return fmt.Errorf("Browser doesn't support WebGL2")
  346. }
  347. // Create WebGL state
  348. gl, err := gls.New(webglCtx)
  349. if err != nil {
  350. return err
  351. }
  352. w.gls = gl
  353. // Disable right-click context menu on the canvas
  354. w.onCtxMenu = js.FuncOf(func(this js.Value, args []js.Value) interface{} { return false })
  355. w.canvas.Set("oncontextmenu", w.onCtxMenu)
  356. // TODO scaling/hidpi (device pixel ratio)
  357. // Set up key down callback to dispatch event
  358. w.keyDown = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  359. event := args[0]
  360. eventCode := event.Get("code").String()
  361. w.keyEv.Key = Key(keyMap[eventCode])
  362. w.keyEv.Mods = getModifiers(event)
  363. w.Dispatch(OnKeyDown, &w.keyEv)
  364. return nil
  365. })
  366. js.Global().Call("addEventListener", "keydown", w.keyDown)
  367. // Set up key up callback to dispatch event
  368. w.keyUp = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  369. event := args[0]
  370. eventCode := event.Get("code").String()
  371. w.keyEv.Key = Key(keyMap[eventCode])
  372. w.keyEv.Mods = getModifiers(event)
  373. w.Dispatch(OnKeyUp, &w.keyEv)
  374. return nil
  375. })
  376. js.Global().Call("addEventListener", "keyup", w.keyUp)
  377. // Set up mouse down callback to dispatch event
  378. w.mouseDown = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  379. event := args[0]
  380. w.mouseEv.Button = MouseButton(event.Get("button").Int())
  381. w.mouseEv.Xpos = float32(event.Get("offsetX").Int()) //* float32(w.scaleX) TODO
  382. w.mouseEv.Ypos = float32(event.Get("offsetY").Int()) //* float32(w.scaleY)
  383. w.mouseEv.Mods = getModifiers(event)
  384. w.Dispatch(OnMouseDown, &w.mouseEv)
  385. return nil
  386. })
  387. w.canvas.Call("addEventListener", "mousedown", w.mouseDown)
  388. // Set up mouse down callback to dispatch event
  389. w.mouseUp = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  390. event := args[0]
  391. w.mouseEv.Button = MouseButton(event.Get("button").Int())
  392. w.mouseEv.Xpos = float32(event.Get("offsetX").Float()) //* float32(w.scaleX) TODO
  393. w.mouseEv.Ypos = float32(event.Get("offsetY").Float()) //* float32(w.scaleY)
  394. w.mouseEv.Mods = getModifiers(event)
  395. w.Dispatch(OnMouseUp, &w.mouseEv)
  396. return nil
  397. })
  398. w.canvas.Call("addEventListener", "mouseup", w.mouseUp)
  399. // Set up mouse move callback to dispatch event
  400. w.mouseMove = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  401. event := args[0]
  402. w.cursorEv.Xpos = float32(event.Get("offsetX").Float()) //* float32(w.scaleX) TODO
  403. w.cursorEv.Ypos = float32(event.Get("offsetY").Float()) //* float32(w.scaleY)
  404. w.cursorEv.Mods = getModifiers(event)
  405. w.Dispatch(OnCursor, &w.cursorEv)
  406. return nil
  407. })
  408. w.canvas.Call("addEventListener", "mousemove", w.mouseMove)
  409. // Set up mouse wheel callback to dispatch event
  410. w.mouseWheel = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  411. event := args[0]
  412. event.Call("preventDefault")
  413. w.scrollEv.Xoffset = -float32(event.Get("deltaX").Float()) / 100.0
  414. w.scrollEv.Yoffset = -float32(event.Get("deltaY").Float()) / 100.0
  415. w.scrollEv.Mods = getModifiers(event)
  416. w.Dispatch(OnScroll, &w.scrollEv)
  417. return nil
  418. })
  419. w.canvas.Call("addEventListener", "wheel", w.mouseWheel)
  420. // Set up window resize callback to dispatch event
  421. w.winResize = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  422. w.sizeEv.Width = w.canvas.Get("width").Int()
  423. w.sizeEv.Height = w.canvas.Get("height").Int()
  424. // TODO device pixel ratio
  425. //fbw, fbh := x.GetFramebufferSize()
  426. //w.scaleX = float64(fbw) / float64(width)
  427. //w.scaleY = float64(fbh) / float64(height)
  428. w.Dispatch(OnWindowSize, &w.sizeEv)
  429. return nil
  430. })
  431. js.Global().Get("window").Call("addEventListener", "resize", w.winResize)
  432. // Set up window focus callback to dispatch event
  433. w.winFocus = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  434. w.focusEv.Focused = true
  435. w.Dispatch(OnWindowFocus, &w.focusEv)
  436. return nil
  437. })
  438. w.winBlur = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  439. w.focusEv.Focused = false
  440. w.Dispatch(OnWindowFocus, &w.focusEv)
  441. return nil
  442. })
  443. js.Global().Get("window").Call("addEventListener", "onfocus", w.winFocus)
  444. js.Global().Get("window").Call("addEventListener", "onblur", w.winBlur)
  445. //// Set up char callback to dispatch event TODO
  446. //w.SetCharModsCallback(func(x *glfw.Window, char rune, mods glfw.ModifierKey) { //
  447. // w.charEv.Char = char
  448. // w.charEv.Mods = ModifierKey(mods)
  449. // w.Dispatch(OnChar, &w.charEv)
  450. //})
  451. win = w // Set singleton
  452. return nil
  453. }
  454. // getModifiers extracts a ModifierKey bitmask from a Javascript event object.
  455. func getModifiers(event js.Value) ModifierKey {
  456. shiftKey := event.Get("shiftKey").Bool()
  457. ctrlKey := event.Get("ctrlKey").Bool()
  458. altKey := event.Get("altKey").Bool()
  459. metaKey := event.Get("metaKey").Bool()
  460. var mods ModifierKey
  461. if shiftKey {
  462. mods = mods | ModShift
  463. }
  464. if ctrlKey {
  465. mods = mods | ModControl
  466. }
  467. if altKey {
  468. mods = mods | ModAlt
  469. }
  470. if metaKey {
  471. mods = mods | ModSuper
  472. }
  473. return mods
  474. }
  475. // Canvas returns the associated WebGL WebGlCanvas.
  476. func (w *WebGlCanvas) Canvas() js.Value {
  477. return w.canvas
  478. }
  479. // Gls returns the associated OpenGL state
  480. func (w *WebGlCanvas) Gls() *gls.GLS {
  481. return w.gls
  482. }
  483. // FullScreen returns whether this canvas is fullscreen
  484. func (w *WebGlCanvas) FullScreen() bool {
  485. // TODO
  486. return false
  487. }
  488. // SetFullScreen sets this window full screen state for the primary monitor
  489. func (w *WebGlCanvas) SetFullScreen(full bool) {
  490. // TODO
  491. // Make it so that the first user interaction (e.g. click) should set the canvas as fullscreen.
  492. }
  493. // Destroy destroys the WebGL canvas and removes all event listeners.
  494. func (w *WebGlCanvas) Destroy() {
  495. // Remove event listeners
  496. w.canvas.Set("oncontextmenu", js.Null())
  497. js.Global().Call("removeEventListener", "keydown", w.keyDown)
  498. js.Global().Call("removeEventListener", "keyup", w.keyUp)
  499. w.canvas.Call("removeEventListener", "mousedown", w.mouseDown)
  500. w.canvas.Call("removeEventListener", "mouseup", w.mouseUp)
  501. w.canvas.Call("removeEventListener", "mousemove", w.mouseMove)
  502. w.canvas.Call("removeEventListener", "wheel", w.mouseWheel)
  503. js.Global().Get("window").Call("removeEventListener", "resize", w.winResize)
  504. js.Global().Get("window").Call("removeEventListener", "onfocus", w.winFocus)
  505. js.Global().Get("window").Call("removeEventListener", "onfocus", w.winBlur)
  506. // Release callbacks
  507. w.onCtxMenu.Release()
  508. w.keyDown.Release()
  509. w.keyUp.Release()
  510. w.mouseDown.Release()
  511. w.mouseUp.Release()
  512. w.mouseMove.Release()
  513. w.mouseWheel.Release()
  514. w.winResize.Release()
  515. w.winFocus.Release()
  516. w.winBlur.Release()
  517. }
  518. // GetFramebufferSize returns the framebuffer size.
  519. func (w *WebGlCanvas) GetFramebufferSize() (width int, height int) {
  520. // TODO device pixel ratio
  521. return w.canvas.Get("width").Int(), w.canvas.Get("height").Int()
  522. }
  523. // GetSize returns this window's size in screen coordinates.
  524. func (w *WebGlCanvas) GetSize() (width int, height int) {
  525. return w.canvas.Get("width").Int(), w.canvas.Get("height").Int()
  526. }
  527. // SetSize sets the size, in screen coordinates, of the canvas.
  528. func (w *WebGlCanvas) SetSize(width int, height int) {
  529. w.canvas.Set("width", width)
  530. w.canvas.Set("height", height)
  531. }
  532. // Scale returns this window's DPI scale factor (FramebufferSize / Size)
  533. func (w *WebGlCanvas) GetScale() (x float64, y float64) {
  534. // TODO device pixel ratio
  535. return 1, 1
  536. }
  537. // CreateCursor creates a new custom cursor and returns an int handle.
  538. func (w *WebGlCanvas) CreateCursor(imgFile string, xhot, yhot int) (Cursor, error) {
  539. // TODO
  540. return 0, nil
  541. }
  542. // SetCursor sets the window's cursor to a standard one
  543. func (w *WebGlCanvas) SetCursor(cursor Cursor) {
  544. // TODO
  545. }
  546. // DisposeAllCursors deletes all existing custom cursors.
  547. func (w *WebGlCanvas) DisposeAllCustomCursors() {
  548. // TODO
  549. }
  550. // SetInputMode changes specified input to specified state
  551. //func (w *WebGlCanvas) SetInputMode(mode InputMode, state int) {
  552. //
  553. // // TODO
  554. // // Hide cursor etc
  555. //}