Replace GopherWasm with syscall/js

Fixes #857
This commit is contained in:
Hajime Hoshi 2019-05-01 02:15:28 +09:00
parent ab84184b4f
commit 10fb5e33be
12 changed files with 113 additions and 68 deletions

View File

@ -22,9 +22,9 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"math" "math"
"syscall/js"
"time" "time"
"github.com/gopherjs/gopherwasm/js"
"github.com/hajimehoshi/ebiten/audio" "github.com/hajimehoshi/ebiten/audio"
) )
@ -198,7 +198,7 @@ func decode(context *audio.Context, buf []byte, try int) (*Stream, error) {
u8 := js.TypedArrayOf(buf) u8 := js.TypedArrayOf(buf)
a := u8.Get("buffer").Call("slice", u8.Get("byteOffset"), u8.Get("byteOffset").Int()+u8.Get("byteLength").Int()) a := u8.Get("buffer").Call("slice", u8.Get("byteOffset"), u8.Get("byteOffset").Int()+u8.Get("byteLength").Int())
oc.Call("decodeAudioData", a, js.NewCallback(func(args []js.Value) { succeeded := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
buf := args[0] buf := args[0]
s.leftData = float32ArrayToSlice(buf.Call("getChannelData", 0)) s.leftData = float32ArrayToSlice(buf.Call("getChannelData", 0))
switch n := buf.Get("numberOfChannels").Int(); n { switch n := buf.Get("numberOfChannels").Int(); n {
@ -211,7 +211,11 @@ func decode(context *audio.Context, buf []byte, try int) (*Stream, error) {
default: default:
ch <- fmt.Errorf("audio/mp3: number of channels must be 1 or 2 but %d", n) ch <- fmt.Errorf("audio/mp3: number of channels must be 1 or 2 but %d", n)
} }
}), js.NewCallback(func(args []js.Value) { return nil
})
defer succeeded.Release()
failed := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
err := args[0] err := args[0]
if err != js.Null() || err != js.Undefined() { if err != js.Null() || err != js.Undefined() {
ch <- fmt.Errorf("audio/mp3: decodeAudioData failed: %v", err) ch <- fmt.Errorf("audio/mp3: decodeAudioData failed: %v", err)
@ -220,7 +224,11 @@ func decode(context *audio.Context, buf []byte, try int) (*Stream, error) {
// from the next frame (#438). // from the next frame (#438).
ch <- errTryAgain ch <- errTryAgain
} }
})) return nil
})
defer failed.Release()
oc.Call("decodeAudioData", a, succeeded, failed)
u8.Release() u8.Release()
timeout := time.Duration(math.Pow(2, float64(try))) * time.Second timeout := time.Duration(math.Pow(2, float64(try))) * time.Second

View File

@ -17,8 +17,7 @@ package stb
import ( import (
"fmt" "fmt"
"io" "io"
"syscall/js"
"github.com/gopherjs/gopherwasm/js"
) )
var flatten = js.Global().Get("window").Call("eval", `(function(arr) { var flatten = js.Global().Get("window").Call("eval", `(function(arr) {
@ -90,21 +89,18 @@ func DecodeVorbis(buf []byte) (*Samples, int, int, error) {
samples := &Samples{} samples := &Samples{}
sampleRate := 0 sampleRate := 0
var f js.Callback f := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
f = js.NewCallback(func(args []js.Value) {
r := args[0] r := args[0]
if e := r.Get("error"); e != js.Null() { if e := r.Get("error"); e != js.Null() {
ch <- fmt.Errorf("audio/vorbis/internal/stb: decode error: %s", e.String()) ch <- fmt.Errorf("audio/vorbis/internal/stb: decode error: %s", e.String())
close(ch) close(ch)
f.Release() return nil
return
} }
if r.Get("eof").Bool() { if r.Get("eof").Bool() {
close(ch) close(ch)
f.Release() return nil
return
} }
if samples.channels == 0 { if samples.channels == 0 {
@ -117,7 +113,7 @@ func DecodeVorbis(buf []byte) (*Samples, int, int, error) {
flattened := flatten.Invoke(r.Get("data")) flattened := flatten.Invoke(r.Get("data"))
if flattened.Length() == 0 { if flattened.Length() == 0 {
return return nil
} }
s := make([]float32, flattened.Length()) s := make([]float32, flattened.Length())
@ -127,7 +123,9 @@ func DecodeVorbis(buf []byte) (*Samples, int, int, error) {
samples.samples = append(samples.samples, s) samples.samples = append(samples.samples, s)
samples.lengthInSamples += int64(len(s)) / int64(samples.channels) samples.lengthInSamples += int64(len(s)) / int64(samples.channels)
return nil
}) })
defer f.Release()
arr := js.TypedArrayOf(buf) arr := js.TypedArrayOf(buf)
js.Global().Get("stbvorbis").Call("decode", arr, f) js.Global().Get("stbvorbis").Call("decode", arr, f)

View File

@ -20,8 +20,7 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"syscall/js"
"github.com/gopherjs/gopherwasm/js"
) )
type file struct { type file struct {
@ -39,23 +38,25 @@ func OpenFile(path string) (ReadSeekCloser, error) {
req := js.Global().Get("XMLHttpRequest").New() req := js.Global().Get("XMLHttpRequest").New()
req.Call("open", "GET", path, true) req.Call("open", "GET", path, true)
req.Set("responseType", "arraybuffer") req.Set("responseType", "arraybuffer")
loadCallback := js.NewCallback(func([]js.Value) { loadf := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
defer close(ch) defer close(ch)
status := req.Get("status").Int() status := req.Get("status").Int()
if 200 <= status && status < 400 { if 200 <= status && status < 400 {
content = req.Get("response") content = req.Get("response")
return return nil
} }
err = errors.New(fmt.Sprintf("http error: %d", status)) err = errors.New(fmt.Sprintf("http error: %d", status))
return nil
}) })
defer loadCallback.Release() defer loadf.Release()
req.Call("addEventListener", "load", loadCallback) req.Call("addEventListener", "load", loadf)
errorCallback := js.NewCallback(func([]js.Value) { errorf := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
defer close(ch) defer close(ch)
err = errors.New(fmt.Sprintf("XMLHttpRequest error: %s", req.Get("statusText").String())) err = errors.New(fmt.Sprintf("XMLHttpRequest error: %s", req.Get("statusText").String()))
return nil
}) })
req.Call("addEventListener", "error", errorCallback) req.Call("addEventListener", "error", errorf)
defer errorCallback.Release() defer errorf.Release()
req.Call("send") req.Call("send")
<-ch <-ch
if err != nil { if err != nil {

View File

@ -23,10 +23,9 @@ import (
_ "image/jpeg" _ "image/jpeg"
"log" "log"
"math" "math"
"syscall/js"
"time" "time"
"github.com/gopherjs/gopherwasm/js"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil" "github.com/hajimehoshi/ebiten/ebitenutil"
"github.com/hajimehoshi/ebiten/examples/resources/images" "github.com/hajimehoshi/ebiten/examples/resources/images"

4
go.mod
View File

@ -4,10 +4,10 @@ require (
github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f
github.com/gofrs/flock v0.7.0 github.com/gofrs/flock v0.7.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/gopherjs/gopherwasm v1.1.0 github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c
github.com/hajimehoshi/bitmapfont v1.1.1 github.com/hajimehoshi/bitmapfont v1.1.1
github.com/hajimehoshi/go-mp3 v0.2.0 github.com/hajimehoshi/go-mp3 v0.2.0
github.com/hajimehoshi/oto v0.3.4-0.20190430120619-1c8ecbb2424a github.com/hajimehoshi/oto v0.3.4-0.20190501045152-031fb1b9274d
github.com/jakecoffman/cp v0.1.0 github.com/jakecoffman/cp v0.1.0
github.com/jfreymuth/oggvorbis v1.0.0 github.com/jfreymuth/oggvorbis v1.0.0
github.com/jfreymuth/vorbis v1.0.0 // indirect github.com/jfreymuth/vorbis v1.0.0 // indirect

6
go.sum
View File

@ -5,8 +5,8 @@ github.com/gofrs/flock v0.7.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw=
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherwasm v0.1.1/go.mod h1:kx4n9a+MzHH0BJJhvlsQ65hqLFXDO/m256AsaDPQ+/4= github.com/gopherjs/gopherwasm v0.1.1/go.mod h1:kx4n9a+MzHH0BJJhvlsQ65hqLFXDO/m256AsaDPQ+/4=
github.com/gopherjs/gopherwasm v1.0.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI= github.com/gopherjs/gopherwasm v1.0.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI=
github.com/gopherjs/gopherwasm v1.1.0 h1:fA2uLoctU5+T3OhOn2vYP0DVT6pxc7xhTlBB1paATqQ= github.com/gopherjs/gopherwasm v1.1.0 h1:fA2uLoctU5+T3OhOn2vYP0DVT6pxc7xhTlBB1paATqQ=
@ -18,8 +18,8 @@ github.com/hajimehoshi/go-mp3 v0.1.2/go.mod h1:4i+c5pDNKDrxl1iu9iG90/+fhP37lio6g
github.com/hajimehoshi/go-mp3 v0.2.0 h1:isy34iDg+96PsNuFbTdRRXzKr6a1gc2nhsPuFSfXacY= github.com/hajimehoshi/go-mp3 v0.2.0 h1:isy34iDg+96PsNuFbTdRRXzKr6a1gc2nhsPuFSfXacY=
github.com/hajimehoshi/go-mp3 v0.2.0/go.mod h1:4i+c5pDNKDrxl1iu9iG90/+fhP37lio6gNhjCx9WBJw= github.com/hajimehoshi/go-mp3 v0.2.0/go.mod h1:4i+c5pDNKDrxl1iu9iG90/+fhP37lio6gNhjCx9WBJw=
github.com/hajimehoshi/oto v0.1.1/go.mod h1:hUiLWeBQnbDu4pZsAhOnGqMI1ZGibS6e2qhQdfpwz04= github.com/hajimehoshi/oto v0.1.1/go.mod h1:hUiLWeBQnbDu4pZsAhOnGqMI1ZGibS6e2qhQdfpwz04=
github.com/hajimehoshi/oto v0.3.4-0.20190430120619-1c8ecbb2424a h1:mNlGg4s1p2aifTjhMFn9dtTPYW+MTxKFQjWvm9DZ11U= github.com/hajimehoshi/oto v0.3.4-0.20190501045152-031fb1b9274d h1:C2A7WySl23l4SD7FL1wnr2fk7TWtTf7W30pDDmpsZB4=
github.com/hajimehoshi/oto v0.3.4-0.20190430120619-1c8ecbb2424a/go.mod h1:e9eTLBB9iZto045HLbzfHJIc+jP3xaKrjZTghvb6fdM= github.com/hajimehoshi/oto v0.3.4-0.20190501045152-031fb1b9274d/go.mod h1:rj1gPEbAKUBdaV4JiWw2bqUpHtYxBemL7pC5U8rxkO4=
github.com/jakecoffman/cp v0.1.0 h1:sgSYEGUgfwiT447fRjloa2c5b6UyYP+7muR3gQK+Ep0= github.com/jakecoffman/cp v0.1.0 h1:sgSYEGUgfwiT447fRjloa2c5b6UyYP+7muR3gQK+Ep0=
github.com/jakecoffman/cp v0.1.0/go.mod h1:a3xPx9N8RyFAACD644t2dj/nK4SuLg1v+jL61m2yVo4= github.com/jakecoffman/cp v0.1.0/go.mod h1:a3xPx9N8RyFAACD644t2dj/nK4SuLg1v+jL61m2yVo4=
github.com/jfreymuth/oggvorbis v1.0.0 h1:aOpiihGrFLXpsh2osOlEvTcg5/aluzGQeC7m3uYWOZ0= github.com/jfreymuth/oggvorbis v1.0.0 h1:aOpiihGrFLXpsh2osOlEvTcg5/aluzGQeC7m3uYWOZ0=

View File

@ -17,9 +17,8 @@
package clock package clock
import ( import (
"syscall/js"
"time" "time"
"github.com/gopherjs/gopherwasm/js"
) )
func now() int64 { func now() int64 {

View File

@ -17,7 +17,7 @@
package devicescale package devicescale
import ( import (
"github.com/gopherjs/gopherwasm/js" "syscall/js"
) )
func impl(x, y int) float64 { func impl(x, y int) float64 {

View File

@ -19,8 +19,7 @@ package opengl
import ( import (
"errors" "errors"
"fmt" "fmt"
"syscall/js"
"github.com/gopherjs/gopherwasm/js"
"github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/graphics"
) )

View File

@ -17,10 +17,9 @@
package js package js
import ( import (
"syscall/js"
"unicode" "unicode"
"github.com/gopherjs/gopherwasm/js"
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
) )

View File

@ -21,8 +21,7 @@ import (
"log" "log"
"runtime" "runtime"
"strconv" "strconv"
"syscall/js"
"github.com/gopherjs/gopherwasm/js"
"github.com/hajimehoshi/ebiten/internal/devicescale" "github.com/hajimehoshi/ebiten/internal/devicescale"
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
@ -226,28 +225,30 @@ func (u *UserInterface) loop(context driver.UIContext) <-chan error {
u.context = context u.context = context
ch := make(chan error) ch := make(chan error)
var cf js.Callback var cf js.Func
f := func([]js.Value) { f := func(this js.Value, args []js.Value) interface{} {
if u.contextLost { if u.contextLost {
requestAnimationFrame.Invoke(cf) requestAnimationFrame.Invoke(cf)
return return nil
} }
if err := u.update(); err != nil { if err := u.update(); err != nil {
ch <- err ch <- err
close(ch) close(ch)
return return nil
} }
if u.vsync { if u.vsync {
requestAnimationFrame.Invoke(cf) requestAnimationFrame.Invoke(cf)
} else { } else {
setTimeout.Invoke(cf, 0) setTimeout.Invoke(cf, 0)
} }
return nil
} }
cf = js.NewCallback(f) // TODO: Should cf be released after the game ends?
cf = js.FuncOf(f)
// Call f asyncly to be async since ch is used in f. // Call f asyncly to be async since ch is used in f.
go func() { go func() {
f(nil) f(js.Value{}, nil)
}() }()
return ch return ch
} }
@ -255,38 +256,43 @@ func (u *UserInterface) loop(context driver.UIContext) <-chan error {
func init() { func init() {
if document.Get("body") == js.Null() { if document.Get("body") == js.Null() {
ch := make(chan struct{}) ch := make(chan struct{})
window.Call("addEventListener", "load", js.NewCallback(func([]js.Value) { window.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
close(ch) close(ch)
return nil
})) }))
<-ch <-ch
} }
window.Call("addEventListener", "focus", js.NewCallback(func([]js.Value) { window.Call("addEventListener", "focus", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
theUI.windowFocus = true theUI.windowFocus = true
if theUI.suspended() { if theUI.suspended() {
theUI.context.SuspendAudio() theUI.context.SuspendAudio()
} else { } else {
theUI.context.ResumeAudio() theUI.context.ResumeAudio()
} }
return nil
})) }))
window.Call("addEventListener", "blur", js.NewCallback(func([]js.Value) { window.Call("addEventListener", "blur", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
theUI.windowFocus = false theUI.windowFocus = false
if theUI.suspended() { if theUI.suspended() {
theUI.context.SuspendAudio() theUI.context.SuspendAudio()
} else { } else {
theUI.context.ResumeAudio() theUI.context.ResumeAudio()
} }
return nil
})) }))
document.Call("addEventListener", "visibilitychange", js.NewCallback(func([]js.Value) { document.Call("addEventListener", "visibilitychange", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
theUI.pageVisible = !document.Get("hidden").Bool() theUI.pageVisible = !document.Get("hidden").Bool()
if theUI.suspended() { if theUI.suspended() {
theUI.context.SuspendAudio() theUI.context.SuspendAudio()
} else { } else {
theUI.context.ResumeAudio() theUI.context.ResumeAudio()
} }
return nil
})) }))
window.Call("addEventListener", "resize", js.NewCallback(func([]js.Value) { window.Call("addEventListener", "resize", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
theUI.updateScreenSize() theUI.updateScreenSize()
return nil
})) }))
// Adjust the initial scale to 1. // Adjust the initial scale to 1.
@ -318,8 +324,9 @@ func init() {
// TODO: This is OK as long as the game is in an independent iframe. // TODO: This is OK as long as the game is in an independent iframe.
// What if the canvas is embedded in a HTML directly? // What if the canvas is embedded in a HTML directly?
document.Get("body").Call("addEventListener", "click", js.NewCallback(func([]js.Value) { document.Get("body").Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
canvas.Call("focus") canvas.Call("focus")
return nil
})) }))
canvasStyle := canvas.Get("style") canvasStyle := canvas.Get("style")
@ -330,57 +337,93 @@ func init() {
canvas.Get("style").Set("outline", "none") canvas.Get("style").Set("outline", "none")
// Keyboard // Keyboard
canvas.Call("addEventListener", "keydown", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
// Don't 'preventDefault' on keydown events or keypress events wouldn't work (#715). // Don't 'preventDefault' on keydown events or keypress events wouldn't work (#715).
canvas.Call("addEventListener", "keydown", js.NewEventCallback(0, func(e js.Value) {
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
canvas.Call("addEventListener", "keypress", js.NewEventCallback(js.PreventDefault, func(e js.Value) { canvas.Call("addEventListener", "keypress", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
canvas.Call("addEventListener", "keyup", js.NewEventCallback(js.PreventDefault, func(e js.Value) { canvas.Call("addEventListener", "keyup", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
// Mouse // Mouse
canvas.Call("addEventListener", "mousedown", js.NewEventCallback(js.PreventDefault, func(e js.Value) { canvas.Call("addEventListener", "mousedown", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
canvas.Call("addEventListener", "mouseup", js.NewEventCallback(js.PreventDefault, func(e js.Value) { canvas.Call("addEventListener", "mouseup", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
canvas.Call("addEventListener", "mousemove", js.NewEventCallback(js.PreventDefault, func(e js.Value) { canvas.Call("addEventListener", "mousemove", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
canvas.Call("addEventListener", "wheel", js.NewEventCallback(js.PreventDefault, func(e js.Value) { canvas.Call("addEventListener", "wheel", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
// Touch // Touch
canvas.Call("addEventListener", "touchstart", js.NewEventCallback(js.PreventDefault, func(e js.Value) { canvas.Call("addEventListener", "touchstart", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
canvas.Call("addEventListener", "touchend", js.NewEventCallback(js.PreventDefault, func(e js.Value) { canvas.Call("addEventListener", "touchend", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
canvas.Call("addEventListener", "touchmove", js.NewEventCallback(js.PreventDefault, func(e js.Value) { canvas.Call("addEventListener", "touchmove", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.input.Update(e) theUI.input.Update(e)
return nil
})) }))
// Gamepad // Gamepad
window.Call("addEventListener", "gamepadconnected", js.NewCallback(func(e []js.Value) { window.Call("addEventListener", "gamepadconnected", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
// Do nothing. // Do nothing.
return nil
})) }))
canvas.Call("addEventListener", "contextmenu", js.NewEventCallback(js.PreventDefault, func(js.Value) { canvas.Call("addEventListener", "contextmenu", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
// Do nothing. e := args[0]
e.Call("preventDefault")
return nil
})) }))
// Context // Context
canvas.Call("addEventListener", "webglcontextlost", js.NewEventCallback(js.PreventDefault, func(js.Value) { canvas.Call("addEventListener", "webglcontextlost", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
e := args[0]
e.Call("preventDefault")
theUI.contextLost = true theUI.contextLost = true
return nil
})) }))
canvas.Call("addEventListener", "webglcontextrestored", js.NewCallback(func(e []js.Value) { canvas.Call("addEventListener", "webglcontextrestored", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
theUI.contextLost = false theUI.contextLost = false
return nil
})) }))
} }

View File

@ -19,8 +19,7 @@ package web
import ( import (
"runtime" "runtime"
"strings" "strings"
"syscall/js"
"github.com/gopherjs/gopherwasm/js"
) )
func IsGopherJS() bool { func IsGopherJS() bool {