RFC 6143

The Remote Framebuffer Protocol

๐Ÿ“… published March 2011, from ietf

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 (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 the SubrectsColored bit 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