aboutsummaryrefslogtreecommitdiffstats
path: root/win
diff options
context:
space:
mode:
authorfaiface <faiface@ksp.sk>2017-08-18 19:33:01 +0200
committerfaiface <faiface@ksp.sk>2017-08-18 19:33:01 +0200
commitee3366ded862f02a1a5ee4ea856a06e46bb889ee (patch)
tree3bc57102892faa30167971f844461df061077874 /win
parentc900b5f71877660fffad5bc276aeda4c9a345ad1 (diff)
downloadgui-ee3366ded862f02a1a5ee4ea856a06e46bb889ee.zip
add win package
Diffstat (limited to 'win')
-rw-r--r--win/events.go49
-rw-r--r--win/win.go178
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
+}