The SPICE protocol supports guests with more than one monitor. Windows guests and Linux guests behave somewhat differently, so they need to be configured in slightly different ways.
Both Windows and Linux guests should be configured with a QXL video device and a spice vdagent to take full advantage of the multiple monitor functionality.
One major difference between Linux and Windows QXL devices is that the Linux QXL driver supports multiple displays (up to 4) with a single video device, whereas the Windows QXL driver only supports a single display for each video device. So to enable 4 monitors, a Linux guest would need only a single QXL device, while a Windows guest would need to be configured with 4 separate QXL devices.
Since qemu 2.4.0, it is possible to limit the number of displays that a single linux driver supports by setting the qxl-vga.max_outputs. If you are using libvirt to configure your guest, you may need to ensure that the heads parameter for the video device is set properly.
If you want to use multiple displays on your guest, you need to make sure that the device has enough video memory to support the number and size of screens you intend to use. There are several QXL parameters in qemu that you can use to control the amount of memory allocated to the QXL devices. These parameters are:
The QXL driver device has two memory bars. ram_size/ram_size_mb controls the size of the first memory bar, and vram_size/vram_size_mb/vram64_size_mb controls the size of the second memory bar. vgamem_mb is located within the first memory bar, which means that ram_size_mb is constrained to be a least 2 times as large as vgamem_mb (but should be considerably larger for best results).
These memory bars are used in different ways by different QXL drivers. In the Windows QXL driver and non-KMS Linux drivers (e.g. RHEL 6), vgamem_mb determines the size of the primary surface, and therefore determines the maximum resolution of the device. For devices that support multiple displays with a single device (i.e. Linux drivers), this primary surface must be large enough to contain all displays. This bar is also used for dynamic memory such as cursors, commands, and images. The second bar is used for offscreen memory. Offscreen memory is not used in RHEL6 or Windows, so this second bar is generally not used at all in these guests.
In KMS-based Linux drivers (e.g. RHEL7), all surfaces (including the primary surface) are allocated in the second memory bar and the first memory bar is used for dynamic memory such as cursors, commands, images, and monitor configs. Therefore, devices that support multiple displays will need larger values for vram_size / vram_size_mb.
64 bit drivers support a larger second memory bar that can be configured with vram64_size_mb. If vram_size_mb is configured to 32 and vram64_size_mb is configured to 128, you will get a 32 bit memory bar of 32 megabytes and a 64 bit memory bar of 128 megabytes. The first 32 megabytes of the 64 bit memory bar will contain the same data as the 32 bit memory bar.
The following table gives some recommended memory values for different guests:
Guest OS | Monitors | QXL devices | ram_size_mb | vgamem_mb | vram_size_mb |
---|---|---|---|---|---|
Linux w/ UMS driver (e.g. RHEL6) | 1 | 1 | 64 | 16 | 8 |
2 | 1 | 128 | 32 | 8 | |
4 | 1 | 256 | 64 | 8 | |
Linux w/ KMS driver (e.g. RHEL7) | 1 | 1 | 8 [1] | 16 | 32 |
2 | 1 | 8 [1] | 32 | 64 | |
4 | 1 | 8 [1] | 64 | 128 | |
Windows | 1 | 1 | 64 | 16 | 8 |
2 | 2 | 64 | 16 | 8 | |
4 | 4 | 64 | 16 | 8 |
[1] | (1, 2, 3) Note that at the time of writing, qemu does not allow you to configure the first memory bar (specified by ram_size_mb) to a value smaller than 2 * vgamem_mb, even though the KMS driver does not use the first memory bar for display surfaces. |
Additional displays are enabled and disabled via the spice protocol using agent data messages on the Main channel. When the client wants to adjust the display of the guest (either adjusting the resolution of displays, enabling a new display, or disabling an existing display), it constructs a new VD_AGENT_MONITORS_CONFIG message that specifies the dimensions of all of the monitors that should be enabled. This message is then sent to the server. The server receives this message and attempts to adjust the display to match the new configuration. If a display that is currently enabled is not listed in this message (or if it is explicitly set to a height or width of 0), that display will be disabled.
After the displays are reconfigured, the server sends a MONITORS_CONFIG message on the DisplayChannel associated with the display that was changed. When the client receives this message it should update its displays to match the current state of the guest.
As mentioned above, the Linux QXL device supports multiple displays in a single device, whereas the Windows QXL device requires a separate device for each display. This has implications at the protocol level as well. A Linux guest will have a single DisplayChannel which manages up to 4 displays on the same channel. A Windows guest will have multiple Display channels, one for each device and each managing a single display.
Since many different components are involved, the implementation can vary significantly between different guest operating systems or even between different versions of the components. Several of these differences have already been discussed above, but there are others to be aware of as well.
When the server receives a new monitors config message from the client, the server checks whether the qxl device implementation provides a monitors config callback function. If it does, that callback is executed. If that callback is not provided, the monitors config message is instead sent via a char device to the spice vdagent running within the guest. These different approaches will be discussed in more detail below.
Whenever the guest display configuration changes, the QXL device will be updated and the server will send new monitors config messages to the client to notify the client of the changes. Note that different guests may change the display configuration in different ways and the change is not necessarily done atomically. In other words, when a client requests a display configuration change, the change may happen in several steps (e.g. enable an additional display and then move that display to the proper location and then resize that display to the appropriate resolution). These different steps may each result in a monitors config message being sent from the server to the client. Because of this, there is no guarantee of a one-to-one relationship between a monitors config request from a client and a monitors config reply from the server.
Software components within the guest are responsible for doing the display reconfiguration. The implementation can vary based on the guest.
Older versions of the QXL driver do not provide the callback mentioned above and instead handle all monitor configuration changes via the spice-vdagent executable running within the guest. The configuration is sent from the host to the guest via the vdagent char device. The spice-vdagent executable then uses platform APIs (e.g. xrandr on Linux) to attempt to configure the displays to match the requested configuration.
Newer versions of the QXL implementation (for example, RHEL7 and newer) provide an implementation of the monitors config callback function mentioned above. In broad terms, this callback function updates some internal ROM data within the virtual device and then triggers an interrupt within the guest. The guest driver handles this interrupt and generates a hotplug event. This hotplug event is then handled by the userspace software in the same way a normal physical hotplug event would be handled. This has the advantage of consolidating all of the monitor reconfiguration functionality within a single component (in GNOME3, that component is the window manager) instead of both the vdagent and the desktop environment responsible display configuration in different situations. In the past this duplication has caused minor issues where the desktop environment attempted to apply a saved display configuration that conflicted with the vdagent.
The spice-gtk library is the main library used for implementing spice clients. Although spice-gtk provides the means to support multiple monitors, not all clients implement that feature. The most full-featured client at the moment is virt-viewer, so that is the client that we will address here.
When a client connects to the spice server, it will receive a monitors config message from the display channel indicating the current display state of the guest. The client receives this message and opens a window for each monitor and sets the window to the appropriate size.
When a client window is resized, the client sends a monitors config message to the server to adjust the guest display resolution to match the size of the client window. The client has a timeout to avoid sending excessive and repeated monitor configs while the user is resizing window.
When a client window is closed, the client sends a monitors config message to the server to disable the closed display. And when a new window is opened, the client sends a monitors config message to the server to enable an additional display. When the guest configuration is updated, the server will send a new monitors config message back to the client.
When virt-viewer constructs a new monitors config message to send to the server, it places one display at (0,0) and arranges additional monitors linearly in a horizontal row without gaps. It uses the locations of the client windows to determine how to order the displays. This helps with predictability because the guest displays will be arranged in roughly the same order as the client windows.
One exception to this linear arrangement is in fullscreen mode. In fullscreen mode, virt-viewer attempts to enable a single guest display for each physical client monitor and set the resolution of the guest displays to be equal to the resolution of the client monitors. It also arranges the guest displays in the same arrangement as the client monitors. For example, if the client has four monitors arranged in a 2x2 grid, the guest displays will also be configured to a 2x2 grid instead of a horizontal array of 4 monitors.
One additional feature of virt-viewer is the ability to use a configuration file to fine-tune the fullscreen behavior. If your client machine has multiple monitors, but you want to run virt-viewer in fullscreen mode on only a subset of those monitors, you can specify which monitors to use. Perhaps you want to retain one client monitor to use for local applications, but want to display the remote guest on the second and third monitors. To do so, you can edit the virt-viewer configuration file (located at ~/.config/virt-viewer/settings on linux). This behavior is controlled by the monitor-mapping configuration option. monitor-mapping is a semi-colon separated array of mappings from guest display IDs to client monitor IDs. For example, the following configuration will apply to every guest that you connect to. It will place the first guest display on the second client monitor and will place the second guest display on the third client monitor:
monitor-mapping=1:2;2:3
If you want the same configuration applied to every remote guest, the monitor-mapping option should be placed within the [fallback] group (create the group if it doesn't already exist).
[fallback] monitor-mapping=1:2;2:3
If you want the configuration to apply to only a single guest, then create a group named according to the UUID of the appropriate guest and place the configuration option within that group. For example:
[139eb0bb-9cfd-4783-85ed-a70bbd362c2d] monitor-mapping=1:2;2:3
This configuration option only affects fullscreen mode, and there are some limitations. For example, guest displays should be specified in order without any gaps. For example, you should not try to enable only guest display 2. If the second display is enabled, the first should also be enabled. Also note that since this configuration is specified in the client's settings file, it will only apply to that particular client machine.
While a vdagent and QXL driver are not absolutely required, they are highly recommended.
Without a spice vdagent running in the guest, it may be possible to enable or disable displays from within the guest (and the spice client will adapt to those changes), but it will not be possible to enable or disable displays from the client.
Without a QXL driver in the guest, it may be possible to resize a display, but it will not be possible to resize it to an arbitrary resolution. Each display will only support a fixed set of resolutions.