2022-11-13 20:44:19 -07:00
|
|
|
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.
|
2022-11-15 22:29:23 -07:00
|
|
|
func factory (
|
|
|
|
application *stone.Application,
|
|
|
|
callbackManager *stone.CallbackManager,
|
|
|
|
) (
|
|
|
|
output stone.Backend,
|
|
|
|
err error,
|
|
|
|
) {
|
2022-11-13 20:44:19 -07:00
|
|
|
backend := &Backend {
|
2022-11-15 22:29:23 -07:00
|
|
|
application: application,
|
|
|
|
config: application.Config(),
|
|
|
|
callbackManager: callbackManager,
|
2022-11-13 20:44:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
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()
|
2022-11-16 09:08:30 -07:00
|
|
|
xevent.Quit(backend.connection)
|
2022-11-13 20:44:19 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
// 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)
|
2022-11-14 13:58:34 -07:00
|
|
|
xevent.KeyPressFun(backend.handleKeyPress).
|
|
|
|
Connect(backend.connection, backend.window.Id)
|
|
|
|
xevent.KeyReleaseFun(backend.handleKeyRelease).
|
|
|
|
Connect(backend.connection, backend.window.Id)
|
2022-11-14 21:32:05 -07:00
|
|
|
|
|
|
|
// uncomment these to draw debug bounds
|
2022-11-17 19:19:23 -07:00
|
|
|
// backend.drawCellBounds = true
|
2022-11-14 21:32:05 -07:00
|
|
|
// backend.drawBufferBounds = true
|
2022-11-13 20:44:19 -07:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|