Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ccbbb735fd | |||
| ab6bdeaba3 | |||
| 93d7eed21f | |||
| b18f747f0c | |||
| fa2ef954b2 | |||
| e4fdde3da1 |
2
go.mod
2
go.mod
@@ -3,7 +3,7 @@ module git.tebibyte.media/tomo/backend
|
|||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.tebibyte.media/tomo/tomo v0.45.0
|
git.tebibyte.media/tomo/tomo v0.46.1
|
||||||
git.tebibyte.media/tomo/typeset v0.7.1
|
git.tebibyte.media/tomo/typeset v0.7.1
|
||||||
git.tebibyte.media/tomo/xgbkb v1.0.1
|
git.tebibyte.media/tomo/xgbkb v1.0.1
|
||||||
github.com/jezek/xgb v1.1.1
|
github.com/jezek/xgb v1.1.1
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -1,6 +1,6 @@
|
|||||||
git.tebibyte.media/sashakoshka/xgbkb v1.0.0/go.mod h1:pNcE6TRO93vHd6q42SdwLSTTj25L0Yzggz7yLe0JV6Q=
|
git.tebibyte.media/sashakoshka/xgbkb v1.0.0/go.mod h1:pNcE6TRO93vHd6q42SdwLSTTj25L0Yzggz7yLe0JV6Q=
|
||||||
git.tebibyte.media/tomo/tomo v0.45.0 h1:fQH0WIPidW275hOq9dE6R7p064xG1RGx2QU68Avlr84=
|
git.tebibyte.media/tomo/tomo v0.46.1 h1:/8fT6I9l4TK529zokrThbNDHGRvUsNgif1Zs++0PBSQ=
|
||||||
git.tebibyte.media/tomo/tomo v0.45.0/go.mod h1:WrtilgKB1y8O2Yu7X4mYcRiqOlPR8NuUnoA/ynkQWrs=
|
git.tebibyte.media/tomo/tomo v0.46.1/go.mod h1:WrtilgKB1y8O2Yu7X4mYcRiqOlPR8NuUnoA/ynkQWrs=
|
||||||
git.tebibyte.media/tomo/typeset v0.7.1 h1:aZrsHwCG5ZB4f5CruRFsxLv5ezJUCFUFsQJJso2sXQ8=
|
git.tebibyte.media/tomo/typeset v0.7.1 h1:aZrsHwCG5ZB4f5CruRFsxLv5ezJUCFUFsQJJso2sXQ8=
|
||||||
git.tebibyte.media/tomo/typeset v0.7.1/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g=
|
git.tebibyte.media/tomo/typeset v0.7.1/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g=
|
||||||
git.tebibyte.media/tomo/xgbkb v1.0.1 h1:b3HDUopjdQp1MZrb5Vpil4bOtk3NnNXtfQW27Blw2kE=
|
git.tebibyte.media/tomo/xgbkb v1.0.1 h1:b3HDUopjdQp1MZrb5Vpil4bOtk3NnNXtfQW27Blw2kE=
|
||||||
|
|||||||
@@ -130,11 +130,15 @@ func (this *box) Tag (tag string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *box) SetTag (tag string, on bool) {
|
func (this *box) SetTag (tag string, on bool) {
|
||||||
|
wasOn := this.tags.Has(tag)
|
||||||
if on {
|
if on {
|
||||||
this.tags.Add(tag)
|
this.tags.Add(tag)
|
||||||
} else {
|
} else {
|
||||||
delete(this.tags, tag)
|
delete(this.tags, tag)
|
||||||
}
|
}
|
||||||
|
if wasOn != on {
|
||||||
|
this.invalidateStyle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *box) SetAttr (attr tomo.Attr) {
|
func (this *box) SetAttr (attr tomo.Attr) {
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import "git.tebibyte.media/tomo/tomo"
|
|||||||
import "git.tebibyte.media/tomo/backend/style"
|
import "git.tebibyte.media/tomo/backend/style"
|
||||||
|
|
||||||
type styleApplicator struct {
|
type styleApplicator struct {
|
||||||
style *style.Style
|
style *style.Style
|
||||||
role tomo.Role
|
role tomo.Role
|
||||||
rules []style.Rule
|
rules []style.Rule
|
||||||
|
currentSet style.AttrSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *styleApplicator) apply (box anyBox) {
|
func (this *styleApplicator) apply (box anyBox) {
|
||||||
@@ -41,7 +42,18 @@ func (this *styleApplicator) apply (box anyBox) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset an attribute if it is no longer specified
|
||||||
|
if this.currentSet != nil {
|
||||||
|
for kind := range this.currentSet {
|
||||||
|
_, exists := attrs[kind]
|
||||||
|
if !exists {
|
||||||
|
box.unsetAttr(kind, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// apply that list of attributes
|
// apply that list of attributes
|
||||||
|
this.currentSet = attrs
|
||||||
for _, attr := range attrs {
|
for _, attr := range attrs {
|
||||||
box.setAttr(attr, false)
|
box.setAttr(attr, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,3 +132,32 @@ func convertColor (c color.Color) xgraphics.BGRA {
|
|||||||
A: uint8(a >> 8),
|
A: uint8(a >> 8),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For some reason, xgraphics.BGRA does not specify whether or not it uses
|
||||||
|
// premultiplied alpha, and information regarding this is contradictory.
|
||||||
|
// Basically:
|
||||||
|
// - BGRAModel just takes the result of c.RGBA and bit shifts it, without
|
||||||
|
// un-doing the aplha premultiplication that is required by Color.RGBA,
|
||||||
|
// suggesting that xgraphics.BGRA stores alpha-premultiplied color.
|
||||||
|
// - xgraphics.BlendBGRA lerps between dest and src using only the alpha of
|
||||||
|
// src (temporarily converting the colors to fucking floats for some reason)
|
||||||
|
// which seems to suggest that xgraphics.BGRA *does not* store alpha-
|
||||||
|
// premultiplied color.
|
||||||
|
// There is no issues page on xgbutil so we may never get an answer to this
|
||||||
|
// question. However, in this package we just use xgraphics.BGRA to store alpha-
|
||||||
|
// premultiplied color anyway because its way faster, and I would sooner eat
|
||||||
|
// spaghetti with a spoon than convert to and from float64 to blend pixels.
|
||||||
|
func blendPremultipliedBGRA (dst, src xgraphics.BGRA) xgraphics.BGRA {
|
||||||
|
// https://en.wikipedia.org/wiki/Alpha_compositing
|
||||||
|
return xgraphics.BGRA {
|
||||||
|
B: blendPremultipliedChannel(dst.B, src.B, src.A),
|
||||||
|
G: blendPremultipliedChannel(dst.G, src.G, src.A),
|
||||||
|
R: blendPremultipliedChannel(dst.R, src.R, src.A),
|
||||||
|
A: blendPremultipliedChannel(dst.A, src.A, src.A),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func blendPremultipliedChannel (dst, src, a uint8) uint8 {
|
||||||
|
dst16, src16, a16 := uint16(dst), uint16(src), uint16(a)
|
||||||
|
return uint8(src16 + ((dst16 * (255 - a16)) >> 8))
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func (this *pen) textureRectangleTransparent (bounds image.Rectangle) {
|
|||||||
srcPos := pos.Add(offset)
|
srcPos := pos.Add(offset)
|
||||||
dstIndex := this.image.PixOffset(pos.X, pos.Y)
|
dstIndex := this.image.PixOffset(pos.X, pos.Y)
|
||||||
srcIndex := this.texture.PixOffset(srcPos.X, srcPos.Y)
|
srcIndex := this.texture.PixOffset(srcPos.X, srcPos.Y)
|
||||||
pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
|
pixel := blendPremultipliedBGRA(xgraphics.BGRA {
|
||||||
B: dst[dstIndex + 0],
|
B: dst[dstIndex + 0],
|
||||||
G: dst[dstIndex + 1],
|
G: dst[dstIndex + 1],
|
||||||
R: dst[dstIndex + 2],
|
R: dst[dstIndex + 2],
|
||||||
@@ -93,7 +93,7 @@ func (this *pen) fillRectangleTransparent (c xgraphics.BGRA, bounds image.Rectan
|
|||||||
for pos.Y = bounds.Min.Y; pos.Y < bounds.Max.Y; pos.Y ++ {
|
for pos.Y = bounds.Min.Y; pos.Y < bounds.Max.Y; pos.Y ++ {
|
||||||
for pos.X = bounds.Min.X; pos.X < bounds.Max.X; pos.X ++ {
|
for pos.X = bounds.Min.X; pos.X < bounds.Max.X; pos.X ++ {
|
||||||
index := this.image.PixOffset(pos.X, pos.Y)
|
index := this.image.PixOffset(pos.X, pos.Y)
|
||||||
pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
|
pixel := blendPremultipliedBGRA(xgraphics.BGRA {
|
||||||
B: this.image.Pix[index + 0],
|
B: this.image.Pix[index + 0],
|
||||||
G: this.image.Pix[index + 1],
|
G: this.image.Pix[index + 1],
|
||||||
R: this.image.Pix[index + 2],
|
R: this.image.Pix[index + 2],
|
||||||
@@ -256,7 +256,7 @@ func (context *fillingContext) fillPolygonHotTransparent () {
|
|||||||
// fill pixels in between
|
// fill pixels in between
|
||||||
for x := left; x < right; x ++ {
|
for x := left; x < right; x ++ {
|
||||||
index := context.image.PixOffset(x, context.y)
|
index := context.image.PixOffset(x, context.y)
|
||||||
pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
|
pixel := blendPremultipliedBGRA(xgraphics.BGRA {
|
||||||
B: context.image.Pix[index + 0],
|
B: context.image.Pix[index + 0],
|
||||||
G: context.image.Pix[index + 1],
|
G: context.image.Pix[index + 1],
|
||||||
R: context.image.Pix[index + 2],
|
R: context.image.Pix[index + 2],
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func (context plottingContext) plot (center image.Point) {
|
|||||||
for y := square.Min.Y; y < square.Max.Y; y ++ {
|
for y := square.Min.Y; y < square.Max.Y; y ++ {
|
||||||
for x := square.Min.X; x < square.Max.X; x ++ {
|
for x := square.Min.X; x < square.Max.X; x ++ {
|
||||||
index := context.image.PixOffset(x, y)
|
index := context.image.PixOffset(x, y)
|
||||||
pixel := xgraphics.BlendBGRA(xgraphics.BGRA {
|
pixel := blendPremultipliedBGRA(xgraphics.BGRA {
|
||||||
B: context.image.Pix[index + 0],
|
B: context.image.Pix[index + 0],
|
||||||
G: context.image.Pix[index + 1],
|
G: context.image.Pix[index + 1],
|
||||||
R: context.image.Pix[index + 2],
|
R: context.image.Pix[index + 2],
|
||||||
|
|||||||
19
x/event.go
19
x/event.go
@@ -145,12 +145,15 @@ func (window *window) updateBounds () {
|
|||||||
// need to sum up all their positions.
|
// need to sum up all their positions.
|
||||||
decorGeometry, _ := window.xWindow.DecorGeometry()
|
decorGeometry, _ := window.xWindow.DecorGeometry()
|
||||||
windowGeometry, _ := window.xWindow.Geometry()
|
windowGeometry, _ := window.xWindow.Geometry()
|
||||||
origin := image.Pt(
|
origin := image.Pt (
|
||||||
windowGeometry.X() + decorGeometry.X(),
|
decorGeometry.X(),
|
||||||
windowGeometry.Y() + decorGeometry.Y())
|
decorGeometry.Y())
|
||||||
window.metrics.bounds = image.Rectangle {
|
innerOrigin := origin.Add(image.Pt (
|
||||||
Min: origin,
|
windowGeometry.X(),
|
||||||
Max: origin.Add(image.Pt(windowGeometry.Width(), windowGeometry.Height())),
|
windowGeometry.Y()))
|
||||||
|
window.metrics.innerBounds = image.Rectangle {
|
||||||
|
Min: innerOrigin,
|
||||||
|
Max: innerOrigin.Add(image.Pt(windowGeometry.Width(), windowGeometry.Height())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,9 +164,9 @@ func (window *window) handleConfigureNotify (
|
|||||||
configureEvent := *event.ConfigureNotifyEvent
|
configureEvent := *event.ConfigureNotifyEvent
|
||||||
configureEvent = window.compressConfigureNotify(configureEvent)
|
configureEvent = window.compressConfigureNotify(configureEvent)
|
||||||
|
|
||||||
oldBounds := window.metrics.bounds
|
oldBounds := window.metrics.innerBounds
|
||||||
window.updateBounds()
|
window.updateBounds()
|
||||||
newBounds := window.metrics.bounds
|
newBounds := window.metrics.innerBounds
|
||||||
|
|
||||||
sizeChanged :=
|
sizeChanged :=
|
||||||
oldBounds.Dx() != newBounds.Dx() ||
|
oldBounds.Dx() != newBounds.Dx() ||
|
||||||
|
|||||||
33
x/window.go
33
x/window.go
@@ -37,7 +37,8 @@ type window struct {
|
|||||||
resizeY bool
|
resizeY bool
|
||||||
|
|
||||||
metrics struct {
|
metrics struct {
|
||||||
bounds image.Rectangle
|
bounds image.Rectangle // bounds, including frame
|
||||||
|
innerBounds image.Rectangle // bounds of the drawable area
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose event.FuncBroadcaster
|
onClose event.FuncBroadcaster
|
||||||
@@ -156,7 +157,7 @@ func (this *Backend) newWindow (
|
|||||||
// xevent.SelectionRequestFun(window.handleSelectionRequest).
|
// xevent.SelectionRequestFun(window.handleSelectionRequest).
|
||||||
// Connect(this.x, window.xWindow.Id)
|
// Connect(this.x, window.xWindow.Id)
|
||||||
|
|
||||||
window.metrics.bounds = bounds
|
window.metrics.innerBounds = bounds
|
||||||
window.doMinimumSize()
|
window.doMinimumSize()
|
||||||
|
|
||||||
this.windows[window.xWindow.Id] = window
|
this.windows[window.xWindow.Id] = window
|
||||||
@@ -165,6 +166,14 @@ func (this *Backend) newWindow (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *window) Bounds () image.Rectangle {
|
||||||
|
return this.metrics.bounds.Sub(this.metrics.innerBounds.Min)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *window) InnerBounds () image.Rectangle {
|
||||||
|
return this.metrics.innerBounds.Sub(this.metrics.innerBounds.Min)
|
||||||
|
}
|
||||||
|
|
||||||
func (this *window) SetRoot (root tomo.Object) {
|
func (this *window) SetRoot (root tomo.Object) {
|
||||||
if root == nil {
|
if root == nil {
|
||||||
this.hierarchy.SetRoot(nil)
|
this.hierarchy.SetRoot(nil)
|
||||||
@@ -242,7 +251,7 @@ func (this *window) NewChild (bounds image.Rectangle) (tomo.Window, error) {
|
|||||||
leader := this.leader
|
leader := this.leader
|
||||||
|
|
||||||
child, err := this.backend.newWindow (
|
child, err := this.backend.newWindow (
|
||||||
bounds.Add(this.metrics.bounds.Min), false)
|
bounds.Add(this.metrics.innerBounds.Min), false)
|
||||||
child.leader = leader
|
child.leader = leader
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
|
|
||||||
@@ -260,7 +269,7 @@ func (this *window) NewChild (bounds image.Rectangle) (tomo.Window, error) {
|
|||||||
|
|
||||||
func (this *window) NewMenu (bounds image.Rectangle) (tomo.Window, error) {
|
func (this *window) NewMenu (bounds image.Rectangle) (tomo.Window, error) {
|
||||||
menu, err := this.backend.newWindow (
|
menu, err := this.backend.newWindow (
|
||||||
bounds.Add(this.metrics.bounds.Min), true)
|
bounds.Add(this.metrics.innerBounds.Min), true)
|
||||||
menu.shy = true
|
menu.shy = true
|
||||||
icccm.WmTransientForSet (
|
icccm.WmTransientForSet (
|
||||||
this.backend.x,
|
this.backend.x,
|
||||||
@@ -273,7 +282,7 @@ func (this *window) NewMenu (bounds image.Rectangle) (tomo.Window, error) {
|
|||||||
|
|
||||||
func (this *window) NewModal (bounds image.Rectangle) (tomo.Window, error) {
|
func (this *window) NewModal (bounds image.Rectangle) (tomo.Window, error) {
|
||||||
modal, err := this.backend.newWindow (
|
modal, err := this.backend.newWindow (
|
||||||
bounds.Add(this.metrics.bounds.Min), false)
|
bounds.Add(this.metrics.innerBounds.Min), false)
|
||||||
icccm.WmTransientForSet (
|
icccm.WmTransientForSet (
|
||||||
this.backend.x,
|
this.backend.x,
|
||||||
modal.xWindow.Id,
|
modal.xWindow.Id,
|
||||||
@@ -382,8 +391,8 @@ func (this *window) reallocateCanvas () {
|
|||||||
previousHeight = this.xCanvas.Bounds().Dy()
|
previousHeight = this.xCanvas.Bounds().Dy()
|
||||||
}
|
}
|
||||||
|
|
||||||
newWidth := this.metrics.bounds.Dx()
|
newWidth := this.metrics.innerBounds.Dx()
|
||||||
newHeight := this.metrics.bounds.Dy()
|
newHeight := this.metrics.innerBounds.Dy()
|
||||||
larger := newWidth > previousWidth || newHeight > previousHeight
|
larger := newWidth > previousWidth || newHeight > previousHeight
|
||||||
smaller := newWidth < previousWidth / 2 || newHeight < previousHeight / 2
|
smaller := newWidth < previousWidth / 2 || newHeight < previousHeight / 2
|
||||||
|
|
||||||
@@ -403,7 +412,7 @@ func (this *window) reallocateCanvas () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.hierarchy.SetCanvas(this.xCanvas.SubCanvas (
|
this.hierarchy.SetCanvas(this.xCanvas.SubCanvas (
|
||||||
this.metrics.bounds.Sub(this.metrics.bounds.Min)))
|
this.metrics.innerBounds.Sub(this.metrics.innerBounds.Min)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *window) pushAll () {
|
func (this *window) pushAll () {
|
||||||
@@ -458,12 +467,12 @@ func (this *window) doMinimumSize () {
|
|||||||
this.backend.x,
|
this.backend.x,
|
||||||
this.xWindow.Id,
|
this.xWindow.Id,
|
||||||
&hints)
|
&hints)
|
||||||
newWidth := this.metrics.bounds.Dx()
|
newWidth := this.metrics.innerBounds.Dx()
|
||||||
newHeight := this.metrics.bounds.Dy()
|
newHeight := this.metrics.innerBounds.Dy()
|
||||||
if newWidth < size.X { newWidth = size.X }
|
if newWidth < size.X { newWidth = size.X }
|
||||||
if newHeight < size.Y { newHeight = size.Y }
|
if newHeight < size.Y { newHeight = size.Y }
|
||||||
if newWidth != this.metrics.bounds.Dx() ||
|
if newWidth != this.metrics.innerBounds.Dx() ||
|
||||||
newHeight != this.metrics.bounds.Dy() {
|
newHeight != this.metrics.innerBounds.Dy() {
|
||||||
this.xWindow.Resize(newWidth, newHeight)
|
this.xWindow.Resize(newWidth, newHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user