run another check against the duplication api. Also fix mac building with CGO enabled.
This commit is contained in:
@@ -92,6 +92,8 @@ WORKDIR /app
|
|||||||
|
|
||||||
# openssl/ca-certificates: TLS cert generation + HTTPS validation.
|
# openssl/ca-certificates: TLS cert generation + HTTPS validation.
|
||||||
# wget/tar/unzip/xz-utils: required by toolchain-manager for on-demand downloads.
|
# wget/tar/unzip/xz-utils: required by toolchain-manager for on-demand downloads.
|
||||||
|
# clang: fallback C compiler for darwin/CGO agent builds (no toolchain mapping in
|
||||||
|
# toolchain-manager.ts, so build-process.ts falls back to the default `cc`).
|
||||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
apt-get update && apt-get install -y --no-install-recommends \
|
apt-get update && apt-get install -y --no-install-recommends \
|
||||||
@@ -102,6 +104,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|||||||
unzip \
|
unzip \
|
||||||
xz-utils \
|
xz-utils \
|
||||||
git \
|
git \
|
||||||
|
clang \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Reuse Go + garble from the builder so we don't re-download.
|
# Reuse Go + garble from the builder so we don't re-download.
|
||||||
|
|||||||
@@ -304,9 +304,10 @@ func safeActiveDisplays() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func safeCaptureDisplay(display int) (*image.RGBA, error) {
|
func safeCaptureDisplay(display int) (*image.RGBA, error) {
|
||||||
defer func() {
|
// Log panics with a stack trace so the actual culprit is captured in the
|
||||||
_ = recover()
|
// crash log instead of being silently swallowed (returns nil image, which
|
||||||
}()
|
// the caller treats as a transient capture failure).
|
||||||
|
defer recoverAndLog("safeCaptureDisplay", nil)
|
||||||
img, err := captureDisplayFn(display)
|
img, err := captureDisplayFn(display)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -701,12 +701,18 @@ func (s *duplicationState) capture(display int) (*image.RGBA, error) {
|
|||||||
}
|
}
|
||||||
if s.lastBase != nil {
|
if s.lastBase != nil {
|
||||||
img := s.composeFrame(s.lastBase, int(s.desc.ModeDesc.Width), int(s.desc.ModeDesc.Height))
|
img := s.composeFrame(s.lastBase, int(s.desc.ModeDesc.Width), int(s.desc.ModeDesc.Height))
|
||||||
s.lastFrame = img
|
// composeFrame may return s.lastBase or s.cursorScratch directly;
|
||||||
|
// both are reused across captures, so the caller's PutRGBA would
|
||||||
|
// hand them to the pool and a subsequent capture would race.
|
||||||
|
if img == s.lastBase || img == s.cursorScratch {
|
||||||
|
img = cloneRGBA(img)
|
||||||
|
}
|
||||||
|
s.lastFrame = cloneRGBA(img)
|
||||||
s.lastFrameAt = time.Now()
|
s.lastFrameAt = time.Now()
|
||||||
return img, nil
|
return img, nil
|
||||||
}
|
}
|
||||||
if s.lastFrame != nil {
|
if s.lastFrame != nil {
|
||||||
return s.lastFrame, nil
|
return cloneRGBA(s.lastFrame), nil
|
||||||
}
|
}
|
||||||
if waitMs >= 50 {
|
if waitMs >= 50 {
|
||||||
return nil, errors.New("dxgi: frame timeout")
|
return nil, errors.New("dxgi: frame timeout")
|
||||||
@@ -749,22 +755,25 @@ func (s *duplicationState) capture(display int) (*image.RGBA, error) {
|
|||||||
_ = s.dup.ReleaseFrame()
|
_ = s.dup.ReleaseFrame()
|
||||||
return nil, fmt.Errorf("dxgi: map desktop surface failed 0x%x", hr)
|
return nil, fmt.Errorf("dxgi: map desktop surface failed 0x%x", hr)
|
||||||
}
|
}
|
||||||
|
// Unmap and release the frame on every exit, including a panic in
|
||||||
|
// the conversion code below. Leaving the surface mapped wedges the
|
||||||
|
// duplication object until the device is fully closed.
|
||||||
|
dup := s.dup
|
||||||
|
defer func() {
|
||||||
|
_ = dup.UnMapDesktopSurface()
|
||||||
|
_ = dup.ReleaseFrame()
|
||||||
|
}()
|
||||||
|
|
||||||
if mapped.Pitch <= 0 || mapped.Bits == nil {
|
if mapped.Pitch <= 0 || mapped.Bits == nil {
|
||||||
_ = s.dup.UnMapDesktopSurface()
|
|
||||||
_ = s.dup.ReleaseFrame()
|
|
||||||
return nil, errors.New("dxgi: invalid mapped surface")
|
return nil, errors.New("dxgi: invalid mapped surface")
|
||||||
}
|
}
|
||||||
|
|
||||||
pitch := int(mapped.Pitch)
|
pitch := int(mapped.Pitch)
|
||||||
if pitch < width*4 {
|
if pitch < width*4 {
|
||||||
_ = s.dup.UnMapDesktopSurface()
|
|
||||||
_ = s.dup.ReleaseFrame()
|
|
||||||
return nil, fmt.Errorf("dxgi: pitch %d too small for width %d", pitch, width)
|
return nil, fmt.Errorf("dxgi: pitch %d too small for width %d", pitch, width)
|
||||||
}
|
}
|
||||||
totalBytes := pitch * height
|
totalBytes := pitch * height
|
||||||
if totalBytes/height != pitch {
|
if totalBytes/height != pitch {
|
||||||
_ = s.dup.UnMapDesktopSurface()
|
|
||||||
_ = s.dup.ReleaseFrame()
|
|
||||||
return nil, fmt.Errorf("dxgi: pitch*height overflow (%d * %d)", pitch, height)
|
return nil, fmt.Errorf("dxgi: pitch*height overflow (%d * %d)", pitch, height)
|
||||||
}
|
}
|
||||||
src := unsafe.Slice(mapped.Bits, totalBytes)
|
src := unsafe.Slice(mapped.Bits, totalBytes)
|
||||||
@@ -774,8 +783,6 @@ func (s *duplicationState) capture(display int) (*image.RGBA, error) {
|
|||||||
if img == nil {
|
if img == nil {
|
||||||
img = convertBGRA(src, pitch, width, height, s.desc.Rotation)
|
img = convertBGRA(src, pitch, width, height, s.desc.Rotation)
|
||||||
}
|
}
|
||||||
_ = s.dup.UnMapDesktopSurface()
|
|
||||||
_ = s.dup.ReleaseFrame()
|
|
||||||
if img == nil {
|
if img == nil {
|
||||||
return nil, errors.New("dxgi: pixel conversion failed (buffer too small)")
|
return nil, errors.New("dxgi: pixel conversion failed (buffer too small)")
|
||||||
}
|
}
|
||||||
@@ -788,9 +795,17 @@ func (s *duplicationState) capture(display int) (*image.RGBA, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.lastBase = img
|
// Cache an independent clone so the timeout path (which serves the cached
|
||||||
|
// frame back to callers) doesn't alias a buffer the caller will return to
|
||||||
|
// the RGBA pool via PutRGBA.
|
||||||
|
s.lastBase = cloneRGBA(img)
|
||||||
img = s.composeFrame(img, width, height)
|
img = s.composeFrame(img, width, height)
|
||||||
s.lastFrame = img
|
if img == s.cursorScratch {
|
||||||
|
// cursorScratch is reused across captures; clone before returning so
|
||||||
|
// the next compose doesn't write into a buffer the caller has pooled.
|
||||||
|
img = cloneRGBA(img)
|
||||||
|
}
|
||||||
|
s.lastFrame = cloneRGBA(img)
|
||||||
s.lastFrameAt = time.Now()
|
s.lastFrameAt = time.Now()
|
||||||
|
|
||||||
return img, nil
|
return img, nil
|
||||||
|
|||||||
Reference in New Issue
Block a user