248 lines
11 KiB
Markdown
248 lines
11 KiB
Markdown
# Sommelier - Nested Wayland compositor with support for X11 forwarding
|
||
|
||
Sommelier is an implementation of a Wayland compositor that delegates
|
||
compositing to a 'host' compositor. Sommelier includes a set of features that
|
||
allows it to run inside a tight jail or virtual machine.
|
||
|
||
Sommelier can run as service or as a wrapper around the execution of a
|
||
program. As a service, it spawns new processes as needed to service clients.
|
||
The parent process is called the master sommelier.
|
||
|
||
## Sommeliers
|
||
|
||
### Master Sommelier
|
||
|
||
The master sommelier instance will create a wayland socket in XDG_RUNTIME_DIR
|
||
and accept connections from regular wayland clients. Each connection will be
|
||
serviced by spawning a child sommelier process.
|
||
|
||
### X11 Sommelier
|
||
|
||
An X11 sommelier instance provides X11 forwarding. Xwayland is used to
|
||
accomplish this. A single X11 sommelier instance is typically shared across
|
||
all X11 clients as they often expect that they can use a shared X server for
|
||
communication. If the X11 sommelier instance crashes in this setup, it takes
|
||
all running X11 programs down with it. Multiple X11 sommelier instances
|
||
can be used for improved isolation or when per-client configuration is
|
||
needed, but it will be at the cost of losing the ability for programs to use
|
||
the X server for communication between each other.
|
||
|
||
### Peer Sommelier
|
||
|
||
Each Linux program that support the Wayland protocol can have its own sommelier.
|
||
This provides better use of multiple cores when servicing clients, and it
|
||
prevents errors in one client from causing other clients to crash.
|
||
|
||
## Host Compositor Channel
|
||
|
||
Sommelier needs a channel to the host compositor in order to serve Wayland
|
||
clients inside a container. If the container environment provides a socket
|
||
that can be used to establish a connection to the host compositor, then
|
||
pointing sommelier to this socket using the `--display=DISPLAY` flag is
|
||
sufficient.
|
||
|
||
### VirtWL
|
||
|
||
The VirtWL device can be used to establish a new connection when no socket
|
||
is available (typically when running inside a VM). If a VirtWL device has been
|
||
specified (e.g. `--virtwl-device=/dev/wl0`) then sommelier will use this
|
||
mechanism by default to establish new channels between the host compositor and
|
||
sommelier instances. Data is forwarded between the VirtWL device and the core
|
||
Wayland dispatch mechanism using non-blocking I/O multiplexing.
|
||
|
||
## Shared Memory Drivers
|
||
|
||
Shared memory allocated inside a container cannot always be shared with the
|
||
host compositor. Sommelier provides a shared memory driver option as a
|
||
solution for this. What's the most appropriate option depends on the host
|
||
compositor and device drivers available for allocating buffers.
|
||
|
||
### Noop
|
||
|
||
The `noop` shared memory driver simply forwards buffers to the host without
|
||
any special processing. This requires that the client inside the container is
|
||
using memory that can be shared with the host compositor.
|
||
|
||
### VirtWL
|
||
|
||
The `virtwl` driver creates a set of intermediate virtwl buffers for each
|
||
surface, and copies minimal damaged areas from the client’s standard shared
|
||
memory buffers into the virtwl buffers that can be shared with the host
|
||
compositor.
|
||
|
||
### VirtWL-DMABuf
|
||
|
||
The `virtwl-dmabuf` works the same way as the `virtwl` driver but allocates
|
||
buffers that can be shared with the host compositor using the linux_dmabuf
|
||
protocol. The benefits of using this driver over the basic `virtwl` driver
|
||
are:
|
||
|
||
* Larger set of supported formats (E.g NV12).
|
||
* Host compositor can avoid expensive texture uploads.
|
||
* HW overlays can be used for presentation if support by the host compositor.
|
||
|
||
### DMABuf
|
||
|
||
The `dmabuf` driver is similar to the `virtwl-dmabuf` driver. It creates a set
|
||
of intermediate buffers for each surface and copies minimal damaged areas from
|
||
the client’s standard shared memory buffer into the DMABuf buffer. However,
|
||
the buffer is allocated using a DRM device and a prime FD is used to access
|
||
buffer memory inside the container. Intermediate buffers are shared with the
|
||
host compositor using the linux_dmabuf protocol.
|
||
|
||
## Damage Tracking
|
||
|
||
Shared memory drivers that use intermediate buffers require some form of
|
||
damage tracking in order to update intermediate buffers.
|
||
|
||
### Surface Buffer Queue
|
||
|
||
Each client surface in sommelier is associated with a buffer queue. Each
|
||
buffer in the buffer queue has a region (list of rectangles) that describes
|
||
the part of the buffer that is damaged compared to the last frame submitted
|
||
by the client. This provides high precision damage tracking across multiple
|
||
frames. Each new frame from the client adds damage to existing buffers. When
|
||
submitting a frame to the host compositor, the next available buffer is
|
||
dequeued and updated to not contain any damage. This is done by copying
|
||
contents from the current client buffer into the dequeued buffer.
|
||
|
||
The client's buffer is released as soon as this copy operation described above
|
||
is complete and the client can then reuse the shared memory buffer for another
|
||
frame.
|
||
|
||
Note: It is important to release the buffer immediately as clients don’t
|
||
expect it to be held by the compositor for long when using shared memory.
|
||
|
||
### Back Pressure
|
||
|
||
Sommelier doesn’t provide any back pressure for when the client is producing
|
||
contents faster than the host compositor can consume it. The size of the
|
||
buffer queue can as a result grow large. This is not a problem as Xwayland
|
||
and other clients handle back pressure themselves using Wayland frame
|
||
callbacks or similar mechanism.
|
||
|
||
## Data Drivers
|
||
|
||
Socket pairs created inside a container cannot always be shared with the
|
||
host compositor. Sommelier provides a data driver option as a solution
|
||
for this.
|
||
|
||
### Noop
|
||
|
||
The `noop` driver simply forwards socket pair FDs to the host without any
|
||
special processing. This requires that the client inside the container is
|
||
using socket pairs that can be shared with the host compositor.
|
||
|
||
### VirtWL
|
||
|
||
The `virtwl` driver creates a special pipe that can be shared with the host
|
||
compositor and forwards all data received over this pipe to the client FD.
|
||
Forwarding is done using non-blocking I/O multiplexing.
|
||
|
||
## Flags and Settings
|
||
|
||
Sommelier has two forms of configuration. Command line flags and environment
|
||
variables. Standard practice is to expose each option both as a command line
|
||
flag and as an environment variable. Command line flags will always override
|
||
the configuration provided by environment variables. This makes it easy to
|
||
run sommelier as a systemd service and allow the system-wide configuration
|
||
to be overridden using a local user provided systemd override file.
|
||
|
||
## Density and Scaling
|
||
|
||
A protocol aware proxy compositor between the client and the host compositor
|
||
makes it easier to support Linux programs that lack good HiDPI support.
|
||
It can also be used to adjust the scale of contents to support the dynamic
|
||
density changes that Chrome OS UI provide, and it gives the user an option
|
||
override any density decisions made by the host compositor. For example,
|
||
HiDPI aware programs can run at native display resolution, while some older
|
||
programs can use half of that resolution.
|
||
|
||
### Contents Scaling
|
||
|
||
Contents scaling can be applied to both native wayland clients and X11
|
||
clients. It can be controlled using the `--scale=SCALE` flag or
|
||
`SOMMELIER_SCALE=SCALE` variable. Where `SCALE` is a display density
|
||
multiplier. For example, if the default density is 200 DPI, then using
|
||
`--scale=0.5` will result in contents produced for 100 DPI.
|
||
|
||
### Scale Factor
|
||
|
||
An optimal scale factor is calculated for Wayland clients based on contents
|
||
scale setting and the current host compositor scaling. This allows Wayland
|
||
clients to produce contents at an optimal resolution for all combinations of
|
||
scaling used by sommelier and the host compositor.
|
||
|
||
### DPI
|
||
|
||
An exact value for DPI is calculated by sommelier. However, many Linux
|
||
programs expect DPI to be one out of a well known set of values. Sommelier
|
||
solves this by adjusting DPI using a set of buckets. For example, given the
|
||
default set of buckets (72, 96, 160, 240), Sommelier will use 96 as DPI when
|
||
the exact value is 112, or 160 when exact value is 188. The DPI buckets that
|
||
sommelier should use can be specified with `--dpi=[DPI[,DPI...]]`. Where,
|
||
`--dpi=””` will result in sommelier exposing the exact DPI value to clients.
|
||
|
||
### XCursor
|
||
|
||
Sommelier will set `XCURSOR_SIZE` environment variable automatically based on
|
||
the contents scale and preferred host compositor scale factor.
|
||
|
||
## Accelerators
|
||
|
||
If the host compositor support dynamic handling of keyboard events, then
|
||
keyboard shortcuts are forwarded to the Linux program by default. A small set
|
||
of shortcuts are expected to be reserved by the host compositor. A list of
|
||
reserved shortcuts on Chrome OS can be found
|
||
[here](https://chromium.googlesource.com/chromium/src/+/master/ash/accelerators/accelerator_table.h#22).
|
||
|
||
There’s unfortunately no reliable way to detect if a Linux program handled a
|
||
key event or not. This means that all non-reserved shortcuts that the user
|
||
want the host compositor to handle needs to be explicitly listed as an
|
||
accelerator. For example, on Chrome OS, the launcher can be brought up using
|
||
the "launcher" button during normal usage. The "launcher" button event is
|
||
forwarded to Linux programs by default so it won’t work when a Linux program
|
||
has keyboard focus unless this shortcut is explicitly listed as an accelerator.
|
||
|
||
Sommelier provides the `--accelerator=ACCELERATORS` flag for this purpose.
|
||
`ACCELERATORS` is a comma separated list of accelerators that shouldn’t be
|
||
forwarded to the Linux program but instead handled by the host compositor.
|
||
Each accelerator can contain a list of modifiers (e.g. `<Control><Alt>`) and
|
||
must be followed by an XKB keysym. The `xev` utility can be used to determine
|
||
what the XKB keysym is for a specific key. Given the launcher button example
|
||
above (which happens to have XKB keysym `Super_L` on the Chromebook Pixel),
|
||
`--accelerators=Super_L` needs to be passed to sommelier for the this button to
|
||
bring up the application launcher when Linux programs have keyboard focus.
|
||
|
||
Consistent with other flags, `SOMMELIER_ACCELERATORS` environment variable can
|
||
be used as an alternative to the command line flag.
|
||
|
||
## Examples
|
||
|
||
Start master sommelier and use wayland-1 as name of socket to listen on:
|
||
|
||
```
|
||
sommelier --master --socket=wayland-1
|
||
```
|
||
|
||
Start sommelier that runs weston-terminal with density scale multiplier 1.5:
|
||
|
||
```
|
||
sommelier --scale=1.5 weston-terminal
|
||
```
|
||
|
||
Start sommelier that runs inkscape with density scale multiplier 0.75 and 120
|
||
dots per inch (note that -X is specified as inkscape is an X11 client and
|
||
requires X11 forwarding):
|
||
|
||
```
|
||
sommelier -X --scale=0.75 --dpi=120 inkscape
|
||
```
|
||
|
||
Start sommelier that runs gedit with some accelerators reserved to the host
|
||
compositor instead of being sent to gedit:
|
||
|
||
```
|
||
sommelier --accelerators="<Alt>Bracketright,<Alt>Bracketleft" gedit
|
||
```
|