canvas.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  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 callbacks to dispatch events
  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.Keycode = 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. w.keyUp = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  365. event := args[0]
  366. eventCode := event.Get("code").String()
  367. w.keyEv.Keycode = Key(keyMap[eventCode])
  368. w.keyEv.Mods = getModifiers(event)
  369. w.Dispatch(OnKeyUp, &w.keyEv)
  370. return nil
  371. })
  372. js.Global().Call("addEventListener", "keyup", w.keyUp)
  373. // Set up mouse button callbacks to dispatch events // TODO make these comments consistent
  374. w.mouseDown = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  375. event := args[0]
  376. w.mouseEv.Button = MouseButton(event.Get("button").Int())
  377. w.mouseEv.Xpos = float32(event.Get("offsetX").Int()) //* float32(w.scaleX) TODO
  378. w.mouseEv.Ypos = float32(event.Get("offsetY").Int()) //* float32(w.scaleY)
  379. w.mouseEv.Mods = getModifiers(event)
  380. w.Dispatch(OnMouseDown, &w.mouseEv)
  381. return nil
  382. })
  383. w.canvas.Call("addEventListener", "mousedown", w.mouseDown)
  384. w.mouseUp = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  385. event := args[0]
  386. w.mouseEv.Button = MouseButton(event.Get("button").Int())
  387. w.mouseEv.Xpos = float32(event.Get("offsetX").Float()) //* float32(w.scaleX) TODO
  388. w.mouseEv.Ypos = float32(event.Get("offsetY").Float()) //* float32(w.scaleY)
  389. w.mouseEv.Mods = getModifiers(event)
  390. w.Dispatch(OnMouseUp, &w.mouseEv)
  391. return nil
  392. })
  393. w.canvas.Call("addEventListener", "mouseup", w.mouseUp)
  394. w.mouseMove = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  395. event := args[0]
  396. w.cursorEv.Xpos = float32(event.Get("offsetX").Float()) //* float32(w.scaleX) TODO
  397. w.cursorEv.Ypos = float32(event.Get("offsetY").Float()) //* float32(w.scaleY)
  398. w.cursorEv.Mods = getModifiers(event)
  399. w.Dispatch(OnCursor, &w.cursorEv)
  400. return nil
  401. })
  402. w.canvas.Call("addEventListener", "mousemove", w.mouseMove)
  403. w.mouseWheel = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  404. event := args[0]
  405. event.Call("preventDefault")
  406. w.scrollEv.Xoffset = -float32(event.Get("deltaX").Float()) / 100.0
  407. w.scrollEv.Yoffset = -float32(event.Get("deltaY").Float()) / 100.0
  408. w.scrollEv.Mods = getModifiers(event)
  409. w.Dispatch(OnScroll, &w.scrollEv)
  410. return nil
  411. })
  412. w.canvas.Call("addEventListener", "wheel", w.mouseWheel)
  413. w.winResize = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  414. w.sizeEv.Width = w.canvas.Get("width").Int()
  415. w.sizeEv.Height = w.canvas.Get("height").Int()
  416. // TODO device pixel ratio
  417. //fbw, fbh := x.GetFramebufferSize()
  418. //w.scaleX = float64(fbw) / float64(width)
  419. //w.scaleY = float64(fbh) / float64(height)
  420. w.Dispatch(OnWindowSize, &w.sizeEv)
  421. return nil
  422. })
  423. js.Global().Get("window").Call("addEventListener", "resize", w.winResize)
  424. //// Set char callback TODO
  425. //w.SetCharModsCallback(func(x *glfw.Window, char rune, mods glfw.ModifierKey) { //
  426. // w.charEv.Char = char
  427. // w.charEv.Mods = ModifierKey(mods)
  428. // w.Dispatch(OnChar, &w.charEv)
  429. //})
  430. win = w // Set singleton
  431. return nil
  432. }
  433. // getModifiers extracts a ModifierKey bitmask from a Javascript event object.
  434. func getModifiers(event js.Value) ModifierKey {
  435. shiftKey := event.Get("shiftKey").Bool()
  436. ctrlKey := event.Get("ctrlKey").Bool()
  437. altKey := event.Get("altKey").Bool()
  438. metaKey := event.Get("metaKey").Bool()
  439. var mods ModifierKey
  440. if shiftKey {
  441. mods = mods | ModShift
  442. }
  443. if ctrlKey {
  444. mods = mods | ModControl
  445. }
  446. if altKey {
  447. mods = mods | ModAlt
  448. }
  449. if metaKey {
  450. mods = mods | ModSuper
  451. }
  452. return mods
  453. }
  454. // Canvas returns the associated WebGL WebGlCanvas.
  455. func (w *WebGlCanvas) Canvas() js.Value {
  456. return w.canvas
  457. }
  458. // Gls returns the associated OpenGL state
  459. func (w *WebGlCanvas) Gls() *gls.GLS {
  460. return w.gls
  461. }
  462. // FullScreen returns whether this canvas is fullscreen
  463. func (w *WebGlCanvas) FullScreen() bool {
  464. // TODO
  465. return false
  466. }
  467. // SetFullScreen sets this window full screen state for the primary monitor
  468. func (w *WebGlCanvas) SetFullScreen(full bool) {
  469. // TODO
  470. // Make it so that the first user interaction (e.g. click) should set the canvas as fullscreen.
  471. }
  472. // Destroy destroys the WebGL canvas and removes all event listeners.
  473. func (w *WebGlCanvas) Destroy() {
  474. // Remove event listeners
  475. w.canvas.Set("oncontextmenu", js.Null())
  476. js.Global().Call("removeEventListener", "keydown", w.keyDown)
  477. js.Global().Call("removeEventListener", "keyup", w.keyUp)
  478. w.canvas.Call("removeEventListener", "mousedown", w.mouseDown)
  479. w.canvas.Call("removeEventListener", "mouseup", w.mouseUp)
  480. w.canvas.Call("removeEventListener", "mousemove", w.mouseMove)
  481. w.canvas.Call("removeEventListener", "wheel", w.mouseWheel)
  482. js.Global().Get("window").Call("removeEventListener", "resize", w.winResize)
  483. // Release callbacks
  484. w.onCtxMenu.Release()
  485. w.keyDown.Release()
  486. w.keyUp.Release()
  487. w.mouseDown.Release()
  488. w.mouseUp.Release()
  489. w.mouseMove.Release()
  490. w.mouseWheel.Release()
  491. w.winResize.Release()
  492. }
  493. // GetFramebufferSize returns the framebuffer size.
  494. func (w *WebGlCanvas) GetFramebufferSize() (width int, height int) {
  495. // TODO device pixel ratio
  496. return w.canvas.Get("width").Int(), w.canvas.Get("height").Int()
  497. }
  498. // GetSize returns this window's size in screen coordinates.
  499. func (w *WebGlCanvas) GetSize() (width int, height int) {
  500. return w.canvas.Get("width").Int(), w.canvas.Get("height").Int()
  501. }
  502. // SetSize sets the size, in screen coordinates, of the canvas.
  503. func (w *WebGlCanvas) SetSize(width int, height int) {
  504. w.canvas.Set("width", width)
  505. w.canvas.Set("height", height)
  506. }
  507. // Scale returns this window's DPI scale factor (FramebufferSize / Size)
  508. func (w *WebGlCanvas) GetScale() (x float64, y float64) {
  509. // TODO device pixel ratio
  510. return 1, 1
  511. }
  512. // CreateCursor creates a new custom cursor and returns an int handle.
  513. func (w *WebGlCanvas) CreateCursor(imgFile string, xhot, yhot int) (Cursor, error) {
  514. // TODO
  515. return 0, nil
  516. }
  517. // SetCursor sets the window's cursor to a standard one
  518. func (w *WebGlCanvas) SetCursor(cursor Cursor) {
  519. // TODO
  520. }
  521. // DisposeAllCursors deletes all existing custom cursors.
  522. func (w *WebGlCanvas) DisposeAllCustomCursors() {
  523. // TODO
  524. }
  525. // SetInputMode changes specified input to specified state
  526. //func (w *WebGlCanvas) SetInputMode(mode InputMode, state int) {
  527. //
  528. // // TODO
  529. // // Hide cursor etc
  530. //}