RFC 6143
The Remote Framebuffer Protocol
The Remote Framebuffer Protocol ๐ฅ๏ธโจ
RFC 6143, "The Remote Framebuffer Protocol" ๐
Authors: T. Richardson and J. Levine (RealVNC Ltd.) ๐
Category: Informational ๐
Date: March 2011 ๐
ISSN: 2070-1721
No "Obsoletes" or "Updates" line appears in this header. ๐ฆ
Abstract โจ
okay so RFB ("remote framebuffer") is a simple protocol for remote access to graphical user interfaces ๐ฅ๏ธ It lets a client view and control a window system on another computer ๐ Because it works at the framebuffer level, RFB applies to all windowing systems and applications ๐ This doc describes the protocol that a RFB client and RFB server use to talk to each other ๐ฌ RFB is also the protocol used in VNC ๐ซถ
Status of This Memo ๐
ngl this one is not a standards track spec. It's published for informational purposes ๐ธ
This doc is a product of the Internet Engineering Task Force (IETF) ๐ It represents the consensus of the IETF community and got public review before the Internet Engineering Steering Group (IESG) approved it for publication โจ Not every IESG-approved doc is a candidate for any level of Internet Standard, so check Section 2 of RFC 5741 if you're curious ๐
Info about the current status, any errata, and how to give feedback lives at http://www.rfc-editor.org/info/rfc6143 ๐
Copyright Notice ๐
Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved. ๐
This doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they're in effect on the date of publication. Read those carefully, bestie, because they explain your rights and restrictions for this doc ๐ Code Components pulled out of this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions. They come without warranty, as described in the Simplified BSD License ๐
Table of Contents ๐๏ธ
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
2. Initial Connection . . . . . . . . . . . . . . . . . . . . . . 4
3. Display Protocol . . . . . . . . . . . . . . . . . . . . . . . 4
4. Input Protocol . . . . . . . . . . . . . . . . . . . . . . . . 5
5. Representation of Pixel Data . . . . . . . . . . . . . . . . . 5
6. Protocol Versions and Extensions . . . . . . . . . . . . . . . 6
7. Protocol Messages . . . . . . . . . . . . . . . . . . . . . . 7
7.1. Handshake Messages . . . . . . . . . . . . . . . . . . . . 8
7.1.1. ProtocolVersion Handshake . . . . . . . . . . . . . . 8
7.1.2. Security Handshake . . . . . . . . . . . . . . . . . . 8
7.1.3. SecurityResult Handshake . . . . . . . . . . . . . . . 10
7.2. Security Types . . . . . . . . . . . . . . . . . . . . . . 10
7.2.1. None . . . . . . . . . . . . . . . . . . . . . . . . . 10
7.2.2. VNC Authentication . . . . . . . . . . . . . . . . . . 10
7.3. Initialization Messages . . . . . . . . . . . . . . . . . 11
7.3.1. ClientInit . . . . . . . . . . . . . . . . . . . . . . 11
7.3.2. ServerInit . . . . . . . . . . . . . . . . . . . . . . 11
7.4. Pixel Format Data Structure . . . . . . . . . . . . . . . 12
7.5. Client-to-Server Messages . . . . . . . . . . . . . . . . 13
7.5.1. SetPixelFormat . . . . . . . . . . . . . . . . . . . . 13
7.5.2. SetEncodings . . . . . . . . . . . . . . . . . . . . . 14
7.5.3. FramebufferUpdateRequest . . . . . . . . . . . . . . . 15
7.5.4. KeyEvent . . . . . . . . . . . . . . . . . . . . . . . 16
7.5.5. PointerEvent . . . . . . . . . . . . . . . . . . . . . 19
7.5.6. ClientCutText . . . . . . . . . . . . . . . . . . . . 19
7.6. Server-to-Client Messages . . . . . . . . . . . . . . . . 20
7.6.1. FramebufferUpdate . . . . . . . . . . . . . . . . . . 20
7.6.2. SetColorMapEntries . . . . . . . . . . . . . . . . . . 21
7.6.3. Bell . . . . . . . . . . . . . . . . . . . . . . . . . 22
7.6.4. ServerCutText . . . . . . . . . . . . . . . . . . . . 22
7.7. Encodings . . . . . . . . . . . . . . . . . . . . . . . . 22
7.7.1. Raw Encoding . . . . . . . . . . . . . . . . . . . . . 23
7.7.2. CopyRect Encoding . . . . . . . . . . . . . . . . . . 23
7.7.3. RRE Encoding . . . . . . . . . . . . . . . . . . . . . 23
7.7.4. Hextile Encoding . . . . . . . . . . . . . . . . . . . 24
7.7.5. TRLE . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.7.6. ZRLE . . . . . . . . . . . . . . . . . . . . . . . . . 30
7.8. Pseudo-Encodings . . . . . . . . . . . . . . . . . . . . . 30
7.8.1. Cursor Pseudo-Encoding . . . . . . . . . . . . . . . . 30
7.8.2. DesktopSize Pseudo-Encoding . . . . . . . . . . . . . 31
8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 31
8.1. RFB Security Types . . . . . . . . . . . . . . . . . . . . 32
8.1.1. Registry Name . . . . . . . . . . . . . . . . . . . . 32
8.1.2. Registry Contents . . . . . . . . . . . . . . . . . . 32
8.2. Client-to-Server Message Types . . . . . . . . . . . . . . 32
8.2.1. Registry Name . . . . . . . . . . . . . . . . . . . . 32
8.2.2. Registry Contents . . . . . . . . . . . . . . . . . . 32
8.3. Server-to-Client Message Types . . . . . . . . . . . . . . 33
8.3.1. Registry Name . . . . . . . . . . . . . . . . . . . . 33
8.3.2. Registry Contents . . . . . . . . . . . . . . . . . . 33
8.4. RFB Encoding Types . . . . . . . . . . . . . . . . . . . . 34
8.4.1. Registry Name . . . . . . . . . . . . . . . . . . . . 34
8.4.2. Registry Contents . . . . . . . . . . . . . . . . . . 34
9. Security . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
10. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 36
11. References . . . . . . . . . . . . . . . . . . . . . . . . . . 36
11.1. Normative References . . . . . . . . . . . . . . . . . . . 36
11.2. Informative References . . . . . . . . . . . . . . . . . . 36
Appendix A. Differences in Earlier Protocol Versions . . . . . . 38
A.1. Differences in the Version 3.3 Protocol . . . . . . . . . 38
A.2. Differences in the Version 3.7 Protocol . . . . . . . . . 38
1. Introduction ๐ธ
RFB ("remote framebuffer") is a simple protocol for remote access to graphical user interfaces ๐ฅ๏ธ Because it works at the framebuffer level, it applies to all windowing systems and applications, including X11, Windows, and Macintosh ๐ RFB is the protocol used in VNC ๐ซถ It's widely implemented and has had fairly good interoperability, which is honestly iconic ๐
The remote endpoint where the user sits (usually with a display, keyboard, and pointer) is called the RFB client or viewer ๐ The endpoint where changes to the framebuffer come from (aka the windowing system and applications) is known as the RFB server ๐ป
RFB is a "thin client" protocol โจ The whole design goal is to put very few requirements on the client ๐ That way clients can run on the widest range of hardware, and building a client is as simple as possible ๐ง
The protocol also makes the client stateless ๐ฆ If a client disconnects from a server and then reconnects to that same server, the state of the user interface is still there ๐ฅน A different client endpoint can connect to the same RFB server too, and the user sees exactly the same GUI as at the original endpoint ๐ญ The interface to the user's apps basically becomes completely mobile ๐ Wherever there's suitable network connectivity, the user can get to their personal applications, and their state is kept between accesses from different locations ๐ It's a familiar, uniform view of the computing infrastructure wherever they go, and it's giving main character energy ๐
The RFB protocol has evolved over the past decade and has been implemented several times, including at least one open source version ๐ This document describes RFB as it's actually implemented, so future implementers can interoperate with existing clients and servers ๐ซถ
2. Initial Connection ๐
An RFB server is typically a long-lived process that keeps the state of a framebuffer ๐ง RFB clients usually connect, talk to the server for a while to use and manipulate the framebuffer, and then disconnect ๐ A later RFB session picks up where the previous one left off, with the framebuffer state still intact ๐
An RFB client contacts the server on TCP port 5900 ๐ On systems with several RFB servers, server N usually listens on port 5900+N, which is basically how X Window servers listen on port 6000+N ๐ช
Some browser-based clients use a Java application to run the RFB protocol โ RFB servers sometimes run a simple HTTP server on port 5800 that serves the needed Java applet ๐
Sometimes the initial roles of client and server are flipped ๐ The RFB client listens on port 5500, and the RFB server contacts the RFB client. Once the connection is established, the two sides take their normal roles, with the RFB server sending the first handshake message โจ
Note that the only port number IANA assigned for RFB is port 5900 ๐ So RFB clients and servers should avoid using other port numbers unless they're talking to servers or clients known to use the non-standard ports ๐
3. Display Protocol ๐ผ๏ธ
The display side of the protocol is built around one graphics primitive: "put a rectangle of pixel data at a given x,y position" ๐ฆ This might seem like an inefficient way to draw lots of UI components, ngl ๐ค But allowing different encodings for the pixel data gives a lot of flexibility to trade off things like network bandwidth, client drawing speed, and server processing speed ๐
A sequence of these rectangles makes a framebuffer update (called "update" for short) ๐๏ธ An update is a change from one valid framebuffer state to another, so it's kinda like a frame of video ๐บ The rectangles in an update are usually, but not always, disjoint ๐งฉ
The update protocol is demand-driven by the client ๐โโ๏ธ That means an update is only sent from the server to the client in response to an explicit request from the client ๐ซถ This gives the protocol an adaptive quality โจ The slower the client and network are, the lower the rate of updates ๐ข With typical apps, changes to the same area of the framebuffer tend to happen soon after one another, so with a slow client or network, transient states can just get skipped ๐ That means less network traffic and less drawing for the client ๐
After the initial handshake sequence, the protocol is asynchronous, with each side sending messages as needed ๐ฌ The server must not send unsolicited updates ๐ซ An update must only be sent in response to a request from the client ๐ When several requests from the client are outstanding, a single update from the server can satisfy all of them ๐
4. Input Protocol โจ๏ธ
okay so the input side is based on a totally standard workstation setup: a keyboard and a multi-button pointing device ๐ฑ๏ธ ๐ Whenever the user presses a key or a pointer button, or moves the pointing device, the client just sends those input events to the server. no cap, it's that simple. These events can also be made from other non-standard I/O devices. Like, a pen-based handwriting recognition engine might generate keyboard events โ๏ธ which is so iconic ๐ญ
5. Representation of Pixel Data ๐จ
When the RFB client and server first start talking, they negotiate the format and encoding of the pixel data that's gonna get sent ๐ซถ This negotiation is designed to make the client's life as easy as possible ๐ The server must always be able to supply pixel data in the form the client wants. However, if the client can handle several different formats or encodings equally well, it may pick the one that's easier for the server to produce โจ
Pixel format is about how individual colors get represented by pixel values ๐ The most common ones are 24-bit or 16-bit "true color", where bit-fields inside the pixel value map straight onto red, green, and blue intensities, and 8-bit "color map" (palette), where the pixel values are indices into a 256-entry table that holds the actual RGB intensities ๐
Encoding is about how a rectangle of pixel data gets sent to the client ๐ฆ Every rectangle is prefixed by a header giving the X,Y position of the rectangle on the screen, the width and height of the rectangle, and an encoding type that says how the pixel data is encoded. The data itself then follows using that encoding ๐ง
The encoding types defined right now are: Raw, CopyRect, RRE, TRLE, Hextile, and ZRLE. In practice, current servers mostly use ZRLE, TRLE, and CopyRect, since they give the best compression for typical desktops ๐ฅ๏ธ Clients generally support Hextile too, which older RFB servers that didn't support TRLE used a lot. See Section 7.7 for a description of each of the encodings ๐ธ
6. Protocol Versions and Extensions ๐ฆ
RFB has gone through three published versions: 3.3, 3.7, and 3.8. This doc mainly covers the final one, 3.8. The differences from the earlier versions are minor and live in Appendix A โจ Under no circumstances should an implementation use a protocol version number other than one defined in this document.
Lowkey the most important bit: over the years, different implementations tried using different version numbers to sneak in undocumented extensions ๐ So to interoperate, any unknown 3.x version has to be treated as 3.3. That means you can't add a 3.9 or higher in a backward-compatible way ๐ญ Future evolution of RFB will use 4.x version numbers.
It's not necessary to change the protocol version number to extend the protocol. Within an existing version, the protocol can be extended by:
- New encodings: A new encoding type can be added relatively easily while keeping compatibility with existing clients and servers ๐ Existing servers will simply ignore requests for a new encoding they don't support. Existing clients will never request the new encoding, so they'll never see rectangles encoded that way ๐
- Pseudo-encodings: In addition to genuine encodings, a client can request a "pseudo-encoding" to declare to the server that it supports a certain extension to the protocol ๐ซง A server that doesn't support the extension will simply ignore the pseudo-encoding. Note that this means the client must assume the server does not support the extension until it gets some extension-specific confirmation from the server. See Section 7.8 for a description of current pseudo-encodings ๐
- New security types: Adding a new security type gives full flexibility in modifying the behavior of the protocol without sacrificing compatibility with existing clients and servers ๐ฅ A client and server that agree on a new security type can effectively talk whatever protocol they like after that. It doesn't necessarily have to be anything like the RFB protocol ๐ฆ
See Section 8 for information on obtaining an ID for a new encoding or security type ๐
7. Protocol Messages ๐จ
The RFB protocol can operate over any reliable transport, either byte-stream or message based. It usually operates over a TCP/IP connection ๐ There are three stages to the protocol. First is the handshaking phase, whose purpose is to agree upon the protocol version and the type of security to be used ๐ค The second stage is an initialization phase, where the client and server exchange ClientInit and ServerInit messages. The final stage is the normal protocol interaction. The client can send whichever messages it wants, and may receive messages from the server as a result. All these messages begin with a message-type byte, followed by message-specific data ๐
The following descriptions of protocol messages use the basic types U8, U16, U32, S8, S16, and S32. These represent, respectively, 8-, 16-, and 32-bit unsigned integers and 8-, 16-, and 32-bit signed integers ๐ข All multiple-byte integers (other than pixel values themselves) are in big endian order (most significant byte first). Some messages use arrays of the basic types, with the number of entries in the array determined from fields preceding the array ๐งฎ
The type PIXEL means a pixel value of bytesPerPixel bytes, where bytesPerPixel is the number of bits-per-pixel divided by 8. The bits-per-pixel is agreed by the client and server, either in the ServerInit message (Section 7.3.2) or a SetPixelFormat message (Section 7.5.1). See Section 7.4 for the detailed description of the pixel format ๐จ
Several message formats include padding bits or bytes. For maximum compatibility, messages should be generated with padding set to zero, but message recipients should not assume padding has any particular value ๐ซถ
7.1. Handshake Messages ๐ค
When an RFB client and server first connect, they exchange a sequence of handshake messages that determine the protocol version, what type of connection security (if any) to use, a password check if the security type requires it, and some initialization information ๐
7.1.1. ProtocolVersion Handshake
Handshaking begins with the server sending the client a ProtocolVersion message. This lets the client know which is the highest RFB protocol version number supported by the server ๐ The client then replies with a similar message giving the version number of the protocol that should actually be used (which may be different to the one quoted by the server). A client should never request a protocol version higher than the one offered by the server. It's intended that both clients and servers may provide some level of backwards compatibility through this mechanism ๐ฆ
The only published protocol versions at this time are 3.3, 3.7, and 3.8. Other version numbers are reported by some servers and clients, but should be interpreted as 3.3, since they don't implement the different handshake in 3.7 or 3.8 ๐ Adding a new encoding or pseudo-encoding type doesn't require a change in protocol version, since a server can simply ignore encodings it doesn't understand โจ
The ProtocolVersion message consists of 12 bytes interpreted as a string of ASCII characters in the format "RFB xxx.yyy\n", where xxx and yyy are the major and minor version numbers, left-padded with zeros ๐ง
RFB 003.008\n (hex 52 46 42 20 30 30 33 2e 30 30 38 0a)
7.1.2. Security Handshake
Once the protocol version has been decided, the server and client must agree on the type of security to be used on the connection. The server lists the security types that it supports ๐
+--------------------------+-------------+--------------------------+
| No. of bytes | Type | Description |
| | [Value] | |
+--------------------------+-------------+--------------------------+
| 1 | U8 | number-of-security-types |
| number-of-security-types | U8 array | security-types |
+--------------------------+-------------+--------------------------+
If the server listed at least one valid security type supported by the client, the client sends back a single byte indicating which security type is to be used on the connection ๐
+--------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+---------------+
| 1 | U8 | security-type |
+--------------+--------------+---------------+
If number-of-security-types is zero, then for some reason the connection failed (e.g., the server cannot support the desired protocol version). This is followed by a string describing the reason (where a string is specified as a length followed by that many ASCII characters) ๐ญ
+---------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+---------------+--------------+---------------+
| 4 | U32 | reason-length |
| reason-length | U8 array | reason-string |
+---------------+--------------+---------------+
The server closes the connection after sending the reason-string ๐
The security types defined in this document are:
+--------+--------------------+
| Number | Name |
+--------+--------------------+
| 0 | Invalid |
| 1 | None |
| 2 | VNC Authentication |
+--------+--------------------+
Other security types exist but are not publicly documented ๐คซ
Once the security-type has been decided, data specific to that security-type follows (see Section 7.2 for details). At the end of the security handshaking phase, the protocol normally continues with the SecurityResult message ๐
Note that after the security handshaking phase, it's possible that further communication happens over an encrypted or otherwise altered channel, if the two ends agree on an extended security type beyond the ones described here ๐โจ
7.1.3. SecurityResult Handshake โจ
okay so the server sends a word ๐ to tell the client whether the security handshaking worked out, bestie ๐
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 4 | U32 | status: |
| | 0 | OK |
| | 1 | failed |
+--------------+--------------+-------------+
if it's successful, the protocol moves on to the initialization phase (Section 7.3) ๐ฆโจ
if it's unsuccessful, the server sends a string explaining why it failed, and then closes the connection ๐ rip
+---------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+---------------+--------------+---------------+
| 4 | U32 | reason-length |
| reason-length | U8 array | reason-string |
+---------------+--------------+---------------+
7.2. Security Types ๐
okay, two security types are defined here, and we're going through both ๐ซถ
7.2.1. None ๐ธ
no authentication needed, literally zero drama ๐ the protocol just keeps going with the SecurityResult message.
7.2.2. VNC Authentication ๐
so VNC authentication is the one we use here. the server sends a random 16-byte challenge ๐ฒ
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 16 | U8 | challenge |
+--------------+--------------+-------------+
the client encrypts that challenge with DES ๐ง, using a password the user typed in as the key. to build the key, the password gets truncated to eight characters, or padded with null bytes on the right. then the client sends back the resulting 16-byte response ๐
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 16 | U8 | response |
+--------------+--------------+-------------+
after that, the protocol continues with the SecurityResult message โจ
โ ๏ธ bestie, this one is serious. this type of authentication is known to be cryptographically weak, and it is not intended for use on untrusted networks ๐ญ a lot of implementations will want stronger security, like running the session over an encrypted channel provided by IPsec [RFC4301] or SSH [RFC4254]. don't be the girl who skips this part ๐
7.3. Initialization Messages ๐
once the client and server agree on a security type (and maybe validate it too), the protocol moves into the initialization stage ๐ง the client sends a ClientInit message first. then the server sends a ServerInit message.
7.3.1. ClientInit ๐
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 1 | U8 | shared-flag |
+--------------+--------------+-------------+
the shared-flag is non-zero (true) if the server should try to share the desktop by leaving other clients connected. it's zero (false) if the server should give this client exclusive access by disconnecting every other client ๐
7.3.2. ServerInit ๐
after receiving the ClientInit message, the server sends a ServerInit message. this tells the client the width and height of the server's framebuffer, its pixel format, and the name associated with the desktop ๐ฅ๏ธโจ
+--------------+--------------+------------------------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+------------------------------+
| 2 | U16 | framebuffer-width in pixels |
| 2 | U16 | framebuffer-height in pixels |
| 16 | PIXEL_FORMAT | server-pixel-format |
| 4 | U32 | name-length |
| name-length | U8 array | name-string |
+--------------+--------------+------------------------------+
the server-pixel-format is the server's natural pixel format. that format gets used unless the client asks for a different one with the SetPixelFormat message (Section 7.5.1) ๐
7.4. Pixel Format Data Structure ๐ธ
several server-to-client messages include a PIXEL_FORMAT, which is a 16-byte structure describing how a pixel gets transmitted ๐ฆ
+--------------+--------------+-----------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-----------------+
| 1 | U8 | bits-per-pixel |
| 1 | U8 | depth |
| 1 | U8 | big-endian-flag |
| 1 | U8 | true-color-flag |
| 2 | U16 | red-max |
| 2 | U16 | green-max |
| 2 | U16 | blue-max |
| 1 | U8 | red-shift |
| 1 | U8 | green-shift |
| 1 | U8 | blue-shift |
| 3 | | padding |
+--------------+--------------+-----------------+
so bits-per-pixel is the number of bits used for each pixel value on the wire ๐ฆ it has to be greater than or equal to the depth, which is the number of useful bits in the pixel value. right now, bits-per-pixel has to be 8, 16, or 32. big-endian-flag is non-zero (true) if multi-byte pixels are read as big endian. the depth should line up with bits-per-pixel and the various -max values, but clients don't use it when they interpret pixel data ๐ค
if true-color-flag is non-zero (true), then the last six items tell you how to pull out the red, green, and blue intensities from the pixel value ๐ red-max is the maximum red value, and it has to be 2^N - 1, where N is the number of bits used for red. note that the -max values are always in big endian order ๐ red-shift is the number of shifts needed to get the red value in a pixel down to the least significant bit. green-max, green-shift, blue-max, and blue-shift work the same way for green and blue.
for example, here's how to find the red value (between 0 and red-max) from a given pixel:
- Swap the pixel value according to big-endian-flag. for instance, if big-endian-flag is zero (false) and host byte order is big endian, then swap.
- Shift right by red-shift.
- AND with red-max (in host byte order).
if true-color-flag is zero (false), then the server doesn't use pixel values built straight from the red, green, and blue intensities. instead, the pixel values act as indices into a color map ๐บ๏ธ the server sets the entries in that color map with the SetColorMapEntries message (See Section 7.6.2) ๐
7.5. Client-to-Server Messages ๐ค
these are the client-to-server message types defined in this document:
+--------+--------------------------+
| Number | Name |
+--------+--------------------------+
| 0 | SetPixelFormat |
| 2 | SetEncodings |
| 3 | FramebufferUpdateRequest |
| 4 | KeyEvent |
| 5 | PointerEvent |
| 6 | ClientCutText |
+--------+--------------------------+
other message types exist, but they aren't publicly documented ๐คซ before a client sends any message other than the ones in this document, it must have figured out that the server supports the relevant extension, which means it has to have received an appropriate extension-specific confirmation from the server ๐ซถ
7.5.1. SetPixelFormat ๐จ
a SetPixelFormat message sets the format that pixel values should be sent in within FramebufferUpdate messages. if the client never sends a SetPixelFormat message, the server sends pixel values in its natural format, which is the one specified in the ServerInit message (Section 7.3.2) โจ
if true-color-flag is zero (false), that means a "color map" is being used. the server can set any of the entries in the color map with the SetColorMapEntries message (Section 7.6.2). right after the client sends this message, the contents of the color map are undefined, even if the server had set entries before ๐ญ chaos, I know.
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [0] | message-type |
| 3 | | padding |
| 16 | PIXEL_FORMAT | pixel-format |
+--------------+--------------+--------------+
PIXEL_FORMAT is as described in Section 7.4 ๐ธ
7.5.2. SetEncodings ๐ฆ
a SetEncodings message sets the encoding types the server is allowed to use when it sends pixel data ๐งฉ the order of the encoding types in this message is just a hint from the client about its preference, with the first encoding listed being the most preferred one. the server can take that hint or leave it ๐ pixel data can always be sent in raw encoding, even if it isn't listed explicitly here.
on top of the real encodings, a client can also request "pseudo-encodings" to tell the server it supports certain extensions to the protocol โจ if the server doesn't support the extension, it just ignores the pseudo-encoding. so the client has to assume the server doesn't support the extension until it gets some extension-specific confirmation from the server ๐ซถ
check Section 7.7 for a description of each encoding, and Section 7.8 for what the pseudo-encodings mean ๐
+--------------+--------------+---------------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+---------------------+
| 1 | U8 [2] | message-type |
| 1 | | padding |
| 2 | U16 | number-of-encodings |
+--------------+--------------+---------------------+
this is followed by number-of-encodings repetitions of the following:
+--------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+---------------+
| 4 | S32 | encoding-type |
+--------------+--------------+---------------+
7.5.3. FramebufferUpdateRequest ๐ฆ
okay so a FramebufferUpdateRequest is how the client basically says "hey, I'm interested in this part of the screen" ๐ It sends x-position, y-position, width, and height to say exactly which area it means. The server usually answers with a FramebufferUpdate, and one FramebufferUpdate can answer several FramebufferUpdateRequests at once. Iconic efficiency ๐
The server assumes the client keeps a copy of every part of the framebuffer it cares about ๐ง So normally the server only needs to send incremental updates, which is so smart and so efficient.
If the client has lost the contents of an area it needs ๐ญ, it sends a FramebufferUpdateRequest with incremental set to zero (false). That's basically the client going "pls send me the WHOLE thing as soon as possible." The area will not be updated using the CopyRect encoding ๐
If the client still has everything it needs in that area ๐ธ, it sends a FramebufferUpdateRequest with incremental set to non-zero (true). Then, if and when that area changes, the server will send a FramebufferUpdate. Heads up though: there may be an indefinite period between the FramebufferUpdateRequest and the FramebufferUpdate. So it might be a while, no cap ๐
And if the client is a fast one โก, it may want to regulate how often it sends incremental FramebufferUpdateRequests so it doesn't flood the network with excessive traffic. Self-control is a whole vibe ๐ซถ
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [3] | message-type |
| 1 | U8 | incremental |
| 2 | U16 | x-position |
| 2 | U16 | y-position |
| 2 | U16 | width |
| 2 | U16 | height |
+--------------+--------------+--------------+
7.5.4. KeyEvent โจ๏ธ
A KeyEvent message indicates a key press or release ๐ The down-flag is non-zero (true) if the key is now pressed, and zero (false) if it's now released. The key itself is specified using the "keysym" values defined by the X Window System ๐ช even if the client or server isn't running the X Window System at all. Bestie, that's a big plot twist.
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [4] | message-type |
| 1 | U8 | down-flag |
| 2 | | padding |
| 4 | U32 | key |
+--------------+--------------+--------------+
For most ordinary keys, the keysym is the same as the corresponding ASCII value โจ For the full details, check out [XLIBREF] or the header file <X11/keysymdef.h> in the X Window System distribution. Some other common keys are:
+-----------------+--------------------+
| Key name | Keysym value (hex) |
+-----------------+--------------------+
| BackSpace | 0xff08 |
| Tab | 0xff09 |
| Return or Enter | 0xff0d |
| Escape | 0xff1b |
| Insert | 0xff63 |
| Delete | 0xffff |
| Home | 0xff50 |
| End | 0xff57 |
| Page Up | 0xff55 |
| Page Down | 0xff56 |
| Left | 0xff51 |
| Up | 0xff52 |
| Right | 0xff53 |
| Down | 0xff54 |
| F1 | 0xffbe |
| F2 | 0xffbf |
| F3 | 0xffc0 |
| F4 | 0xffc1 |
| ... | ... |
| F12 | 0xffc9 |
| Shift (left) | 0xffe1 |
| Shift (right) | 0xffe2 |
| Control (left) | 0xffe3 |
| Control (right) | 0xffe4 |
| Meta (left) | 0xffe7 |
| Meta (right) | 0xffe8 |
| Alt (left) | 0xffe9 |
| Alt (right) | 0xffea |
+-----------------+--------------------+
The interpretation of keysyms is a complex area ๐ฅด so to be as widely interoperable as possible, here are the guidelines that should be followed:
- The "shift state" (basically whether either of the Shift keysyms is down) should only be used as a hint when interpreting a keysym ๐ค For example, '#' is shifted on a US keyboard but not on a UK keyboard. A server with a US keyboard that receives a '#' from a UK client won't have been sent any shift presses. So in that case, the server will likely need to simulate a shift press on its own system to get a '#' and not a '3' ๐
- The difference between upper and lower case keysyms is significant ๐ This is unlike some of the keyboard processing in the X Window System, which treats them as the same. So a server that gets an upper case 'A' keysym with no shift presses should interpret it as an upper case 'A'. Again, this may involve an internal simulated shift press.
- Servers should ignore "lock" keysyms such as CapsLock and NumLock where possible ๐ Instead, they should interpret each character-based keysym according to its case.
- Unlike Shift, the state of modifier keys such as Control and Alt should be taken as modifying the interpretation of other keysyms ๐ Also, there are no keysyms for ASCII control characters such as Ctrl-A. Those should be generated by clients sending a Control press followed by an 'a' press.
- On a client where modifiers like Control and Alt can also be used to generate character-based keysyms, the client may need to send extra "release" events so the keysym gets interpreted correctly ๐งฉ For example, on a German PC keyboard, Ctrl-Alt-Q generates the '@' character. In that case, the client needs to send simulated release events for Control and Alt so the '@' is interpreted correctly, since Ctrl-Alt-@ may mean something completely different to the server ๐
- There's no universal standard for "backward tab" in the X Window System ๐ซ On some systems shift+tab gives the keysym "ISO_Left_Tab", on others it gives a private "BackTab" keysym, and on others it gives "Tab" and applications tell from the shift state that it means backward-tab rather than forward-tab. In the RFB protocol, the latter approach is preferred ๐ Clients should generate a shifted Tab rather than ISO_Left_Tab. However, to be backwards-compatible with existing clients, servers should also recognize ISO_Left_Tab as meaning a shifted Tab.
- Modern versions of the X Window System handle keysyms for Unicode characters, made up of the Unicode character with the hex 1000000 bit set ๐ For maximum compatibility, if a key has both a Unicode and a legacy encoding, clients should send the legacy encoding.
- Some systems give a special interpretation to key combinations such as Ctrl-Alt-Delete ๐ โโ๏ธ RFB clients typically provide a menu or toolbar function to send these combos. The RFB protocol doesn't treat them specially. To send Ctrl-Alt-Delete, the client sends the key presses for left or right Control, left or right Alt, and Delete, followed by the key releases. Many RFB servers accept Shift-Ctrl-Alt-Delete as a synonym for Ctrl-Alt-Delete that can be entered directly from the keyboard โจ๏ธโจ
7.5.5. PointerEvent ๐ฑ๏ธ
A PointerEvent message indicates either pointer movement or a pointer button press or release ๐ฆ The pointer is now at (x-position, y-position), and the current state of buttons 1 to 8 is represented by bits 0 to 7 of button-mask, respectively. 0 means up, 1 means down (pressed).
On a conventional mouse, buttons 1, 2, and 3 correspond to the left, middle, and right buttons ๐ On a wheel mouse, each step of the wheel upwards is represented by a press and release of button 4, and each step downwards is represented by a press and release of button 5. Scroll-brained behavior, but make it structured ๐
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [5] | message-type |
| 1 | U8 | button-mask |
| 2 | U16 | x-position |
| 2 | U16 | y-position |
+--------------+--------------+--------------+
7.5.6. ClientCutText ๐
RFB has limited support for syncing the "cut buffer" of selected text between client and server ๐ This message tells the server that the client has new ISO 8859-1 (Latin-1) text in its cut buffer. Ends of lines are represented by the newline character (hex 0a) alone. No carriage-return (hex 0d) is used. And ngl, there's no way to transfer text outside the Latin-1 character set ๐ฅฒ
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [6] | message-type |
| 3 | | padding |
| 4 | U32 | length |
| length | U8 array | text |
+--------------+--------------+--------------+
7.6. Server-to-Client Messages ๐
The server-to-client message types defined in this document are:
+--------+--------------------+
| Number | Name |
+--------+--------------------+
| 0 | FramebufferUpdate |
| 1 | SetColorMapEntries |
| 2 | Bell |
| 3 | ServerCutText |
+--------+--------------------+
Other private message types exist, but they're not publicly documented ๐คซ Before sending a message other than those described in this document, a server must have determined that the client supports the relevant extension. It does that by receiving some extension-specific confirmation from the client, usually a request for a given pseudo-encoding. Main character energy for protocol negotiation ๐ง
7.6.1. FramebufferUpdate โจ
okay so a FramebufferUpdate is basically the server saying "here are the pixels you asked for, bestie" ๐ It's a bunch of rectangles of pixel data that the client should slap into its framebuffer. The server sends it in response to a FramebufferUpdateRequest from the client. ngl, there can be an indefinite period between the request and the update, so don't panic if the reply takes a minute ๐
+--------------+--------------+----------------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+----------------------+
| 1 | U8 [0] | message-type |
| 1 | | padding |
| 2 | U16 | number-of-rectangles |
+--------------+--------------+----------------------+
The header is followed by number-of-rectangles rectangles of pixel data. Each rectangle starts with its own rectangle header ๐
+--------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+---------------+
| 2 | U16 | x-position |
| 2 | U16 | y-position |
| 2 | U16 | width |
| 2 | U16 | height |
| 4 | S32 | encoding-type |
+--------------+--------------+---------------+
The rectangle header is followed by the pixel data in the specified encoding. See Section 7.7 for the format of the data for each encoding and Section 7.8 for the meaning of pseudo-encodings. It's giving layers, honestly ๐ง
7.6.2. SetColorMapEntries ๐
When the pixel format uses a "color map", this message tells the client that the specified pixel values should be mapped to the given RGB values. The main tea is that this message may only update part of the color map, so it's not always the whole palette ๐ This message should not be sent by the server until after the client has sent at least one FramebufferUpdateRequest, and only when the agreed pixel format uses a color map. Timing matters, bestie โญ
Color map values are always 16 bits, with the range running from 0 to 65535, regardless of the display hardware in use. The color map value for white, for example, is 65535,65535,65535 โจ
The message starts with a header describing the range of colormap entries to be updated.
+--------------+--------------+------------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+------------------+
| 1 | U8 [1] | message-type |
| 1 | | padding |
| 2 | U16 | first-color |
| 2 | U16 | number-of-colors |
+--------------+--------------+------------------+
This header is followed by number-of-colors RGB values, each in this format:
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 2 | U16 | red |
| 2 | U16 | green |
| 2 | U16 | blue |
+--------------+--------------+-------------+
7.6.3. Bell ๐
A Bell message makes an audible signal on the client, if it provides one. So not every client is going to sing for you, and that's valid ๐ฆ
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [2] | message-type |
+--------------+--------------+--------------+
7.6.4. ServerCutText ๐
The server has new ISO 8859-1 (Latin-1) text in its cut buffer. Line endings are represented by the newline character (hex 0a) alone, with no carriage-return (hex 0d) used. And there's no way to transfer text outside the Latin-1 character set, so emoji copy-paste is a no-go here ๐ญ
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 [3] | message-type |
| 3 | | padding |
| 4 | U32 | length |
| length | U8 array | text |
+--------------+--------------+--------------+
7.7. Encodings ๐จ
The encodings defined in this document are the following. Think of them as the different outfits the pixels can wear when they travel ๐
+--------+-----------------------------+
| Number | Name |
+--------+-----------------------------+
| 0 | Raw |
| 1 | CopyRect |
| 2 | RRE |
| 5 | Hextile |
| 15 | TRLE |
| 16 | ZRLE |
| -239 | Cursor pseudo-encoding |
| -223 | DesktopSize pseudo-encoding |
+--------+-----------------------------+
Other encoding types exist but are not publicly documented. Mysterious, iykyk ๐
7.7.1. Raw Encoding ๐
The simplest encoding type is raw pixel data, no frills at all. The data consists of width*height pixel values (where width and height are the width and height of the rectangle). The values simply represent each pixel in left-to-right scan line order, very straightforward โจ All RFB clients must be able to handle pixel data in this raw encoding. RFB servers should only produce raw encoding unless the client specifically asks for some other encoding type, so the raw encoding is the default vibe unless someone requests a glow-up ๐
+----------------------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+----------------------------+--------------+-------------+
| width*height*bytesPerPixel | PIXEL array | pixels |
+----------------------------+--------------+-------------+
7.7.2. CopyRect Encoding ๐ฆ
The CopyRect (copy rectangle) encoding is a very simple and efficient encoding. It can be used when the client already has the same pixel data elsewhere in its framebuffer, so why resend it? ๐ง The encoding on the wire simply consists of an X,Y coordinate, which gives a position in the framebuffer from which the client can copy the rectangle of pixel data. This shows up in a variety of situations, the most common being when the user moves a window across the screen, and when the contents of a window are scrolled. Iconic, genuinely efficient ๐ญ
+--------------+--------------+----------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+----------------+
| 2 | U16 | src-x-position |
| 2 | U16 | src-y-position |
+--------------+--------------+----------------+
For maximum compatibility, the source rectangle of a CopyRect should not include pixels updated by previous entries in the same FramebufferUpdate message. Bestie, don't copy from pixels that just got updated in the same message, that's the whole rule ๐ซถ
7.7.3. RRE Encoding ๐ธ
Note: RRE encoding is obsolescent. In general, ZRLE and TRLE encodings are more compact, so they're the ones to hype up ๐
RRE stands for rise-and-run-length encoding. As its name implies, it is essentially a two-dimensional analogue of run-length encoding, so it's like RLE went to the gym and got wider ๐ช RRE-encoded rectangles arrive at the client in a form that can be rendered immediately by the simplest of graphics engines. RRE is not appropriate for complex desktops, but it can be useful in some situations, so it's not totally cooked ๐ณ
The basic idea behind RRE is partitioning a rectangle of pixel data into rectangular subregions (subrectangles), each of which consists of pixels of a single value. The union of those subregions comprises the original rectangular region. Finding a near-optimal partition into such subrectangles is relatively easy to compute, which is a lowkey win ๐ง
The encoding consists of a background pixel value, Vb (typically the most prevalent pixel value in the rectangle), and a count N. That's followed by a list of N subrectangles, each a tuple <v,x,y,w,h>. Here v (which should be different from Vb) is the pixel value, (x,y) are the coordinates of the subrectangle relative to the top-left corner of the rectangle, and (w,h) are the width and height of the subrectangle. To render the original rectangle, the client draws a filled rectangle of the background pixel value, then draws a filled rectangle for each subrectangle. Layering, but make it pixel art ๐
On the wire, the data begins with this header:
+---------------+--------------+-------------------------+
| No. of bytes | Type [Value] | Description |
+---------------+--------------+-------------------------+
| 4 | U32 | number-of-subrectangles |
| bytesPerPixel | PIXEL | background-pixel-value |
+---------------+--------------+-------------------------+
This is followed by number-of-subrectangles instances of the following structure:
+---------------+--------------+---------------------+
| No. of bytes | Type [Value] | Description |
+---------------+--------------+---------------------+
| bytesPerPixel | PIXEL | subrect-pixel-value |
| 2 | U16 | x-position |
| 2 | U16 | y-position |
| 2 | U16 | width |
| 2 | U16 | height |
+---------------+--------------+---------------------+
7.7.4. Hextile Encoding โจ
Note: Hextile encoding is obsolescent. In general, ZRLE and TRLE encodings are more compact. ๐
okay so Hextile is basically a variation on RRE ๐ฆ The rectangle gets chopped into 16x16 tiles, and the dimensions of each subrectangle get squished into 4 bits each, 16 bits total ๐งฉ The tiles get handled starting at the top left, going left-to-right and then top-to-bottom, and their encoded contents just follow one another in that predetermined order ๐ If the width of the whole rectangle isn't an exact multiple of 16, the last tile in each row gets correspondingly skinnier. Same thing vertically: if the height isn't an exact multiple of 16, each tile in the final row is shorter ๐ธ
Each tile is either raw pixel data or a variation on RRE. Every tile has a background pixel value, as before ๐ The background doesn't need to be explicitly specified for a tile if it matches the background of the previous tile. BUT, and this is important bestie, the background MAY NOT be carried over if the previous tile was raw ๐ญ
If all the subrectangles in a tile share the same pixel value, you can specify it once as the foreground pixel value for the whole tile โจ Just like the background, the foreground can be left unspecified, which means it gets carried over from the previous tile. The foreground MAY NOT be carried over if the previous tile was raw or had the SubrectsColored bit set. It MAY, however, be carried over from a previous tile that has the AnySubrects bit clear, as long as that tile itself carried over a valid foreground from its previous tile ๐ง
The data is each tile encoded in order. Every tile starts with a subencoding type byte, which is a mask made up of a bunch of bits ๐
+--------------+--------------+---------------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+---------------------+
| 1 | U8 | subencoding-mask: |
| | [1] | Raw |
| | [2] | BackgroundSpecified |
| | [4] | ForegroundSpecified |
| | [8] | AnySubrects |
| | [16] | SubrectsColored |
+--------------+--------------+---------------------+
If the Raw bit is set, the other bits are irrelevant ๐ Instead you get width*height pixel values, where width and height are the width and height of the tile. Otherwise, the other bits in the mask work like this:
BackgroundSpecified: If set, a pixel value of bytesPerPixel bytes follows and specifies the background color for this tile. The first non-raw tile in a rectangle MUST have this bit set. If this bit isn't set, then the background is the same as the last tile ๐ForegroundSpecified: If set, a pixel value of bytesPerPixel bytes follows and specifies the foreground color to be used for all subrectangles in this tile. If this bit is set, then theSubrectsColoredbit MUST be zero ๐ฅAnySubrects: If set, a single byte follows and gives the number of subrectangles following. If not set, there are no subrectangles, meaning the whole tile is just solid background color ๐SubrectsColored: If set, then each subrectangle is preceded by a pixel value giving that subrectangle's color, so a subrectangle looks like this:
+---------------+--------------+---------------------+
| No. of bytes | Type [Value] | Description |
+---------------+--------------+---------------------+
| bytesPerPixel | PIXEL | subrect-pixel-value |
| 1 | U8 | x-and-y-position |
| 1 | U8 | width-and-height |
+---------------+--------------+---------------------+
If not set, all the subrectangles are the same color, which is the foreground color. If the ForegroundSpecified bit wasn't set, then the foreground is the same as the last tile. A subrectangle looks like this:
+--------------+--------------+------------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+------------------+
| 1 | U8 | x-and-y-position |
| 1 | U8 | width-and-height |
+--------------+--------------+------------------+
The position and size of each subrectangle get packed into two bytes, x-and-y-position and width-and-height ๐ฆ In x-and-y-position, the most significant 4 bits give the X position and the least significant 4 bits give the Y position. In width-and-height, the most significant 4 bits give the width minus 1, and the least significant 4 bits give the height minus 1. Yes, the minus 1 is doing a lot of work here, and it's honestly so clever ๐ง ๐
7.7.5. TRLE โจ
okay so TRLE stands for Tiled Run-Length Encoding, and it's giving two techniques in one ๐ It mixes tiling, palettization, and run-length encoding. The rectangle gets chopped into 16x16 pixel tiles, going left-to-right and then top-to-bottom, kinda like Hextile ๐งฉ If the rectangle's width isn't an exact multiple of 16, the last tile in each row is skinnier. And if the height isn't an exact multiple of 16, the tiles in the final row are shorter. ๐ธ
TRLE uses a special type called CPIXEL (compressed pixel) ๐ A CPIXEL is basically a PIXEL in the agreed pixel format, with one twist. It gets a more compact form when all of these are true: true-color-flag is non-zero, bits-per-pixel is 32, depth is 24 or less, and all the bits for the red, green, and blue intensities fit in either the least significant 3 bytes or the most significant 3 bytes. When that happens, a CPIXEL is only 3 bytes long, and it holds whichever 3 bytes are the right ones. bytesPerCPixel is just the number of bytes in a CPIXEL. โจ
Every tile starts with a subencoding type byte ๐ฆ The top bit tells you if the tile was run-length encoded: set means yes, clear means no. The bottom 7 bits tell you the palette size. 0 means no palette, 1 means the whole tile is one single color, and 2 through 127 mean a palette of that many entries. The special values 129 and 127 tell you to reuse the palette from the last tile that had one. 129 is with RLE and 127 is without. ๐ฅ
Quick note before we go on: in this part, div(a,b) means a/b, truncated to an integer. ๐งฎ
The possible values of subencoding are:
0: Raw pixel data. width*height pixel values follow (where width and height are the width and height of the tile):
+-----------------------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+-----------------------------+--------------+-------------+
| width*height*BytesPerCPixel | CPIXEL array | pixels |
+-----------------------------+--------------+-------------+
1: A solid tile in one single color. The pixel value follows:
+----------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+----------------+--------------+-------------+
| bytesPerCPixel | CPIXEL | pixelValue |
+----------------+--------------+-------------+
2 to 16: Packed palette types. The paletteSize is the subencoding value, followed by the palette itself, which holds paletteSize pixel values. Then come the packed pixels, where each pixel is a bit field that gives a zero-based index into the palette. For paletteSize 2, the bit field is 1 bit. For paletteSize 3 or 4, it's 2 bits. For paletteSize 5 to 16, it's 4 bits. The bit fields get packed into bytes, with the most significant bits holding the leftmost pixel (that's big endian ๐). For tiles whose width isn't a multiple of 8, 4, or 2 pixels (depending on the case), padding bits fill out each row to a whole number of bytes. ๐งธ
+----------------------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+----------------------------+--------------+--------------+
| paletteSize*bytesPerCPixel | CPIXEL array | palette |
| m | U8 array | packedPixels |
+----------------------------+--------------+--------------+
Here m is how many bytes the packed pixels take up. For paletteSize 2, that's div(width+7,8)*height. For paletteSize 3 or 4, it's div(width+3,4)*height. And for paletteSize 5 to 16, it's div(width+1,2)*height.
17 to 126: Unused ๐ โโ๏ธ (Packed palettes of these sizes wouldn't be any better than palette RLE, so why bother.)
127: Packed palette, reusing the palette from the previous tile. The subencoding byte is followed by the packed pixels, described above for the packed palette types. ๐
128: Plain RLE. The data is a bunch of runs, repeated until the tile is done. Runs are allowed to keep going from the end of one row into the start of the next. Each run is one pixel value followed by the run's length. The length takes one or more bytes. The length works out to one more than the sum of all the length bytes. Any byte that isn't 255 is the final byte. So for example, length 1 is [0], 255 is [254], 256 is [255,0], 257 is [255,1], 510 is [255,254], 511 is [255,255,0], and so on. ๐ฆ
+-------------------------+--------------+-----------------------+
| No. of bytes | Type [Value] | Description |
+-------------------------+--------------+-----------------------+
| bytesPerCPixel | CPIXEL | pixelValue |
| div(runLength - 1, 255) | U8 array | 255 |
| 1 | U8 | (runLength-1) mod 255 |
+-------------------------+--------------+-----------------------+
129: Palette RLE, reusing the palette from the previous tile. After that comes a bunch of runs, repeated until the tile is done, same as described below for 130 to 255. ๐
130 to 255: Palette RLE. This is followed by the palette, which has paletteSize = (subencoding - 128) pixel values:
+----------------------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+----------------------------+--------------+-------------+
| paletteSize*bytesPerCPixel | CPIXEL array | palette |
+----------------------------+--------------+-------------+
After the palette, just like plain RLE, you get a bunch of runs, repeated until the tile is done. A run of length one is just a palette index:
+--------------+--------------+--------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+--------------+
| 1 | U8 | paletteIndex |
+--------------+--------------+--------------+
A run of length more than one is a palette index with the top bit set, followed by the run's length, encoded the same way as plain RLE. ๐
+-------------------------+--------------+-----------------------+
| No. of bytes | Type [Value] | Description |
+-------------------------+--------------+-----------------------+
| 1 | U8 | paletteIndex + 128 |
| div(runLength - 1, 255) | U8 array | 255 |
| 1 | U8 | (runLength-1) mod 255 |
+-------------------------+--------------+-----------------------+
7.7.6. ZRLE โจ
ZRLE stands for Zlib (see [RFC1950] and [RFC1951]) Run-Length Encoding ๐ง It takes an encoding that's a lot like TRLE and wraps it in zlib compression. On the wire, the rectangle starts with a 4-byte length field, and then that many bytes of zlib-compressed data follow. One single zlib "stream" object is used for the whole RFB protocol connection, so ZRLE rectangles MUST be encoded and decoded strictly in order. Lowkey strict, but it's for a reason ๐ซถ
+--------------+--------------+-------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+-------------+
| 4 | U32 | length |
| length | U8 array | zlibData |
+--------------+--------------+-------------+
Once it's uncompressed, the zlibData represents tiles going left-to-right and then top-to-bottom, similar to TRLE, except the tiles are 64x64 pixels this time ๐ท If the rectangle's width isn't an exact multiple of 64, the last tile in each row is smaller. And if the height isn't an exact multiple of 64, the tiles in the final row are shorter.
The tiles are encoded exactly the same way as in TRLE, with one big difference: subencoding MUST NOT take the values 127 or 129. In other words, palettes can't be reused between tiles ๐ โโ๏ธ
The server flushes the zlib stream to a byte boundary at the end of each ZRLE-encoded rectangle. It doesn't need to flush the stream between tiles inside a rectangle. A single rectangle's zlibData can get pretty big, so clients can decode and interpret it bit by bit as it comes in, but they MUST NOT assume that the encoded tile data is byte aligned. Byte alignment is not guaranteed, bestie ๐
7.8. Pseudo-Encodings
An update rectangle with a "pseudo-encoding" doesn't actually carry pixel data. Instead, it lets the server send pretty much any data it wants to the client ๐ง What that data means depends on which pseudo-encoding it is.
7.8.1. Cursor Pseudo-Encoding
A client that requests the Cursor pseudo-encoding is saying "I can draw a pointer cursor myself, locally" ๐ฑ๏ธ That can make things feel way snappier over slow links, honestly iconic โจ The server sets the cursor shape by sending a rectangle with the Cursor pseudo-encoding as part of an update. The rectangle's x-position and y-position give the cursor's hotspot, and its width and height give the cursor's size in pixels. The data is width*height raw pixel values, followed by a shape bitmask, with one bit for each pixel in the cursor rectangle. The bitmask is made of left-to-right, top-to-bottom scan lines. Each scan line gets padded out to a whole number of bytes, and that number is div(width+7,8). Inside each byte, the most significant bit is the leftmost pixel. A bit set to 1 means the matching cursor pixel is valid ๐ฏ
+----------------------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+----------------------------+--------------+---------------+
| width*height*bytesPerPixel | PIXEL array | cursor-pixels |
| div(width+7,8)*height | U8 array | bitmask |
+----------------------------+--------------+---------------+
7.8.2. DesktopSize Pseudo-Encoding
A client that requests the DesktopSize pseudo-encoding is saying it can handle the framebuffer's width and height changing mid-session ๐ The server changes the desktop size by sending a rectangle with the DesktopSize pseudo-encoding as the last rectangle in an update. The rectangle's x-position and y-position get ignored, and its width and height are the new framebuffer width and height ๐ธ
There's no extra data attached to the rectangle. After the desktop size changes, the server MUST assume the client no longer has the old framebuffer contents. That usually leads to a full update of the framebuffer on the next update. But for the best possible compatibility with existing servers, the client SHOULD keep the top-left part of the framebuffer that the old and new sizes have in common ๐
8. IANA Considerations โจ
IANA has allocated port 5900 to the RFB protocol ๐ The other port numbers mentioned in Section 2 are only there for historical context, and they don't match IANA allocations.
Future assignments to the IANA registries created by this specification are to be made through either "Expert Review" or "IESG Approval" (if there is no currently appointed expert), as defined in [RFC5226] ๐
8.1. RFB Security Types ๐
8.1.1. Registry Name
okay so the name of this registry is "Remote Framebuffer Security Types" ๐
8.1.2. Registry Contents
IANA set up a registry for the security types used with the RFB protocol ๐ Here are the initial entries, bestie โจ
+------------+-------------------------+-----------------------+
| Number | Name | References |
+------------+-------------------------+-----------------------+
| 0 | Invalid | (this document) |
| 1 | None | (this document) |
| 2 | VNC Authentication | (this document) |
| 3 to 15 | RealVNC | (historic assignment) |
| 16 | Tight | (historic assignment) |
| 17 | Ultra | (historic assignment) |
| 18 | TLS | (historic assignment) |
| 19 | VeNCrypt | (historic assignment) |
| 20 | GTK-VNC SASL | (historic assignment) |
| 21 | MD5 hash authentication | (historic assignment) |
| 22 | Colin Dean xvp | (historic assignment) |
| 128 to 255 | RealVNC | (historic assignment) |
+------------+-------------------------+-----------------------+
8.2. Client-to-Server Message Types ๐จ
8.2.1. Registry Name
the name of this one is "Remote Framebuffer Client-to-Server Message Types" ๐
8.2.2. Registry Contents
IANA made a registry for the client-to-server message types used with the RFB protocol ๐ฆ The starting entries look like this:
+--------+------------------------------+-----------------------+
| Number | Name | References |
+--------+------------------------------+-----------------------+
| 0 | SetPixelFormat | (this document) |
| 2 | SetEncodings | (this document) |
| 3 | FramebufferUpdateRequest | (this document) |
| 4 | KeyEvent | (this document) |
| 5 | PointerEvent | (this document) |
| 6 | ClientCutText | (this document) |
| 127 | VMWare | (historic assignment) |
| 128 | Nokia Terminal Mode Spec | (historic assignment) |
| 249 | OLIVE Call Control | (historic assignment) |
| 250 | Colin Dean xvp | (historic assignment) |
| 251 | Pierre Ossman SetDesktopSize | (historic assignment) |
| 252 | tight | (historic assignment) |
| 253 | gii | (historic assignment) |
| 254 | VMWare | (historic assignment) |
| 255 | Anthony Liguori | (historic assignment) |
+--------+------------------------------+-----------------------+
8.3. Server-to-Client Message Types ๐ฌ
8.3.1. Registry Name
this registry is called "Remote Framebuffer Server-to-Client Message Types" ๐ธ
8.3.2. Registry Contents
IANA set up a registry for the server-to-client message types used with the RFB protocol ๐ง The initial entries are:
+--------+--------------------------+-----------------------+
| Number | Name | References |
+--------+--------------------------+-----------------------+
| 0 | FramebufferUpdate | (this document) |
| 1 | SetColourMapEntries | (this document) |
| 2 | Bell | (this document) |
| 3 | ServerCutText | (this document) |
| 127 | VMWare | (historic assignment) |
| 128 | Nokia Terminal Mode Spec | (historic assignment) |
| 249 | OLIVE Call Control | (historic assignment) |
| 250 | Colin Dean xvp | (historic assignment) |
| 252 | tight | (historic assignment) |
| 253 | gii | (historic assignment) |
| 254 | VMWare | (historic assignment) |
| 255 | Anthony Liguori | (historic assignment) |
+--------+--------------------------+-----------------------+
8.4. RFB Encoding Types ๐จ
8.4.1. Registry Name
the name here is "Remote Framebuffer Encoding Types" ๐
8.4.2. Registry Contents
IANA made a registry for the encoding types used with the RFB protocol ๐ The initial entries are:
+-------------------+----------------------------+------------------+
| Number | Name | References |
+-------------------+----------------------------+------------------+
| 0 | Raw | (this document) |
| 1 | CopyRect | (this document) |
| 2 | RRE | (this document) |
| 5 | Hextile | (this document) |
| 16 | ZRLE | (this document) |
| -239 | Cursor pseudo-encoding | (this document) |
| -223 | DesktopSize | (this document) |
| | pseudo-encoding | |
| 4 | CoRRE | (historic |
| | | assignment) |
| 6 | zlib | (historic |
| | | assignment) |
| 7 | tight | (historic |
| | | assignment) |
| 8 | zlibhex | (historic |
| | | assignment) |
| 15 | TRLE | (this document) |
| 17 | Hitachi ZYWRLE | (historic |
| | | assignment) |
| 1024 to 1099 | RealVNC | (historic |
| | | assignment) |
| -1 to -222 | tight options | (historic |
| | | assignment) |
| -224 to -238 | tight options | (historic |
| | | assignment) |
| -240 to -256 | tight options | (historic |
| | | assignment) |
| -257 to -272 | Anthony Liguori | (historic |
| | | assignment) |
| -273 to -304 | VMWare | (historic |
| | | assignment) |
| -305 | gii | (historic |
| | | assignment) |
| -306 | popa | (historic |
| | | assignment) |
| -307 | Peter Astrand DesktopName | (historic |
| | | assignment) |
| -308 | Pierre Ossman | (historic |
| | ExtendedDesktopSize | assignment) |
| -309 | Colin Dean xvp | (historic |
| | | assignment) |
| -310 | OLIVE Call Control | (historic |
| | | assignment) |
| -412 to -512 | TurboVNC fine-grained | (historic |
| | quality level | assignment) |
| -523 to -524 | Nokia Terminal Mode Spec | (historic |
| | | assignment) |
| -763 to -768 | TurboVNC subsampling level | (historic |
| | | assignment) |
| 0x574d5600 to | VMWare | (historic |
| 0x574d56ff | | assignment) |
+-------------------+----------------------------+------------------+
9. Security ๐
okay this one is SERIOUS bestie, so listen up ๐จ The RFB protocol as defined here gives you no security beyond the optional and cryptographically weak password check described in Section 7.2.2. It doesn't protect against anyone observing or tampering with the data stream at all. It has typically been used on secure physical or virtual networks.
Heads up: the base protocol is not a fortress ๐ Anyone who can see or touch the data stream can see or touch it. Plan accordingly.
Security methods beyond the ones described here may be used to protect the integrity of the data ๐ก๏ธ The client and server might agree to use an extended security type to encrypt the session. The session might also be sent over a secure channel such as IPsec [RFC4301] or SSH [RFC4254] ๐
10. Acknowledgements ๐ซถ
James Weatherall, Andy Harter, and Ken Wood also helped design the RFB protocol, so shoutout to them ๐
RFB and VNC are registered trademarks of RealVNC Ltd. in the U.S. and in other countries ๐ผ
11. References ๐
11.1. Normative References
[RFC1950] Deutsch, L. and J-L. Gailly, "ZLIB Compressed Data Format
Specification version 3.3", RFC 1950, May 1996.
[RFC1951] Deutsch, P., "DEFLATE Compressed Data Format Specification
version 1.3", RFC 1951, May 1996.
[XLIBREF] Nye, A., "XLIB Reference Manual R5", June 1994.
11.2. Informative References
[RFC4254] Ylonen, T. and C. Lonvick, "The Secure Shell (SSH)
Connection Protocol", RFC 4254, January 2006.
[RFC4301] Kent, S. and K. Seo, "Security Architecture for the
Internet Protocol", RFC 4301, December 2005.
[RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
IANA Considerations Section in RFCs", BCP 26, RFC 5226,
May 2008.
Appendix A. Differences in Earlier Protocol Versions ๐ฐ๏ธ
for maximum interoperability, clients and servers should be prepared to fall back to the earlier 3.3 and 3.7 versions of the RFB protocol ๐ Any version reported other than 3.7 or 3.8 should be treated as 3.3, no exceptions ๐
all of the differences happen in the initial handshake phase. Once the session reaches the ClientInit and ServerInit messages, all three protocol versions are identical โจ Even within a single protocol version, clients and servers may support different subsets of the encoding and pseudo-encoding types ๐ฆ
A.1. Differences in the Version 3.3 Protocol โจ
okay so this is the 3.3 version, and bestie, the ProtocolVersion message is literally just this string ๐
RFB 003.003\n (hex 52 46 42 20 30 30 33 2e 30 30 33 0a)
in the security handshake (see Section 7.1.2), 3.3 is lowkey different. Instead of a two-way negotiation, the server picks the security type and sends one single word ๐
+--------------+--------------+---------------+
| No. of bytes | Type [Value] | Description |
+--------------+--------------+---------------+
| 4 | U32 | security-type |
+--------------+--------------+---------------+
The security-type may only take the value 0, 1, or 2. ๐ซถ A value of 0 means the connection has failed, and it's followed by a string giving the reason, as described in Section 7.1.2. Yikes ๐ญ
If the security-type is 1, which is for no authentication, the server does not send the SecurityResult message. It just goes straight to the initialization messages (Section 7.3). No cap, it's giving speedrun ๐โโ๏ธ๐จ
In VNC Authentication (Section 7.2.2), if the authentication fails, the server sends the SecurityResult message, but it does not send an error message before closing the connection. Not me being like "wait, why no error message?" ๐
A.2. Differences in the Version 3.7 Protocol ๐ธ
the 3.7 version has its own ProtocolVersion message, and it's giving a slightly different number ๐ฆ
RFB 003.007\n (hex 52 46 42 20 30 30 33 2e 30 30 37 0a)
After the security handshake, if the security-type is 1, for no authentication, the server does not send the SecurityResult message. It proceeds directly to the initialization messages (Section 7.3). Iconic ๐
In VNC Authentication (Section 7.2.2), if the authentication fails, the server sends the SecurityResult message, but does not send an error message before closing the connection. Same energy as 3.3, periodt ๐
Authors' Addresses ๐
Tristan Richardson ๐
RealVNC Ltd.
Betjeman House, 104 Hills Road
Cambridge CB2 1LQ
UK
Phone: +44 1223 310400
EMail: standards@realvnc.com
URI: http://www.realvnc.com
John Levine ๐ง
RealVNC Ltd.
Phone: +44 1223 790005
EMail: standards@taugh.com
URI: http://jl.ly
๐ฆ where this page comes from
- ietf: rfc6143 2011-03
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms