Merge pull request 'redo-event-system' (#1) from redo-event-system into main

Reviewed-on: #1
This commit is contained in:
Sasha Koshka 2022-11-09 23:54:10 +00:00
commit acbf2a3954
7 changed files with 176 additions and 116 deletions

View File

@ -1,6 +1,5 @@
package stone package stone
import "time"
import "image/color" import "image/color"
// Application represents an application. // Application represents an application.
@ -12,12 +11,6 @@ type Application struct {
config Config 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 // SetTitle sets the application's title. If in a window, it will appear as the
// window's name. // window's name.
func (application *Application) SetTitle (title string) { func (application *Application) SetTitle (title string) {
@ -25,12 +18,10 @@ func (application *Application) SetTitle (title string) {
application.backend.SetTitle(title) application.backend.SetTitle(title)
} }
// Run initializes the application, and then calls callback. Operations inside // Run initializes the application, starts it, and then returns a channel that
// of callback are allowed to interact with the application. Depending on the // broadcasts events. If no suitable backend can be found, an error is returned.
// backend used, this function may bind to the main thread. func (application *Application) Run () (
func (application *Application) Run ( channel chan(Event),
callback func (application *Application),
) (
err error, err error,
) { ) {
// default values for certain parameters // default values for certain parameters
@ -51,29 +42,10 @@ func (application *Application) Run (
application.backend, err = instantiateBackend(application) application.backend, err = instantiateBackend(application)
if err != nil { return } if err != nil { return }
application.backend.Run(callback)
return channel = make(chan(Event))
} go application.backend.Run(channel)
// 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 return
} }
@ -82,41 +54,3 @@ func (application *Application) Config () (config *Config) {
config = &application.config config = &application.config
return 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
}

View File

@ -1,20 +1,12 @@
package stone package stone
import "time" import "image"
import "errors" import "errors"
type Backend interface { type Backend interface {
Run (callback func (application *Application)) () Run (channel chan(Event))
Await (timeout time.Duration) (keepRunning bool)
Poll () (keepRunning bool)
SetTitle (title string) SetTitle (title string)
JustPressed (button Button) (pressed bool) SetIcon (icons []image.Image)
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)
} }
type BackendFactory func (application *Application) (backend Backend, err error) type BackendFactory func (application *Application) (backend Backend, err error)

127
backends/x/x.go Normal file
View File

@ -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)
}

12
event.go Normal file
View File

@ -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
}

View File

@ -1,31 +1,25 @@
package main package main
import "os"
import "fmt" import "fmt"
import "time" import "time"
import "git.tebibyte.media/sashakoshka/stone" import "git.tebibyte.media/sashakoshka/stone"
import _ "git.tebibyte.media/sashakoshka/stone/backends/pixel" import _ "git.tebibyte.media/sashakoshka/stone/backends/x"
func main () { func main () {
application := stone.Application { } application := &stone.Application { }
err := application.Run(run) channel, err := application.Run()
if err != nil { panic(err) } if err != nil { panic(err) }
}
func run (application *stone.Application) {
currentTime := time.Time { } currentTime := time.Time { }
frameDelay := time.Second / 2
textBuffer := ""
for { for {
typed := application.Typed() event := <- channel
textBuffer += typed switch event.(type) {
case stone.EventQuit:
os.Exit(0)
shouldRender := case stone.EventResize:
application.Resized() ||
time.Since(currentTime) > frameDelay ||
len(typed) > 0
if shouldRender {
currentTime = time.Now() currentTime = time.Now()
application.ResetDot() application.ResetDot()
@ -43,18 +37,6 @@ func run (application *stone.Application) {
application.SetRune(5, 1, ':') application.SetRune(5, 1, ':')
application.SetRune(6, 1, rune(second / 10 + 48)) application.SetRune(6, 1, rune(second / 10 + 48))
application.SetRune(7, 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 }
} }
} }

4
go.mod
View File

@ -4,14 +4,18 @@ go 1.18
require ( require (
github.com/faiface/pixel v0.10.0 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 golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff
) )
require ( 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/glhf v0.0.0-20181018222622-82a6317ac380 // indirect
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 // 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/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/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72 // indirect
github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7 // 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 github.com/pkg/errors v0.8.1 // indirect
) )

9
go.sum
View File

@ -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 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= 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/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 h1:THttjeRn1iiz69E875U6gAik8KTWk/JYAHoSVpUxBBI=
github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ= 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/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 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=