package x import "os" import "golang.org/x/image/font" import "golang.org/x/image/font/opentype" import "golang.org/x/image/font/basicfont" import "github.com/jezek/xgbutil" import "github.com/jezek/xgb/xproto" import "github.com/jezek/xgbutil/icccm" import "github.com/jezek/xgbutil/xevent" import "github.com/jezek/xgbutil/xwindow" import "github.com/jezek/xgbutil/keybind" import "github.com/jezek/xgbutil/xgraphics" import "git.tebibyte.media/sashakoshka/stone" import "github.com/flopp/go-findfont" // factory instantiates an X backend. func factory (application *stone.Application) (output stone.Backend, err error) { backend := &Backend { application: application, config: application.Config(), } // load font backend.font.face = findAndLoadFont ( backend.config.FontName(), float64(backend.config.FontSize())) if backend.font.face == nil { backend.font.face = basicfont.Face7x13 } // pre-calculate colors for index := 0; index < len(backend.colors); index ++ { color := backend.config.Color(stone.Color(index)) r, g, b, a := color.RGBA() r >>= 8 g >>= 8 b >>= 8 a >>= 8 backend.colors[index] = xgraphics.BGRA { R: uint8(r), G: uint8(g), B: uint8(b), A: uint8(a), } } // calculate metrics metrics := backend.font.face.Metrics() glyphAdvance, _ := backend.font.face.GlyphAdvance('M') backend.metrics.cellWidth = glyphAdvance.Round() backend.metrics.cellHeight = metrics.Height.Round() backend.metrics.descent = metrics.Descent.Round() 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 } keybind.Initialize(backend.connection) // create the window backend.window.Create ( backend.connection.RootWin(), 0, 0, backend.metrics.windowWidth, backend.metrics.windowHeight, 0) backend.window.Map() // TODO: also listen to mouse movement (compressed) and mouse and // keyboard buttons (uncompressed) err = backend.window.Listen ( xproto.EventMaskStructureNotify, xproto.EventMaskPointerMotion, xproto.EventMaskKeyPress, xproto.EventMaskKeyRelease, xproto.EventMaskButtonPress, xproto.EventMaskButtonRelease) backend.SetTitle(application.Title()) backend.SetIcon(application.Icon()) if err != nil { return } // set minimum dimensions minWidth := backend.metrics.cellWidth + backend.metrics.padding * 2 minHeight := backend.metrics.cellHeight + backend.metrics.padding * 2 err = icccm.WmNormalHintsSet ( backend.connection, backend.window.Id, &icccm.NormalHints { Flags: icccm.SizeHintPMinSize, MinWidth: uint(minWidth), MinHeight: uint(minHeight), }) if err != nil { return } // create a canvas backend.reallocateCanvas() // attatch graceful close handler backend.window.WMGracefulClose (func (window *xwindow.Window) { backend.window.Destroy() backend.shutDown() }) // attatch event handlers xevent.ConfigureNotifyFun(backend.handleConfigureNotify). Connect(backend.connection, backend.window.Id) xevent.ButtonPressFun(backend.handleButtonPress). Connect(backend.connection, backend.window.Id) xevent.ButtonReleaseFun(backend.handleButtonRelease). Connect(backend.connection, backend.window.Id) xevent.MotionNotifyFun(backend.handleMotionNotify). Connect(backend.connection, backend.window.Id) xevent.KeyPressFun(backend.handleKeyPress). Connect(backend.connection, backend.window.Id) xevent.KeyReleaseFun(backend.handleKeyRelease). Connect(backend.connection, backend.window.Id) output = backend return } func findAndLoadFont (name string, size float64) (face font.Face) { if name == "" { return } fontPath, err := findfont.Find(name) if err != nil { return } fontFile, err := os.Open(fontPath) if err != nil { return } fontObject, err := opentype.ParseReaderAt(fontFile) if err != nil { return } face, err = opentype.NewFace (fontObject, &opentype.FaceOptions { Size: size, DPI: 96, Hinting: font.HintingFull, }) if err != nil { face = nil } return } // init registers this backend when the program starts. func init () { stone.RegisterBackend(factory) }