aboutsummaryrefslogtreecommitdiffstats
path: root/win/win.go
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2024-08-24 14:49:13 -0400
committerSam Anthony <sam@samanthony.xyz>2024-08-24 14:49:13 -0400
commiteac0b4b31a1ae323222076dcb31dc7cd4d9402d5 (patch)
tree37736bf9cdf1452ec7629a86d3fad072cc8b94df /win/win.go
parent050f26f34a64689e16fc9d20c9a9dc3f7e43b1d0 (diff)
downloadgui-eac0b4b31a1ae323222076dcb31dc7cd4d9402d5.zip
move win to gui package
Diffstat (limited to 'win/win.go')
-rw-r--r--win/win.go358
1 files changed, 0 insertions, 358 deletions
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()
-}