backend/x/backend.go
2024-08-10 20:47:36 -04:00

172 lines
3.8 KiB
Go

package x
import "image"
import "errors"
import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/xgbkb"
import "git.tebibyte.media/tomo/tomo/data"
import "git.tebibyte.media/tomo/tomo/canvas"
import "git.tebibyte.media/tomo/backend/style"
import "git.tebibyte.media/tomo/backend/x/canvas"
import "git.tebibyte.media/tomo/backend/internal/system"
import "github.com/jezek/xgbutil"
import "github.com/jezek/xgb/xproto"
import "github.com/jezek/xgbutil/xevent"
import "github.com/jezek/xgbutil/keybind"
import "github.com/jezek/xgbutil/mousebind"
type Backend struct {
x *xgbutil.XUtil
system *system.System
style *style.Style
iconSet style.IconSet
doChannel chan func()
windows map[xproto.Window] *window
open bool
}
type backendLink struct {
backend *Backend
}
func (this *backendLink) NewTexture (source image.Image) canvas.TextureCloser {
return this.backend.NewTexture(source)
}
func (this *backendLink) NewSurface (bounds image.Rectangle) (system.SurfaceLink, error) {
// TODO
return nil, errors.New("x: not implemented")
}
func New () (tomo.Backend, error) {
backend := &Backend {
doChannel: make(chan func (), 32),
windows: make(map[xproto.Window] *window),
open: true,
}
link := &backendLink {
backend: backend,
}
backend.system = system.New(link)
var err error
backend.x, err = xgbutil.NewConn()
if err != nil { return nil, err }
keybind .Initialize(backend.x)
xgbkb .Initialize(backend.x)
mousebind.Initialize(backend.x)
return backend, nil
}
func (this *Backend) Run () error {
this.assert()
pingBefore,
pingAfter,
pingQuit := xevent.MainPing(this.x)
for {
select {
case <- pingBefore:
<- pingAfter
case callback := <- this.doChannel:
callback()
case <- pingQuit:
return nil // FIXME: if we exited due to an error say so
}
this.afterEvent()
}
}
func (this *Backend) Stop () {
this.assert()
if !this.open { return }
this.open = false
toClose := []*window { }
for _, panel := range this.windows {
toClose = append(toClose, panel)
}
for _, panel := range toClose {
panel.Close()
}
xevent.Quit(this.x)
this.x.Conn().Close()
}
func (this *Backend) Do (callback func ()) {
this.assert()
this.doChannel <- callback
}
func (this *Backend) NewBox () tomo.Box {
return this.system.NewBox()
}
func (this *Backend) NewTextBox () tomo.TextBox {
return this.system.NewTextBox()
}
func (this *Backend) NewCanvasBox () tomo.CanvasBox {
return this.system.NewCanvasBox()
}
func (this *Backend) NewSurfaceBox () (tomo.SurfaceBox, error) {
return this.system.NewSurfaceBox()
}
func (this *Backend) NewContainerBox () tomo.ContainerBox {
return this.system.NewContainerBox()
}
func (this *Backend) NewTexture (source image.Image) canvas.TextureCloser {
return xcanvas.NewTextureFrom(source)
}
func (this *Backend) NewCanvas (bounds image.Rectangle) canvas.CanvasCloser {
return xcanvas.NewCanvas(this.x, bounds)
}
func (this *Backend) ColorRGBA (id tomo.Color) (r, g, b, a uint32) {
if col, ok := this.style.Colors[id]; ok {
return col.RGBA()
}
return 0xFFFF, 0, 0xFFFF, 0xFFFF // punish bad styles
}
func (this *Backend) IconTexture (id tomo.Icon, size tomo.IconSize) canvas.Texture {
return this.iconSet.Icon(id, size)
}
func (this *Backend) MimeIconTexture (mime data.Mime, size tomo.IconSize) canvas.Texture {
return this.iconSet.MimeIcon(mime, size)
}
func (this *Backend) SetStyle (style *style.Style) {
this.style = style
this.system.SetStyle(style)
}
func (this *Backend) SetIconSet (iconSet style.IconSet) {
this.iconSet = iconSet
this.system.SetIconSet(iconSet)
}
func (this *Backend) SetFaceSet (faceSet style.FaceSet) {
this.system.SetFaceSet(faceSet)
}
func (this *Backend) assert () {
if this == nil { panic("x: nil backend") }
}
func (this *Backend) afterEvent () {
for _, window := range this.windows {
window.hierarchy.AfterEvent()
}
}