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:
@@ -16,6 +16,7 @@ import (
|
||||
type SelectionState struct {
|
||||
hasSelection bool // There's a selection to display (pre-loaded or user-drawn)
|
||||
dragging bool // User is actively drawing a new selection
|
||||
surface *OutputSurface // Surface where selection was made
|
||||
// Surface-local logical coordinates (from pointer events)
|
||||
anchorX float64
|
||||
anchorY float64
|
||||
@@ -88,6 +89,9 @@ type RegionSelector struct {
|
||||
running bool
|
||||
cancelled bool
|
||||
result Region
|
||||
|
||||
capturedBuffer *ShmBuffer
|
||||
capturedRegion Region
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
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 {
|
||||
return Region{}, false, fmt.Errorf("roundtrip after registry: %w", err)
|
||||
return nil, false, fmt.Errorf("roundtrip after registry: %w", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
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:
|
||||
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:
|
||||
return Region{}, false, fmt.Errorf("no seat available")
|
||||
return nil, false, fmt.Errorf("no seat available")
|
||||
case r.compositor == nil:
|
||||
return Region{}, false, fmt.Errorf("compositor not available")
|
||||
return nil, false, fmt.Errorf("compositor not available")
|
||||
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:
|
||||
return Region{}, false, fmt.Errorf("no outputs available")
|
||||
return nil, false, fmt.Errorf("no outputs available")
|
||||
}
|
||||
|
||||
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 {
|
||||
return Region{}, false, fmt.Errorf("create surfaces: %w", err)
|
||||
return nil, false, fmt.Errorf("create surfaces: %w", err)
|
||||
}
|
||||
|
||||
_ = r.createCursor()
|
||||
|
||||
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
|
||||
for r.running {
|
||||
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 {
|
||||
@@ -610,6 +627,7 @@ func (r *RegionSelector) applyPreSelection(os *OutputSurface) {
|
||||
|
||||
r.selection.hasSelection = true
|
||||
r.selection.dragging = false
|
||||
r.selection.surface = os
|
||||
r.selection.anchorX = x1
|
||||
r.selection.anchorY = y1
|
||||
r.selection.currentX = x2
|
||||
|
||||
@@ -71,6 +71,7 @@ func (r *RegionSelector) setupPointerHandlers() {
|
||||
case 1: // pressed
|
||||
r.selection.hasSelection = true
|
||||
r.selection.dragging = true
|
||||
r.selection.surface = r.activeSurface // Lock to this surface
|
||||
r.selection.anchorX = r.pointerX
|
||||
r.selection.anchorY = r.pointerY
|
||||
r.selection.currentX = r.pointerX
|
||||
@@ -115,12 +116,17 @@ func (r *RegionSelector) setupKeyboardHandlers() {
|
||||
}
|
||||
|
||||
func (r *RegionSelector) finishSelection() {
|
||||
if r.activeSurface == nil {
|
||||
if r.selection.surface == nil {
|
||||
r.running = false
|
||||
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
|
||||
x2, y2 := r.selection.currentX, r.selection.currentY
|
||||
@@ -133,13 +139,29 @@ func (r *RegionSelector) finishSelection() {
|
||||
}
|
||||
|
||||
scaleX, scaleY := 1.0, 1.0
|
||||
if os.logicalW > 0 && os.screenBuf != nil {
|
||||
scaleX = float64(os.screenBuf.Width) / float64(os.logicalW)
|
||||
scaleY = float64(os.screenBuf.Height) / float64(os.logicalH)
|
||||
if os.logicalW > 0 {
|
||||
scaleX = float64(srcBuf.Width) / float64(os.logicalW)
|
||||
scaleY = float64(srcBuf.Height) / float64(os.logicalH)
|
||||
}
|
||||
|
||||
bx1, by1 := int32(x1*scaleX), int32(y1*scaleY)
|
||||
bx2, by2 := int32(x2*scaleX), int32(y2*scaleY)
|
||||
bx1 := int(x1 * scaleX)
|
||||
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
|
||||
if w < 1 {
|
||||
@@ -149,11 +171,51 @@ func (r *RegionSelector) finishSelection() {
|
||||
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{
|
||||
X: bx1 + os.output.x,
|
||||
Y: by1 + os.output.y,
|
||||
Width: w,
|
||||
Height: h,
|
||||
X: int32(bx1) + os.output.x,
|
||||
Y: int32(by1) + os.output.y,
|
||||
Width: int32(w),
|
||||
Height: int32(h),
|
||||
Output: os.output.name,
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
|
||||
|
||||
r.drawHUD(data, stride, w, h)
|
||||
|
||||
if !r.selection.hasSelection || r.activeSurface != os {
|
||||
if !r.selection.hasSelection || r.selection.surface != os {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -103,24 +103,19 @@ func (s *Screenshoter) captureLastRegion() (*CaptureResult, error) {
|
||||
|
||||
func (s *Screenshoter) captureRegion() (*CaptureResult, error) {
|
||||
selector := NewRegionSelector(s)
|
||||
region, cancelled, err := selector.Run()
|
||||
result, cancelled, err := selector.Run()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("region selection: %w", err)
|
||||
}
|
||||
if cancelled {
|
||||
if cancelled || result == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
output := s.findOutputForRegion(region)
|
||||
if output == nil {
|
||||
return nil, fmt.Errorf("no output found for region")
|
||||
}
|
||||
|
||||
if err := SaveLastRegion(region); err != nil {
|
||||
if err := SaveLastRegion(result.Region); err != nil {
|
||||
log.Debug("failed to save last region", "err", err)
|
||||
}
|
||||
|
||||
return s.captureRegionOnOutput(output, region)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Screenshoter) captureFullScreen() (*CaptureResult, error) {
|
||||
|
||||
Reference in New Issue
Block a user