mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
screenshot: fix some region mappings
This commit is contained in:
@@ -14,8 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SelectionState struct {
|
type SelectionState struct {
|
||||||
hasSelection bool // There's a selection to display (pre-loaded or user-drawn)
|
hasSelection bool // There's a selection to display (pre-loaded or user-drawn)
|
||||||
dragging bool // User is actively drawing a new selection
|
dragging bool // User is actively drawing a new selection
|
||||||
|
surface *OutputSurface // Surface where selection was made
|
||||||
// Surface-local logical coordinates (from pointer events)
|
// Surface-local logical coordinates (from pointer events)
|
||||||
anchorX float64
|
anchorX float64
|
||||||
anchorY float64
|
anchorY float64
|
||||||
@@ -88,6 +89,9 @@ type RegionSelector struct {
|
|||||||
running bool
|
running bool
|
||||||
cancelled bool
|
cancelled bool
|
||||||
result Region
|
result Region
|
||||||
|
|
||||||
|
capturedBuffer *ShmBuffer
|
||||||
|
capturedRegion Region
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRegionSelector(s *Screenshoter) *RegionSelector {
|
func NewRegionSelector(s *Screenshoter) *RegionSelector {
|
||||||
@@ -98,59 +102,72 @@ func NewRegionSelector(s *Screenshoter) *RegionSelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RegionSelector) Run() (Region, bool, error) {
|
func (r *RegionSelector) Run() (*CaptureResult, bool, error) {
|
||||||
r.preSelect = GetLastRegion()
|
r.preSelect = GetLastRegion()
|
||||||
|
|
||||||
if err := r.connect(); err != nil {
|
if err := r.connect(); err != nil {
|
||||||
return Region{}, false, fmt.Errorf("wayland connect: %w", err)
|
return nil, false, fmt.Errorf("wayland connect: %w", err)
|
||||||
}
|
}
|
||||||
defer r.cleanup()
|
defer r.cleanup()
|
||||||
|
|
||||||
if err := r.setupRegistry(); err != nil {
|
if err := r.setupRegistry(); err != nil {
|
||||||
return Region{}, false, fmt.Errorf("registry setup: %w", err)
|
return nil, false, fmt.Errorf("registry setup: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.roundtrip(); err != nil {
|
if err := r.roundtrip(); err != nil {
|
||||||
return Region{}, false, fmt.Errorf("roundtrip after registry: %w", err)
|
return nil, false, fmt.Errorf("roundtrip after registry: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case r.screencopy == nil:
|
case r.screencopy == nil:
|
||||||
return Region{}, false, fmt.Errorf("compositor does not support wlr-screencopy-unstable-v1")
|
return nil, false, fmt.Errorf("compositor does not support wlr-screencopy-unstable-v1")
|
||||||
case r.layerShell == nil:
|
case r.layerShell == nil:
|
||||||
return Region{}, false, fmt.Errorf("compositor does not support wlr-layer-shell-unstable-v1")
|
return nil, false, fmt.Errorf("compositor does not support wlr-layer-shell-unstable-v1")
|
||||||
case r.seat == nil:
|
case r.seat == nil:
|
||||||
return Region{}, false, fmt.Errorf("no seat available")
|
return nil, false, fmt.Errorf("no seat available")
|
||||||
case r.compositor == nil:
|
case r.compositor == nil:
|
||||||
return Region{}, false, fmt.Errorf("compositor not available")
|
return nil, false, fmt.Errorf("compositor not available")
|
||||||
case r.shm == nil:
|
case r.shm == nil:
|
||||||
return Region{}, false, fmt.Errorf("wl_shm not available")
|
return nil, false, fmt.Errorf("wl_shm not available")
|
||||||
case len(r.outputs) == 0:
|
case len(r.outputs) == 0:
|
||||||
return Region{}, false, fmt.Errorf("no outputs available")
|
return nil, false, fmt.Errorf("no outputs available")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.roundtrip(); err != nil {
|
if err := r.roundtrip(); err != nil {
|
||||||
return Region{}, false, fmt.Errorf("roundtrip after protocol check: %w", err)
|
return nil, false, fmt.Errorf("roundtrip after protocol check: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.createSurfaces(); err != nil {
|
if err := r.createSurfaces(); err != nil {
|
||||||
return Region{}, false, fmt.Errorf("create surfaces: %w", err)
|
return nil, false, fmt.Errorf("create surfaces: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = r.createCursor()
|
_ = r.createCursor()
|
||||||
|
|
||||||
if err := r.roundtrip(); err != nil {
|
if err := r.roundtrip(); err != nil {
|
||||||
return Region{}, false, fmt.Errorf("roundtrip after surfaces: %w", err)
|
return nil, false, fmt.Errorf("roundtrip after surfaces: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.running = true
|
r.running = true
|
||||||
for r.running {
|
for r.running {
|
||||||
if err := r.ctx.Dispatch(); err != nil {
|
if err := r.ctx.Dispatch(); err != nil {
|
||||||
return Region{}, false, fmt.Errorf("dispatch: %w", err)
|
return nil, false, fmt.Errorf("dispatch: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.result, r.cancelled, nil
|
if r.cancelled || r.capturedBuffer == nil {
|
||||||
|
return nil, r.cancelled, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
yInverted := false
|
||||||
|
if r.selection.surface != nil {
|
||||||
|
yInverted = r.selection.surface.yInverted
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CaptureResult{
|
||||||
|
Buffer: r.capturedBuffer,
|
||||||
|
Region: r.result, // Global coords for saving last region
|
||||||
|
YInverted: yInverted,
|
||||||
|
}, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RegionSelector) connect() error {
|
func (r *RegionSelector) connect() error {
|
||||||
@@ -610,6 +627,7 @@ func (r *RegionSelector) applyPreSelection(os *OutputSurface) {
|
|||||||
|
|
||||||
r.selection.hasSelection = true
|
r.selection.hasSelection = true
|
||||||
r.selection.dragging = false
|
r.selection.dragging = false
|
||||||
|
r.selection.surface = os
|
||||||
r.selection.anchorX = x1
|
r.selection.anchorX = x1
|
||||||
r.selection.anchorY = y1
|
r.selection.anchorY = y1
|
||||||
r.selection.currentX = x2
|
r.selection.currentX = x2
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ func (r *RegionSelector) setupPointerHandlers() {
|
|||||||
case 1: // pressed
|
case 1: // pressed
|
||||||
r.selection.hasSelection = true
|
r.selection.hasSelection = true
|
||||||
r.selection.dragging = true
|
r.selection.dragging = true
|
||||||
|
r.selection.surface = r.activeSurface // Lock to this surface
|
||||||
r.selection.anchorX = r.pointerX
|
r.selection.anchorX = r.pointerX
|
||||||
r.selection.anchorY = r.pointerY
|
r.selection.anchorY = r.pointerY
|
||||||
r.selection.currentX = r.pointerX
|
r.selection.currentX = r.pointerX
|
||||||
@@ -115,12 +116,17 @@ func (r *RegionSelector) setupKeyboardHandlers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RegionSelector) finishSelection() {
|
func (r *RegionSelector) finishSelection() {
|
||||||
if r.activeSurface == nil {
|
if r.selection.surface == nil {
|
||||||
r.running = false
|
r.running = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
os := r.activeSurface
|
os := r.selection.surface
|
||||||
|
srcBuf := r.getSourceBuffer(os)
|
||||||
|
if srcBuf == nil {
|
||||||
|
r.running = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
x1, y1 := r.selection.anchorX, r.selection.anchorY
|
x1, y1 := r.selection.anchorX, r.selection.anchorY
|
||||||
x2, y2 := r.selection.currentX, r.selection.currentY
|
x2, y2 := r.selection.currentX, r.selection.currentY
|
||||||
@@ -133,13 +139,29 @@ func (r *RegionSelector) finishSelection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scaleX, scaleY := 1.0, 1.0
|
scaleX, scaleY := 1.0, 1.0
|
||||||
if os.logicalW > 0 && os.screenBuf != nil {
|
if os.logicalW > 0 {
|
||||||
scaleX = float64(os.screenBuf.Width) / float64(os.logicalW)
|
scaleX = float64(srcBuf.Width) / float64(os.logicalW)
|
||||||
scaleY = float64(os.screenBuf.Height) / float64(os.logicalH)
|
scaleY = float64(srcBuf.Height) / float64(os.logicalH)
|
||||||
}
|
}
|
||||||
|
|
||||||
bx1, by1 := int32(x1*scaleX), int32(y1*scaleY)
|
bx1 := int(x1 * scaleX)
|
||||||
bx2, by2 := int32(x2*scaleX), int32(y2*scaleY)
|
by1 := int(y1 * scaleY)
|
||||||
|
bx2 := int(x2 * scaleX)
|
||||||
|
by2 := int(y2 * scaleY)
|
||||||
|
|
||||||
|
// Clamp to buffer bounds
|
||||||
|
if bx1 < 0 {
|
||||||
|
bx1 = 0
|
||||||
|
}
|
||||||
|
if by1 < 0 {
|
||||||
|
by1 = 0
|
||||||
|
}
|
||||||
|
if bx2 > srcBuf.Width {
|
||||||
|
bx2 = srcBuf.Width
|
||||||
|
}
|
||||||
|
if by2 > srcBuf.Height {
|
||||||
|
by2 = srcBuf.Height
|
||||||
|
}
|
||||||
|
|
||||||
w, h := bx2-bx1, by2-by1
|
w, h := bx2-bx1, by2-by1
|
||||||
if w < 1 {
|
if w < 1 {
|
||||||
@@ -149,11 +171,51 @@ func (r *RegionSelector) finishSelection() {
|
|||||||
h = 1
|
h = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create cropped buffer and copy pixels directly
|
||||||
|
cropped, err := CreateShmBuffer(w, h, w*4)
|
||||||
|
if err != nil {
|
||||||
|
r.running = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
srcData := srcBuf.Data()
|
||||||
|
dstData := cropped.Data()
|
||||||
|
for y := 0; y < h; y++ {
|
||||||
|
srcY := by1 + y
|
||||||
|
if srcY >= srcBuf.Height {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for x := 0; x < w; x++ {
|
||||||
|
srcX := bx1 + x
|
||||||
|
if srcX >= srcBuf.Width {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
si := srcY*srcBuf.Stride + srcX*4
|
||||||
|
di := y*cropped.Stride + x*4
|
||||||
|
if si+3 < len(srcData) && di+3 < len(dstData) {
|
||||||
|
dstData[di+0] = srcData[si+0]
|
||||||
|
dstData[di+1] = srcData[si+1]
|
||||||
|
dstData[di+2] = srcData[si+2]
|
||||||
|
dstData[di+3] = srcData[si+3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.capturedBuffer = cropped
|
||||||
|
r.capturedRegion = Region{
|
||||||
|
X: int32(bx1),
|
||||||
|
Y: int32(by1),
|
||||||
|
Width: int32(w),
|
||||||
|
Height: int32(h),
|
||||||
|
Output: os.output.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also store for "last region" feature with global coords
|
||||||
r.result = Region{
|
r.result = Region{
|
||||||
X: bx1 + os.output.x,
|
X: int32(bx1) + os.output.x,
|
||||||
Y: by1 + os.output.y,
|
Y: int32(by1) + os.output.y,
|
||||||
Width: w,
|
Width: int32(w),
|
||||||
Height: h,
|
Height: int32(h),
|
||||||
Output: os.output.name,
|
Output: os.output.name,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
|
|||||||
|
|
||||||
r.drawHUD(data, stride, w, h)
|
r.drawHUD(data, stride, w, h)
|
||||||
|
|
||||||
if !r.selection.hasSelection || r.activeSurface != os {
|
if !r.selection.hasSelection || r.selection.surface != os {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -103,24 +103,19 @@ func (s *Screenshoter) captureLastRegion() (*CaptureResult, error) {
|
|||||||
|
|
||||||
func (s *Screenshoter) captureRegion() (*CaptureResult, error) {
|
func (s *Screenshoter) captureRegion() (*CaptureResult, error) {
|
||||||
selector := NewRegionSelector(s)
|
selector := NewRegionSelector(s)
|
||||||
region, cancelled, err := selector.Run()
|
result, cancelled, err := selector.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("region selection: %w", err)
|
return nil, fmt.Errorf("region selection: %w", err)
|
||||||
}
|
}
|
||||||
if cancelled {
|
if cancelled || result == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
output := s.findOutputForRegion(region)
|
if err := SaveLastRegion(result.Region); err != nil {
|
||||||
if output == nil {
|
|
||||||
return nil, fmt.Errorf("no output found for region")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := SaveLastRegion(region); err != nil {
|
|
||||||
log.Debug("failed to save last region", "err", err)
|
log.Debug("failed to save last region", "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.captureRegionOnOutput(output, region)
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Screenshoter) captureFullScreen() (*CaptureResult, error) {
|
func (s *Screenshoter) captureFullScreen() (*CaptureResult, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user