From c93ca17fe5b80b04f2f2782eb6fa6bdc834c81c8 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Wed, 9 Nov 2022 18:53:14 -0500 Subject: [PATCH] x backend has an event loop --- application.go | 8 ++- backends/x/x.go | 111 +++++++++++++++++++++++++++++++++++++---- examples/hello/main.go | 50 +------------------ 3 files changed, 110 insertions(+), 59 deletions(-) diff --git a/application.go b/application.go index 525021b..7fe44c4 100644 --- a/application.go +++ b/application.go @@ -44,7 +44,13 @@ func (application *Application) Run () ( if err != nil { return } channel = make(chan(Event)) - application.backend.Run(channel) + go application.backend.Run(channel) return } + +// Config returns a pointer to the application's configuration. +func (application *Application) Config () (config *Config) { + config = &application.config + return +} diff --git a/backends/x/x.go b/backends/x/x.go index 67b1ab5..517c8b3 100644 --- a/backends/x/x.go +++ b/backends/x/x.go @@ -4,25 +4,49 @@ import "image" import "github.com/jezek/xgbutil" // import "github.com/jezek/xgbutil/ewmh" -// import "github.com/jezek/xgbutil/xevent" +import "github.com/jezek/xgbutil/xevent" import "github.com/jezek/xgbutil/xwindow" import "github.com/jezek/xgbutil/xgraphics" import "git.tebibyte.media/sashakoshka/stone" type Backend struct { - connection *xgbutil.Conn - window *xwindow.Window - canvas *xgraphics.Image + application *stone.Application + config *stone.Config + connection *xgbutil.XUtil + window *xwindow.Window + canvas *xgraphics.Image + channel chan(stone.Event) + + ping struct { + before chan(struct { }) + after chan(struct { }) + quit chan(struct { }) + } + + metrics struct { + cellWidth int + cellHeight int + padding int + paddingX int + paddingY int + descent int + } } func (backend *Backend) Run (channel chan(stone.Event)) { - // TODO: setup - go backend.mainLoop(channel) -} - -func (backend *Backend) mainLoop (channel chan(stone.Event)) { + backend.channel = channel + for { + select { + case <- backend.ping.before: + <- backend.ping.after + + case <- backend.ping.quit: + backend.shutDown() + return + } + } } func (backend *Backend) SetTitle (title string) { @@ -32,3 +56,72 @@ func (backend *Backend) SetTitle (title string) { func (backend *Backend) SetIcon (icons []image.Image) { } + +func (backend *Backend) shutDown () { + backend.channel <- stone.EventQuit { } +} + +// calculateWindowSize calculates window bounds based on the internal buffer +// size. +func (backend *Backend) calculateWindowSize () (x, y int) { + width, height := backend.application.Size() + x = + width * backend.metrics.cellWidth + + backend.metrics.padding * 2 + y = + height * backend.metrics.cellHeight + + backend.metrics.padding * 2 + return +} + + +// factory instantiates an X backend. +func factory (application *stone.Application) (output stone.Backend, err error) { + backend := &Backend { + application: application, + config: application.Config(), + } + + // calculate metrics + // TODO: base these off of font metrics + backend.metrics.cellWidth = 8 + backend.metrics.cellHeight = 16 + backend.metrics.padding = + backend.config.Padding() * + backend.metrics.cellHeight + backend.metrics.paddingX = backend.metrics.padding + backend.metrics.paddingY = backend.metrics.padding + + // connect to X + backend.connection, err = xgbutil.NewConn() + if err != nil { return } + backend.window, err = xwindow.Generate(backend.connection) + if err != nil { return } + + // create the window + windowWidth, windowHeight := backend.calculateWindowSize() + backend.window.Create ( + backend.connection.RootWin(), + 0, 0, windowWidth, windowHeight, + 0) + backend.window.Map() + + // attatch graceful close handler + backend.window.WMGracefulClose (func (window *xwindow.Window) { + backend.window.Destroy() + backend.shutDown() + }) + + // start event loop + backend.ping.before, + backend.ping.after, + backend.ping.quit = xevent.MainPing(backend.connection) + + output = backend + return +} + +// init registers this backend when the program starts. +func init () { + stone.RegisterBackend(factory) +} diff --git a/examples/hello/main.go b/examples/hello/main.go index 456631f..1dd1f89 100644 --- a/examples/hello/main.go +++ b/examples/hello/main.go @@ -4,7 +4,7 @@ import "os" import "fmt" import "time" import "git.tebibyte.media/sashakoshka/stone" -// import _ "git.tebibyte.media/sashakoshka/stone/backends/x" +import _ "git.tebibyte.media/sashakoshka/stone/backends/x" func main () { application := &stone.Application { } @@ -40,51 +40,3 @@ func main () { } } } - -// func run (application *stone.Application) { - // currentTime := time.Time { } - // frameDelay := time.Second / 2 - // textBuffer := "" - // - // for { - // typed := application.Typed() - // textBuffer += typed - // - // shouldRender := - // application.Resized() || - // time.Since(currentTime) > frameDelay || - // len(typed) > 0 - // - // if shouldRender { - // currentTime = time.Now() -// - // application.ResetDot() - // fmt.Fprintln(application, "hellorld!") -// - // hour := currentTime.Hour() - // minute := currentTime.Minute() - // second := currentTime.Second() -// - // application.SetRune(0, 1, rune(hour / 10 + 48)) - // application.SetRune(1, 1, rune(hour % 10 + 48)) - // application.SetRune(2, 1, ':') - // application.SetRune(3, 1, rune(minute / 10 + 48)) - // application.SetRune(4, 1, rune(minute % 10 + 48)) - // application.SetRune(5, 1, ':') - // application.SetRune(6, 1, rune(second / 10 + 48)) - // application.SetRune(7, 1, rune(second % 10 + 48)) -// - // application.Dot.X = 0 - // application.Dot.Y = 2 - // fmt.Fprintln(application, textBuffer) -// - // } -// - // if application.Pressed(stone.MouseButtonLeft) { - // x, y := application.MousePosition() - // application.SetRune(x, y, '#') - // } - // - // if !application.Await(frameDelay) { break } - // } -// }