mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-22 22:59:09 +02:00
Retry a transient cursor-source failure and stop reporting stale framebuffer geometry
This commit is contained in:
@@ -53,22 +53,33 @@ func (p *FBPoller) ClientDisconnect() {
|
||||
if p.clients <= 0 && p.capturer != nil {
|
||||
p.capturer.Close()
|
||||
p.capturer = nil
|
||||
// Drop the geometry with the device. The next client re-reads it, and
|
||||
// the framebuffer may be gone or resized by then.
|
||||
p.w, p.h = 0, 0
|
||||
}
|
||||
}
|
||||
|
||||
// Width returns the framebuffer width, doing lazy init if needed.
|
||||
// Width returns the framebuffer width, doing lazy init if needed. Zero when
|
||||
// the device cannot be opened: the geometry cached from an earlier session is
|
||||
// no evidence the framebuffer is still there, and reporting it admits a VNC
|
||||
// session whose every frame then fails.
|
||||
func (p *FBPoller) Width() int {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
_ = p.ensureCapturerLocked()
|
||||
if err := p.ensureCapturerLocked(); err != nil {
|
||||
return 0
|
||||
}
|
||||
return p.w
|
||||
}
|
||||
|
||||
// Height returns the framebuffer height, doing lazy init if needed.
|
||||
// Height returns the framebuffer height, doing lazy init if needed. Zero when
|
||||
// the device cannot be opened, for the reason given on Width.
|
||||
func (p *FBPoller) Height() int {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
_ = p.ensureCapturerLocked()
|
||||
if err := p.ensureCapturerLocked(); err != nil {
|
||||
return 0
|
||||
}
|
||||
return p.h
|
||||
}
|
||||
|
||||
@@ -103,6 +114,7 @@ func (p *FBPoller) Close() {
|
||||
p.capturer.Close()
|
||||
p.capturer = nil
|
||||
}
|
||||
p.w, p.h = 0, 0
|
||||
}
|
||||
|
||||
func (p *FBPoller) ensureCapturerLocked() error {
|
||||
|
||||
@@ -114,6 +114,12 @@ type session struct {
|
||||
// source so the encoder stops polling for the rest of the session.
|
||||
// Reset on SetEncodings so a reconnect can retry.
|
||||
cursorSourceFailed bool
|
||||
// cursorSourceFailures counts consecutive errors from the cursor source.
|
||||
// A single error does not latch: the Windows sampler fails across a
|
||||
// desktop switch and recovers on the next capture, and latching there
|
||||
// costs the client its cursor for the rest of the session. Reset by the
|
||||
// first success.
|
||||
cursorSourceFailures int
|
||||
// showRemoteCursor switches the encoder to compositing the server
|
||||
// cursor sprite into the captured framebuffer at the remote position
|
||||
// instead of emitting the Cursor pseudo-encoding. Toggled by the
|
||||
@@ -489,6 +495,7 @@ func (s *session) resetEncodingCaps() {
|
||||
s.clientSupportsCursor = false
|
||||
s.clientSupportsExtMouseButtons = false
|
||||
s.cursorSourceFailed = false
|
||||
s.cursorSourceFailures = 0
|
||||
s.clientJPEGQuality = -1
|
||||
s.clientZlibLevel = -1
|
||||
}
|
||||
|
||||
@@ -8,6 +8,13 @@ import (
|
||||
"image"
|
||||
)
|
||||
|
||||
// cursorSourceFailureLimit is how many consecutive Cursor() errors are taken as
|
||||
// the source being genuinely unavailable rather than momentarily unreadable.
|
||||
// Transient failures are normal: the Windows sampler cannot read the cursor
|
||||
// across a desktop switch or while the secure desktop is up, and recovers by
|
||||
// the next capture.
|
||||
const cursorSourceFailureLimit = 10
|
||||
|
||||
// pendingCursorRect returns the Cursor pseudo-rect for the current sprite
|
||||
// when the client negotiated the encoding and the platform exposes a
|
||||
// cursor source whose serial has changed since the last emission. A nil
|
||||
@@ -46,11 +53,18 @@ func (s *session) pendingCursorRect(pf clientPixelFormat) []byte {
|
||||
img, hotX, hotY, serial, err := src.Cursor()
|
||||
if err != nil {
|
||||
s.encMu.Lock()
|
||||
s.cursorSourceFailed = true
|
||||
s.cursorSourceFailures++
|
||||
failures := s.cursorSourceFailures
|
||||
if failures >= cursorSourceFailureLimit {
|
||||
s.cursorSourceFailed = true
|
||||
}
|
||||
s.encMu.Unlock()
|
||||
s.log.Debugf("cursor source unavailable: %v", err)
|
||||
s.log.Debugf("cursor source unavailable (%d consecutive): %v", failures, err)
|
||||
return nil
|
||||
}
|
||||
s.encMu.Lock()
|
||||
s.cursorSourceFailures = 0
|
||||
s.encMu.Unlock()
|
||||
if img == nil {
|
||||
s.logCursorSkip("no-sprite", "no cursor rect: capturer returned no sprite")
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user