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,13 +18,11 @@ 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
width, height := application.Size() width, height := application.Size()
@ -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) SetTitle (title string)
Poll () (keepRunning bool) SetIcon (icons []image.Image)
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)
} }
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=