canvas.go 13 KB

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