mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
screenshot/colorpicker: fix scaling, update go-wayland to fix object
destruction, fix hyprland window detection
This commit is contained in:
@@ -2,7 +2,6 @@ package colorpicker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
||||||
@@ -116,6 +115,11 @@ func (p *Picker) Run() (*Color, error) {
|
|||||||
return nil, fmt.Errorf("roundtrip: %w", err)
|
return nil, fmt.Errorf("roundtrip: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extra roundtrip to ensure pointer/keyboard from seat capabilities are registered
|
||||||
|
if err := p.roundtrip(); err != nil {
|
||||||
|
return nil, fmt.Errorf("roundtrip after seat: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := p.createSurfaces(); err != nil {
|
if err := p.createSurfaces(); err != nil {
|
||||||
return nil, fmt.Errorf("create surfaces: %w", err)
|
return nil, fmt.Errorf("create surfaces: %w", err)
|
||||||
}
|
}
|
||||||
@@ -405,15 +409,10 @@ func (p *Picker) createLayerSurface(output *Output) (*LayerSurface, error) {
|
|||||||
|
|
||||||
func (p *Picker) computeSurfaceScale(ls *LayerSurface) int32 {
|
func (p *Picker) computeSurfaceScale(ls *LayerSurface) int32 {
|
||||||
out := ls.output
|
out := ls.output
|
||||||
if out == nil || out.fractionalScale <= 0 {
|
if out == nil || out.scale <= 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
return out.scale
|
||||||
scale := int32(math.Ceil(out.fractionalScale))
|
|
||||||
if scale <= 0 {
|
|
||||||
scale = 1
|
|
||||||
}
|
|
||||||
return scale
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Picker) ensureShortcutsInhibitor(ls *LayerSurface) {
|
func (p *Picker) ensureShortcutsInhibitor(ls *LayerSurface) {
|
||||||
@@ -485,6 +484,13 @@ func (p *Picker) captureForSurface(ls *LayerSurface) {
|
|||||||
|
|
||||||
frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
|
frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
|
||||||
ls.state.OnScreencopyReady()
|
ls.state.OnScreencopyReady()
|
||||||
|
|
||||||
|
logicalW, _ := ls.state.LogicalSize()
|
||||||
|
screenBuf := ls.state.ScreenBuffer()
|
||||||
|
if logicalW > 0 && screenBuf != nil {
|
||||||
|
ls.output.fractionalScale = float64(screenBuf.Width) / float64(logicalW)
|
||||||
|
}
|
||||||
|
|
||||||
scale := p.computeSurfaceScale(ls)
|
scale := p.computeSurfaceScale(ls)
|
||||||
ls.state.SetScale(scale)
|
ls.state.SetScale(scale)
|
||||||
frame.Destroy()
|
frame.Destroy()
|
||||||
@@ -545,18 +551,17 @@ func (p *Picker) redrawSurface(ls *LayerSurface) {
|
|||||||
logicalH = int(ls.output.height)
|
logicalH = int(ls.output.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
scale := ls.state.Scale()
|
|
||||||
if scale <= 0 {
|
|
||||||
scale = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if ls.viewport != nil {
|
if ls.viewport != nil {
|
||||||
srcW := float64(renderBuf.Width) / float64(scale)
|
_ = ls.wlSurface.SetBufferScale(1)
|
||||||
srcH := float64(renderBuf.Height) / float64(scale)
|
_ = ls.viewport.SetSource(0, 0, float64(renderBuf.Width), float64(renderBuf.Height))
|
||||||
_ = ls.viewport.SetSource(0, 0, srcW, srcH)
|
|
||||||
_ = ls.viewport.SetDestination(int32(logicalW), int32(logicalH))
|
_ = ls.viewport.SetDestination(int32(logicalW), int32(logicalH))
|
||||||
|
} else {
|
||||||
|
bufferScale := ls.output.scale
|
||||||
|
if bufferScale <= 0 {
|
||||||
|
bufferScale = 1
|
||||||
|
}
|
||||||
|
_ = ls.wlSurface.SetBufferScale(bufferScale)
|
||||||
}
|
}
|
||||||
_ = ls.wlSurface.SetBufferScale(scale)
|
|
||||||
_ = ls.wlSurface.Attach(wlBuffer, 0, 0)
|
_ = ls.wlSurface.Attach(wlBuffer, 0, 0)
|
||||||
_ = ls.wlSurface.Damage(0, 0, int32(logicalW), int32(logicalH))
|
_ = ls.wlSurface.Damage(0, 0, int32(logicalW), int32(logicalH))
|
||||||
_ = ls.wlSurface.Commit()
|
_ = ls.wlSurface.Commit()
|
||||||
@@ -581,17 +586,19 @@ func (p *Picker) setupInput() {
|
|||||||
p.seat.SetCapabilitiesHandler(func(e client.SeatCapabilitiesEvent) {
|
p.seat.SetCapabilitiesHandler(func(e client.SeatCapabilitiesEvent) {
|
||||||
if e.Capabilities&uint32(client.SeatCapabilityPointer) != 0 && p.pointer == nil {
|
if e.Capabilities&uint32(client.SeatCapabilityPointer) != 0 && p.pointer == nil {
|
||||||
pointer, err := p.seat.GetPointer()
|
pointer, err := p.seat.GetPointer()
|
||||||
if err == nil {
|
if err != nil {
|
||||||
p.pointer = pointer
|
return
|
||||||
p.setupPointerHandlers()
|
|
||||||
}
|
}
|
||||||
|
p.pointer = pointer
|
||||||
|
p.setupPointerHandlers()
|
||||||
}
|
}
|
||||||
if e.Capabilities&uint32(client.SeatCapabilityKeyboard) != 0 && p.keyboard == nil {
|
if e.Capabilities&uint32(client.SeatCapabilityKeyboard) != 0 && p.keyboard == nil {
|
||||||
keyboard, err := p.seat.GetKeyboard()
|
keyboard, err := p.seat.GetKeyboard()
|
||||||
if err == nil {
|
if err != nil {
|
||||||
p.keyboard = keyboard
|
return
|
||||||
p.setupKeyboardHandlers()
|
|
||||||
}
|
}
|
||||||
|
p.keyboard = keyboard
|
||||||
|
p.setupKeyboardHandlers()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,12 +269,17 @@ func (s *SurfaceState) Redraw() *ShmBuffer {
|
|||||||
px = clamp(px, 0, dst.Width-1)
|
px = clamp(px, 0, dst.Width-1)
|
||||||
py = clamp(py, 0, dst.Height-1)
|
py = clamp(py, 0, dst.Height-1)
|
||||||
|
|
||||||
picked := GetPixelColorWithFormat(s.screenBuf, px, py, s.screenFormat)
|
sampleY := py
|
||||||
|
if s.yInverted {
|
||||||
|
sampleY = s.screenBuf.Height - 1 - py
|
||||||
|
}
|
||||||
|
|
||||||
drawMagnifier(
|
picked := GetPixelColorWithFormat(s.screenBuf, px, sampleY, s.screenFormat)
|
||||||
|
|
||||||
|
drawMagnifierWithInversion(
|
||||||
dst.Data(), dst.Stride, dst.Width, dst.Height,
|
dst.Data(), dst.Stride, dst.Width, dst.Height,
|
||||||
s.screenBuf.Data(), s.screenBuf.Stride, s.screenBuf.Width, s.screenBuf.Height,
|
s.screenBuf.Data(), s.screenBuf.Stride, s.screenBuf.Width, s.screenBuf.Height,
|
||||||
px, py, picked,
|
px, py, picked, s.yInverted,
|
||||||
)
|
)
|
||||||
|
|
||||||
drawColorPreview(dst.Data(), dst.Stride, dst.Width, dst.Height, px, py, picked, s.displayFormat, s.lowercase)
|
drawColorPreview(dst.Data(), dst.Stride, dst.Width, dst.Height, px, py, picked, s.displayFormat, s.lowercase)
|
||||||
@@ -379,11 +384,12 @@ func blendColors(bg, fg Color, alpha float64) Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func drawMagnifier(
|
func drawMagnifierWithInversion(
|
||||||
dst []byte, dstStride, dstW, dstH int,
|
dst []byte, dstStride, dstW, dstH int,
|
||||||
src []byte, srcStride, srcW, srcH int,
|
src []byte, srcStride, srcW, srcH int,
|
||||||
cx, cy int,
|
cx, cy int,
|
||||||
borderColor Color,
|
borderColor Color,
|
||||||
|
yInverted bool,
|
||||||
) {
|
) {
|
||||||
if dstW <= 0 || dstH <= 0 || srcW <= 0 || srcH <= 0 {
|
if dstW <= 0 || dstH <= 0 || srcW <= 0 || srcH <= 0 {
|
||||||
return
|
return
|
||||||
@@ -439,10 +445,11 @@ func drawMagnifier(
|
|||||||
finalColor = blendColors(bgColor, borderColor, alpha)
|
finalColor = blendColors(bgColor, borderColor, alpha)
|
||||||
|
|
||||||
case dist > innerRadius:
|
case dist > innerRadius:
|
||||||
if dist > outerRadiusF-aaWidth {
|
switch {
|
||||||
|
case dist > outerRadiusF-aaWidth:
|
||||||
alpha := clampF((outerRadiusF-dist)/aaWidth, 0, 1)
|
alpha := clampF((outerRadiusF-dist)/aaWidth, 0, 1)
|
||||||
finalColor = blendColors(borderColor, borderColor, alpha)
|
finalColor = blendColors(borderColor, borderColor, alpha)
|
||||||
} else if dist < innerRadius+aaWidth {
|
case dist < innerRadius+aaWidth:
|
||||||
alpha := clampF((dist-innerRadius)/aaWidth, 0, 1)
|
alpha := clampF((dist-innerRadius)/aaWidth, 0, 1)
|
||||||
fx := float64(dx) / zoom
|
fx := float64(dx) / zoom
|
||||||
fy := float64(dy) / zoom
|
fy := float64(dy) / zoom
|
||||||
@@ -450,6 +457,9 @@ func drawMagnifier(
|
|||||||
sy := cy + int(math.Round(fy))
|
sy := cy + int(math.Round(fy))
|
||||||
sx = clamp(sx, 0, srcW-1)
|
sx = clamp(sx, 0, srcW-1)
|
||||||
sy = clamp(sy, 0, srcH-1)
|
sy = clamp(sy, 0, srcH-1)
|
||||||
|
if yInverted {
|
||||||
|
sy = srcH - 1 - sy
|
||||||
|
}
|
||||||
srcOff := sy*srcStride + sx*4
|
srcOff := sy*srcStride + sx*4
|
||||||
if srcOff+4 <= len(src) {
|
if srcOff+4 <= len(src) {
|
||||||
magColor := Color{B: src[srcOff+0], G: src[srcOff+1], R: src[srcOff+2], A: 255}
|
magColor := Color{B: src[srcOff+0], G: src[srcOff+1], R: src[srcOff+2], A: 255}
|
||||||
@@ -457,7 +467,7 @@ func drawMagnifier(
|
|||||||
} else {
|
} else {
|
||||||
finalColor = borderColor
|
finalColor = borderColor
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
finalColor = borderColor
|
finalColor = borderColor
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,6 +478,9 @@ func drawMagnifier(
|
|||||||
sy := cy + int(math.Round(fy))
|
sy := cy + int(math.Round(fy))
|
||||||
sx = clamp(sx, 0, srcW-1)
|
sx = clamp(sx, 0, srcW-1)
|
||||||
sy = clamp(sy, 0, srcH-1)
|
sy = clamp(sy, 0, srcH-1)
|
||||||
|
if yInverted {
|
||||||
|
sy = srcH - 1 - sy
|
||||||
|
}
|
||||||
srcOff := sy*srcStride + sx*4
|
srcOff := sy*srcStride + sx*4
|
||||||
if srcOff+4 <= len(src) {
|
if srcOff+4 <= len(src) {
|
||||||
finalColor = Color{B: src[srcOff+0], G: src[srcOff+1], R: src[srcOff+2], A: 255}
|
finalColor = Color{B: src[srcOff+0], G: src[srcOff+1], R: src[srcOff+2], A: 255}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func NewZdwlIpcManagerV2(ctx *client.Context) *ZdwlIpcManagerV2 {
|
|||||||
// Indicates that the client will not the dwl_ipc_manager object anymore.
|
// Indicates that the client will not the dwl_ipc_manager object anymore.
|
||||||
// Objects created through this instance are not affected.
|
// Objects created through this instance are not affected.
|
||||||
func (i *ZdwlIpcManagerV2) Release() error {
|
func (i *ZdwlIpcManagerV2) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -188,7 +188,7 @@ func NewZdwlIpcOutputV2(ctx *client.Context) *ZdwlIpcOutputV2 {
|
|||||||
//
|
//
|
||||||
// Indicates to that the client no longer needs this dwl_ipc_output.
|
// Indicates to that the client no longer needs this dwl_ipc_output.
|
||||||
func (i *ZdwlIpcOutputV2) Release() error {
|
func (i *ZdwlIpcOutputV2) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ func (i *ExtWorkspaceManagerV1) Stop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *ExtWorkspaceManagerV1) Destroy() error {
|
func (i *ExtWorkspaceManagerV1) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,7 +385,7 @@ func (i *ExtWorkspaceGroupHandleV1) CreateWorkspace(workspace string) error {
|
|||||||
// use the workspace group object any more or after the removed event to finalize
|
// use the workspace group object any more or after the removed event to finalize
|
||||||
// the destruction of the object.
|
// the destruction of the object.
|
||||||
func (i *ExtWorkspaceGroupHandleV1) Destroy() error {
|
func (i *ExtWorkspaceGroupHandleV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -655,7 +655,7 @@ func NewExtWorkspaceHandleV1(ctx *client.Context) *ExtWorkspaceHandleV1 {
|
|||||||
// use the workspace object any more or after the remove event to finalize
|
// use the workspace object any more or after the remove event to finalize
|
||||||
// the destruction of the object.
|
// the destruction of the object.
|
||||||
func (i *ExtWorkspaceHandleV1) Destroy() error {
|
func (i *ExtWorkspaceHandleV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func NewZwpKeyboardShortcutsInhibitManagerV1(ctx *client.Context) *ZwpKeyboardSh
|
|||||||
//
|
//
|
||||||
// Destroy the keyboard shortcuts inhibitor manager.
|
// Destroy the keyboard shortcuts inhibitor manager.
|
||||||
func (i *ZwpKeyboardShortcutsInhibitManagerV1) Destroy() error {
|
func (i *ZwpKeyboardShortcutsInhibitManagerV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -218,7 +218,7 @@ func NewZwpKeyboardShortcutsInhibitorV1(ctx *client.Context) *ZwpKeyboardShortcu
|
|||||||
//
|
//
|
||||||
// Remove the keyboard shortcuts inhibitor from the associated wl_surface.
|
// Remove the keyboard shortcuts inhibitor from the associated wl_surface.
|
||||||
func (i *ZwpKeyboardShortcutsInhibitorV1) Destroy() error {
|
func (i *ZwpKeyboardShortcutsInhibitorV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func (i *ZwlrGammaControlManagerV1) GetGammaControl(output *client.Output) (*Zwl
|
|||||||
// All objects created by the manager will still remain valid, until their
|
// All objects created by the manager will still remain valid, until their
|
||||||
// appropriate destroy request has been called.
|
// appropriate destroy request has been called.
|
||||||
func (i *ZwlrGammaControlManagerV1) Destroy() error {
|
func (i *ZwlrGammaControlManagerV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -169,7 +169,7 @@ func (i *ZwlrGammaControlV1) SetGamma(fd int) error {
|
|||||||
// Destroys the gamma control object. If the object is still valid, this
|
// Destroys the gamma control object. If the object is still valid, this
|
||||||
// restores the original gamma tables.
|
// restores the original gamma tables.
|
||||||
func (i *ZwlrGammaControlV1) Destroy() error {
|
func (i *ZwlrGammaControlV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ func (i *ZwlrLayerShellV1) GetLayerSurface(surface *client.Surface, output *clie
|
|||||||
// object any more. Objects that have been created through this instance
|
// object any more. Objects that have been created through this instance
|
||||||
// are not affected.
|
// are not affected.
|
||||||
func (i *ZwlrLayerShellV1) Destroy() error {
|
func (i *ZwlrLayerShellV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -509,7 +509,7 @@ func (i *ZwlrLayerSurfaceV1) AckConfigure(serial uint32) error {
|
|||||||
//
|
//
|
||||||
// This request destroys the layer surface.
|
// This request destroys the layer surface.
|
||||||
func (i *ZwlrLayerSurfaceV1) Destroy() error {
|
func (i *ZwlrLayerSurfaceV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 7
|
const opcode = 7
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ func (i *ZwlrOutputManagerV1) Stop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *ZwlrOutputManagerV1) Destroy() error {
|
func (i *ZwlrOutputManagerV1) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +334,7 @@ func NewZwlrOutputHeadV1(ctx *client.Context) *ZwlrOutputHeadV1 {
|
|||||||
// This request indicates that the client will no longer use this head
|
// This request indicates that the client will no longer use this head
|
||||||
// object.
|
// object.
|
||||||
func (i *ZwlrOutputHeadV1) Release() error {
|
func (i *ZwlrOutputHeadV1) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -879,7 +879,7 @@ func NewZwlrOutputModeV1(ctx *client.Context) *ZwlrOutputModeV1 {
|
|||||||
// This request indicates that the client will no longer use this mode
|
// This request indicates that the client will no longer use this mode
|
||||||
// object.
|
// object.
|
||||||
func (i *ZwlrOutputModeV1) Release() error {
|
func (i *ZwlrOutputModeV1) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -1132,7 +1132,7 @@ func (i *ZwlrOutputConfigurationV1) Test() error {
|
|||||||
// This request also destroys wlr_output_configuration_head objects created
|
// This request also destroys wlr_output_configuration_head objects created
|
||||||
// via this object.
|
// via this object.
|
||||||
func (i *ZwlrOutputConfigurationV1) Destroy() error {
|
func (i *ZwlrOutputConfigurationV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 4
|
const opcode = 4
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -1415,7 +1415,7 @@ func (i *ZwlrOutputConfigurationHeadV1) SetAdaptiveSync(state uint32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *ZwlrOutputConfigurationHeadV1) Destroy() error {
|
func (i *ZwlrOutputConfigurationHeadV1) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func (i *ZwlrOutputPowerManagerV1) GetOutputPower(output *client.Output) (*ZwlrO
|
|||||||
// All objects created by the manager will still remain valid, until their
|
// All objects created by the manager will still remain valid, until their
|
||||||
// appropriate destroy request has been called.
|
// appropriate destroy request has been called.
|
||||||
func (i *ZwlrOutputPowerManagerV1) Destroy() error {
|
func (i *ZwlrOutputPowerManagerV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -143,7 +143,7 @@ func (i *ZwlrOutputPowerV1) SetMode(mode uint32) error {
|
|||||||
//
|
//
|
||||||
// Destroys the output power management mode control object.
|
// Destroys the output power management mode control object.
|
||||||
func (i *ZwlrOutputPowerV1) Destroy() error {
|
func (i *ZwlrOutputPowerV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ func (i *ZwlrScreencopyManagerV1) CaptureOutputRegion(overlayCursor int32, outpu
|
|||||||
// All objects created by the manager will still remain valid, until their
|
// All objects created by the manager will still remain valid, until their
|
||||||
// appropriate destroy request has been called.
|
// appropriate destroy request has been called.
|
||||||
func (i *ZwlrScreencopyManagerV1) Destroy() error {
|
func (i *ZwlrScreencopyManagerV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 2
|
const opcode = 2
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -219,7 +219,7 @@ func (i *ZwlrScreencopyFrameV1) Copy(buffer *client.Buffer) error {
|
|||||||
//
|
//
|
||||||
// Destroys the frame. This request can be sent at any time by the client.
|
// Destroys the frame. This request can be sent at any time by the client.
|
||||||
func (i *ZwlrScreencopyFrameV1) Destroy() error {
|
func (i *ZwlrScreencopyFrameV1) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func NewWpViewporter(ctx *client.Context) *WpViewporter {
|
|||||||
// protocol object anymore. This does not affect any other objects,
|
// protocol object anymore. This does not affect any other objects,
|
||||||
// wp_viewport objects included.
|
// wp_viewport objects included.
|
||||||
func (i *WpViewporter) Destroy() error {
|
func (i *WpViewporter) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -267,7 +267,7 @@ func NewWpViewport(ctx *client.Context) *WpViewport {
|
|||||||
// The associated wl_surface's crop and scale state is removed.
|
// The associated wl_surface's crop and scale state is removed.
|
||||||
// The change is applied on the next wl_surface.commit.
|
// The change is applied on the next wl_surface.commit.
|
||||||
func (i *WpViewport) Destroy() error {
|
func (i *WpViewport) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
|
|||||||
@@ -12,13 +12,44 @@ type Compositor int
|
|||||||
const (
|
const (
|
||||||
CompositorUnknown Compositor = iota
|
CompositorUnknown Compositor = iota
|
||||||
CompositorHyprland
|
CompositorHyprland
|
||||||
|
CompositorSway
|
||||||
|
CompositorNiri
|
||||||
|
CompositorDWL
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var detectedCompositor Compositor = -1
|
||||||
|
|
||||||
func DetectCompositor() Compositor {
|
func DetectCompositor() Compositor {
|
||||||
if os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") != "" {
|
if detectedCompositor >= 0 {
|
||||||
return CompositorHyprland
|
return detectedCompositor
|
||||||
}
|
}
|
||||||
return CompositorUnknown
|
|
||||||
|
hyprlandSig := os.Getenv("HYPRLAND_INSTANCE_SIGNATURE")
|
||||||
|
niriSocket := os.Getenv("NIRI_SOCKET")
|
||||||
|
swaySocket := os.Getenv("SWAYSOCK")
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case niriSocket != "":
|
||||||
|
if _, err := os.Stat(niriSocket); err == nil {
|
||||||
|
detectedCompositor = CompositorNiri
|
||||||
|
return detectedCompositor
|
||||||
|
}
|
||||||
|
case swaySocket != "":
|
||||||
|
if _, err := os.Stat(swaySocket); err == nil {
|
||||||
|
detectedCompositor = CompositorSway
|
||||||
|
return detectedCompositor
|
||||||
|
}
|
||||||
|
case hyprlandSig != "":
|
||||||
|
detectedCompositor = CompositorHyprland
|
||||||
|
return detectedCompositor
|
||||||
|
}
|
||||||
|
|
||||||
|
detectedCompositor = CompositorUnknown
|
||||||
|
return detectedCompositor
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetCompositorDWL() {
|
||||||
|
detectedCompositor = CompositorDWL
|
||||||
}
|
}
|
||||||
|
|
||||||
type WindowGeometry struct {
|
type WindowGeometry struct {
|
||||||
@@ -29,13 +60,11 @@ type WindowGeometry struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetActiveWindow() (*WindowGeometry, error) {
|
func GetActiveWindow() (*WindowGeometry, error) {
|
||||||
compositor := DetectCompositor()
|
switch DetectCompositor() {
|
||||||
|
|
||||||
switch compositor {
|
|
||||||
case CompositorHyprland:
|
case CompositorHyprland:
|
||||||
return getHyprlandActiveWindow()
|
return getHyprlandActiveWindow()
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("window capture requires Hyprland (other compositors not yet supported)")
|
return nil, fmt.Errorf("window capture requires Hyprland")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,8 +74,7 @@ type hyprlandWindow struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getHyprlandActiveWindow() (*WindowGeometry, error) {
|
func getHyprlandActiveWindow() (*WindowGeometry, error) {
|
||||||
cmd := exec.Command("hyprctl", "-j", "activewindow")
|
output, err := exec.Command("hyprctl", "-j", "activewindow").Output()
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("hyprctl activewindow: %w", err)
|
return nil, fmt.Errorf("hyprctl activewindow: %w", err)
|
||||||
}
|
}
|
||||||
@@ -67,3 +95,144 @@ func getHyprlandActiveWindow() (*WindowGeometry, error) {
|
|||||||
Height: win.Size[1],
|
Height: win.Size[1],
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type hyprlandMonitor struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
X int32 `json:"x"`
|
||||||
|
Y int32 `json:"y"`
|
||||||
|
Width int32 `json:"width"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
Scale float64 `json:"scale"`
|
||||||
|
Focused bool `json:"focused"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHyprlandMonitorScale(name string) float64 {
|
||||||
|
output, err := exec.Command("hyprctl", "-j", "monitors").Output()
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var monitors []hyprlandMonitor
|
||||||
|
if err := json.Unmarshal(output, &monitors); err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range monitors {
|
||||||
|
if m.Name == name {
|
||||||
|
return m.Scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHyprlandFocusedMonitor() string {
|
||||||
|
output, err := exec.Command("hyprctl", "-j", "monitors").Output()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var monitors []hyprlandMonitor
|
||||||
|
if err := json.Unmarshal(output, &monitors); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range monitors {
|
||||||
|
if m.Focused {
|
||||||
|
return m.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHyprlandMonitorGeometry(name string) (x, y, w, h int32, ok bool) {
|
||||||
|
output, err := exec.Command("hyprctl", "-j", "monitors").Output()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
var monitors []hyprlandMonitor
|
||||||
|
if err := json.Unmarshal(output, &monitors); err != nil {
|
||||||
|
return 0, 0, 0, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range monitors {
|
||||||
|
if m.Name == name {
|
||||||
|
logicalW := int32(float64(m.Width) / m.Scale)
|
||||||
|
logicalH := int32(float64(m.Height) / m.Scale)
|
||||||
|
return m.X, m.Y, logicalW, logicalH, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, 0, 0, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
type swayWorkspace struct {
|
||||||
|
Output string `json:"output"`
|
||||||
|
Focused bool `json:"focused"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSwayFocusedMonitor() string {
|
||||||
|
output, err := exec.Command("swaymsg", "-t", "get_workspaces").Output()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var workspaces []swayWorkspace
|
||||||
|
if err := json.Unmarshal(output, &workspaces); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ws := range workspaces {
|
||||||
|
if ws.Focused {
|
||||||
|
return ws.Output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type niriWorkspace struct {
|
||||||
|
Output string `json:"output"`
|
||||||
|
IsFocused bool `json:"is_focused"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNiriFocusedMonitor() string {
|
||||||
|
output, err := exec.Command("niri", "msg", "-j", "workspaces").Output()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var workspaces []niriWorkspace
|
||||||
|
if err := json.Unmarshal(output, &workspaces); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ws := range workspaces {
|
||||||
|
if ws.IsFocused {
|
||||||
|
return ws.Output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var dwlActiveOutput string
|
||||||
|
|
||||||
|
func SetDWLActiveOutput(name string) {
|
||||||
|
dwlActiveOutput = name
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDWLFocusedMonitor() string {
|
||||||
|
return dwlActiveOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFocusedMonitor() string {
|
||||||
|
switch DetectCompositor() {
|
||||||
|
case CompositorHyprland:
|
||||||
|
return getHyprlandFocusedMonitor()
|
||||||
|
case CompositorSway:
|
||||||
|
return getSwayFocusedMonitor()
|
||||||
|
case CompositorNiri:
|
||||||
|
return getNiriFocusedMonitor()
|
||||||
|
case CompositorDWL:
|
||||||
|
return getDWLFocusedMonitor()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -251,9 +251,10 @@ func (r *RegionSelector) handleGlobal(e client.RegistryGlobalEvent) {
|
|||||||
if err := r.registry.Bind(e.Name, e.Interface, version, output); err == nil {
|
if err := r.registry.Bind(e.Name, e.Interface, version, output); err == nil {
|
||||||
r.outputsMu.Lock()
|
r.outputsMu.Lock()
|
||||||
r.outputs[e.Name] = &WaylandOutput{
|
r.outputs[e.Name] = &WaylandOutput{
|
||||||
wlOutput: output,
|
wlOutput: output,
|
||||||
globalName: e.Name,
|
globalName: e.Name,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
|
fractionalScale: 1.0,
|
||||||
}
|
}
|
||||||
r.outputsMu.Unlock()
|
r.outputsMu.Unlock()
|
||||||
r.setupOutputHandlers(e.Name, output)
|
r.setupOutputHandlers(e.Name, output)
|
||||||
@@ -320,6 +321,7 @@ func (r *RegionSelector) setupOutputHandlers(name uint32, output *client.Output)
|
|||||||
r.outputsMu.Lock()
|
r.outputsMu.Lock()
|
||||||
if o, ok := r.outputs[name]; ok {
|
if o, ok := r.outputs[name]; ok {
|
||||||
o.scale = e.Factor
|
o.scale = e.Factor
|
||||||
|
o.fractionalScale = float64(e.Factor)
|
||||||
}
|
}
|
||||||
r.outputsMu.Unlock()
|
r.outputsMu.Unlock()
|
||||||
})
|
})
|
||||||
@@ -607,6 +609,10 @@ func (r *RegionSelector) captureForSurface(os *OutputSurface) {
|
|||||||
os.screenFormat = pc.format
|
os.screenFormat = pc.format
|
||||||
os.yInverted = pc.yInverted
|
os.yInverted = pc.yInverted
|
||||||
|
|
||||||
|
if os.logicalW > 0 && os.screenBuf != nil {
|
||||||
|
os.output.fractionalScale = float64(os.screenBuf.Width) / float64(os.logicalW)
|
||||||
|
}
|
||||||
|
|
||||||
r.initRenderBuffer(os)
|
r.initRenderBuffer(os)
|
||||||
r.applyPreSelection(os)
|
r.applyPreSelection(os)
|
||||||
r.redrawSurface(os)
|
r.redrawSurface(os)
|
||||||
@@ -713,19 +719,17 @@ func (r *RegionSelector) redrawSurface(os *OutputSurface) {
|
|||||||
// Draw overlay (dimming + selection) into this slot
|
// Draw overlay (dimming + selection) into this slot
|
||||||
r.drawOverlay(os, slot.shm)
|
r.drawOverlay(os, slot.shm)
|
||||||
|
|
||||||
// Attach and commit (viewport only needs to be set once, but it's cheap)
|
|
||||||
scale := os.output.scale
|
|
||||||
if scale <= 0 {
|
|
||||||
scale = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.viewport != nil {
|
if os.viewport != nil {
|
||||||
srcW := float64(slot.shm.Width) / float64(scale)
|
_ = os.wlSurface.SetBufferScale(1)
|
||||||
srcH := float64(slot.shm.Height) / float64(scale)
|
_ = os.viewport.SetSource(0, 0, float64(slot.shm.Width), float64(slot.shm.Height))
|
||||||
_ = os.viewport.SetSource(0, 0, srcW, srcH)
|
|
||||||
_ = os.viewport.SetDestination(int32(os.logicalW), int32(os.logicalH))
|
_ = os.viewport.SetDestination(int32(os.logicalW), int32(os.logicalH))
|
||||||
|
} else {
|
||||||
|
bufferScale := os.output.scale
|
||||||
|
if bufferScale <= 0 {
|
||||||
|
bufferScale = 1
|
||||||
|
}
|
||||||
|
_ = os.wlSurface.SetBufferScale(bufferScale)
|
||||||
}
|
}
|
||||||
_ = os.wlSurface.SetBufferScale(scale)
|
|
||||||
|
|
||||||
_ = os.wlSurface.Attach(slot.wlBuf, 0, 0)
|
_ = os.wlSurface.Attach(slot.wlBuf, 0, 0)
|
||||||
_ = os.wlSurface.Damage(0, 0, int32(os.logicalW), int32(os.logicalH))
|
_ = os.wlSurface.Damage(0, 0, int32(os.logicalW), int32(os.logicalH))
|
||||||
|
|||||||
@@ -223,16 +223,23 @@ func (r *RegionSelector) finishSelection() {
|
|||||||
dstData := cropped.Data()
|
dstData := cropped.Data()
|
||||||
for y := 0; y < h; y++ {
|
for y := 0; y < h; y++ {
|
||||||
srcY := by1 + y
|
srcY := by1 + y
|
||||||
if srcY >= srcBuf.Height {
|
if os.yInverted {
|
||||||
break
|
srcY = srcBuf.Height - 1 - (by1 + y)
|
||||||
|
}
|
||||||
|
if srcY < 0 || srcY >= srcBuf.Height {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dstY := y
|
||||||
|
if os.yInverted {
|
||||||
|
dstY = h - 1 - y
|
||||||
}
|
}
|
||||||
for x := 0; x < w; x++ {
|
for x := 0; x < w; x++ {
|
||||||
srcX := bx1 + x
|
srcX := bx1 + x
|
||||||
if srcX >= srcBuf.Width {
|
if srcX < 0 || srcX >= srcBuf.Width {
|
||||||
break
|
continue
|
||||||
}
|
}
|
||||||
si := srcY*srcBuf.Stride + srcX*4
|
si := srcY*srcBuf.Stride + srcX*4
|
||||||
di := y*cropped.Stride + x*4
|
di := dstY*cropped.Stride + x*4
|
||||||
if si+3 < len(srcData) && di+3 < len(dstData) {
|
if si+3 < len(srcData) && di+3 < len(dstData) {
|
||||||
dstData[di+0] = srcData[si+0]
|
dstData[di+0] = srcData[si+0]
|
||||||
dstData[di+1] = srcData[si+1]
|
dstData[di+1] = srcData[si+1]
|
||||||
|
|||||||
@@ -11,14 +11,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type WaylandOutput struct {
|
type WaylandOutput struct {
|
||||||
wlOutput *client.Output
|
wlOutput *client.Output
|
||||||
globalName uint32
|
globalName uint32
|
||||||
name string
|
name string
|
||||||
x, y int32
|
x, y int32
|
||||||
width int32
|
width int32
|
||||||
height int32
|
height int32
|
||||||
scale int32
|
scale int32
|
||||||
transform int32
|
fractionalScale float64
|
||||||
|
transform int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type CaptureResult struct {
|
type CaptureResult struct {
|
||||||
@@ -139,6 +140,10 @@ func (s *Screenshoter) captureWindow() (*CaptureResult, error) {
|
|||||||
return nil, fmt.Errorf("could not find output for window")
|
return nil, fmt.Errorf("could not find output for window")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if DetectCompositor() == CompositorHyprland {
|
||||||
|
return s.captureAndCrop(output, region)
|
||||||
|
}
|
||||||
|
|
||||||
return s.captureRegionOnOutput(output, region)
|
return s.captureRegionOnOutput(output, region)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,33 +186,8 @@ func (s *Screenshoter) captureOutput(name string) (*CaptureResult, error) {
|
|||||||
func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) {
|
func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) {
|
||||||
s.outputsMu.Lock()
|
s.outputsMu.Lock()
|
||||||
outputs := make([]*WaylandOutput, 0, len(s.outputs))
|
outputs := make([]*WaylandOutput, 0, len(s.outputs))
|
||||||
var minX, minY, maxX, maxY int32
|
|
||||||
first := true
|
|
||||||
|
|
||||||
for _, o := range s.outputs {
|
for _, o := range s.outputs {
|
||||||
outputs = append(outputs, o)
|
outputs = append(outputs, o)
|
||||||
right := o.x + o.width
|
|
||||||
bottom := o.y + o.height
|
|
||||||
|
|
||||||
if first {
|
|
||||||
minX, minY = o.x, o.y
|
|
||||||
maxX, maxY = right, bottom
|
|
||||||
first = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if o.x < minX {
|
|
||||||
minX = o.x
|
|
||||||
}
|
|
||||||
if o.y < minY {
|
|
||||||
minY = o.y
|
|
||||||
}
|
|
||||||
if right > maxX {
|
|
||||||
maxX = right
|
|
||||||
}
|
|
||||||
if bottom > maxY {
|
|
||||||
maxY = bottom
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
s.outputsMu.Unlock()
|
s.outputsMu.Unlock()
|
||||||
|
|
||||||
@@ -219,18 +199,18 @@ func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) {
|
|||||||
return s.captureWholeOutput(outputs[0])
|
return s.captureWholeOutput(outputs[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
totalW := maxX - minX
|
// Capture all outputs first to get actual buffer sizes
|
||||||
totalH := maxY - minY
|
type capturedOutput struct {
|
||||||
|
output *WaylandOutput
|
||||||
compositeStride := int(totalW) * 4
|
result *CaptureResult
|
||||||
composite, err := CreateShmBuffer(int(totalW), int(totalH), compositeStride)
|
physX int
|
||||||
if err != nil {
|
physY int
|
||||||
return nil, fmt.Errorf("create composite buffer: %w", err)
|
|
||||||
}
|
}
|
||||||
|
captured := make([]capturedOutput, 0, len(outputs))
|
||||||
|
|
||||||
composite.Clear()
|
var minX, minY, maxX, maxY int
|
||||||
|
first := true
|
||||||
|
|
||||||
var format uint32
|
|
||||||
for _, output := range outputs {
|
for _, output := range outputs {
|
||||||
result, err := s.captureWholeOutput(output)
|
result, err := s.captureWholeOutput(output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -238,16 +218,88 @@ func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == 0 {
|
outX, outY := output.x, output.y
|
||||||
format = result.Format
|
scale := float64(output.scale)
|
||||||
|
if DetectCompositor() == CompositorHyprland {
|
||||||
|
if hx, hy, _, _, ok := GetHyprlandMonitorGeometry(output.name); ok {
|
||||||
|
outX, outY = hx, hy
|
||||||
|
}
|
||||||
|
if s := GetHyprlandMonitorScale(output.name); s > 0 {
|
||||||
|
scale = s
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.blitBuffer(composite, result.Buffer, int(output.x-minX), int(output.y-minY), result.YInverted)
|
if scale <= 0 {
|
||||||
result.Buffer.Close()
|
scale = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
physX := int(float64(outX) * scale)
|
||||||
|
physY := int(float64(outY) * scale)
|
||||||
|
|
||||||
|
captured = append(captured, capturedOutput{
|
||||||
|
output: output,
|
||||||
|
result: result,
|
||||||
|
physX: physX,
|
||||||
|
physY: physY,
|
||||||
|
})
|
||||||
|
|
||||||
|
right := physX + result.Buffer.Width
|
||||||
|
bottom := physY + result.Buffer.Height
|
||||||
|
|
||||||
|
if first {
|
||||||
|
minX, minY = physX, physY
|
||||||
|
maxX, maxY = right, bottom
|
||||||
|
first = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if physX < minX {
|
||||||
|
minX = physX
|
||||||
|
}
|
||||||
|
if physY < minY {
|
||||||
|
minY = physY
|
||||||
|
}
|
||||||
|
if right > maxX {
|
||||||
|
maxX = right
|
||||||
|
}
|
||||||
|
if bottom > maxY {
|
||||||
|
maxY = bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(captured) == 0 {
|
||||||
|
return nil, fmt.Errorf("failed to capture any outputs")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(captured) == 1 {
|
||||||
|
return captured[0].result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
totalW := maxX - minX
|
||||||
|
totalH := maxY - minY
|
||||||
|
|
||||||
|
compositeStride := totalW * 4
|
||||||
|
composite, err := CreateShmBuffer(totalW, totalH, compositeStride)
|
||||||
|
if err != nil {
|
||||||
|
for _, c := range captured {
|
||||||
|
c.result.Buffer.Close()
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("create composite buffer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
composite.Clear()
|
||||||
|
|
||||||
|
var format uint32
|
||||||
|
for _, c := range captured {
|
||||||
|
if format == 0 {
|
||||||
|
format = c.result.Format
|
||||||
|
}
|
||||||
|
s.blitBuffer(composite, c.result.Buffer, c.physX-minX, c.physY-minY, c.result.YInverted)
|
||||||
|
c.result.Buffer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CaptureResult{
|
return &CaptureResult{
|
||||||
Buffer: composite,
|
Buffer: composite,
|
||||||
Region: Region{X: minX, Y: minY, Width: totalW, Height: totalH},
|
Region: Region{X: int32(minX), Y: int32(minY), Width: int32(totalW), Height: int32(totalH)},
|
||||||
Format: format,
|
Format: format,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -311,21 +363,106 @@ func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Screenshoter) captureAndCrop(output *WaylandOutput, region Region) (*CaptureResult, error) {
|
||||||
|
result, err := s.captureWholeOutput(output)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
outX, outY := output.x, output.y
|
||||||
|
scale := float64(output.scale)
|
||||||
|
if hx, hy, _, _, ok := GetHyprlandMonitorGeometry(output.name); ok {
|
||||||
|
outX, outY = hx, hy
|
||||||
|
}
|
||||||
|
if s := GetHyprlandMonitorScale(output.name); s > 0 {
|
||||||
|
scale = s
|
||||||
|
}
|
||||||
|
if scale <= 0 {
|
||||||
|
scale = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
localX := int(float64(region.X-outX) * scale)
|
||||||
|
localY := int(float64(region.Y-outY) * scale)
|
||||||
|
w := int(float64(region.Width) * scale)
|
||||||
|
h := int(float64(region.Height) * scale)
|
||||||
|
|
||||||
|
cropped, err := CreateShmBuffer(w, h, w*4)
|
||||||
|
if err != nil {
|
||||||
|
result.Buffer.Close()
|
||||||
|
return nil, fmt.Errorf("create crop buffer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcData := result.Buffer.Data()
|
||||||
|
dstData := cropped.Data()
|
||||||
|
|
||||||
|
for y := 0; y < h; y++ {
|
||||||
|
srcY := localY + y
|
||||||
|
if result.YInverted {
|
||||||
|
srcY = result.Buffer.Height - 1 - (localY + y)
|
||||||
|
}
|
||||||
|
if srcY < 0 || srcY >= result.Buffer.Height {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dstY := y
|
||||||
|
if result.YInverted {
|
||||||
|
dstY = h - 1 - y
|
||||||
|
}
|
||||||
|
|
||||||
|
for x := 0; x < w; x++ {
|
||||||
|
srcX := localX + x
|
||||||
|
if srcX < 0 || srcX >= result.Buffer.Width {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
si := srcY*result.Buffer.Stride + srcX*4
|
||||||
|
di := dstY*cropped.Stride + x*4
|
||||||
|
|
||||||
|
if si+3 >= len(srcData) || di+3 >= len(dstData) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dstData[di+0] = srcData[si+0]
|
||||||
|
dstData[di+1] = srcData[si+1]
|
||||||
|
dstData[di+2] = srcData[si+2]
|
||||||
|
dstData[di+3] = srcData[si+3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Buffer.Close()
|
||||||
|
cropped.Format = PixelFormat(result.Format)
|
||||||
|
|
||||||
|
return &CaptureResult{
|
||||||
|
Buffer: cropped,
|
||||||
|
Region: region,
|
||||||
|
YInverted: false,
|
||||||
|
Format: result.Format,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Region) (*CaptureResult, error) {
|
func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Region) (*CaptureResult, error) {
|
||||||
localX := region.X - output.x
|
scale := output.fractionalScale
|
||||||
localY := region.Y - output.y
|
if scale <= 0 && DetectCompositor() == CompositorHyprland {
|
||||||
|
scale = GetHyprlandMonitorScale(output.name)
|
||||||
|
}
|
||||||
|
if scale <= 0 {
|
||||||
|
scale = float64(output.scale)
|
||||||
|
}
|
||||||
|
if scale <= 0 {
|
||||||
|
scale = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
localX := int32(float64(region.X-output.x) * scale)
|
||||||
|
localY := int32(float64(region.Y-output.y) * scale)
|
||||||
|
w := int32(float64(region.Width) * scale)
|
||||||
|
h := int32(float64(region.Height) * scale)
|
||||||
|
|
||||||
cursor := int32(0)
|
cursor := int32(0)
|
||||||
if s.config.IncludeCursor {
|
if s.config.IncludeCursor {
|
||||||
cursor = 1
|
cursor = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
frame, err := s.screencopy.CaptureOutputRegion(
|
frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h)
|
||||||
cursor,
|
|
||||||
output.wlOutput,
|
|
||||||
localX, localY,
|
|
||||||
region.Width, region.Height,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("capture region: %w", err)
|
return nil, fmt.Errorf("capture region: %w", err)
|
||||||
}
|
}
|
||||||
@@ -335,6 +472,8 @@ func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Regio
|
|||||||
|
|
||||||
func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1, region Region) (*CaptureResult, error) {
|
func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1, region Region) (*CaptureResult, error) {
|
||||||
var buf *ShmBuffer
|
var buf *ShmBuffer
|
||||||
|
var pool *client.ShmPool
|
||||||
|
var wlBuf *client.Buffer
|
||||||
var format PixelFormat
|
var format PixelFormat
|
||||||
var yInverted bool
|
var yInverted bool
|
||||||
ready := false
|
ready := false
|
||||||
@@ -360,15 +499,17 @@ func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pool, err := s.shm.CreatePool(buf.Fd(), int32(buf.Size()))
|
var err error
|
||||||
|
pool, err = s.shm.CreatePool(buf.Fd(), int32(buf.Size()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to create pool", "err", err)
|
log.Error("failed to create pool", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wlBuf, err := pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), uint32(format))
|
wlBuf, err = pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), uint32(format))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pool.Destroy()
|
pool.Destroy()
|
||||||
|
pool = nil
|
||||||
log.Error("failed to create wl_buffer", "err", err)
|
log.Error("failed to create wl_buffer", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -376,8 +517,6 @@ func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1,
|
|||||||
if err := frame.Copy(wlBuf); err != nil {
|
if err := frame.Copy(wlBuf); err != nil {
|
||||||
log.Error("failed to copy frame", "err", err)
|
log.Error("failed to copy frame", "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pool.Destroy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
|
frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
|
||||||
@@ -396,6 +535,12 @@ func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
frame.Destroy()
|
frame.Destroy()
|
||||||
|
if wlBuf != nil {
|
||||||
|
wlBuf.Destroy()
|
||||||
|
}
|
||||||
|
if pool != nil {
|
||||||
|
pool.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
if failed {
|
if failed {
|
||||||
if buf != nil {
|
if buf != nil {
|
||||||
@@ -420,14 +565,26 @@ func (s *Screenshoter) findOutputForRegion(region Region) *WaylandOutput {
|
|||||||
cy := region.Y + region.Height/2
|
cy := region.Y + region.Height/2
|
||||||
|
|
||||||
for _, o := range s.outputs {
|
for _, o := range s.outputs {
|
||||||
if cx >= o.x && cx < o.x+o.width && cy >= o.y && cy < o.y+o.height {
|
x, y, w, h := o.x, o.y, o.width, o.height
|
||||||
|
if DetectCompositor() == CompositorHyprland {
|
||||||
|
if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok {
|
||||||
|
x, y, w, h = hx, hy, hw, hh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cx >= x && cx < x+w && cy >= y && cy < y+h {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range s.outputs {
|
for _, o := range s.outputs {
|
||||||
if region.X >= o.x && region.X < o.x+o.width &&
|
x, y, w, h := o.x, o.y, o.width, o.height
|
||||||
region.Y >= o.y && region.Y < o.y+o.height {
|
if DetectCompositor() == CompositorHyprland {
|
||||||
|
if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok {
|
||||||
|
x, y, w, h = hx, hy, hw, hh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if region.X >= x && region.X < x+w &&
|
||||||
|
region.Y >= y && region.Y < y+h {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,6 +593,15 @@ func (s *Screenshoter) findOutputForRegion(region Region) *WaylandOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Screenshoter) findFocusedOutput() *WaylandOutput {
|
func (s *Screenshoter) findFocusedOutput() *WaylandOutput {
|
||||||
|
if mon := GetFocusedMonitor(); mon != "" {
|
||||||
|
s.outputsMu.Lock()
|
||||||
|
defer s.outputsMu.Unlock()
|
||||||
|
for _, o := range s.outputs {
|
||||||
|
if o.name == mon {
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
s.outputsMu.Lock()
|
s.outputsMu.Lock()
|
||||||
defer s.outputsMu.Unlock()
|
defer s.outputsMu.Unlock()
|
||||||
for _, o := range s.outputs {
|
for _, o := range s.outputs {
|
||||||
@@ -501,9 +667,10 @@ func (s *Screenshoter) handleGlobal(e client.RegistryGlobalEvent) {
|
|||||||
if err := s.registry.Bind(e.Name, e.Interface, version, output); err == nil {
|
if err := s.registry.Bind(e.Name, e.Interface, version, output); err == nil {
|
||||||
s.outputsMu.Lock()
|
s.outputsMu.Lock()
|
||||||
s.outputs[e.Name] = &WaylandOutput{
|
s.outputs[e.Name] = &WaylandOutput{
|
||||||
wlOutput: output,
|
wlOutput: output,
|
||||||
globalName: e.Name,
|
globalName: e.Name,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
|
fractionalScale: 1.0,
|
||||||
}
|
}
|
||||||
s.outputsMu.Unlock()
|
s.outputsMu.Unlock()
|
||||||
s.setupOutputHandlers(e.Name, output)
|
s.setupOutputHandlers(e.Name, output)
|
||||||
@@ -546,6 +713,7 @@ func (s *Screenshoter) setupOutputHandlers(name uint32, output *client.Output) {
|
|||||||
s.outputsMu.Lock()
|
s.outputsMu.Lock()
|
||||||
if o, ok := s.outputs[name]; ok {
|
if o, ok := s.outputs[name]; ok {
|
||||||
o.scale = e.Factor
|
o.scale = e.Factor
|
||||||
|
o.fractionalScale = float64(e.Factor)
|
||||||
}
|
}
|
||||||
s.outputsMu.Unlock()
|
s.outputsMu.Unlock()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func (i *Display) GetRegistry() (*Registry, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Display) Destroy() error {
|
func (i *Display) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,15 +224,16 @@ func (i *Display) Dispatch(opcode uint32, fd int, data []byte) {
|
|||||||
|
|
||||||
i.errorHandler(e)
|
i.errorHandler(e)
|
||||||
case 1:
|
case 1:
|
||||||
if i.deleteIdHandler == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var e DisplayDeleteIdEvent
|
var e DisplayDeleteIdEvent
|
||||||
l := 0
|
l := 0
|
||||||
e.Id = Uint32(data[l : l+4])
|
e.Id = Uint32(data[l : l+4])
|
||||||
l += 4
|
l += 4
|
||||||
|
|
||||||
i.deleteIdHandler(e)
|
i.Context().DeleteID(e.Id)
|
||||||
|
|
||||||
|
if i.deleteIdHandler != nil {
|
||||||
|
i.deleteIdHandler(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,7 +327,7 @@ func (i *Registry) Bind(name uint32, iface string, version uint32, id Proxy) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Registry) Destroy() error {
|
func (i *Registry) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,7 +434,7 @@ func NewCallback(ctx *Context) *Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Callback) Destroy() error {
|
func (i *Callback) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +530,7 @@ func (i *Compositor) CreateRegion() (*Region, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Compositor) Destroy() error {
|
func (i *Compositor) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,7 +620,7 @@ func (i *ShmPool) CreateBuffer(offset, width, height, stride int32, format uint3
|
|||||||
// buffers that have been created from this pool
|
// buffers that have been created from this pool
|
||||||
// are gone.
|
// are gone.
|
||||||
func (i *ShmPool) Destroy() error {
|
func (i *ShmPool) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -735,7 +736,7 @@ func (i *Shm) CreatePool(fd int, size int32) (*ShmPool, error) {
|
|||||||
//
|
//
|
||||||
// Objects created via this interface remain unaffected.
|
// Objects created via this interface remain unaffected.
|
||||||
func (i *Shm) Release() error {
|
func (i *Shm) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -1642,7 +1643,7 @@ func NewBuffer(ctx *Context) *Buffer {
|
|||||||
//
|
//
|
||||||
// For possible side-effects to a surface, see wl_surface.attach.
|
// For possible side-effects to a surface, see wl_surface.attach.
|
||||||
func (i *Buffer) Destroy() error {
|
func (i *Buffer) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -1803,7 +1804,7 @@ func (i *DataOffer) Receive(mimeType string, fd int) error {
|
|||||||
//
|
//
|
||||||
// Destroy the data offer.
|
// Destroy the data offer.
|
||||||
func (i *DataOffer) Destroy() error {
|
func (i *DataOffer) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 2
|
const opcode = 2
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -2120,7 +2121,7 @@ func (i *DataSource) Offer(mimeType string) error {
|
|||||||
//
|
//
|
||||||
// Destroy the data source.
|
// Destroy the data source.
|
||||||
func (i *DataSource) Destroy() error {
|
func (i *DataSource) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -2540,7 +2541,7 @@ func (i *DataDevice) SetSelection(source *DataSource, serial uint32) error {
|
|||||||
//
|
//
|
||||||
// This request destroys the data device.
|
// This request destroys the data device.
|
||||||
func (i *DataDevice) Release() error {
|
func (i *DataDevice) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 2
|
const opcode = 2
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -2859,7 +2860,7 @@ func (i *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *DataDeviceManager) Destroy() error {
|
func (i *DataDeviceManager) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3000,7 +3001,7 @@ func (i *Shell) GetShellSurface(surface *Surface) (*ShellSurface, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Shell) Destroy() error {
|
func (i *Shell) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3421,7 +3422,7 @@ func (i *ShellSurface) SetClass(class string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *ShellSurface) Destroy() error {
|
func (i *ShellSurface) Destroy() error {
|
||||||
i.Context().Unregister(i)
|
i.MarkZombie()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3798,7 +3799,7 @@ func NewSurface(ctx *Context) *Surface {
|
|||||||
//
|
//
|
||||||
// Deletes the surface and invalidates its object ID.
|
// Deletes the surface and invalidates its object ID.
|
||||||
func (i *Surface) Destroy() error {
|
func (i *Surface) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -4618,7 +4619,7 @@ func (i *Seat) GetTouch() (*Touch, error) {
|
|||||||
// Using this request a client can tell the server that it is not going to
|
// Using this request a client can tell the server that it is not going to
|
||||||
// use the seat object anymore.
|
// use the seat object anymore.
|
||||||
func (i *Seat) Release() error {
|
func (i *Seat) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 3
|
const opcode = 3
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -4920,7 +4921,7 @@ func (i *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX, hotspotY
|
|||||||
// This request destroys the pointer proxy object, so clients must not call
|
// This request destroys the pointer proxy object, so clients must not call
|
||||||
// wl_pointer_destroy() after using this request.
|
// wl_pointer_destroy() after using this request.
|
||||||
func (i *Pointer) Release() error {
|
func (i *Pointer) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 1
|
const opcode = 1
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -5685,7 +5686,7 @@ func NewKeyboard(ctx *Context) *Keyboard {
|
|||||||
|
|
||||||
// Release : release the keyboard object
|
// Release : release the keyboard object
|
||||||
func (i *Keyboard) Release() error {
|
func (i *Keyboard) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -6091,7 +6092,7 @@ func NewTouch(ctx *Context) *Touch {
|
|||||||
|
|
||||||
// Release : release the touch object
|
// Release : release the touch object
|
||||||
func (i *Touch) Release() error {
|
func (i *Touch) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -6406,7 +6407,7 @@ func NewOutput(ctx *Context) *Output {
|
|||||||
// Using this request a client can tell the server that it is not going to
|
// Using this request a client can tell the server that it is not going to
|
||||||
// use the output object anymore.
|
// use the output object anymore.
|
||||||
func (i *Output) Release() error {
|
func (i *Output) Release() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -6923,7 +6924,7 @@ func NewRegion(ctx *Context) *Region {
|
|||||||
//
|
//
|
||||||
// Destroy the region. This will invalidate the object ID.
|
// Destroy the region. This will invalidate the object ID.
|
||||||
func (i *Region) Destroy() error {
|
func (i *Region) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -7057,7 +7058,7 @@ func NewSubcompositor(ctx *Context) *Subcompositor {
|
|||||||
// protocol object anymore. This does not affect any other
|
// protocol object anymore. This does not affect any other
|
||||||
// objects, wl_subsurface objects included.
|
// objects, wl_subsurface objects included.
|
||||||
func (i *Subcompositor) Destroy() error {
|
func (i *Subcompositor) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -7280,7 +7281,7 @@ func NewSubsurface(ctx *Context) *Subsurface {
|
|||||||
// wl_subcompositor.get_subsurface request. The wl_surface's association
|
// wl_subcompositor.get_subsurface request. The wl_surface's association
|
||||||
// to the parent is deleted. The wl_surface is unmapped immediately.
|
// to the parent is deleted. The wl_surface is unmapped immediately.
|
||||||
func (i *Subsurface) Destroy() error {
|
func (i *Subsurface) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
@@ -7499,7 +7500,7 @@ func NewFixes(ctx *Context) *Fixes {
|
|||||||
|
|
||||||
// Destroy : destroys this object
|
// Destroy : destroys this object
|
||||||
func (i *Fixes) Destroy() error {
|
func (i *Fixes) Destroy() error {
|
||||||
defer i.Context().Unregister(i)
|
defer i.MarkZombie()
|
||||||
const opcode = 0
|
const opcode = 0
|
||||||
const _reqBufLen = 8
|
const _reqBufLen = 8
|
||||||
var _reqBuf [_reqBufLen]byte
|
var _reqBuf [_reqBufLen]byte
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
|
import "sync/atomic"
|
||||||
|
|
||||||
type Dispatcher interface {
|
type Dispatcher interface {
|
||||||
Dispatch(opcode uint32, fd int, data []byte)
|
Dispatch(opcode uint32, fd int, data []byte)
|
||||||
}
|
}
|
||||||
@@ -9,11 +11,14 @@ type Proxy interface {
|
|||||||
SetContext(ctx *Context)
|
SetContext(ctx *Context)
|
||||||
ID() uint32
|
ID() uint32
|
||||||
SetID(id uint32)
|
SetID(id uint32)
|
||||||
|
IsZombie() bool
|
||||||
|
MarkZombie()
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseProxy struct {
|
type BaseProxy struct {
|
||||||
ctx *Context
|
ctx *Context
|
||||||
id uint32
|
id uint32
|
||||||
|
zombie atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *BaseProxy) ID() uint32 {
|
func (p *BaseProxy) ID() uint32 {
|
||||||
@@ -31,3 +36,11 @@ func (p *BaseProxy) Context() *Context {
|
|||||||
func (p *BaseProxy) SetContext(ctx *Context) {
|
func (p *BaseProxy) SetContext(ctx *Context) {
|
||||||
p.ctx = ctx
|
p.ctx = ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *BaseProxy) IsZombie() bool {
|
||||||
|
return p.zombie.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseProxy) MarkZombie() {
|
||||||
|
p.zombie.Store(true)
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ func (ctx *Context) Unregister(p Proxy) {
|
|||||||
ctx.objects.Delete(p.ID())
|
ctx.objects.Delete(p.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *Context) DeleteID(id uint32) {
|
||||||
|
ctx.objects.Delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *Context) GetProxy(id uint32) Proxy {
|
func (ctx *Context) GetProxy(id uint32) Proxy {
|
||||||
if val, ok := ctx.objects.Load(id); ok {
|
if val, ok := ctx.objects.Load(id); ok {
|
||||||
return val
|
return val
|
||||||
@@ -72,7 +76,11 @@ func (ctx *Context) GetDispatch() func() error {
|
|||||||
return func() error {
|
return func() error {
|
||||||
proxy, ok := ctx.objects.Load(senderID)
|
proxy, ok := ctx.objects.Load(senderID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%w (senderID=%d)", ErrDispatchSenderNotFound, senderID)
|
return nil // Proxy already deleted via delete_id, silently ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if proxy.IsZombie() {
|
||||||
|
return nil // Zombie proxy, discard late events
|
||||||
}
|
}
|
||||||
|
|
||||||
sender, ok := proxy.(Dispatcher)
|
sender, ok := proxy.(Dispatcher)
|
||||||
|
|||||||
Reference in New Issue
Block a user