1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

screenshot: ensure screencopy before surface creation

This commit is contained in:
bbedward
2025-12-05 17:39:35 -05:00
parent f9a6b4ce2c
commit 2ddc448150
2 changed files with 157 additions and 95 deletions

View File

@@ -49,6 +49,13 @@ type OutputSurface struct {
slotsReady bool
}
type PreCapture struct {
screenBuf *ShmBuffer
screenBufNoCursor *ShmBuffer
format uint32
yInverted bool
}
type RegionSelector struct {
screenshoter *Screenshoter
@@ -68,8 +75,9 @@ type RegionSelector struct {
shortcutsInhibitMgr *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitManagerV1
shortcutsInhibitor *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitorV1
outputs map[uint32]*WaylandOutput
outputsMu sync.Mutex
outputs map[uint32]*WaylandOutput
outputsMu sync.Mutex
preCapture map[*WaylandOutput]*PreCapture
surfaces []*OutputSurface
activeSurface *OutputSurface
@@ -99,6 +107,7 @@ func NewRegionSelector(s *Screenshoter) *RegionSelector {
return &RegionSelector{
screenshoter: s,
outputs: make(map[uint32]*WaylandOutput),
preCapture: make(map[*WaylandOutput]*PreCapture),
showCapturedCursor: true,
}
}
@@ -138,6 +147,10 @@ func (r *RegionSelector) Run() (*CaptureResult, bool, error) {
return nil, false, fmt.Errorf("roundtrip after protocol check: %w", err)
}
if err := r.preCaptureAllOutputs(); err != nil {
return nil, false, fmt.Errorf("pre-capture: %w", err)
}
if err := r.createSurfaces(); err != nil {
return nil, false, fmt.Errorf("create surfaces: %w", err)
}
@@ -320,6 +333,102 @@ func (r *RegionSelector) setupOutputHandlers(name uint32, output *client.Output)
})
}
func (r *RegionSelector) preCaptureAllOutputs() error {
r.outputsMu.Lock()
outputs := make([]*WaylandOutput, 0, len(r.outputs))
for _, o := range r.outputs {
outputs = append(outputs, o)
}
r.outputsMu.Unlock()
pending := len(outputs) * 2
done := make(chan struct{}, pending)
for _, output := range outputs {
pc := &PreCapture{}
r.preCapture[output] = pc
r.preCaptureOutput(output, pc, true, func() { done <- struct{}{} })
r.preCaptureOutput(output, pc, false, func() { done <- struct{}{} })
}
for i := 0; i < pending; i++ {
if err := r.ctx.Dispatch(); err != nil {
return err
}
select {
case <-done:
default:
i--
}
}
return nil
}
func (r *RegionSelector) preCaptureOutput(output *WaylandOutput, pc *PreCapture, withCursor bool, onReady func()) {
cursor := int32(0)
if withCursor {
cursor = 1
}
frame, err := r.screencopy.CaptureOutput(cursor, output.wlOutput)
if err != nil {
log.Error("screencopy capture failed", "err", err)
onReady()
return
}
frame.SetBufferHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferEvent) {
buf, err := CreateShmBuffer(int(e.Width), int(e.Height), int(e.Stride))
if err != nil {
log.Error("create screen buffer failed", "err", err)
return
}
if withCursor {
pc.screenBuf = buf
pc.format = e.Format
} else {
pc.screenBufNoCursor = buf
}
pool, err := r.shm.CreatePool(buf.Fd(), int32(buf.Size()))
if err != nil {
log.Error("create shm pool failed", "err", err)
return
}
wlBuf, err := pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), e.Format)
if err != nil {
log.Error("create wl_buffer failed", "err", err)
pool.Destroy()
return
}
if err := frame.Copy(wlBuf); err != nil {
log.Error("frame copy failed", "err", err)
}
pool.Destroy()
})
frame.SetFlagsHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FlagsEvent) {
if withCursor {
pc.yInverted = (e.Flags & 1) != 0
}
})
frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
frame.Destroy()
onReady()
})
frame.SetFailedHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FailedEvent) {
log.Error("screencopy failed")
frame.Destroy()
onReady()
})
}
func (r *RegionSelector) createSurfaces() error {
r.outputsMu.Lock()
outputs := make([]*WaylandOutput, 0, len(r.outputs))
@@ -488,77 +597,19 @@ func (r *RegionSelector) ensureShortcutsInhibitor(os *OutputSurface) {
}
func (r *RegionSelector) captureForSurface(os *OutputSurface) {
r.captureWithCursor(os, true, func() {
r.captureWithCursor(os, false, func() {
r.initRenderBuffer(os)
r.applyPreSelection(os)
r.redrawSurface(os)
})
})
}
func (r *RegionSelector) captureWithCursor(os *OutputSurface, withCursor bool, onReady func()) {
cursor := int32(0)
if withCursor {
cursor = 1
}
frame, err := r.screencopy.CaptureOutput(cursor, os.output.wlOutput)
if err != nil {
log.Error("screencopy capture failed", "err", err)
pc := r.preCapture[os.output]
if pc == nil {
return
}
frame.SetBufferHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferEvent) {
buf, err := CreateShmBuffer(int(e.Width), int(e.Height), int(e.Stride))
if err != nil {
log.Error("create screen buffer failed", "err", err)
return
}
os.screenBuf = pc.screenBuf
os.screenBufNoCursor = pc.screenBufNoCursor
os.screenFormat = pc.format
os.yInverted = pc.yInverted
if withCursor {
os.screenBuf = buf
os.screenFormat = e.Format
} else {
os.screenBufNoCursor = buf
}
pool, err := r.shm.CreatePool(buf.Fd(), int32(buf.Size()))
if err != nil {
log.Error("create shm pool failed", "err", err)
return
}
wlBuf, err := pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), e.Format)
if err != nil {
log.Error("create wl_buffer failed", "err", err)
pool.Destroy()
return
}
if err := frame.Copy(wlBuf); err != nil {
log.Error("frame copy failed", "err", err)
}
pool.Destroy()
})
frame.SetFlagsHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FlagsEvent) {
if withCursor {
os.yInverted = (e.Flags & 1) != 0
}
})
frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
frame.Destroy()
if onReady != nil {
onReady()
}
})
frame.SetFailedHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FailedEvent) {
log.Error("screencopy failed")
frame.Destroy()
})
r.initRenderBuffer(os)
r.applyPreSelection(os)
r.redrawSurface(os)
}
func (r *RegionSelector) initRenderBuffer(os *OutputSurface) {

View File

@@ -55,6 +55,7 @@ func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
data := renderBuf.Data()
stride := renderBuf.Stride
w, h := renderBuf.Width, renderBuf.Height
format := os.screenFormat
// Dim the entire buffer
for y := 0; y < h; y++ {
@@ -70,7 +71,7 @@ func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
}
}
r.drawHUD(data, stride, w, h)
r.drawHUD(data, stride, w, h, format)
if !r.selection.hasSelection || r.selection.surface != os {
return
@@ -121,11 +122,11 @@ func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
selW = selH
}
}
r.drawBorder(data, stride, w, h, bx1, by1, selW, selH)
r.drawDimensions(data, stride, w, h, bx1, by1, selW, selH)
r.drawBorder(data, stride, w, h, bx1, by1, selW, selH, format)
r.drawDimensions(data, stride, w, h, bx1, by1, selW, selH, format)
}
func (r *RegionSelector) drawHUD(data []byte, stride, bufW, bufH int) {
func (r *RegionSelector) drawHUD(data []byte, stride, bufW, bufH int, format uint32) {
if r.selection.dragging {
return
}
@@ -158,16 +159,16 @@ func (r *RegionSelector) drawHUD(data []byte, stride, bufW, bufH int) {
hudY := bufH - hudH - 20
r.fillRect(data, stride, bufW, bufH, hudX, hudY, hudW, hudH,
style.BackgroundR, style.BackgroundG, style.BackgroundB, style.BackgroundA)
style.BackgroundR, style.BackgroundG, style.BackgroundB, style.BackgroundA, format)
tx, ty := hudX+padding, hudY+padding
for i, item := range items {
r.drawText(data, stride, bufW, bufH, tx, ty, item.key,
style.AccentR, style.AccentG, style.AccentB)
style.AccentR, style.AccentG, style.AccentB, format)
tx += len(item.key) * (charW + 1)
r.drawText(data, stride, bufW, bufH, tx, ty, " "+item.desc,
style.TextR, style.TextG, style.TextB)
style.TextR, style.TextG, style.TextB, format)
tx += (1 + len(item.desc)) * (charW + 1)
if i < len(items)-1 {
@@ -176,17 +177,17 @@ func (r *RegionSelector) drawHUD(data []byte, stride, bufW, bufH int) {
}
}
func (r *RegionSelector) drawBorder(data []byte, stride, bufW, bufH, x, y, w, h int) {
func (r *RegionSelector) drawBorder(data []byte, stride, bufW, bufH, x, y, w, h int, format uint32) {
const thickness = 2
for i := 0; i < thickness; i++ {
r.drawHLine(data, stride, bufW, bufH, x-i, y-i, w+2*i)
r.drawHLine(data, stride, bufW, bufH, x-i, y+h+i-1, w+2*i)
r.drawVLine(data, stride, bufW, bufH, x-i, y-i, h+2*i)
r.drawVLine(data, stride, bufW, bufH, x+w+i-1, y-i, h+2*i)
r.drawHLine(data, stride, bufW, bufH, x-i, y-i, w+2*i, format)
r.drawHLine(data, stride, bufW, bufH, x-i, y+h+i-1, w+2*i, format)
r.drawVLine(data, stride, bufW, bufH, x-i, y-i, h+2*i, format)
r.drawVLine(data, stride, bufW, bufH, x+w+i-1, y-i, h+2*i, format)
}
}
func (r *RegionSelector) drawHLine(data []byte, stride, bufW, bufH, x, y, length int) {
func (r *RegionSelector) drawHLine(data []byte, stride, bufW, bufH, x, y, length int, _ uint32) {
if y < 0 || y >= bufH {
return
}
@@ -204,7 +205,7 @@ func (r *RegionSelector) drawHLine(data []byte, stride, bufW, bufH, x, y, length
}
}
func (r *RegionSelector) drawVLine(data []byte, stride, bufW, bufH, x, y, length int) {
func (r *RegionSelector) drawVLine(data []byte, stride, bufW, bufH, x, y, length int, _ uint32) {
if x < 0 || x >= bufW {
return
}
@@ -221,7 +222,7 @@ func (r *RegionSelector) drawVLine(data []byte, stride, bufW, bufH, x, y, length
}
}
func (r *RegionSelector) drawDimensions(data []byte, stride, bufW, bufH, x, y, w, h int) {
func (r *RegionSelector) drawDimensions(data []byte, stride, bufW, bufH, x, y, w, h int, format uint32) {
text := fmt.Sprintf("%dx%d", w, h)
const charW, charH = 8, 12
@@ -236,14 +237,19 @@ func (r *RegionSelector) drawDimensions(data []byte, stride, bufW, bufH, x, y, w
}
tx = clamp(tx, 0, bufW-textW)
r.fillRect(data, stride, bufW, bufH, tx-4, ty-2, textW+8, textH+4, 0, 0, 0, 200)
r.drawText(data, stride, bufW, bufH, tx, ty, text, 255, 255, 255)
r.fillRect(data, stride, bufW, bufH, tx-4, ty-2, textW+8, textH+4, 0, 0, 0, 200, format)
r.drawText(data, stride, bufW, bufH, tx, ty, text, 255, 255, 255, format)
}
func (r *RegionSelector) fillRect(data []byte, stride, bufW, bufH, x, y, w, h int, br, bg, bb, ba uint8) {
alpha := float64(ba) / 255.0
func (r *RegionSelector) fillRect(data []byte, stride, bufW, bufH, x, y, w, h int, cr, cg, cb, ca uint8, format uint32) {
alpha := float64(ca) / 255.0
invAlpha := 1.0 - alpha
c0, c2 := cb, cr
if format == uint32(FormatABGR8888) || format == uint32(FormatXBGR8888) {
c0, c2 = cr, cb
}
for py := y; py < y+h && py < bufH; py++ {
if py < 0 {
continue
@@ -256,26 +262,31 @@ func (r *RegionSelector) fillRect(data []byte, stride, bufW, bufH, x, y, w, h in
if off+3 >= len(data) {
continue
}
data[off+0] = uint8(float64(data[off+0])*invAlpha + float64(bb)*alpha)
data[off+1] = uint8(float64(data[off+1])*invAlpha + float64(bg)*alpha)
data[off+2] = uint8(float64(data[off+2])*invAlpha + float64(br)*alpha)
data[off+0] = uint8(float64(data[off+0])*invAlpha + float64(c0)*alpha)
data[off+1] = uint8(float64(data[off+1])*invAlpha + float64(cg)*alpha)
data[off+2] = uint8(float64(data[off+2])*invAlpha + float64(c2)*alpha)
data[off+3] = 255
}
}
}
func (r *RegionSelector) drawText(data []byte, stride, bufW, bufH, x, y int, text string, cr, cg, cb uint8) {
func (r *RegionSelector) drawText(data []byte, stride, bufW, bufH, x, y int, text string, cr, cg, cb uint8, format uint32) {
for i, ch := range text {
r.drawChar(data, stride, bufW, bufH, x+i*9, y, ch, cr, cg, cb)
r.drawChar(data, stride, bufW, bufH, x+i*9, y, ch, cr, cg, cb, format)
}
}
func (r *RegionSelector) drawChar(data []byte, stride, bufW, bufH, x, y int, ch rune, cr, cg, cb uint8) {
func (r *RegionSelector) drawChar(data []byte, stride, bufW, bufH, x, y int, ch rune, cr, cg, cb uint8, format uint32) {
glyph, ok := fontGlyphs[ch]
if !ok {
return
}
c0, c2 := cb, cr
if format == uint32(FormatABGR8888) || format == uint32(FormatXBGR8888) {
c0, c2 = cr, cb
}
for row := 0; row < 12; row++ {
py := y + row
if py < 0 || py >= bufH {
@@ -294,7 +305,7 @@ func (r *RegionSelector) drawChar(data []byte, stride, bufW, bufH, x, y int, ch
if off+3 >= len(data) {
continue
}
data[off], data[off+1], data[off+2], data[off+3] = cb, cg, cr, 255
data[off], data[off+1], data[off+2], data[off+3] = c0, cg, c2, 255
}
}
}