Merge pull request 'redo-event-system' (#1) from redo-event-system into main
Reviewed-on: #1
This commit is contained in:
commit
acbf2a3954
@ -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
|
||||
}
|
||||
|
16
backend.go
16
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)
|
||||
|
127
backends/x/x.go
Normal file
127
backends/x/x.go
Normal 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
12
event.go
Normal 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
|
||||
}
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
4
go.mod
4
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
|
||||
)
|
||||
|
9
go.sum
9
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=
|
||||
|
Loading…
Reference in New Issue
Block a user