browser.go 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  1. //go:build js && !wasm
  2. // +build js,!wasm
  3. package glfw
  4. import (
  5. "errors"
  6. "fmt"
  7. "io"
  8. "log"
  9. "net/http"
  10. "runtime"
  11. "github.com/gopherjs/gopherjs/js"
  12. "honnef.co/go/js/dom"
  13. )
  14. const (
  15. True int = 1
  16. False int = 0
  17. DontCare int = -1
  18. )
  19. var document = dom.GetWindow().Document().(dom.HTMLDocument)
  20. var contextWatcher ContextWatcher
  21. func Init(cw ContextWatcher) error {
  22. contextWatcher = cw
  23. return nil
  24. }
  25. func Terminate() error {
  26. return nil
  27. }
  28. func CreateWindow(_, _ int, title string, monitor *Monitor, share *Window) (*Window, error) {
  29. // THINK: Consider https://developer.mozilla.org/en-US/docs/Web/API/Window.open?
  30. // HACK: Go fullscreen?
  31. width := dom.GetWindow().InnerWidth()
  32. height := dom.GetWindow().InnerHeight()
  33. canvas := document.CreateElement("canvas").(*dom.HTMLCanvasElement)
  34. devicePixelRatio := js.Global.Get("devicePixelRatio").Float()
  35. canvas.Width = int(float64(width)*devicePixelRatio + 0.5) // Nearest non-negative int.
  36. canvas.Height = int(float64(height)*devicePixelRatio + 0.5) // Nearest non-negative int.
  37. canvas.Style().SetProperty("width", fmt.Sprintf("%vpx", width), "")
  38. canvas.Style().SetProperty("height", fmt.Sprintf("%vpx", height), "")
  39. if js.Global.Get("document").Get("body") == nil {
  40. body := js.Global.Get("document").Call("createElement", "body")
  41. js.Global.Get("document").Set("body", body)
  42. }
  43. document.Body().Style().SetProperty("margin", "0", "")
  44. document.Body().AppendChild(canvas)
  45. document.SetTitle(title)
  46. // DEBUG: Add framebuffer information div.
  47. if false {
  48. //canvas.Height -= 30
  49. text := document.CreateElement("div")
  50. textContent := fmt.Sprintf("%v %v (%v) @%v", dom.GetWindow().InnerWidth(), canvas.Width, float64(width)*devicePixelRatio, devicePixelRatio)
  51. text.SetTextContent(textContent)
  52. document.Body().AppendChild(text)
  53. }
  54. // Use glfw hints.
  55. attrs := defaultAttributes()
  56. attrs.Alpha = (hints[AlphaBits] > 0)
  57. if _, ok := hints[DepthBits]; ok {
  58. attrs.Depth = (hints[DepthBits] > 0)
  59. }
  60. attrs.Stencil = (hints[StencilBits] > 0)
  61. attrs.Antialias = (hints[Samples] > 0)
  62. attrs.PremultipliedAlpha = (hints[PremultipliedAlpha] > 0)
  63. attrs.PreserveDrawingBuffer = (hints[PreserveDrawingBuffer] > 0)
  64. attrs.PreferLowPowerToHighPerformance = (hints[PreferLowPowerToHighPerformance] > 0)
  65. attrs.FailIfMajorPerformanceCaveat = (hints[FailIfMajorPerformanceCaveat] > 0)
  66. // Create GL context.
  67. context, err := newContext(canvas.Underlying(), attrs)
  68. if err != nil {
  69. return nil, err
  70. }
  71. w := &Window{
  72. canvas: canvas,
  73. context: context,
  74. devicePixelRatio: devicePixelRatio,
  75. }
  76. if w.canvas.Underlying().Get("requestPointerLock") == js.Undefined ||
  77. document.Underlying().Get("exitPointerLock") == js.Undefined {
  78. w.missing.pointerLock = true
  79. }
  80. if w.canvas.Underlying().Get("webkitRequestFullscreen") == js.Undefined ||
  81. document.Underlying().Get("webkitExitFullscreen") == js.Undefined {
  82. w.missing.fullscreen = true
  83. }
  84. if monitor != nil {
  85. if w.missing.fullscreen {
  86. log.Println("warning: Fullscreen API unsupported")
  87. } else {
  88. w.requestFullscreen = true
  89. }
  90. }
  91. dom.GetWindow().AddEventListener("resize", false, func(event dom.Event) {
  92. // HACK: Go fullscreen?
  93. width := dom.GetWindow().InnerWidth()
  94. height := dom.GetWindow().InnerHeight()
  95. devicePixelRatio := js.Global.Get("devicePixelRatio").Float()
  96. w.canvas.Width = int(float64(width)*devicePixelRatio + 0.5) // Nearest non-negative int.
  97. w.canvas.Height = int(float64(height)*devicePixelRatio + 0.5) // Nearest non-negative int.
  98. if w.framebufferSizeCallback != nil {
  99. // TODO: Callbacks may be blocking so they need to happen asyncronously. However,
  100. // GLFW API promises the callbacks will occur from one thread (i.e., sequentially), so may want to do that.
  101. go w.framebufferSizeCallback(w, w.canvas.Width, w.canvas.Height)
  102. }
  103. if w.sizeCallback != nil {
  104. boundingW, boundingH := width, height
  105. go w.sizeCallback(w, boundingW, boundingH)
  106. }
  107. })
  108. document.AddEventListener("keydown", false, func(event dom.Event) {
  109. w.goFullscreenIfRequested()
  110. ke := event.(*dom.KeyboardEvent)
  111. action := Press
  112. if ke.Repeat {
  113. action = Repeat
  114. }
  115. key := toKey(ke)
  116. // Extend slice if needed.
  117. neededSize := int(key) + 1
  118. if neededSize > len(w.keys) {
  119. w.keys = append(w.keys, make([]Action, neededSize-len(w.keys))...)
  120. }
  121. w.keys[key] = action
  122. mods := toModifierKey(ke)
  123. if w.keyCallback != nil {
  124. go w.keyCallback(w, key, -1, action, mods)
  125. }
  126. if w.charCallback != nil && mods < 2 {
  127. if len(ke.Key) == 1 {
  128. keyRune := []rune(ke.Key)
  129. go w.charCallback(w, keyRune[0])
  130. }
  131. }
  132. ke.PreventDefault()
  133. })
  134. document.AddEventListener("keyup", false, func(event dom.Event) {
  135. w.goFullscreenIfRequested()
  136. ke := event.(*dom.KeyboardEvent)
  137. key := toKey(ke)
  138. // Extend slice if needed.
  139. neededSize := int(key) + 1
  140. if neededSize > len(w.keys) {
  141. w.keys = append(w.keys, make([]Action, neededSize-len(w.keys))...)
  142. }
  143. w.keys[key] = Release
  144. if w.keyCallback != nil {
  145. mods := toModifierKey(ke)
  146. go w.keyCallback(w, key, -1, Release, mods)
  147. }
  148. ke.PreventDefault()
  149. })
  150. document.AddEventListener("mousedown", false, func(event dom.Event) {
  151. w.goFullscreenIfRequested()
  152. me := event.(*dom.MouseEvent)
  153. if !(me.Button >= 0 && me.Button <= 2) {
  154. return
  155. }
  156. w.mouseButton[me.Button] = Press
  157. if w.mouseButtonCallback != nil {
  158. go w.mouseButtonCallback(w, MouseButton(me.Button), Press, 0)
  159. }
  160. me.PreventDefault()
  161. })
  162. document.AddEventListener("mouseup", false, func(event dom.Event) {
  163. w.goFullscreenIfRequested()
  164. me := event.(*dom.MouseEvent)
  165. if !(me.Button >= 0 && me.Button <= 2) {
  166. return
  167. }
  168. w.mouseButton[me.Button] = Release
  169. if w.mouseButtonCallback != nil {
  170. go w.mouseButtonCallback(w, MouseButton(me.Button), Release, 0)
  171. }
  172. me.PreventDefault()
  173. })
  174. document.AddEventListener("contextmenu", false, func(event dom.Event) {
  175. event.PreventDefault()
  176. })
  177. document.AddEventListener("mousemove", false, func(event dom.Event) {
  178. me := event.(*dom.MouseEvent)
  179. var movementX, movementY float64
  180. if !w.missing.pointerLock {
  181. movementX = float64(me.MovementX)
  182. movementY = float64(me.MovementY)
  183. } else {
  184. movementX = float64(me.ClientX) - w.cursorPos[0]
  185. movementY = float64(me.ClientY) - w.cursorPos[1]
  186. }
  187. w.cursorPos[0], w.cursorPos[1] = float64(me.ClientX), float64(me.ClientY)
  188. if w.cursorPosCallback != nil {
  189. go w.cursorPosCallback(w, w.cursorPos[0], w.cursorPos[1])
  190. }
  191. if w.mouseMovementCallback != nil {
  192. go w.mouseMovementCallback(w, w.cursorPos[0], w.cursorPos[1], movementX, movementY)
  193. }
  194. me.PreventDefault()
  195. })
  196. document.AddEventListener("wheel", false, func(event dom.Event) {
  197. we := event.(*dom.WheelEvent)
  198. var multiplier float64
  199. switch we.DeltaMode {
  200. case dom.DeltaPixel:
  201. multiplier = 0.1
  202. case dom.DeltaLine:
  203. multiplier = 1
  204. default:
  205. log.Println("unsupported WheelEvent.DeltaMode:", we.DeltaMode)
  206. multiplier = 1
  207. }
  208. if w.scrollCallback != nil {
  209. go w.scrollCallback(w, -we.DeltaX*multiplier, -we.DeltaY*multiplier)
  210. }
  211. we.PreventDefault()
  212. })
  213. // Hacky mouse-emulation-via-touch.
  214. touchHandler := func(event dom.Event) {
  215. w.goFullscreenIfRequested()
  216. te := event.(*dom.TouchEvent)
  217. touches := te.Get("touches")
  218. if touches.Length() > 0 {
  219. t := touches.Index(0)
  220. if w.touches != nil && w.touches.Length() > 0 { // This event is a movement only if we previously had > 0 touch points.
  221. if w.mouseMovementCallback != nil {
  222. go w.mouseMovementCallback(w, t.Get("clientX").Float(), t.Get("clientY").Float(), t.Get("clientX").Float()-w.cursorPos[0], t.Get("clientY").Float()-w.cursorPos[1])
  223. }
  224. }
  225. w.cursorPos[0], w.cursorPos[1] = t.Get("clientX").Float(), t.Get("clientY").Float()
  226. if w.cursorPosCallback != nil {
  227. go w.cursorPosCallback(w, w.cursorPos[0], w.cursorPos[1])
  228. }
  229. }
  230. w.touches = touches
  231. te.PreventDefault()
  232. }
  233. document.AddEventListener("touchstart", false, touchHandler)
  234. document.AddEventListener("touchmove", false, touchHandler)
  235. document.AddEventListener("touchend", false, touchHandler)
  236. // Request first animation frame.
  237. js.Global.Call("requestAnimationFrame", animationFrame)
  238. return w, nil
  239. }
  240. func (w *Window) SetAttrib(attrib Hint, value int) {
  241. // TODO: Implement.
  242. }
  243. func SwapInterval(interval int) error {
  244. // TODO: Implement.
  245. return nil
  246. }
  247. type Window struct {
  248. canvas *dom.HTMLCanvasElement
  249. context *js.Object
  250. requestFullscreen bool // requestFullscreen is set to true when fullscreen should be entered as soon as possible (in a user input handler).
  251. fullscreen bool // fullscreen is true if we're currently in fullscreen mode.
  252. // Unavailable browser APIs.
  253. missing struct {
  254. pointerLock bool // Pointer Lock API.
  255. fullscreen bool // Fullscreen API.
  256. }
  257. devicePixelRatio float64
  258. cursorMode int
  259. cursorPos [2]float64
  260. mouseButton [3]Action
  261. keys []Action
  262. cursorPosCallback CursorPosCallback
  263. mouseMovementCallback MouseMovementCallback
  264. mouseButtonCallback MouseButtonCallback
  265. keyCallback KeyCallback
  266. charCallback CharCallback
  267. scrollCallback ScrollCallback
  268. framebufferSizeCallback FramebufferSizeCallback
  269. sizeCallback SizeCallback
  270. touches *js.Object // Hacky mouse-emulation-via-touch.
  271. }
  272. func (w *Window) SetPos(xpos, ypos int) {
  273. fmt.Println("not implemented: SetPos:", xpos, ypos)
  274. }
  275. func (w *Window) SetSize(width, height int) {
  276. fmt.Println("not implemented: SetSize:", width, height)
  277. }
  278. func (w *Window) SetIcon(images interface{}) {
  279. // images is actually of type []image.Image, but no need to import image until we actually do something with it
  280. fmt.Println("not implemented: SetIcon")
  281. }
  282. // goFullscreenIfRequested performs webkitRequestFullscreen if it was scheduled. It is called only from
  283. // user events, because that API will fail if called at any other time.
  284. func (w *Window) goFullscreenIfRequested() {
  285. if !w.requestFullscreen {
  286. return
  287. }
  288. w.requestFullscreen = false
  289. w.canvas.Underlying().Call("webkitRequestFullscreen")
  290. w.fullscreen = true
  291. }
  292. type Monitor struct{}
  293. func (m *Monitor) GetVideoMode() *VidMode {
  294. return &VidMode{
  295. // HACK: Hardcoded sample values.
  296. // TODO: Try to get real values from browser via some API, if possible.
  297. Width: 1680,
  298. Height: 1050,
  299. RedBits: 8,
  300. GreenBits: 8,
  301. BlueBits: 8,
  302. RefreshRate: 60,
  303. }
  304. }
  305. func GetPrimaryMonitor() *Monitor {
  306. // TODO: Implement real functionality.
  307. return &Monitor{}
  308. }
  309. func (w *Window) SetMonitor(monitor *Monitor, xpos, ypos, width, height, refreshRate int) {
  310. // TODO: Implement real functionality.
  311. }
  312. func PollEvents() error {
  313. return nil
  314. }
  315. func (w *Window) MakeContextCurrent() {
  316. contextWatcher.OnMakeCurrent(w.context)
  317. }
  318. func DetachCurrentContext() {
  319. contextWatcher.OnDetach()
  320. }
  321. func GetCurrentContext() *Window {
  322. panic("not implemented")
  323. }
  324. type CursorPosCallback func(w *Window, xpos float64, ypos float64)
  325. func (w *Window) SetCursorPosCallback(cbfun CursorPosCallback) (previous CursorPosCallback) {
  326. w.cursorPosCallback = cbfun
  327. // TODO: Handle previous.
  328. return nil
  329. }
  330. type MouseMovementCallback func(w *Window, xpos float64, ypos float64, xdelta float64, ydelta float64)
  331. func (w *Window) SetMouseMovementCallback(cbfun MouseMovementCallback) (previous MouseMovementCallback) {
  332. w.mouseMovementCallback = cbfun
  333. // TODO: Handle previous.
  334. return nil
  335. }
  336. type KeyCallback func(w *Window, key Key, scancode int, action Action, mods ModifierKey)
  337. func (w *Window) SetKeyCallback(cbfun KeyCallback) (previous KeyCallback) {
  338. w.keyCallback = cbfun
  339. // TODO: Handle previous.
  340. return nil
  341. }
  342. type CharCallback func(w *Window, char rune)
  343. func (w *Window) SetCharCallback(cbfun CharCallback) (previous CharCallback) {
  344. w.charCallback = cbfun
  345. // TODO: Handle previous.
  346. return nil
  347. }
  348. type ScrollCallback func(w *Window, xoff float64, yoff float64)
  349. func (w *Window) SetScrollCallback(cbfun ScrollCallback) (previous ScrollCallback) {
  350. w.scrollCallback = cbfun
  351. // TODO: Handle previous.
  352. return nil
  353. }
  354. type MouseButtonCallback func(w *Window, button MouseButton, action Action, mods ModifierKey)
  355. func (w *Window) SetMouseButtonCallback(cbfun MouseButtonCallback) (previous MouseButtonCallback) {
  356. w.mouseButtonCallback = cbfun
  357. // TODO: Handle previous.
  358. return nil
  359. }
  360. type FramebufferSizeCallback func(w *Window, width int, height int)
  361. func (w *Window) SetFramebufferSizeCallback(cbfun FramebufferSizeCallback) (previous FramebufferSizeCallback) {
  362. w.framebufferSizeCallback = cbfun
  363. // TODO: Handle previous.
  364. return nil
  365. }
  366. func (w *Window) GetSize() (width, height int) {
  367. width = int(w.canvas.GetBoundingClientRect().Width*w.devicePixelRatio + 0.5)
  368. height = int(w.canvas.GetBoundingClientRect().Height*w.devicePixelRatio + 0.5)
  369. return width, height
  370. }
  371. func (w *Window) GetFramebufferSize() (width, height int) {
  372. return w.canvas.Width, w.canvas.Height
  373. }
  374. func (w *Window) GetPos() (x, y int) {
  375. // Not implemented.
  376. return
  377. }
  378. func (w *Window) ShouldClose() bool {
  379. return false
  380. }
  381. func (w *Window) SetShouldClose(value bool) {
  382. // TODO: Implement.
  383. // THINK: What should happen in the browser if we're told to "close" the window. Do we destroy/remove the canvas? Or nothing?
  384. // Perhaps https://developer.mozilla.org/en-US/docs/Web/API/Window.close is relevant.
  385. }
  386. func (w *Window) SwapBuffers() error {
  387. <-animationFrameChan
  388. js.Global.Call("requestAnimationFrame", animationFrame)
  389. return nil
  390. }
  391. var animationFrameChan = make(chan struct{}, 1)
  392. func animationFrame() {
  393. animationFrameChan <- struct{}{}
  394. }
  395. func (w *Window) GetCursorPos() (x, y float64) {
  396. return w.cursorPos[0], w.cursorPos[1]
  397. }
  398. var keyWarnings = 10
  399. func (w *Window) GetKey(key Key) Action {
  400. if key == -1 && keyWarnings > 0 {
  401. // TODO: Implement all keys, get rid of this.
  402. keyWarnings--
  403. log.Println("GetKey: key not implemented.")
  404. return Release
  405. }
  406. if int(key) >= len(w.keys) {
  407. return Release
  408. }
  409. return w.keys[key]
  410. }
  411. func (w *Window) GetMouseButton(button MouseButton) Action {
  412. if !(button >= 0 && button <= 2) {
  413. panic(fmt.Errorf("button is out of range: %v", button))
  414. }
  415. // Hacky mouse-emulation-via-touch.
  416. if w.touches != nil {
  417. switch button {
  418. case MouseButton1:
  419. if w.touches.Length() == 1 || w.touches.Length() == 3 {
  420. return Press
  421. }
  422. case MouseButton2:
  423. if w.touches.Length() == 2 || w.touches.Length() == 3 {
  424. return Press
  425. }
  426. }
  427. return Release
  428. }
  429. return w.mouseButton[button]
  430. }
  431. func (w *Window) GetInputMode(mode InputMode) int {
  432. switch mode {
  433. case CursorMode:
  434. return w.cursorMode
  435. default:
  436. panic(errors.New("not implemented"))
  437. }
  438. }
  439. var ErrInvalidParameter = errors.New("invalid parameter")
  440. var ErrInvalidValue = errors.New("invalid value")
  441. func (w *Window) SetInputMode(mode InputMode, value int) {
  442. switch mode {
  443. case CursorMode:
  444. // TODO; Make cursor API compatible with GLFW and Fyne use/expectation.
  445. /*
  446. if w.missing.pointerLock {
  447. log.Println("warning: Pointer Lock API unsupported")
  448. return
  449. }
  450. switch value {
  451. case CursorNormal:
  452. w.cursorMode = value
  453. document.Underlying().Call("exitPointerLock")
  454. w.canvas.Style().SetProperty("cursor", "initial", "")
  455. return
  456. case CursorHidden:
  457. w.cursorMode = value
  458. document.Underlying().Call("exitPointerLock")
  459. w.canvas.Style().SetProperty("cursor", "none", "")
  460. return
  461. case CursorDisabled:
  462. w.cursorMode = value
  463. w.canvas.Underlying().Call("requestPointerLock")
  464. return
  465. default:
  466. panic(ErrInvalidValue)
  467. }
  468. */
  469. return
  470. case StickyKeysMode:
  471. panic(errors.New("not implemented"))
  472. case StickyMouseButtonsMode:
  473. panic(errors.New("not implemented"))
  474. default:
  475. panic(ErrInvalidParameter)
  476. }
  477. }
  478. type Key int
  479. // TODO: Keys defined as -iota-2 need to be set to a valid positive value that matches the keyCode
  480. //
  481. // generated by browsers. -iota-2 is used as a temporary solution to have unique but invalid values.
  482. // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode.
  483. const (
  484. KeyUnknown Key = -1
  485. KeySpace Key = 32
  486. KeyApostrophe Key = 222
  487. KeyComma Key = 188
  488. KeyMinus Key = 189
  489. KeyPeriod Key = 190
  490. KeySlash Key = 191
  491. Key0 Key = 48
  492. Key1 Key = 49
  493. Key2 Key = 50
  494. Key3 Key = 51
  495. Key4 Key = 52
  496. Key5 Key = 53
  497. Key6 Key = 54
  498. Key7 Key = 55
  499. Key8 Key = 56
  500. Key9 Key = 57
  501. KeySemicolon Key = 186
  502. KeyEqual Key = 187
  503. KeyA Key = 65
  504. KeyB Key = 66
  505. KeyC Key = 67
  506. KeyD Key = 68
  507. KeyE Key = 69
  508. KeyF Key = 70
  509. KeyG Key = 71
  510. KeyH Key = 72
  511. KeyI Key = 73
  512. KeyJ Key = 74
  513. KeyK Key = 75
  514. KeyL Key = 76
  515. KeyM Key = 77
  516. KeyN Key = 78
  517. KeyO Key = 79
  518. KeyP Key = 80
  519. KeyQ Key = 81
  520. KeyR Key = 82
  521. KeyS Key = 83
  522. KeyT Key = 84
  523. KeyU Key = 85
  524. KeyV Key = 86
  525. KeyW Key = 87
  526. KeyX Key = 88
  527. KeyY Key = 89
  528. KeyZ Key = 90
  529. KeyLeftBracket Key = 219
  530. KeyBackslash Key = 220
  531. KeyRightBracket Key = 221
  532. KeyGraveAccent Key = 192
  533. KeyWorld1 Key = -iota - 2
  534. KeyWorld2 Key = -iota - 2
  535. KeyEscape Key = 27
  536. KeyEnter Key = 13
  537. KeyTab Key = 9
  538. KeyBackspace Key = 8
  539. KeyInsert Key = -iota - 2
  540. KeyDelete Key = 46
  541. KeyRight Key = 39
  542. KeyLeft Key = 37
  543. KeyDown Key = 40
  544. KeyUp Key = 38
  545. KeyPageUp Key = -iota - 2
  546. KeyPageDown Key = -iota - 2
  547. KeyHome Key = -iota - 2
  548. KeyEnd Key = -iota - 2
  549. KeyCapsLock Key = 20
  550. KeyScrollLock Key = -iota - 2
  551. KeyNumLock Key = -iota - 2
  552. KeyPrintScreen Key = -iota - 2
  553. KeyPause Key = -iota - 2
  554. KeyF1 Key = 112
  555. KeyF2 Key = 113
  556. KeyF3 Key = 114
  557. KeyF4 Key = 115
  558. KeyF5 Key = 116
  559. KeyF6 Key = 117
  560. KeyF7 Key = 118
  561. KeyF8 Key = 119
  562. KeyF9 Key = 120
  563. KeyF10 Key = 121
  564. KeyF11 Key = 122
  565. KeyF12 Key = 123
  566. KeyF13 Key = -iota - 2
  567. KeyF14 Key = -iota - 2
  568. KeyF15 Key = -iota - 2
  569. KeyF16 Key = -iota - 2
  570. KeyF17 Key = -iota - 2
  571. KeyF18 Key = -iota - 2
  572. KeyF19 Key = -iota - 2
  573. KeyF20 Key = -iota - 2
  574. KeyF21 Key = -iota - 2
  575. KeyF22 Key = -iota - 2
  576. KeyF23 Key = -iota - 2
  577. KeyF24 Key = -iota - 2
  578. KeyF25 Key = -iota - 2
  579. KeyKP0 Key = -iota - 2
  580. KeyKP1 Key = -iota - 2
  581. KeyKP2 Key = -iota - 2
  582. KeyKP3 Key = -iota - 2
  583. KeyKP4 Key = -iota - 2
  584. KeyKP5 Key = -iota - 2
  585. KeyKP6 Key = -iota - 2
  586. KeyKP7 Key = -iota - 2
  587. KeyKP8 Key = -iota - 2
  588. KeyKP9 Key = -iota - 2
  589. KeyKPDecimal Key = -iota - 2
  590. KeyKPDivide Key = -iota - 2
  591. KeyKPMultiply Key = -iota - 2
  592. KeyKPSubtract Key = -iota - 2
  593. KeyKPAdd Key = -iota - 2
  594. KeyKPEnter Key = -iota - 2
  595. KeyKPEqual Key = -iota - 2
  596. KeyLeftShift Key = 340
  597. KeyLeftControl Key = 341
  598. KeyLeftAlt Key = 342
  599. KeyLeftSuper Key = 91
  600. KeyRightShift Key = 344
  601. KeyRightControl Key = 345
  602. KeyRightAlt Key = 346
  603. KeyRightSuper Key = 93
  604. KeyMenu Key = -iota - 2
  605. )
  606. // toKey extracts Key from given KeyboardEvent.
  607. func toKey(ke *dom.KeyboardEvent) Key {
  608. key := Key(ke.KeyCode)
  609. switch {
  610. case key == 16 && ke.Location == dom.KeyLocationLeft:
  611. key = KeyLeftShift
  612. case key == 16 && ke.Location == dom.KeyLocationRight:
  613. key = KeyRightShift
  614. case key == 17 && ke.Location == dom.KeyLocationLeft:
  615. key = KeyLeftControl
  616. case key == 17 && ke.Location == dom.KeyLocationRight:
  617. key = KeyRightControl
  618. case key == 18 && ke.Location == dom.KeyLocationLeft:
  619. key = KeyLeftAlt
  620. case key == 18 && ke.Location == dom.KeyLocationRight:
  621. key = KeyRightAlt
  622. }
  623. return key
  624. }
  625. // toModifierKey extracts ModifierKey from given KeyboardEvent.
  626. func toModifierKey(ke *dom.KeyboardEvent) ModifierKey {
  627. mods := ModifierKey(0)
  628. if ke.ShiftKey {
  629. mods += ModShift
  630. }
  631. if ke.CtrlKey {
  632. mods += ModControl
  633. }
  634. if ke.AltKey {
  635. mods += ModAlt
  636. }
  637. if ke.MetaKey {
  638. mods += ModSuper
  639. }
  640. return mods
  641. }
  642. type MouseButton int
  643. const (
  644. MouseButton1 MouseButton = 0
  645. MouseButton2 MouseButton = 2 // Web MouseEvent has middle and right mouse buttons in reverse order.
  646. MouseButton3 MouseButton = 1 // Web MouseEvent has middle and right mouse buttons in reverse order.
  647. MouseButtonLeft = MouseButton1
  648. MouseButtonRight = MouseButton2
  649. MouseButtonMiddle = MouseButton3
  650. )
  651. type Joystick int
  652. const (
  653. Joystick1 Joystick = iota
  654. Joystick2
  655. Joystick3
  656. Joystick4
  657. Joystick5
  658. Joystick6
  659. Joystick7
  660. Joystick8
  661. Joystick9
  662. Joystick10
  663. Joystick11
  664. Joystick12
  665. Joystick13
  666. Joystick14
  667. Joystick15
  668. Joystick16
  669. JoystickLast = Joystick16
  670. )
  671. type GamepadAxis int
  672. const (
  673. AxisLeftX GamepadAxis = iota
  674. AxisLeftY
  675. AxisRightX
  676. AxisRightY
  677. AxisLeftTrigger
  678. AxisRightTrigger
  679. AxisLast = AxisRightTrigger
  680. )
  681. type GamepadButton int
  682. const (
  683. ButtonA GamepadButton = iota
  684. ButtonB
  685. ButtonX
  686. ButtonY
  687. ButtonLeftBumper
  688. ButtonRightBumper
  689. ButtonBack
  690. ButtonStart
  691. ButtonGuide
  692. ButtonLeftThumb
  693. ButtonRightThumb
  694. ButtonDpadUp
  695. ButtonDpadRight
  696. ButtonDpadDown
  697. ButtonDpadLeft
  698. ButtonLast = ButtonDpadLeft
  699. ButtonCross = ButtonA
  700. ButtonCircle = ButtonB
  701. ButtonSquare = ButtonX
  702. ButtonTriangle = ButtonY
  703. )
  704. type Action int
  705. const (
  706. Release Action = 0
  707. Press Action = 1
  708. Repeat Action = 2
  709. )
  710. type InputMode int
  711. const (
  712. CursorMode InputMode = iota
  713. StickyKeysMode
  714. StickyMouseButtonsMode
  715. LockKeyMods
  716. RawMouseMotion
  717. )
  718. const (
  719. CursorNormal = iota
  720. CursorHidden
  721. CursorDisabled
  722. )
  723. type ModifierKey int
  724. const (
  725. ModShift ModifierKey = (1 << iota)
  726. ModControl
  727. ModAlt
  728. ModSuper
  729. )
  730. func (joy Joystick) IsPresent() bool {
  731. // TODO: Implement.
  732. return false
  733. }
  734. func (joy Joystick) GetGamepadName() string {
  735. // TODO: Implement.
  736. return "Gamepad"
  737. }
  738. func (joy Joystick) GetButtons() []Action {
  739. // TODO: Implement.
  740. return make([]Action, 0)
  741. }
  742. func (joy Joystick) GetAxes() []float32 {
  743. // TODO: Implement.
  744. return make([]float32, 0)
  745. }
  746. // Open opens a named asset. It's the caller's responsibility to close it when done.
  747. func Open(name string) (io.ReadCloser, error) {
  748. resp, err := http.Get(name)
  749. if err != nil {
  750. return nil, err
  751. }
  752. if resp.StatusCode != 200 {
  753. return nil, fmt.Errorf("non-200 status: %s", resp.Status)
  754. }
  755. return resp.Body, nil
  756. }
  757. // ---
  758. func WaitEvents() {
  759. // TODO.
  760. runtime.Gosched()
  761. }
  762. func PostEmptyEvent() {
  763. // TODO: Implement.
  764. }
  765. func DefaultWindowHints() {
  766. // TODO: Implement.
  767. }
  768. func (w *Window) SetClipboardString(str string) {
  769. // Set the clipboard content from the input str
  770. js.Global.Get("navigator").Get("clipboard").Call("writeText", str)
  771. }
  772. func (w *Window) GetClipboardString() (string, error) {
  773. clipboardChan := make(chan *js.Object)
  774. // Call the `readText()` function and send the result to the channel
  775. js.Global.Get("navigator").Get("clipboard").Call("readText").Call("then", js.MakeFunc(func(this *js.Object, p []*js.Object) interface{} {
  776. clipboardContent := p[0]
  777. clipboardChan <- clipboardContent
  778. return nil
  779. })).Call("catch", js.MakeFunc(func(this *js.Object, p []*js.Object) interface{} {
  780. clipboardChan <- nil
  781. return nil
  782. }))
  783. // Get the *js.Object of the clipboard text from the channel
  784. result := <-clipboardChan
  785. if result != nil {
  786. // Convert the value to a string and return the value
  787. return result.String(), nil
  788. }
  789. return "", errors.New("Failed to get clipboard text")
  790. }
  791. func (w *Window) SetTitle(title string) {
  792. document.SetTitle(title)
  793. }
  794. func (w *Window) Show() {
  795. // TODO: Implement.
  796. }
  797. func (w *Window) Hide() {
  798. // TODO: Implement.
  799. }
  800. func (w *Window) Destroy() {
  801. document.Body().RemoveChild(w.canvas)
  802. if w.fullscreen {
  803. if w.missing.fullscreen {
  804. log.Println("warning: Fullscreen API unsupported")
  805. } else {
  806. document.Underlying().Call("webkitExitFullscreen")
  807. w.fullscreen = false
  808. }
  809. }
  810. }
  811. type CloseCallback func(w *Window)
  812. func (w *Window) SetCloseCallback(cbfun CloseCallback) (previous CloseCallback) {
  813. // TODO: Implement.
  814. // TODO: Handle previous.
  815. return nil
  816. }
  817. type RefreshCallback func(w *Window)
  818. func (w *Window) SetRefreshCallback(cbfun RefreshCallback) (previous RefreshCallback) {
  819. // TODO: Implement.
  820. // TODO: Handle previous.
  821. return nil
  822. }
  823. type SizeCallback func(w *Window, width int, height int)
  824. func (w *Window) SetSizeCallback(cbfun SizeCallback) (previous SizeCallback) {
  825. w.sizeCallback = cbfun
  826. // TODO: Handle previous.
  827. return nil
  828. }
  829. type CursorEnterCallback func(w *Window, entered bool)
  830. func (w *Window) SetCursorEnterCallback(cbfun CursorEnterCallback) (previous CursorEnterCallback) {
  831. // TODO: Implement.
  832. // TODO: Handle previous.
  833. return nil
  834. }
  835. type CharModsCallback func(w *Window, char rune, mods ModifierKey)
  836. func (w *Window) SetCharModsCallback(cbfun CharModsCallback) (previous CharModsCallback) {
  837. // TODO: Implement.
  838. // TODO: Handle previous.
  839. return nil
  840. }
  841. type PosCallback func(w *Window, xpos int, ypos int)
  842. func (w *Window) SetPosCallback(cbfun PosCallback) (previous PosCallback) {
  843. // TODO: Implement.
  844. // TODO: Handle previous.
  845. return nil
  846. }
  847. type FocusCallback func(w *Window, focused bool)
  848. func (w *Window) SetFocusCallback(cbfun FocusCallback) (previous FocusCallback) {
  849. // TODO: Implement.
  850. // TODO: Handle previous.
  851. return nil
  852. }
  853. type IconifyCallback func(w *Window, iconified bool)
  854. func (w *Window) SetIconifyCallback(cbfun IconifyCallback) (previous IconifyCallback) {
  855. // TODO: Implement.
  856. // TODO: Handle previous.
  857. return nil
  858. }
  859. type DropCallback func(w *Window, names []string)
  860. func (w *Window) SetDropCallback(cbfun DropCallback) (previous DropCallback) {
  861. // TODO: Implement. Can use HTML5 file drag and drop API?
  862. // TODO: Handle previous.
  863. return nil
  864. }