canvas.go 13 KB

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