diff --git a/application.go b/application.go index 4bd40c9..7fe44c4 100644 --- a/application.go +++ b/application.go @@ -1,6 +1,5 @@ package stone -import "time" import "image/color" // Application represents an application. @@ -12,12 +11,6 @@ type Application struct { config Config } -// SetSize sets the application's buffer size. This may or may not change the -// size of the window, depending on the backend used. -func (application *Application) SetSize (width, height int) { - application.DamageBuffer.SetSize(width, height) -} - // SetTitle sets the application's title. If in a window, it will appear as the // window's name. func (application *Application) SetTitle (title string) { @@ -25,13 +18,11 @@ func (application *Application) SetTitle (title string) { application.backend.SetTitle(title) } -// Run initializes the application, and then calls callback. Operations inside -// of callback are allowed to interact with the application. Depending on the -// backend used, this function may bind to the main thread. -func (application *Application) Run ( - callback func (application *Application), -) ( - err error, +// Run initializes the application, starts it, and then returns a channel that +// broadcasts events. If no suitable backend can be found, an error is returned. +func (application *Application) Run () ( + channel chan(Event), + err error, ) { // default values for certain parameters width, height := application.Size() @@ -51,72 +42,15 @@ func (application *Application) Run ( application.backend, err = instantiateBackend(application) if err != nil { return } - application.backend.Run(callback) + + channel = make(chan(Event)) + go application.backend.Run(channel) return } -// Await blocks until an event is recieved, or until the specified timeout has -// elapsed. If the timeout is zero, it will wait forever. This function returns -// true if the backend is still active, and false if it has closed. -func (application *Application) Await (timeout time.Duration) (keepRunning bool) { - keepRunning = application.backend.Await(timeout) - return -} - -// Poll updates the window and checks for new events. This function returns true -// if the backend is still active, and false if it has closed. -func (application *Application) Poll () (keepRunning bool) { - keepRunning = application.backend.Poll() - return -} - -// Title returns the application's title. -func (application *Application) Title () (title string) { - title = application.title - return -} - // Config returns a pointer to the application's configuration. func (application *Application) Config () (config *Config) { config = &application.config return } - -// JustPressed returns true if the specified button is pressed, but was not -// pressed the last time events were checked. -func (application *Application) JustPressed (button Button) (pressed bool) { - pressed = application.backend.JustPressed(button) - return -} - -// JustReleased returns true if the specified button -func (application *Application) JustReleased (button Button) (released bool) { - released = application.backend.JustReleased(button) - return -} - -func (application *Application) Pressed (button Button) (pressed bool) { - pressed = application.backend.Pressed(button) - return -} - -func (application *Application) Repeated (button Button) (repeated bool) { - repeated = application.backend.Repeated(button) - return -} - -func (application *Application) Typed () (text string) { - text = application.backend.Typed() - return -} - -func (application *Application) Resized () (resized bool) { - resized = application.backend.Resized() - return -} - -func (application *Application) MousePosition () (x, y int) { - x, y = application.backend.MousePosition() - return -} diff --git a/backend.go b/backend.go index 39561e3..a6b5b98 100644 --- a/backend.go +++ b/backend.go @@ -1,20 +1,12 @@ package stone -import "time" +import "image" import "errors" type Backend interface { - Run (callback func (application *Application)) () - Await (timeout time.Duration) (keepRunning bool) - Poll () (keepRunning bool) - SetTitle (title string) - JustPressed (button Button) (pressed bool) - JustReleased (button Button) (released bool) - Pressed (button Button) (pressed bool) - Repeated (button Button) (repeated bool) - Typed () (text string) - Resized () (resized bool) - MousePosition () (x, y int) + Run (channel chan(Event)) + SetTitle (title string) + SetIcon (icons []image.Image) } type BackendFactory func (application *Application) (backend Backend, err error) diff --git a/backends/x/x.go b/backends/x/x.go new file mode 100644 index 0000000..517c8b3 --- /dev/null +++ b/backends/x/x.go @@ -0,0 +1,127 @@ +package x + +import "image" + +import "github.com/jezek/xgbutil" +// import "github.com/jezek/xgbutil/ewmh" +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 { + 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)) { + 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) { + +} + +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/event.go b/event.go new file mode 100644 index 0000000..f60cff2 --- /dev/null +++ b/event.go @@ -0,0 +1,12 @@ +package stone + +type Event interface { } + +type EventQuit struct { } +type EventPress Button +type EventRelease Button +type EventResize struct { } +type EventMouseMove struct { + X int + Y int +} diff --git a/examples/hello/main.go b/examples/hello/main.go index 0623cde..1dd1f89 100644 --- a/examples/hello/main.go +++ b/examples/hello/main.go @@ -1,31 +1,25 @@ package main +import "os" import "fmt" import "time" import "git.tebibyte.media/sashakoshka/stone" -import _ "git.tebibyte.media/sashakoshka/stone/backends/pixel" +import _ "git.tebibyte.media/sashakoshka/stone/backends/x" func main () { - application := stone.Application { } - err := application.Run(run) + application := &stone.Application { } + channel, err := application.Run() if err != nil { panic(err) } -} - -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 { + event := <- channel + switch event.(type) { + case stone.EventQuit: + os.Exit(0) + + case stone.EventResize: currentTime = time.Now() application.ResetDot() @@ -43,18 +37,6 @@ func run (application *stone.Application) { 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 } } } diff --git a/go.mod b/go.mod index adede01..7d11633 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,18 @@ go 1.18 require ( github.com/faiface/pixel v0.10.0 + github.com/jezek/xgbutil v0.0.0-20210302171758-530099784e66 golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff ) require ( + github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 // indirect + github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 // indirect github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380 // indirect github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 // indirect github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72 // indirect github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7 // indirect + github.com/jezek/xgb v1.1.0 // indirect github.com/pkg/errors v0.8.1 // indirect ) diff --git a/go.sum b/go.sum index 6f3f951..6390802 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 h1:1qlsVAQJXZHsaM8b6OLVo6muQUQd4CwkH/D3fnnbHXA= +github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ= +github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 h1:lTG4HQym5oPKjL7nGs+csTgiDna685ZXjxijkne828g= +github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380 h1:FvZ0mIGh6b3kOITxUnxS3tLZMh7yEoHo75v3/AgUqg0= @@ -12,7 +16,12 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72 h1:b+9H1GAsx5 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7 h1:THttjeRn1iiz69E875U6gAik8KTWk/JYAHoSVpUxBBI= github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk= +github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +github.com/jezek/xgbutil v0.0.0-20210302171758-530099784e66 h1:+wPhoJD8EH0/bXipIq8Lc2z477jfox9zkXPCJdhvHj8= +github.com/jezek/xgbutil v0.0.0-20210302171758-530099784e66/go.mod h1:KACeV+k6b+aoLTVrrurywEbu3UpqoQcQywj4qX8aQKM= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=