diff options
| author | Nippy <nippy@rp1.hu> | 2026-05-09 13:33:09 +0200 |
|---|---|---|
| committer | Nippy <nippy@rp1.hu> | 2026-05-09 13:33:09 +0200 |
| commit | a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch) | |
| tree | 1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go | |
| parent | 34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff) | |
| download | RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip | |
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go')
| -rw-r--r-- | raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go | 1089 |
1 files changed, 0 insertions, 1089 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go deleted file mode 100644 index 42d3404..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go +++ /dev/null @@ -1,1089 +0,0 @@ -package screenshot - -import ( - "fmt" - "math" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_screencopy" - wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client" -) - -type WaylandOutput struct { - wlOutput *client.Output - globalName uint32 - name string - x, y int32 - width int32 - height int32 - scale int32 - fractionalScale float64 - transform int32 -} - -type CaptureResult struct { - Buffer *ShmBuffer - Region Region - YInverted bool - Format uint32 -} - -type Screenshoter struct { - config Config - - display *client.Display - registry *client.Registry - ctx *client.Context - - compositor *client.Compositor - shm *client.Shm - screencopy *wlr_screencopy.ZwlrScreencopyManagerV1 - - outputs map[uint32]*WaylandOutput - outputsMu sync.Mutex -} - -func New(config Config) *Screenshoter { - return &Screenshoter{ - config: config, - outputs: make(map[uint32]*WaylandOutput), - } -} - -func (s *Screenshoter) Run() (*CaptureResult, error) { - if err := s.connect(); err != nil { - return nil, fmt.Errorf("wayland connect: %w", err) - } - defer s.cleanup() - - if err := s.setupRegistry(); err != nil { - return nil, fmt.Errorf("registry setup: %w", err) - } - - if err := s.roundtrip(); err != nil { - return nil, fmt.Errorf("roundtrip: %w", err) - } - - if s.screencopy == nil { - return nil, fmt.Errorf("compositor does not support wlr-screencopy-unstable-v1") - } - - if err := s.roundtrip(); err != nil { - return nil, fmt.Errorf("roundtrip: %w", err) - } - - switch s.config.Mode { - case ModeLastRegion: - return s.captureLastRegion() - case ModeRegion: - return s.captureRegion() - case ModeWindow: - return s.captureWindow() - case ModeOutput: - return s.captureOutput(s.config.OutputName) - case ModeFullScreen: - return s.captureFullScreen() - case ModeAllScreens: - return s.captureAllScreens() - default: - return s.captureRegion() - } -} - -func (s *Screenshoter) captureLastRegion() (*CaptureResult, error) { - lastRegion := GetLastRegion() - if lastRegion.IsEmpty() { - return s.captureRegion() - } - - output := s.findOutputForRegion(lastRegion) - if output == nil { - return s.captureRegion() - } - - return s.captureRegionOnOutput(output, lastRegion) -} - -func (s *Screenshoter) captureRegion() (*CaptureResult, error) { - if s.config.Reset { - if err := SaveLastRegion(Region{}); err != nil { - log.Debug("failed to reset last region", "err", err) - } - } - - selector := NewRegionSelector(s) - result, cancelled, err := selector.Run() - if err != nil { - return nil, fmt.Errorf("region selection: %w", err) - } - if cancelled || result == nil { - return nil, nil - } - - if err := SaveLastRegion(result.Region); err != nil { - log.Debug("failed to save last region", "err", err) - } - - return result, nil -} - -func (s *Screenshoter) captureWindow() (*CaptureResult, error) { - geom, err := GetActiveWindow() - if err != nil { - return nil, err - } - - region := Region{ - X: geom.X, - Y: geom.Y, - Width: geom.Width, - Height: geom.Height, - } - - var output *WaylandOutput - if geom.Output != "" { - output = s.findOutputByName(geom.Output) - } - if output == nil { - output = s.findOutputForRegion(region) - } - if output == nil { - return nil, fmt.Errorf("could not find output for window") - } - - switch DetectCompositor() { - case CompositorHyprland: - return s.captureAndCrop(output, region) - case CompositorDWL: - return s.captureDWLWindow(output, region, geom) - default: - return s.captureRegionOnOutput(output, region) - } -} - -func (s *Screenshoter) captureDWLWindow(output *WaylandOutput, region Region, geom *WindowGeometry) (*CaptureResult, error) { - result, err := s.captureWholeOutput(output) - if err != nil { - return nil, err - } - - scale := geom.Scale - if scale <= 0 || scale == 1.0 { - if output.fractionalScale > 1.0 { - scale = output.fractionalScale - } - } - if scale <= 0 { - scale = 1.0 - } - - localX := int(float64(region.X-geom.OutputX) * scale) - localY := int(float64(region.Y-geom.OutputY) * scale) - w := int(float64(region.Width) * scale) - h := int(float64(region.Height) * scale) - - if localX < 0 { - w += localX - localX = 0 - } - if localY < 0 { - h += localY - localY = 0 - } - if localX+w > result.Buffer.Width { - w = result.Buffer.Width - localX - } - if localY+h > result.Buffer.Height { - h = result.Buffer.Height - localY - } - - if w <= 0 || h <= 0 { - result.Buffer.Close() - return nil, fmt.Errorf("window not visible on output") - } - - 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) captureFullScreen() (*CaptureResult, error) { - output := s.findFocusedOutput() - if output == nil { - s.outputsMu.Lock() - for _, o := range s.outputs { - output = o - break - } - s.outputsMu.Unlock() - } - - if output == nil { - return nil, fmt.Errorf("no output available") - } - - return s.captureWholeOutput(output) -} - -func (s *Screenshoter) captureOutput(name string) (*CaptureResult, error) { - s.outputsMu.Lock() - var output *WaylandOutput - for _, o := range s.outputs { - if o.name == name { - output = o - break - } - } - s.outputsMu.Unlock() - - if output == nil { - return nil, fmt.Errorf("output %q not found", name) - } - - return s.captureWholeOutput(output) -} - -func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) { - s.outputsMu.Lock() - outputs := make([]*WaylandOutput, 0, len(s.outputs)) - for _, o := range s.outputs { - outputs = append(outputs, o) - } - s.outputsMu.Unlock() - - if len(outputs) == 0 { - return nil, fmt.Errorf("no outputs available") - } - if len(outputs) == 1 { - return s.captureWholeOutput(outputs[0]) - } - - wlrInfos := getAllOutputInfos() - - type pendingOutput struct { - result *CaptureResult - logX float64 - logY float64 - scale float64 - } - var pending []pendingOutput - maxScale := 1.0 - - for _, output := range outputs { - result, err := s.captureWholeOutput(output) - if err != nil { - log.Warn("failed to capture output", "name", output.name, "err", err) - continue - } - - logX, logY := float64(output.x), float64(output.y) - scale := float64(output.scale) - - switch DetectCompositor() { - case CompositorHyprland: - if hx, hy, _, _, ok := GetHyprlandMonitorGeometry(output.name); ok { - logX, logY = float64(hx), float64(hy) - } - if hs := GetHyprlandMonitorScale(output.name); hs > 0 { - scale = hs - } - default: - if wlrInfos != nil { - if info, ok := wlrInfos[output.name]; ok { - logX, logY = float64(info.x), float64(info.y) - if info.scale > 0 { - scale = info.scale - } - } - } - } - - if scale <= 0 { - scale = 1.0 - } - - pending = append(pending, pendingOutput{result: result, logX: logX, logY: logY, scale: scale}) - if scale > maxScale { - maxScale = scale - } - } - - if len(pending) == 0 { - return nil, fmt.Errorf("failed to capture any outputs") - } - if len(pending) == 1 { - return pending[0].result, nil - } - - type layoutEntry struct { - result *CaptureResult - canvasX int - canvasY int - canvasW int - canvasH int - } - entries := make([]layoutEntry, len(pending)) - var minX, minY, maxX, maxY int - - for i, p := range pending { - cx := int(math.Round(p.logX * maxScale)) - cy := int(math.Round(p.logY * maxScale)) - cw := int(math.Round(float64(p.result.Buffer.Width) * maxScale / p.scale)) - ch := int(math.Round(float64(p.result.Buffer.Height) * maxScale / p.scale)) - - entries[i] = layoutEntry{result: p.result, canvasX: cx, canvasY: cy, canvasW: cw, canvasH: ch} - - right := cx + cw - bottom := cy + ch - if i == 0 { - minX, minY, maxX, maxY = cx, cy, right, bottom - continue - } - if cx < minX { - minX = cx - } - if cy < minY { - minY = cy - } - if right > maxX { - maxX = right - } - if bottom > maxY { - maxY = bottom - } - } - - totalW := maxX - minX - totalH := maxY - minY - composite, err := CreateShmBuffer(totalW, totalH, totalW*4) - if err != nil { - for _, e := range entries { - e.result.Buffer.Close() - } - return nil, fmt.Errorf("create composite buffer: %w", err) - } - composite.Clear() - - var format uint32 - for _, e := range entries { - if format == 0 { - format = e.result.Format - } - s.blitBufferScaled(composite, e.result.Buffer, - e.canvasX-minX, e.canvasY-minY, e.canvasW, e.canvasH, - e.result.YInverted) - e.result.Buffer.Close() - } - - return &CaptureResult{ - Buffer: composite, - Region: Region{X: int32(minX), Y: int32(minY), Width: int32(totalW), Height: int32(totalH)}, - Format: format, - }, nil -} - -func (s *Screenshoter) blitBufferScaled(dst, src *ShmBuffer, dstX, dstY, dstW, dstH int, yInverted bool) { - if dstW <= 0 || dstH <= 0 { - return - } - - srcData := src.Data() - dstData := dst.Data() - - for dy := 0; dy < dstH; dy++ { - canvasY := dstY + dy - if canvasY < 0 || canvasY >= dst.Height { - continue - } - - srcY := dy * src.Height / dstH - if yInverted { - srcY = src.Height - 1 - srcY - } - if srcY < 0 || srcY >= src.Height { - continue - } - - srcRowOff := srcY * src.Stride - dstRowOff := canvasY * dst.Stride - - for dx := 0; dx < dstW; dx++ { - canvasX := dstX + dx - if canvasX < 0 || canvasX >= dst.Width { - continue - } - - srcX := dx * src.Width / dstW - if srcX >= src.Width { - continue - } - - si := srcRowOff + srcX*4 - di := dstRowOff + canvasX*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] - } - } -} - -func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult, error) { - cursor := int32(s.config.Cursor) - - frame, err := s.screencopy.CaptureOutput(cursor, output.wlOutput) - if err != nil { - return nil, fmt.Errorf("capture output: %w", err) - } - - result, err := s.processFrame(frame, Region{ - X: output.x, - Y: output.y, - Width: output.width, - Height: output.height, - Output: output.name, - }) - if err != nil { - return nil, err - } - - if result.YInverted { - result.Buffer.FlipVertical() - result.YInverted = false - } - - if output.transform == TransformNormal { - return result, nil - } - - invTransform := InverseTransform(output.transform) - transformed, err := result.Buffer.ApplyTransform(invTransform) - if err != nil { - result.Buffer.Close() - return nil, fmt.Errorf("apply transform: %w", err) - } - - if transformed != result.Buffer { - result.Buffer.Close() - result.Buffer = transformed - } - - result.Region.Width = int32(transformed.Width) - result.Region.Height = int32(transformed.Height) - - return result, nil -} - -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) { - if output.transform != TransformNormal { - return s.captureRegionOnTransformedOutput(output, region) - } - - scale := output.fractionalScale - 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) - - if DetectCompositor() == CompositorDWL { - scaledOutW := int32(float64(output.width) * scale) - scaledOutH := int32(float64(output.height) * scale) - if localX >= scaledOutW { - localX = localX % scaledOutW - } - if localY >= scaledOutH { - localY = localY % scaledOutH - } - if localX+w > scaledOutW { - w = scaledOutW - localX - } - if localY+h > scaledOutH { - h = scaledOutH - localY - } - if localX < 0 { - w += localX - localX = 0 - } - if localY < 0 { - h += localY - localY = 0 - } - } - - cursor := int32(s.config.Cursor) - - frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h) - if err != nil { - return nil, fmt.Errorf("capture region: %w", err) - } - - return s.processFrame(frame, region) -} - -func (s *Screenshoter) captureRegionOnTransformedOutput(output *WaylandOutput, region Region) (*CaptureResult, error) { - result, err := s.captureWholeOutput(output) - if err != nil { - return nil, err - } - - scale := output.fractionalScale - if scale <= 0 && DetectCompositor() == CompositorHyprland { - scale = GetHyprlandMonitorScale(output.name) - } - if scale <= 0 { - scale = float64(output.scale) - } - if scale <= 0 { - scale = 1.0 - } - - localX := int(float64(region.X-output.x) * scale) - localY := int(float64(region.Y-output.y) * scale) - w := int(float64(region.Width) * scale) - h := int(float64(region.Height) * scale) - - if localX < 0 { - w += localX - localX = 0 - } - if localY < 0 { - h += localY - localY = 0 - } - if localX+w > result.Buffer.Width { - w = result.Buffer.Width - localX - } - if localY+h > result.Buffer.Height { - h = result.Buffer.Height - localY - } - - if w <= 0 || h <= 0 { - result.Buffer.Close() - return nil, fmt.Errorf("region not visible on output") - } - - 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++ { - srcOff := (localY+y)*result.Buffer.Stride + localX*4 - dstOff := y * cropped.Stride - if srcOff+w*4 <= len(srcData) && dstOff+w*4 <= len(dstData) { - copy(dstData[dstOff:dstOff+w*4], srcData[srcOff:srcOff+w*4]) - } - } - - result.Buffer.Close() - cropped.Format = PixelFormat(result.Format) - - return &CaptureResult{ - Buffer: cropped, - Region: region, - YInverted: false, - Format: result.Format, - }, nil -} - -func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1, region Region) (*CaptureResult, error) { - var buf *ShmBuffer - var pool *client.ShmPool - var wlBuf *client.Buffer - var format PixelFormat - var yInverted bool - ready := false - failed := false - - frame.SetBufferHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferEvent) { - format = PixelFormat(e.Format) - bpp := format.BytesPerPixel() - if int(e.Stride) < int(e.Width)*bpp { - log.Error("invalid stride from compositor", "stride", e.Stride, "width", e.Width, "bpp", bpp) - return - } - var err error - buf, err = CreateShmBuffer(int(e.Width), int(e.Height), int(e.Stride)) - if err != nil { - log.Error("failed to create buffer", "err", err) - return - } - buf.Format = format - }) - - frame.SetFlagsHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FlagsEvent) { - yInverted = (e.Flags & 1) != 0 - }) - - frame.SetBufferDoneHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferDoneEvent) { - if buf == nil { - return - } - - var err error - pool, err = s.shm.CreatePool(buf.Fd(), int32(buf.Size())) - if err != nil { - log.Error("failed to create pool", "err", err) - return - } - - wlBuf, err = pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), uint32(format)) - if err != nil { - pool.Destroy() - pool = nil - log.Error("failed to create wl_buffer", "err", err) - return - } - - if err := frame.Copy(wlBuf); err != nil { - log.Error("failed to copy frame", "err", err) - } - }) - - frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) { - ready = true - }) - - frame.SetFailedHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FailedEvent) { - failed = true - }) - - for !ready && !failed { - if err := s.ctx.Dispatch(); err != nil { - frame.Destroy() - return nil, fmt.Errorf("dispatch: %w", err) - } - } - - frame.Destroy() - if wlBuf != nil { - wlBuf.Destroy() - } - if pool != nil { - pool.Destroy() - } - - if failed { - if buf != nil { - buf.Close() - } - return nil, fmt.Errorf("frame capture failed") - } - - if format.Is24Bit() { - converted, newFormat, err := buf.ConvertTo32Bit(format) - if err != nil { - buf.Close() - return nil, fmt.Errorf("convert 24-bit to 32-bit: %w", err) - } - if converted != buf { - buf.Close() - buf = converted - } - format = newFormat - } - - return &CaptureResult{ - Buffer: buf, - Region: region, - YInverted: yInverted, - Format: uint32(format), - }, nil -} - -func (s *Screenshoter) findOutputByName(name string) *WaylandOutput { - s.outputsMu.Lock() - defer s.outputsMu.Unlock() - for _, o := range s.outputs { - if o.name == name { - return o - } - } - return nil -} - -func (s *Screenshoter) findOutputForRegion(region Region) *WaylandOutput { - s.outputsMu.Lock() - defer s.outputsMu.Unlock() - - cx := region.X + region.Width/2 - cy := region.Y + region.Height/2 - - for _, o := range s.outputs { - 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 - } - } - - for _, o := range s.outputs { - 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 region.X >= x && region.X < x+w && - region.Y >= y && region.Y < y+h { - return o - } - } - - return nil -} - -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() - defer s.outputsMu.Unlock() - for _, o := range s.outputs { - return o - } - return nil -} - -func (s *Screenshoter) connect() error { - display, err := client.Connect("") - if err != nil { - return err - } - s.display = display - s.ctx = display.Context() - return nil -} - -func (s *Screenshoter) roundtrip() error { - return wlhelpers.Roundtrip(s.display, s.ctx) -} - -func (s *Screenshoter) setupRegistry() error { - registry, err := s.display.GetRegistry() - if err != nil { - return err - } - s.registry = registry - - registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) { - s.handleGlobal(e) - }) - - registry.SetGlobalRemoveHandler(func(e client.RegistryGlobalRemoveEvent) { - s.outputsMu.Lock() - delete(s.outputs, e.Name) - s.outputsMu.Unlock() - }) - - return nil -} - -func (s *Screenshoter) handleGlobal(e client.RegistryGlobalEvent) { - switch e.Interface { - case client.CompositorInterfaceName: - comp := client.NewCompositor(s.ctx) - if err := s.registry.Bind(e.Name, e.Interface, e.Version, comp); err == nil { - s.compositor = comp - } - - case client.ShmInterfaceName: - shm := client.NewShm(s.ctx) - if err := s.registry.Bind(e.Name, e.Interface, e.Version, shm); err == nil { - s.shm = shm - } - - case client.OutputInterfaceName: - output := client.NewOutput(s.ctx) - version := e.Version - if version > 4 { - version = 4 - } - if err := s.registry.Bind(e.Name, e.Interface, version, output); err == nil { - s.outputsMu.Lock() - s.outputs[e.Name] = &WaylandOutput{ - wlOutput: output, - globalName: e.Name, - scale: 1, - fractionalScale: 1.0, - } - s.outputsMu.Unlock() - s.setupOutputHandlers(e.Name, output) - } - - case wlr_screencopy.ZwlrScreencopyManagerV1InterfaceName: - sc := wlr_screencopy.NewZwlrScreencopyManagerV1(s.ctx) - version := e.Version - if version > 3 { - version = 3 - } - if err := s.registry.Bind(e.Name, e.Interface, version, sc); err == nil { - s.screencopy = sc - } - } -} - -func (s *Screenshoter) setupOutputHandlers(name uint32, output *client.Output) { - output.SetGeometryHandler(func(e client.OutputGeometryEvent) { - s.outputsMu.Lock() - if o, ok := s.outputs[name]; ok { - o.x, o.y = e.X, e.Y - o.transform = int32(e.Transform) - } - s.outputsMu.Unlock() - }) - - output.SetModeHandler(func(e client.OutputModeEvent) { - if e.Flags&uint32(client.OutputModeCurrent) == 0 { - return - } - s.outputsMu.Lock() - if o, ok := s.outputs[name]; ok { - o.width, o.height = e.Width, e.Height - } - s.outputsMu.Unlock() - }) - - output.SetScaleHandler(func(e client.OutputScaleEvent) { - s.outputsMu.Lock() - if o, ok := s.outputs[name]; ok { - o.scale = e.Factor - o.fractionalScale = float64(e.Factor) - } - s.outputsMu.Unlock() - }) - - output.SetNameHandler(func(e client.OutputNameEvent) { - s.outputsMu.Lock() - if o, ok := s.outputs[name]; ok { - o.name = e.Name - } - s.outputsMu.Unlock() - }) -} - -func (s *Screenshoter) cleanup() { - if s.screencopy != nil { - s.screencopy.Destroy() - } - if s.display != nil { - s.ctx.Close() - } -} - -func (s *Screenshoter) GetOutputs() []*WaylandOutput { - s.outputsMu.Lock() - defer s.outputsMu.Unlock() - out := make([]*WaylandOutput, 0, len(s.outputs)) - for _, o := range s.outputs { - out = append(out, o) - } - return out -} - -func ListOutputs() ([]Output, error) { - sc := New(DefaultConfig()) - if err := sc.connect(); err != nil { - return nil, err - } - defer sc.cleanup() - - if err := sc.setupRegistry(); err != nil { - return nil, err - } - if err := sc.roundtrip(); err != nil { - return nil, err - } - if err := sc.roundtrip(); err != nil { - return nil, err - } - - sc.outputsMu.Lock() - defer sc.outputsMu.Unlock() - - compositor := DetectCompositor() - result := make([]Output, 0, len(sc.outputs)) - for _, o := range sc.outputs { - out := Output{ - Name: o.name, - X: o.x, - Y: o.y, - Width: o.width, - Height: o.height, - Scale: o.scale, - FractionalScale: o.fractionalScale, - Transform: o.transform, - } - - switch compositor { - case CompositorHyprland: - if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok { - out.X, out.Y = hx, hy - out.Width, out.Height = hw, hh - } - if s := GetHyprlandMonitorScale(o.name); s > 0 { - out.FractionalScale = s - } - } - - result = append(result, out) - } - return result, nil -} |