package x import "image" import "image/color" // import "golang.org/x/image/font" // import "golang.org/x/image/font/basicfont" import "github.com/jezek/xgb" import "github.com/jezek/xgbutil" import "github.com/jezek/xgb/xproto" // 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 { windowWidth int windowHeight int 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: // if the queue is empty, don't dequeue anything because // it would cause a fucking segfault lmao (???) if !xevent.Empty(backend.connection) { event, err := xevent.Dequeue(backend.connection) if err != nil { // TODO: do something with err } if event != nil { backend.handleXEvent(event) } } <- 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) handleXEvent (event xgb.Event) { switch event.(type) { case xproto.ConfigureNotifyEvent: configureEvent := event.(xproto.ConfigureNotifyEvent) newWidth := int(configureEvent.Width) newHeight := int(configureEvent.Height) sizeChanged := backend.metrics.windowWidth != newWidth || backend.metrics.windowHeight != newHeight backend.metrics.windowWidth = newWidth backend.metrics.windowHeight = newHeight if sizeChanged { // TODO: resize and rebind canvas } } } 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 } func (backend *Backend) bindCanvas () { backend.canvas.XSurfaceSet(backend.window.Id) backend.canvas.XDraw() backend.canvas.XPaint(backend.window.Id) } // 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 backend.metrics.windowWidth, backend.metrics.windowHeight = backend.calculateWindowSize() // 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 backend.window.Create ( backend.connection.RootWin(), 0, 0, backend.metrics.windowWidth, backend.metrics.windowHeight, 0) backend.window.Map() backend.window.Listen(xproto.EventMaskStructureNotify) // create a canvas backend.canvas = xgraphics.New ( backend.connection, image.Rect ( 0, 0, backend.metrics.windowWidth, backend.metrics.windowHeight)) for i := 8; i < 64; i ++ { backend.canvas.Set(8, i, color.RGBA { R: 0xFF, A: 0xFF }) } backend.bindCanvas() // 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) }