From eac0b4b31a1ae323222076dcb31dc7cd4d9402d5 Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Sat, 24 Aug 2024 14:49:13 -0400 Subject: move win to gui package --- win/win.go | 358 ------------------------------------------------------------- 1 file changed, 358 deletions(-) delete mode 100644 win/win.go (limited to 'win/win.go') diff --git a/win/win.go b/win/win.go deleted file mode 100644 index 1741549..0000000 --- a/win/win.go +++ /dev/null @@ -1,358 +0,0 @@ -package win - -import ( - "image" - "image/draw" - "runtime" - "time" - "unsafe" - - "github.com/faiface/gui" - "github.com/faiface/mainthread" - "github.com/go-gl/gl/v2.1/gl" - "github.com/go-gl/glfw/v3.2/glfw" -) - -// Option is a functional option to the window constructor New. -type Option func(*options) - -type options struct { - title string - width, height int - resizable bool - borderless bool - maximized bool -} - -// Title option sets the title (caption) of the window. -func Title(title string) Option { - return func(o *options) { - o.title = title - } -} - -// Size option sets the width and height of the window. -func Size(width, height int) Option { - return func(o *options) { - o.width = width - o.height = height - } -} - -// Resizable option makes the window resizable by the user. -func Resizable() Option { - return func(o *options) { - o.resizable = true - } -} - -// Borderless option makes the window borderless. -func Borderless() Option { - return func(o *options) { - o.borderless = true - } -} - -// Maximized option makes the window start maximized. -func Maximized() Option { - return func(o *options) { - o.maximized = true - } -} - -// New creates a new window with all the supplied options. -// -// The default title is empty and the default size is 640x480. -func New(opts ...Option) (*Win, error) { - o := options{ - title: "", - width: 640, - height: 480, - resizable: false, - borderless: false, - maximized: false, - } - for _, opt := range opts { - opt(&o) - } - - eventsOut, eventsIn := gui.MakeEventsChan() - - w := &Win{ - eventsOut: eventsOut, - eventsIn: eventsIn, - draw: make(chan func(draw.Image) image.Rectangle), - newSize: make(chan image.Rectangle), - finish: make(chan struct{}), - } - - var err error - mainthread.Call(func() { - w.w, err = makeGLFWWin(&o) - }) - if err != nil { - return nil, err - } - - mainthread.Call(func() { - // hiDPI hack - width, _ := w.w.GetFramebufferSize() - w.ratio = width / o.width - if w.ratio < 1 { - w.ratio = 1 - } - if w.ratio != 1 { - o.width /= w.ratio - o.height /= w.ratio - } - w.w.Destroy() - w.w, err = makeGLFWWin(&o) - }) - if err != nil { - return nil, err - } - - bounds := image.Rect(0, 0, o.width*w.ratio, o.height*w.ratio) - w.img = image.NewRGBA(bounds) - - go func() { - runtime.LockOSThread() - w.openGLThread() - }() - - mainthread.CallNonBlock(w.eventThread) - - 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) - } - if o.borderless { - glfw.WindowHint(glfw.Decorated, glfw.False) - } - if o.maximized { - glfw.WindowHint(glfw.Maximized, glfw.True) - } - w, err := glfw.CreateWindow(o.width, o.height, o.title, nil, nil) - if err != nil { - return nil, err - } - if o.maximized { - o.width, o.height = w.GetFramebufferSize() // set o.width and o.height to the window size due to the window being maximized - } - return w, nil -} - -// Win is an Env that handles an actual graphical window. -// -// It receives its events from the OS and it draws to the surface of the window. -// -// Warning: only one window can be open at a time. This will be fixed. -type Win struct { - eventsOut <-chan gui.Event - eventsIn chan<- gui.Event - draw chan func(draw.Image) image.Rectangle - - newSize chan image.Rectangle - finish chan struct{} - - w *glfw.Window - img *image.RGBA - ratio int -} - -// Events returns the events channel of the window. -func (w *Win) Events() <-chan gui.Event { return w.eventsOut } - -// Draw returns the draw channel of the window. -func (w *Win) Draw() chan<- func(draw.Image) image.Rectangle { return w.draw } - -var buttons = map[glfw.MouseButton]Button{ - glfw.MouseButtonLeft: ButtonLeft, - glfw.MouseButtonRight: ButtonRight, - glfw.MouseButtonMiddle: ButtonMiddle, -} - -var keys = map[glfw.Key]Key{ - glfw.KeyLeft: KeyLeft, - glfw.KeyRight: KeyRight, - glfw.KeyUp: KeyUp, - glfw.KeyDown: KeyDown, - glfw.KeyEscape: KeyEscape, - glfw.KeySpace: KeySpace, - glfw.KeyBackspace: KeyBackspace, - glfw.KeyDelete: KeyDelete, - glfw.KeyEnter: KeyEnter, - glfw.KeyTab: KeyTab, - glfw.KeyHome: KeyHome, - glfw.KeyEnd: KeyEnd, - glfw.KeyPageUp: KeyPageUp, - glfw.KeyPageDown: KeyPageDown, - glfw.KeyLeftShift: KeyShift, - glfw.KeyRightShift: KeyShift, - glfw.KeyLeftControl: KeyCtrl, - glfw.KeyRightControl: KeyCtrl, - glfw.KeyLeftAlt: KeyAlt, - glfw.KeyRightAlt: KeyAlt, -} - -func (w *Win) eventThread() { - var moX, moY int - - w.w.SetCursorPosCallback(func(_ *glfw.Window, x, y float64) { - moX, moY = int(x), int(y) - w.eventsIn <- MoMove{image.Pt(moX*w.ratio, moY*w.ratio)} - }) - - w.w.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) { - b, ok := buttons[button] - if !ok { - return - } - switch action { - case glfw.Press: - w.eventsIn <- MoDown{image.Pt(moX*w.ratio, moY*w.ratio), b} - case glfw.Release: - w.eventsIn <- MoUp{image.Pt(moX*w.ratio, moY*w.ratio), b} - } - }) - - w.w.SetScrollCallback(func(_ *glfw.Window, xoff, yoff float64) { - w.eventsIn <- MoScroll{image.Pt(int(xoff), int(yoff))} - }) - - w.w.SetCharCallback(func(_ *glfw.Window, r rune) { - w.eventsIn <- KbType{r} - }) - - w.w.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, _ int, action glfw.Action, _ glfw.ModifierKey) { - k, ok := keys[key] - if !ok { - return - } - switch action { - case glfw.Press: - w.eventsIn <- KbDown{k} - case glfw.Release: - w.eventsIn <- KbUp{k} - case glfw.Repeat: - w.eventsIn <- KbRepeat{k} - } - }) - - w.w.SetFramebufferSizeCallback(func(_ *glfw.Window, width, height int) { - r := image.Rect(0, 0, width, height) - w.newSize <- r - w.eventsIn <- gui.Resize{Rectangle: r} - }) - - w.w.SetCloseCallback(func(_ *glfw.Window) { - w.eventsIn <- WiClose{} - }) - - r := w.img.Bounds() - w.eventsIn <- gui.Resize{Rectangle: r} - - for { - select { - case <-w.finish: - close(w.eventsIn) - w.w.Destroy() - return - default: - glfw.WaitEventsTimeout(1.0 / 30) - } - } -} - -func (w *Win) openGLThread() { - w.w.MakeContextCurrent() - gl.Init() - - w.openGLFlush(w.img.Bounds()) - -loop: - for { - var totalR image.Rectangle - - select { - case r := <-w.newSize: - img := image.NewRGBA(r) - draw.Draw(img, w.img.Bounds(), w.img, w.img.Bounds().Min, draw.Src) - w.img = img - totalR = totalR.Union(r) - - case d, ok := <-w.draw: - if !ok { - close(w.finish) - return - } - r := d(w.img) - totalR = totalR.Union(r) - } - - for { - select { - case <-time.After(time.Second / 960): - w.openGLFlush(totalR) - totalR = image.ZR - continue loop - - case r := <-w.newSize: - img := image.NewRGBA(r) - draw.Draw(img, w.img.Bounds(), w.img, w.img.Bounds().Min, draw.Src) - w.img = img - totalR = totalR.Union(r) - - case d, ok := <-w.draw: - if !ok { - close(w.finish) - return - } - r := d(w.img) - totalR = totalR.Union(r) - } - } - } -} - -func (w *Win) openGLFlush(r image.Rectangle) { - bounds := w.img.Bounds() - r = r.Intersect(bounds) - if r.Empty() { - return - } - - tmp := image.NewRGBA(r) - draw.Draw(tmp, r, w.img, r.Min, draw.Src) - - gl.DrawBuffer(gl.FRONT) - gl.Viewport( - int32(bounds.Min.X), - int32(bounds.Min.Y), - int32(bounds.Dx()), - int32(bounds.Dy()), - ) - gl.RasterPos2d( - -1+2*float64(r.Min.X)/float64(bounds.Dx()), - +1-2*float64(r.Min.Y)/float64(bounds.Dy()), - ) - gl.PixelZoom(1, -1) - gl.DrawPixels( - int32(r.Dx()), - int32(r.Dy()), - gl.RGBA, - gl.UNSIGNED_BYTE, - unsafe.Pointer(&tmp.Pix[0]), - ) - gl.Flush() -} -- cgit v1.2.3