logo

drewdevault.com

[mirror] blog and personal website of Drew DeVault git clone https://hacktivis.me/git/mirror/drewdevault.com.git

Wayland-shells.md (14366B)


  1. ---
  2. date: 2018-07-29
  3. layout: post
  4. title: "Writing a Wayland compositor with wlroots: shells"
  5. tags: [wlroots, wayland, instructional]
  6. ---
  7. I apologise for not writing about wlroots more frequently. I don't really enjoy
  8. working on the McWayface codebase this series of blog posts was originally
  9. about, so we're just going to dismiss that and talk about the various pieces of
  10. a Wayland compositor in a more free-form style. I hope you still find it useful!
  11. Today, we're going to talk about shells. But to make sure we're on the same page
  12. first, a quick refresher on surfaces. A basic primitive of the Wayland protocol
  13. is the concept of a "surface". A surface is a rectangular box of pixels sent
  14. from the client to the compositor to display on-screen. A surface can source
  15. its pixels from a number of places, including raw pixel data in memory, or
  16. opaque handles to GPU resources that can be rendered without copying pixels on
  17. the CPU. These surfaces can also evolve over time, using "damage" to indicate
  18. which parts have changed to reduce the workload of the compositor when
  19. re-rendering them. However, making a surface and filling it with pixels is not
  20. enough to get the compositor to show them.
  21. Shells are how surfaces in Wayland are given meaning. Consider that there are
  22. several kinds of surfaces you'll encounter on your desktop. There are
  23. application windows, sure, but there are also tooltips, right-click menus and
  24. menubars, desktop panels, wallpapers, lock screens, on-screen keyboards, and so
  25. on. Each of these has different semantics - your wallpaper cannot be minimized
  26. or dragged around and resized, but your application windows can be. Likewise,
  27. your application windows cannot cover the entire screen and soak up all input
  28. like your lock screen can. Each of these use cases is fulfilled with a *shell*,
  29. which generally takes a surface resource, assigns it a role (e.g. application
  30. window), and returns a handle with shell-specific interfaces for manipulating
  31. it.
  32. ## Shells in wlroots
  33. I want to first discuss features common to shells as implemented by wlroots.
  34. Each shell has a shell-specific interface that sits on top of the surface. Each
  35. time a client connects and creates one of these, the shell raises a `wl_signal`,
  36. `events.new_surface`, and passes to it a pointer to a shell-specific structure
  37. which encapsulates that shell surface's state.
  38. Many shells require some configuration between the creation of the shell surface
  39. and displaying it on screen. For example, during this period application windows
  40. will typically set the window title so that the compositor never has to show an
  41. empty title. All Wayland interfaces aim for atomicity, so that all changes are
  42. applied in a single fell swoop and we never display an invalid frame. This is
  43. why Wayland is known for addressing vsync problems X suffers from, but is
  44. pervasive across the ecosystem. Even things like setting the window title are
  45. done atomically.
  46. So, once the client is done communiciating the new shell surface's desired
  47. traits to the compositor, it will commit the surface to atomically apply the
  48. changes. The first time this happens, the client is ready to be shown, and the
  49. shell-specific wlroots shell surface interface will communicate this to you with
  50. the surface's `events.map` signal. The reverse is sometimes communicated with
  51. `events.unmap`, when the shell surface should be hidden.
  52. ## xdg-shell
  53. xdg-shell is currently the only shell whose protocol is considered stable, and
  54. it is the shell which describes application windows. You can read the xdg-shell
  55. protocol specification (XML)
  56. [here](https://cgit.freedesktop.org/wayland/wayland-protocols/tree/stable/xdg-shell/xdg-shell.xml)
  57. (you are strongly encouraged to read through the XML for all protocols mentioned
  58. in this article).
  59. The xdg-shell is quite complicated, as it attempts to encapsulate every feature
  60. of a typical graphical desktop session in a single protocol. An xdg-shell
  61. surface is a `wl_surface` wrapped twice - once in a `xdg_surface` and then again
  62. in a `xdg_toplevel` or `xdg_popup`, depending on what kind of window it is. The
  63. wlroots `wlr_xdg_surface` type (the one emitted by
  64. `xdg_shell.events.new_surface`) contains tagged union of `wlr_xdg_toplevel` and
  65. `wlr_xdg_popup`, selected from the `role` field. You can wire up the xdg-shell
  66. with `wlr_xdg_shell_create`.
  67. Most application windows you see are called toplevels. These windows are the
  68. root node of a tree of surfaces which may include arbitrarily nested popups, for
  69. example, as you navigate through a deep menu. These windows can have titles;
  70. parent surfaces; app IDs (e.g. "gnome-calculator"); minimum and maximum sizes;
  71. and maximized, minimized, and fullscreen states. They also often[^1] draw their
  72. own window decorations and drop shadows, and tell the compositor when you click
  73. and drag on the titlebar to move or resize the window. Unfortunately, if the
  74. client is not responding or misbehaving, the user cannot use these controls to
  75. move, resize, or minimize the window[^2].
  76. [^1]: [But not always](https://cgit.freedesktop.org/wayland/wayland-protocols/tree/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml). You're welcome.
  77. [^2]: Which is one of the reasons we made the protocol mentioned in footnote[^1].
  78. The compositor can tell the window to adopt a specific size, though the client
  79. can choose to ignore this. The compositor also lets the client know when it's
  80. "activated", which is used by GTK+, for example, to start rendering the caret
  81. and render a different set of client-side decorations. It can also toggle the
  82. fullscreen, minimized, maximized, and other states.
  83. Each of the various state transitions involved are expressed through
  84. the `wlr_xdg_toplevel.events` signals. The most recent atomically agreed-upon
  85. state is stored in `wlr_xdg_toplevel.current`. When each of the signals in
  86. `events` are emitted, the state change will have been applied to
  87. `client_pending`. However, you must consent to these changes by calling a
  88. corresponding function on the xdg_toplevel (e.g.
  89. `wlr_xdg_toplevel_set_fullscreen`), which will apply the change to
  90. `server_pending`. You shouldn't consider these changes atomically set until the
  91. `wlr_surface.events.commit` signal has been raised. At that point, you can start
  92. showing the window in fullscreen or whatever. There's also some
  93. configure/ack-configure stuff going on here which may eventually become relevant
  94. to you[^3], but wlroots takes care it for the most part.
  95. [^3]: For example, this is relevant for sway, which needs to reach deeper into our shell implementations to atomically syncronize the resizing of several clients at once when rearranging the layout.
  96. The popup interface is used to show a "popup" window, which can be used for a
  97. variety of purposes. These include context menus (or "right click" menus),
  98. tooltips, some confirmation modals[^4], etc. The lifecycle of a popup resource
  99. is managed similarly to that of a toplevel resource, of course with different
  100. states that can be atomically updated. Arguably, the most fundamental of these
  101. states is the relative X and Y position of the popup with respect to its parent
  102. toplevel surface.
  103. [^4]: Some popup windows, the GTK+ file chooser for example, prefer to make a new xdg_toplevel and assign its parent to the application window. This is useful if you want your window to show up in taskbars, be able to be minimized and maximized separately, etc.
  104. The position of the popup can be influenced by an extraordinarily complicated
  105. interface called `xdg_positioner`, also provided by xdg-shell. Since these
  106. articles focus on the compositor side of things, and they focus on using
  107. wlroots, I can thankfully save you from understanding most of the specifics of
  108. this interface. The purpose of this interface is to adjust the position and size
  109. of `xdg_popup` surfaces with respect to the display they live on - for example,
  110. to prevent them from being partially off-screen. The rub is that if you're using
  111. wlroots, when the popup is created you can just call
  112. `wlr_xdg_popup_unconstrain_from_box` to deal with everything, passing it a box
  113. which represents the available space surrounding the parent toplevel for the
  114. popup to be placed in.
  115. Popups are also able to take "grabs", which indicate that they should keep
  116. focus without respect to any of the other goings-on of the seat. This is used so
  117. that you can, for example, use the keyboard to pick items from a context menu.
  118. Grabs are automatically handled for you with `wlr_seat` for you. If you want to
  119. deny or cancel grabs, you can do so through the appropriate `wlr_seat`
  120. interfaces.
  121. One last note: xdg-shell only recently became stable, so client support for the
  122. stable version is hit and miss. The last unstable protocol, xdg-shell v6, is
  123. also supported by wlroots. It mostly behaves in the same way. Eventually it
  124. will be removed from wlroots.
  125. ## layer-shell
  126. Under the umbrella of wlroots, 8 Wayland compositors have been collaborating on
  127. the design of a new shell for desktop shell components. The result is [layer
  128. shell
  129. (XML)](https://github.com/swaywm/wlr-protocols/blob/master/unstable/wlr-layer-shell-unstable-v1.xml).
  130. The purpose of this shell is to provide an interface for desktop components like
  131. panels, lock screens, wallpapers, on-screen keyboards, notifications, and so on,
  132. to display on your compositor.
  133. The layer-shell is organized into four discrete layers: background, bottom, top,
  134. and overlay, which are rendered in that order. Between bottom and top,
  135. application windows are displayed. A wallpaper client might choose to go in the
  136. bottom layer, while a notification could show on the top layer, and a panel on
  137. the bottom layer.
  138. The compositor's job is to decide where to place each surface and how large the
  139. surface can be. The client can specify either or both of its dimensions (width
  140. and height) for the compositor to specify, then provide some hints for the
  141. compositor to do so. The client can, for example, choose to be anchored to edges
  142. of the screen. A notification might be anchored to `TOP | RIGHT`, and a panel
  143. might be anchored to `LEFT | BOTTOM | RIGHT`. A layer surface anchored to an
  144. edge, like our panel, can also request an exclusive zone, which is a number of
  145. pixels from the edge that should not be occluded by other layer surfaces or
  146. application windows. This is used, for example, when maximizing application
  147. windows to prevent them from occluding the panel (or in sway's case, when
  148. arranging tiled windows).
  149. Layer surfaces also have special keyboard input semantics. Some layer surfaces
  150. want to receive keyboard input, such as an application launcher overlay. Others
  151. might prefer that application windows continue to receive keyboard events, such
  152. as a notification. To this end, a layer surface can toggle a boolean indicating
  153. its "keyboard interactivity". For layers beneath application windows, layer
  154. surfaces participate in keyboard focus normally, usually meaning they need to be
  155. clicked to receive keyboard focus. Above application windows, the top-most layer
  156. always has keyboard focus if it requests it.
  157. In wlroots, you can wire up a layer shell to the display with
  158. `wlr_layer_shell_create`. From there it behaves similarly to xdg-shell with
  159. respect to the creation of new surfaces and the handling of atomic state. The
  160. main concern of yours is that, when the surface is committed, you need to
  161. arrange the surfaces in the affected layer and communicate the final dimensions
  162. of the layer surface to the client with `wlr_layer_surface_configure`. You can
  163. implement the arrangement however you want, but you may find the [sway
  164. implementation][sway-layers] to be a useful reference. Also check out the
  165. wlroots [example client][layer-client] to test out your implementation.
  166. [sway-layers]: https://github.com/swaywm/sway/blob/master/sway/desktop/layer_shell.c#L18-L215
  167. [layer-client]: https://github.com/swaywm/wlroots/blob/master/examples/layer-shell.c
  168. Layer surfaces can also have popups, for example when right-clicking on a
  169. taskbar. This borrows xdg-shell's xdg_popup interface, except the parent is set
  170. to the layer surface (this is explicitly allowed for through the xdg_popup spec,
  171. and you may see future shells doing something similar). Most of your code for
  172. xdg_popups can be reused with layer surfaces.
  173. ## Xwayland
  174. Some Wayland developers turn up their nose when I refer to Xwayland as a shell,
  175. and perhaps with good reason. However, wlroots treats Xwayland like a shell, so
  176. the API remains consistent. For that reason, we'll treat it as one in this
  177. article as well.
  178. We figured that you might be writing a Wayland compositor so that you *don't*
  179. have to write an X11 window manager, too. So we wrote one for you, and it's
  180. called `wlr_xwayland`. This interface provides an abstraction over Xwayland
  181. which makes it behave similarly to our other shells. It still lets you dig your
  182. heels into it in any degree so that you can adjust the behavior of your
  183. compositor to suit X-specific needs as necessary.
  184. The resulting wlr_xwayland API is similar to the other shells we've described.
  185. We have a series of events for configuring Xwayland surfaces, a map and unmap
  186. event, and we expose a whole bunch of info about Xwayland surfaces so you can
  187. make the judgement call about how much or how little to obey their requests (X11
  188. windows make more unreasonable requests than other shells, since X11 was the
  189. wild wild west and a lot of clients took advantage of that).
  190. This should be enough to get you started, and if you have questions ask on IRC
  191. for the time being. I could go into more detail, but I think Xwayland deserves
  192. its own article, and probably not written by me.
  193. ## Other shells
  194. There are three other shells of note. Two are not very interesting:
  195. - wl_shell, the now-deprecated original desktop shell of Wayland
  196. - ivi-shell, used for "in-vehicle infotainment" systems running Wayland
  197. wlroots supports neither (though I guess we'd accept a patch adding IVI-shell
  198. support, maybe if the vehicle industry was open to improving that protocol...),
  199. and neither is interesting for desktops, phones, etc. You probably don't need to
  200. worry about them.
  201. The other is the fullscreen-shell, which is used for optimizing the rendering of
  202. fullscreen appliations. I don't know much about how it works, and it's not
  203. supported by wlroots yet; it's not required of a functional Wayland compositor.
  204. Maybe someday!