diff options
| author | faiface <faiface@ksp.sk> | 2017-08-18 19:33:01 +0200 |
|---|---|---|
| committer | faiface <faiface@ksp.sk> | 2017-08-18 19:33:01 +0200 |
| commit | ee3366ded862f02a1a5ee4ea856a06e46bb889ee (patch) | |
| tree | 3bc57102892faa30167971f844461df061077874 /win | |
| parent | c900b5f71877660fffad5bc276aeda4c9a345ad1 (diff) | |
| download | gui-ee3366ded862f02a1a5ee4ea856a06e46bb889ee.zip | |
add win package
Diffstat (limited to 'win')
| -rw-r--r-- | win/events.go | 49 | ||||
| -rw-r--r-- | win/win.go | 178 |
2 files changed, 227 insertions, 0 deletions
diff --git a/win/events.go b/win/events.go new file mode 100644 index 0000000..7ce93e5 --- /dev/null +++ b/win/events.go @@ -0,0 +1,49 @@ +package win + +import ( + "fmt" + "strings" + + "github.com/faiface/gui/event" + "github.com/go-gl/glfw/v3.2/glfw" +) + +func (w *Win) setUpEvents(events chan<- string) { + var moX, moY int + + w.w.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) { + switch action { + case glfw.Press: + events <- mkEvent("mo", "down", moX, moY) + case glfw.Release: + events <- mkEvent("mo", "up", moX, moY) + } + }) + + w.w.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) { + moX, moY = int(x), int(y) + events <- mkEvent("mo", "move", moX, moY) + }) + + w.w.SetCharCallback(func(_ *glfw.Window, r rune) { + events <- mkEvent("kb", "type", r) + }) + + w.w.SetSizeCallback(func(_ *glfw.Window, width, height int) { + w.resize(width, height) + events <- mkEvent("wi", "resize", width, height) + }) + + w.w.SetCloseCallback(func(_ *glfw.Window) { + events <- mkEvent("wi", "close") + w.close() + }) +} + +func mkEvent(a ...interface{}) string { + s := make([]string, len(a)) + for i := range s { + s[i] = fmt.Sprint(a[i]) + } + return strings.Join(s, event.Sep) +} diff --git a/win/win.go b/win/win.go new file mode 100644 index 0000000..2088f20 --- /dev/null +++ b/win/win.go @@ -0,0 +1,178 @@ +package win + +import ( + "image" + "image/draw" + "local/win/event" + "time" + + "github.com/faiface/mainthread" + "github.com/go-gl/gl/v2.1/gl" + "github.com/go-gl/glfw/v3.2/glfw" +) + +func New(opts ...Option) (*Win, error) { + o := options{ + title: "", + width: 640, + height: 480, + resizable: false, + } + for _, opt := range opts { + opt(&o) + } + + w := &Win{ + closed: make(chan struct{}), + } + + var err error + mainthread.Call(func() { + w.w, err = makeGLFWWin(&o) + }) + if err != nil { + return nil, err + } + + w.resize(o.width, o.height) + + events := make(chan string) + mainthread.Call(func() { + w.setUpEvents(events) + }) + + go func() { + for { + select { + case event := <-events: + w.Dispatch.Happen(event) + case <-w.closed: + return + } + } + }() + + go func() { + ticker := time.NewTicker(time.Second / 120) + defer ticker.Stop() + for { + select { + case <-ticker.C: + mainthread.Call(glfw.PollEvents) + case <-w.closed: + return + } + } + }() + + return w, nil +} + +func makeGLFWWin(o *options) (*glfw.Window, error) { + err := glfw.Init() + if err != nil { + return nil, err + } + glfw.WindowHint(glfw.DoubleBuffer, glfw.False) + if o.resizable { + glfw.WindowHint(glfw.Resizable, glfw.True) + } else { + glfw.WindowHint(glfw.Resizable, glfw.False) + } + w, err := glfw.CreateWindow(o.width, o.height, o.title, nil, nil) + if err != nil { + return nil, err + } + return w, nil +} + +type Option func(*options) + +func Title(title string) Option { + return func(o *options) { + o.title = title + } +} + +func Size(width, height int) Option { + return func(o *options) { + o.width = width + o.height = height + } +} + +func Resizable() Option { + return func(o *options) { + o.resizable = true + } +} + +type options struct { + title string + width, height int + resizable bool +} + +type Win struct { + event.Dispatch + w *glfw.Window + rgba *image.RGBA + closed chan struct{} +} + +func (w *Win) Close() error { + return mainthread.CallErr(w.close) +} + +func (w *Win) Image() *image.RGBA { + return w.rgba +} + +var curWin *Win = nil + +func (w *Win) Flush(r image.Rectangle) { + w.Dispatch.Happen(mkEvent("wi", "flush", r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)) + + mainthread.Call(func() { + if curWin != w { + w.w.MakeContextCurrent() + err := gl.Init() + if err != nil { + return + } + curWin = w + } + + tmp := image.NewRGBA(r) + draw.Draw(tmp, r, w.rgba, r.Min, draw.Src) + + gl.RasterPos2d( + -1+2*float64(r.Min.X)/float64(w.rgba.Bounds().Dx()), + +1-2*float64(r.Min.Y)/float64(w.rgba.Bounds().Dy()), + ) + gl.PixelZoom(1, -1) + gl.DrawPixels( + int32(r.Dx()), + int32(r.Dy()), + gl.RGBA, + gl.UNSIGNED_BYTE, + gl.Ptr(tmp.Pix), + ) + gl.Flush() + }) +} + +func (w *Win) close() error { + close(w.closed) + w.w.Destroy() + return nil +} + +func (w *Win) resize(width, height int) { + bounds := image.Rect(0, 0, width, height) + rgba := image.NewRGBA(bounds) + if w.rgba != nil { + draw.Draw(rgba, w.rgba.Bounds(), w.rgba, w.rgba.Bounds().Min, draw.Src) + } + w.rgba = rgba +} |