browser.go 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  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. if w.keyCallback != nil {
  123. mods := toModifierKey(ke)
  124. go w.keyCallback(w, key, -1, action, mods)
  125. }
  126. if w.charCallback != nil {
  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. // generated by browsers. -iota-2 is used as a temporary solution to have unique but invalid values.
  481. // See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode.
  482. const (
  483. KeyUnknown Key = -1
  484. KeySpace Key = 32
  485. KeyApostrophe Key = 222
  486. KeyComma Key = 188
  487. KeyMinus Key = 189
  488. KeyPeriod Key = 190
  489. KeySlash Key = 191
  490. Key0 Key = 48
  491. Key1 Key = 49
  492. Key2 Key = 50
  493. Key3 Key = 51
  494. Key4 Key = 52
  495. Key5 Key = 53
  496. Key6 Key = 54
  497. Key7 Key = 55
  498. Key8 Key = 56
  499. Key9 Key = 57
  500. KeySemicolon Key = 186
  501. KeyEqual Key = 187
  502. KeyA Key = 65
  503. KeyB Key = 66
  504. KeyC Key = 67
  505. KeyD Key = 68
  506. KeyE Key = 69
  507. KeyF Key = 70
  508. KeyG Key = 71
  509. KeyH Key = 72
  510. KeyI Key = 73
  511. KeyJ Key = 74
  512. KeyK Key = 75
  513. KeyL Key = 76
  514. KeyM Key = 77
  515. KeyN Key = 78
  516. KeyO Key = 79
  517. KeyP Key = 80
  518. KeyQ Key = 81
  519. KeyR Key = 82
  520. KeyS Key = 83
  521. KeyT Key = 84
  522. KeyU Key = 85
  523. KeyV Key = 86
  524. KeyW Key = 87
  525. KeyX Key = 88
  526. KeyY Key = 89
  527. KeyZ Key = 90
  528. KeyLeftBracket Key = 219
  529. KeyBackslash Key = 220
  530. KeyRightBracket Key = 221
  531. KeyGraveAccent Key = 192
  532. KeyWorld1 Key = -iota - 2
  533. KeyWorld2 Key = -iota - 2
  534. KeyEscape Key = 27
  535. KeyEnter Key = 13
  536. KeyTab Key = 9
  537. KeyBackspace Key = 8
  538. KeyInsert Key = -iota - 2
  539. KeyDelete Key = 46
  540. KeyRight Key = 39
  541. KeyLeft Key = 37
  542. KeyDown Key = 40
  543. KeyUp Key = 38
  544. KeyPageUp Key = -iota - 2
  545. KeyPageDown Key = -iota - 2
  546. KeyHome Key = -iota - 2
  547. KeyEnd Key = -iota - 2
  548. KeyCapsLock Key = 20
  549. KeyScrollLock Key = -iota - 2
  550. KeyNumLock Key = -iota - 2
  551. KeyPrintScreen Key = -iota - 2
  552. KeyPause Key = -iota - 2
  553. KeyF1 Key = 112
  554. KeyF2 Key = 113
  555. KeyF3 Key = 114
  556. KeyF4 Key = 115
  557. KeyF5 Key = 116
  558. KeyF6 Key = 117
  559. KeyF7 Key = 118
  560. KeyF8 Key = 119
  561. KeyF9 Key = 120
  562. KeyF10 Key = 121
  563. KeyF11 Key = 122
  564. KeyF12 Key = 123
  565. KeyF13 Key = -iota - 2
  566. KeyF14 Key = -iota - 2
  567. KeyF15 Key = -iota - 2
  568. KeyF16 Key = -iota - 2
  569. KeyF17 Key = -iota - 2
  570. KeyF18 Key = -iota - 2
  571. KeyF19 Key = -iota - 2
  572. KeyF20 Key = -iota - 2
  573. KeyF21 Key = -iota - 2
  574. KeyF22 Key = -iota - 2
  575. KeyF23 Key = -iota - 2
  576. KeyF24 Key = -iota - 2
  577. KeyF25 Key = -iota - 2
  578. KeyKP0 Key = -iota - 2
  579. KeyKP1 Key = -iota - 2
  580. KeyKP2 Key = -iota - 2
  581. KeyKP3 Key = -iota - 2
  582. KeyKP4 Key = -iota - 2
  583. KeyKP5 Key = -iota - 2
  584. KeyKP6 Key = -iota - 2
  585. KeyKP7 Key = -iota - 2
  586. KeyKP8 Key = -iota - 2
  587. KeyKP9 Key = -iota - 2
  588. KeyKPDecimal Key = -iota - 2
  589. KeyKPDivide Key = -iota - 2
  590. KeyKPMultiply Key = -iota - 2
  591. KeyKPSubtract Key = -iota - 2
  592. KeyKPAdd Key = -iota - 2
  593. KeyKPEnter Key = -iota - 2
  594. KeyKPEqual Key = -iota - 2
  595. KeyLeftShift Key = 340
  596. KeyLeftControl Key = 341
  597. KeyLeftAlt Key = 342
  598. KeyLeftSuper Key = 91
  599. KeyRightShift Key = 344
  600. KeyRightControl Key = 345
  601. KeyRightAlt Key = 346
  602. KeyRightSuper Key = 93
  603. KeyMenu Key = -iota - 2
  604. )
  605. // toKey extracts Key from given KeyboardEvent.
  606. func toKey(ke *dom.KeyboardEvent) Key {
  607. key := Key(ke.KeyCode)
  608. switch {
  609. case key == 16 && ke.Location == dom.KeyLocationLeft:
  610. key = KeyLeftShift
  611. case key == 16 && ke.Location == dom.KeyLocationRight:
  612. key = KeyRightShift
  613. case key == 17 && ke.Location == dom.KeyLocationLeft:
  614. key = KeyLeftControl
  615. case key == 17 && ke.Location == dom.KeyLocationRight:
  616. key = KeyRightControl
  617. case key == 18 && ke.Location == dom.KeyLocationLeft:
  618. key = KeyLeftAlt
  619. case key == 18 && ke.Location == dom.KeyLocationRight:
  620. key = KeyRightAlt
  621. }
  622. return key
  623. }
  624. // toModifierKey extracts ModifierKey from given KeyboardEvent.
  625. func toModifierKey(ke *dom.KeyboardEvent) ModifierKey {
  626. mods := ModifierKey(0)
  627. if ke.ShiftKey {
  628. mods += ModShift
  629. }
  630. if ke.CtrlKey {
  631. mods += ModControl
  632. }
  633. if ke.AltKey {
  634. mods += ModAlt
  635. }
  636. if ke.MetaKey {
  637. mods += ModSuper
  638. }
  639. return mods
  640. }
  641. type MouseButton int
  642. const (
  643. MouseButton1 MouseButton = 0
  644. MouseButton2 MouseButton = 2 // Web MouseEvent has middle and right mouse buttons in reverse order.
  645. MouseButton3 MouseButton = 1 // Web MouseEvent has middle and right mouse buttons in reverse order.
  646. MouseButtonLeft = MouseButton1
  647. MouseButtonRight = MouseButton2
  648. MouseButtonMiddle = MouseButton3
  649. )
  650. type Joystick int
  651. const (
  652. Joystick1 Joystick = iota
  653. Joystick2
  654. Joystick3
  655. Joystick4
  656. Joystick5
  657. Joystick6
  658. Joystick7
  659. Joystick8
  660. Joystick9
  661. Joystick10
  662. Joystick11
  663. Joystick12
  664. Joystick13
  665. Joystick14
  666. Joystick15
  667. Joystick16
  668. JoystickLast = Joystick16
  669. )
  670. type GamepadAxis int
  671. const (
  672. AxisLeftX GamepadAxis = iota
  673. AxisLeftY
  674. AxisRightX
  675. AxisRightY
  676. AxisLeftTrigger
  677. AxisRightTrigger
  678. AxisLast = AxisRightTrigger
  679. )
  680. type GamepadButton int
  681. const (
  682. ButtonA GamepadButton = iota
  683. ButtonB
  684. ButtonX
  685. ButtonY
  686. ButtonLeftBumper
  687. ButtonRightBumper
  688. ButtonBack
  689. ButtonStart
  690. ButtonGuide
  691. ButtonLeftThumb
  692. ButtonRightThumb
  693. ButtonDpadUp
  694. ButtonDpadRight
  695. ButtonDpadDown
  696. ButtonDpadLeft
  697. ButtonLast = ButtonDpadLeft
  698. ButtonCross = ButtonA
  699. ButtonCircle = ButtonB
  700. ButtonSquare = ButtonX
  701. ButtonTriangle = ButtonY
  702. )
  703. type Action int
  704. const (
  705. Release Action = 0
  706. Press Action = 1
  707. Repeat Action = 2
  708. )
  709. type InputMode int
  710. const (
  711. CursorMode InputMode = iota
  712. StickyKeysMode
  713. StickyMouseButtonsMode
  714. LockKeyMods
  715. RawMouseMotion
  716. )
  717. const (
  718. CursorNormal = iota
  719. CursorHidden
  720. CursorDisabled
  721. )
  722. type ModifierKey int
  723. const (
  724. ModShift ModifierKey = (1 << iota)
  725. ModControl
  726. ModAlt
  727. ModSuper
  728. )
  729. func (joy Joystick) IsPresent() bool {
  730. // TODO: Implement.
  731. return false
  732. }
  733. func (joy Joystick) GetGamepadName() string {
  734. // TODO: Implement.
  735. return "Gamepad"
  736. }
  737. func (joy Joystick) GetButtons() []Action {
  738. // TODO: Implement.
  739. return make([]Action, 0)
  740. }
  741. func (joy Joystick) GetAxes() []float32 {
  742. // TODO: Implement.
  743. return make([]float32, 0)
  744. }
  745. // Open opens a named asset. It's the caller's responsibility to close it when done.
  746. func Open(name string) (io.ReadCloser, error) {
  747. resp, err := http.Get(name)
  748. if err != nil {
  749. return nil, err
  750. }
  751. if resp.StatusCode != 200 {
  752. return nil, fmt.Errorf("non-200 status: %s", resp.Status)
  753. }
  754. return resp.Body, nil
  755. }
  756. // ---
  757. func WaitEvents() {
  758. // TODO.
  759. runtime.Gosched()
  760. }
  761. func PostEmptyEvent() {
  762. // TODO: Implement.
  763. }
  764. func DefaultWindowHints() {
  765. // TODO: Implement.
  766. }
  767. func (w *Window) SetClipboardString(str string) {
  768. // TODO: Implement.
  769. }
  770. func (w *Window) GetClipboardString() (string, error) {
  771. // TODO: Implement.
  772. return "", errors.New("GetClipboardString not implemented")
  773. }
  774. func (w *Window) SetTitle(title string) {
  775. document.SetTitle(title)
  776. }
  777. func (w *Window) Show() {
  778. // TODO: Implement.
  779. }
  780. func (w *Window) Hide() {
  781. // TODO: Implement.
  782. }
  783. func (w *Window) Destroy() {
  784. document.Body().RemoveChild(w.canvas)
  785. if w.fullscreen {
  786. if w.missing.fullscreen {
  787. log.Println("warning: Fullscreen API unsupported")
  788. } else {
  789. document.Underlying().Call("webkitExitFullscreen")
  790. w.fullscreen = false
  791. }
  792. }
  793. }
  794. type CloseCallback func(w *Window)
  795. func (w *Window) SetCloseCallback(cbfun CloseCallback) (previous CloseCallback) {
  796. // TODO: Implement.
  797. // TODO: Handle previous.
  798. return nil
  799. }
  800. type RefreshCallback func(w *Window)
  801. func (w *Window) SetRefreshCallback(cbfun RefreshCallback) (previous RefreshCallback) {
  802. // TODO: Implement.
  803. // TODO: Handle previous.
  804. return nil
  805. }
  806. type SizeCallback func(w *Window, width int, height int)
  807. func (w *Window) SetSizeCallback(cbfun SizeCallback) (previous SizeCallback) {
  808. w.sizeCallback = cbfun
  809. // TODO: Handle previous.
  810. return nil
  811. }
  812. type CursorEnterCallback func(w *Window, entered bool)
  813. func (w *Window) SetCursorEnterCallback(cbfun CursorEnterCallback) (previous CursorEnterCallback) {
  814. // TODO: Implement.
  815. // TODO: Handle previous.
  816. return nil
  817. }
  818. type CharModsCallback func(w *Window, char rune, mods ModifierKey)
  819. func (w *Window) SetCharModsCallback(cbfun CharModsCallback) (previous CharModsCallback) {
  820. // TODO: Implement.
  821. // TODO: Handle previous.
  822. return nil
  823. }
  824. type PosCallback func(w *Window, xpos int, ypos int)
  825. func (w *Window) SetPosCallback(cbfun PosCallback) (previous PosCallback) {
  826. // TODO: Implement.
  827. // TODO: Handle previous.
  828. return nil
  829. }
  830. type FocusCallback func(w *Window, focused bool)
  831. func (w *Window) SetFocusCallback(cbfun FocusCallback) (previous FocusCallback) {
  832. // TODO: Implement.
  833. // TODO: Handle previous.
  834. return nil
  835. }
  836. type IconifyCallback func(w *Window, iconified bool)
  837. func (w *Window) SetIconifyCallback(cbfun IconifyCallback) (previous IconifyCallback) {
  838. // TODO: Implement.
  839. // TODO: Handle previous.
  840. return nil
  841. }
  842. type DropCallback func(w *Window, names []string)
  843. func (w *Window) SetDropCallback(cbfun DropCallback) (previous DropCallback) {
  844. // TODO: Implement. Can use HTML5 file drag and drop API?
  845. // TODO: Handle previous.
  846. return nil
  847. }