Skip to content

Fix black captures from servers announcing DesktopSize before pixel data (#90) - #343

Open
sibson wants to merge 1 commit into
mainfrom
claude/fix-issue-90
Open

Fix black captures from servers announcing DesktopSize before pixel data (#90)#343
sibson wants to merge 1 commit into
mainfrom
claude/fix-issue-90

Conversation

@sibson

@sibson sibson commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Fixes #90 — black screenshots against TightVNC on Windows, reported 2017–2025.

Mechanism (full analysis in the issue comment): TightVNC answers the first non-incremental FramebufferUpdateRequest with an update containing only the DesktopSize pseudo-rectangle. updateDesktopSize replaces self.screen with a black canvas, _doConnection fired commitUpdate unconditionally once the rectangle count hit zero, the pending capture completed, and the CLI saved the black canvas and exited — before the server's pixel frame (sent moments later) arrived.

The fix, two small parts:

  • rfb.py: the DesktopSize rect is removed from rectanglePos before decoding, exactly as the QEMU key-event pseudo-rect already is ("undo append as this is no real update") — pseudo-rectangles carrying no pixels no longer count as update content.
  • client.py: commitUpdate with a pending deferred and no content rectangles re-issues a non-incremental framebufferUpdateRequest and keeps waiting, instead of completing the refresh with nothing on the canvas.

A server that answered every non-incremental request with a content-free update could keep the client requesting until its timeout; real servers send content, and the per-client timeout still bounds the wait either way.

Regression tests live in their permanent home (tests/unit/test_client.py): a desktop-size-only update must not complete a refresh (and must re-request), and the refresh completes normally once pixel data arrives. Full unit suite green, flake8 clean.

End-to-end validation: the scripted server replaying TightVNC's logged behaviour (full repro kit in the issue thread) flips from a black capture (extrema ((0,0),(0,0),(0,0)), server logs could not send pixel data: CLIENT ALREADY DISCONNECTED) to the correct test pattern — after the DesktopSize rect the client now re-requests and receives the RAW frame before completing:

INFO:twisted:x=0 y=0 w=1024 h=640 <Encoding.PSEUDO_DESKTOP_SIZE: -223>
INFO:twisted:x=0 y=0 w=1024 h=640 <Encoding.RAW: 0>
INFO:twisted:Stopping factory <VNCDoCLIFactory ...>

Supersedes #342 (the test-only repro PR): this PR carries that test in final form, renamed for the behaviour it checks per the repo convention. When this merges, close #342 unmerged — its expectedFailure test would report an unexpected success on a fixed tree.

A framebuffer update whose only rectangle is the PSEUDO_DESKTOP_SIZE
pseudo-encoding used to complete a pending refresh: updateDesktopSize
replaces the screen with a black canvas and commitUpdate fired the
deferred regardless, so captureScreen saved the black canvas and the
CLI exited before the server's first pixel frame arrived. TightVNC on
Windows answers the first non-incremental update request exactly this
way, which is the black-screenshot report in #90 (2017-2025).

The DesktopSize rectangle is now excluded from the update's content
rectangles, matching the existing QEMU key-event pseudo-rect handling,
and a pending refresh whose update carried no content re-requests the
framebuffer instead of completing. Fixes #90.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread vncdotool/client.py

def commitUpdate(self, rectangles: list[rfb.Rect] | None = None) -> None:
if self.deferred:
if not rectangles:

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should commitUpdate even get called if we are properly deleting the pseudo rectangle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

always got black picture

2 participants