aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTudor Roman2021-01-26 11:30:02 +0200
committerTudor Roman2021-01-26 11:30:02 +0200
commit832f2423fc9eb93630251e8294898724cbd88743 (patch)
tree8d88761302dc8c178ae6592bf6cf27748e1759e1
parent2ef4aaf848869aecea117a4e775cc43a562d31b2 (diff)
downloadblog-832f2423fc9eb93630251e8294898724cbd88743.tar.gz
blog-832f2423fc9eb93630251e8294898724cbd88743.zip
new article: should you write a wayland compositor
-rw-r--r--_config.yml3
-rw-r--r--_posts/2021-01-24-the-wayland-experience.md227
-rw-r--r--_site/404.html22
-rw-r--r--_site/about/index.html22
-rw-r--r--_site/feed.xml218
-rw-r--r--_site/index.html34
-rw-r--r--_site/technical/2021/01/26/the-wayland-experience/index.html311
-rw-r--r--_site/technical/index.html101
-rw-r--r--_site/writing/index.html97
-rw-r--r--index.md6
10 files changed, 1027 insertions, 14 deletions
diff --git a/_config.yml b/_config.yml
index 2ee3118..e89a537 100644
--- a/_config.yml
+++ b/_config.yml
@@ -10,9 +10,8 @@ author: Tudor Roman
exclude:
- gemset.nix
- default.nix
+ - shell.nix
- vendor
- - technical.md
- - writing.md
meta:
description: the blog of Tudor Roman
diff --git a/_posts/2021-01-24-the-wayland-experience.md b/_posts/2021-01-24-the-wayland-experience.md
new file mode 100644
index 0000000..40a54ed
--- /dev/null
+++ b/_posts/2021-01-24-the-wayland-experience.md
@@ -0,0 +1,227 @@
+---
+layout: post
+title: "Should You Write a Wayland Compositor?"
+date: 2021-01-26 10:59:23+02:00
+category: technical
+---
+
+[Wayland](https://wayland.freedesktop.org/) is the "new" (13 years old already) display server technology on Linux, which is supposed to replace
+the antiquated X11. It promises better security, performance, portability, everything, compared to X11, and
+it sure does deliver, provided that you're not using [unsupported graphics cards](https://www.nvidia.com/).
+You can watch [this talk / rant about X11](https://www.youtube.com/watch?v=RIctzAQOe44) to get an idea about how bad it is.
+
+Some power users also haven't switched to Wayland because their gimmicky window manager doesn't have a
+Wayland equivalent (XMonad, Awesome, Bspwm and the others). Some may wonder "hmm maybe I can port it myself". I wrote this post to make you (re)consider that.
+
+One of the main ideas of Wayland is that it's merely a specialised IPC protocol, and the communication is
+strictly between the clients (applications on your screen mainly) and the server.
+
+The server is what we are interested in now. Unlike X11, where the server is X.Org and the window manager
+is just an X11 client with special privileges, on Wayland the window manager is also a server and a compositor. In fact, the correct terminology is "Wayland compositor". This piece of software has the
+task of doing everything it wants with the clients, which is usually among the lines of showing them
+on the screen and giving them inputs events from your keyboards, mice, touchscreens etc. In fact, [no one
+stops you from leaving window management as an API](https://mir-server.io/).
+
+On my laptop, I prefer using GNOME under Wayland, because it has the most stable, fully-featured and "just works"
+experience there is. However, I wanted a better way of organising windows on my screen than leaving them
+shuffled around. Tiling was also not good, because every time you launch a new window, the others get
+shrunk to fit, which is not good on a small laptop display.
+
+Luckily I discovered [PaperWM](https://github.com/paperwm/PaperWM). It's perfect for laptops, instead of
+shrinking your windows, it just renders them off-screen. To switch between one or the other you can just
+flick three fingers on the touchpad. It's great. It's also "glossy" and polished and has great UI and animations.
+
+It has some disadvantages though, mainly concerning its speed. I find it sluggish, and it's no surprise that
+like all other GNOME Shell extensions, it's a JavaScript behemoth. PaperWM still uses the GNOME Tweener
+framework for its animations, which is entirely written in JS. Because of that, it needs to communicate with the main
+GNOME compositor process on each operation. And because we're talking animations, said operations happen
+for *every frame*. That means there is JS executed for every frame, 60 times a second. It's horrible!
+
+BTW nowadays GNOME uses animations implemented in C (since GNOME 3.34), the "GNOME runs JS on every frame" statement is for the most part false today.
+
+Because of the inefficiency of PaperWM, it also means that it drains my battery quickly, which is bad for a laptop!
+
+So, because I am a student with a lot of free time, together with [Alex](https://alexge50.com/) we figured we shall develop a scrollable tiling (PaperWM-like) Wayland compositor. It's called [Cardboard](https://gitlab.com/cardboardwm/cardboard) (get it??). It also has the nicety of
+being controlled and configured by a remote control program, like bspwm.
+Neither Alex nor I had experience with any kind of Wayland development. However, I wrote [an X11 window manager](https://tudorr.ro/software/windowchef/),
+[some XRandR querying utilities](https://github.com/tudurom/disputils/) and [a window rule daemon](https://github.com/tudurom/ruler). Alex has experience
+with [graphics programming](https://git.alexge50.com/a/gie) and [C++ devilry](https://github.com/alexge50/libwatchpoint).
+
+Our "tech stack" was [wlroots](https://github.com/swaywm/wlroots), which is an exceptional Wayland compositor library (apart from its lack of documentation). The description from the README is spot on:
+
+>About 50,000 lines of code you were going to write anyway.
+
+I (because I wrote most of the Wayland-interfacing code) realised soon that there is still a lot of boilerplate involved in writing a compositor, much more than for an X11 window manager,
+namely setting up your own rendering code, registering and storing input devices and screens in your own data structures, passing input events to windows, calculating bounds for bars
+and other overlays (courtesy of [`layer-shell`](https://github.com/swaywm/wlr-protocols/blob/master/unstable/wlr-layer-shell-unstable-v1.xml)) and others. X11 handles all of this for you,
+the window manager just reacts to events regarding window inputs to establish its behaviour. With Wayland, you handle everything, even with wlroots. The upside is that if you don't like the way X11 does something
+(which is a given), not only that you can do it in your own way on Wayland, you are required to do so.
+
+Because we weren't really prepared for what writing a compositor involved, we thought that it must be approached like a "normal" program: split code into modules, each with their own responsibilities, call wlroots to do its thing, the usual stuff.
+We are writing a program in our way and wlroots lets us "interface with Wayland". Or so we thought.
+
+We were as careful as possible to separate responsibilities into different structures / classes, yet we ended up with most functions taking a pointer to `Server` as their first parameter.
+`Server` is a singleton holding all the other "sub-modules" in it. It represents the compositor itself. The reason most functions need `Server` is that everything is related to everything,
+not because of a mistake in structuring the code, but by design. This is what a compositor requires, the problem of writing a compositor is somewhat complex because it has a great deal variables ranging
+from input events, drawing on the screen, creating behaviours that the user leverages, reacting to application events. All of them can affect the other, you can not separate the thing into modules.
+The best you can do is separate the code in different files and folders based on some criteria, like grouping data structures with related routines.
+
+Excerpt from `Seat.h`:
+
+```cpp
+/// Hides the \a view from the screen without unmapping. Happens when a Workspace is deactivated.
+void hide_view(Server& server, View& view);
+/// Gives keyboard focus to a plain surface (OR xwayland usually)
+void focus_surface(struct wlr_surface* surface);
+/**
+ * \brief Sets the focus state on \a view. Auto-scrolls the Workspace if it's tiled.
+ *
+ * If \a view is null, the previously focused view will be unfocused and no other view will be focused.
+ */
+void focus_view(Server& server, OptionalRef<View> view, bool condense_workspace = false);
+/// Marks the layer as receiving keyboard focus from this seat.
+void focus_layer(Server& server, struct wlr_layer_surface_v1* layer);
+/**
+ * \brief Focus the most recently focused view on \a column.
+ *
+ * \param column - must be from this workspace
+ */
+void focus_column(Server& server, Workspace::Column& column);
+/// Removes the \a view from the focus stack.
+void remove_from_focus_stack(View& view);
+
+void begin_move(Server& server, View& view);
+void begin_resize(Server& server, View& view, uint32_t edges);
+void begin_workspace_scroll(Server& server, Workspace& workspace);
+void process_cursor_motion(Server& server, uint32_t time = 0);
+void process_cursor_move(Server&, GrabState::Move move_data);
+void process_cursor_resize(Server&, GrabState::Resize resize_data);
+void process_swipe_begin(Server& server, uint32_t fingers);
+void process_swipe_update(Server& server, uint32_t fingers, double dx, double dy);
+void process_swipe_end(Server& server);
+void end_interactive(Server& server);
+void end_touchpad_swipe(Server& server);
+
+/// Updates the scroll of the workspace during three-finger swipe, taking in account speed and friction.
+void update_swipe(Server& server);
+
+/// Returns true if the \a view is currently in a grab operation.
+bool is_grabbing(View& view);
+
+/// Returns the workspace under the cursor.
+OptionalRef<Workspace> get_focused_workspace(Server& server);
+
+/// Moves the focus to a different workspace, if the workspace is already on a monitor, it focuses that monitor
+void focus(Server& server, Workspace& workspace); // TODO: yikes, passing Server*
+```
+
+As an example, let's take damage tracking. For starters, because the compositor is also tasked with rendering the content and displaying it on the screen, we have some rendering code that runs on every frame.
+Damage tracking is tracking which areas of the screen have changed in an amount of time. An example would
+be typing a letter in the terminal. The place where the cursor is changes to the letter you typed, and the cursor advances.
+If there is no change, the frame is not rendered, as it would look
+exactly the same as the previous one, which would be a waste of processor time.
+This way, instead of re-rendering everything 60 times a second (I assume that you use a common display), we can render and paint as little as possible to account for the changed region.
+You can read [an introduction to damage tracking](https://emersion.fr/blog/2019/intro-to-damage-tracking/) written by one of the main developers of wlroots.
+
+I have just implemented the most basic form of damage tracking: do not render the frame if nothing on the screen changes.
+It doesn't track the damage itself, just that it exists.
+To do this, I first added a `wlr_output_damage` object to my `Output` structure:
+
+```diff
+struct Output {
+ struct wlr_output* wlr_output;
+ struct wlr_output_damage* wlr_output_damage;
++ struct wlr_box usable_area;
+ ...
+```
+
+This structure tracks the damage accumulated per-output, as rendering is also per-output (this means that you can use screens of different refresh rates, yay!). However, to make this initial attempt at
+damage tracking easier, I decided to trigger rendering for all attached outputs. I added a `set_dirty()` function to the `OutputManager` class that does just that:
+
+```cpp
+void OutputManager::set_dirty()
+{
+ for (auto& output : outputs) {
+ wlr_output_damage_add_whole(output.wlr_output_damage);
+ }
+}
+```
+
+This marks every output as entirely damaged, and as such triggers a render.
+
+With this function set into place, I had to identify when does the "screen" change, namely when a window "commits" (changes its contents) and when a window is moved. The window move is one example
+of the way everything is related to everything in the compositor. Before damage tracking, the `View::move()` (we call windows "views") method just changed the `x` and `y` fields of the `View` structures.
+Now, a move must call a method of `OutputManager`, so we need to give that as a parameter. This is almost like giving `Server` as a parameter, as `OutputManager` is a singleton inside `Server`.
+
+```diff
+-void View::move(int x_, int y_)
++void View::move(OutputManager& output_manager, int x_, int y_)
+{
+ x = x_;
+ y = y_;
++ output_manager.set_dirty();
+}
+```
+
+That's when it hit me that wlroots is more of a framework and the compositor is one of its modules. Thinking that wlroots is an "interface to Wayland" is plain wrong, as the Wayland server is the
+program that I am writing. The next refactor is going to make the `Server` instance global...
+
+Now that we have a `wlr_output_damage` object in place and `set_dirty()` calls where they're needed, we only need to call the render function when `wlr_output_damage` tells us instead of
+every `1/60` seconds:
+
+```diff
+@@ -46,11 +46,12 @@ void register_output(Server& server, Output&& output_)
+ server.output_manager->outputs.emplace_back(output_);
+ auto& output = server.output_manager->outputs.back();
+ output.wlr_output->data = &output;
++ output.wlr_output_damage = wlr_output_damage_create(output.wlr_output);
+
+ register_handlers(server,
+ &output,
+ {
+- { &output.wlr_output->events.frame, Output::frame_handler },
++ { &output.wlr_output_damage->events.frame, Output::frame_handler },
+ { &output.wlr_output->events.present, Output::present_handler },
+ { &output.wlr_output->events.commit, Output::commit_handler },
+ { &output.wlr_output->events.mode, Output::mode_handler },
+```
+
+This is not complete code for a basic damage tracking implementation with wlroots. You can see [the whole commit here](https://gitlab.com/cardboardwm/cardboard/-/commit/f2ef2ff076ddbbd23994553b8eff131f9bd0207f).
+
+This is an example of how wlroots provides yet another "module" that we can use in the grand scheme of the compositor. `wlr_output_damage` accumulates damaged rectangles in time and even turns these
+numerous small rectangles into a big one as an optimisation. It also calls the frame handler when it's needed, and this is not only just when something on the screen changed, but also when the underlying "backend"
+of the compositor changes. The simplest situation is when the compositor starts: it needs to render an initial frame so the screen isn't pitch black.
+
+All in all, I do not recommend writing your own compositor if you only want some gimmicky user interface.
+In the X world there is a WM for every single way of window tiling plus a couple more. It doesn't work like
+that with Wayland, you will spend more time implementing basic compositor duties than
+on your compositor's unique features. Instead, if I were to rewrite Cardboard, I would rather do it
+as a [Wayfire plugin](https://github.com/WayfireWM/wayfire/wiki/Plugin-architecture) or maybe as a KWin script. However, I think Wayfire is more "enthusiast-friendly", as it uses [protocols from
+wlroots](https://github.com/swaywm/wlr-protocols) such as `layer-shell` (for panels, overlays and backgrounds), `gamma-control` (for Redshift), `screencopy` (for screenshots) and others,
+allowing people to write tools that are not specific to the compositor.
+
+Nevertheless, if you want to do it for the learning experience, I definitely recommend writing
+a "full-fledged" compositor with wlroots, learning from other compositors ([Sway][sway],
+[cage][cage], [Wayfire][wayfire], [hikari][hikari] and others; cage is the simplest, hikari second simplest) and from their creators on
+IRC (`#sway-devel` on Freenode), they are very kind and knowledgeable.
+
+There is also a [discussion about introducing a high-level scene API in wlroots](https://github.com/swaywm/wlroots/issues/1826). Maybe when it will arrive, I will change my opinion.
+
+[sway]: https://github.com/swaywm/sway
+[cage]: https://github.com/hjdskes/cage
+[hikari]: https://hikari.acmelabs.space/
+[wayfire]: https://github.com/WayfireWM/wayfire
+
+Recommended lectures:
+* [Writing a Wayland Compositor](https://drewdevault.com/2018/02/17/Writing-a-Wayland-compositor-1.html), by Drew DeVault, the author of Sway and wlroots
+* [The Wayland Protocol Book](https://wayland-book.com/), also by Drew DeVault
+* [The Wayland Protocol](https://wayland.freedesktop.org/docs/html/), the actual protocol (larger than the book)
+* Posts from Simon Ser's blog, maintainer of Sway and wlroots:
+ * [Writing a Wayland rendering loop](https://emersion.fr/blog/2018/wayland-rendering-loop/)
+ * [Introduction to damage tracking](https://emersion.fr/blog/2019/intro-to-damage-tracking/)
+ * [Wayland clipboard and drag & drop](https://emersion.fr/blog/2020/wayland-clipboard-drag-and-drop/)
+* [The init routine of Wayfire](https://github.com/WayfireWM/wayfire/blob/cb7920187d2546ca375f00e7ef0a71d7a4995ba6/src/core/core.cpp#L173). GTK is rather picky when it comes to the order of advertised Wayland protocols.
+Don't waste hours of your life trying to figure out whether you did something wrong, it's GTK...
+
+Also, I suggest you write the compositor in either C, C++ or Zig (with [zig-wlroots](https://github.com/swaywm/zig-wlroots) or just doing your thing, Zig is C compatible).
+See this article on why the [Rust bindings failed](http://way-cooler.org/blog/2019/04/29/rewriting-way-cooler-in-c.html).
diff --git a/_site/404.html b/_site/404.html
index 70d1fc7..fa42134 100644
--- a/_site/404.html
+++ b/_site/404.html
@@ -15,7 +15,7 @@
<meta property="og:site_name" content="tudor's blog">
<!-- jekyll-feed gem required -->
- <link type="application/atom+xml" rel="alternate" href="http://localhost:4000/blog/feed.xml" title="tudor's blog" />
+ <link type="application/atom+xml" rel="alternate" href="/blog/feed.xml" title="tudor's blog" />
<link rel="stylesheet" href="/blog/css/main.css">
<link rel="icon" href="/blog/favicon.png">
@@ -45,6 +45,26 @@
+
+ <li>
+ <a href="/blog/technical/" class="">
+
+ Nerd stuff
+
+ </a>
+ </li>
+
+
+
+ <li>
+ <a href="/blog/writing/" class="">
+
+ Writings
+
+ </a>
+ </li>
+
+
</ul>
</nav>
diff --git a/_site/about/index.html b/_site/about/index.html
index 4ab8f1d..c38bc8d 100644
--- a/_site/about/index.html
+++ b/_site/about/index.html
@@ -15,7 +15,7 @@
<meta property="og:site_name" content="tudor's blog">
<!-- jekyll-feed gem required -->
- <link type="application/atom+xml" rel="alternate" href="http://localhost:4000/blog/feed.xml" title="tudor's blog" />
+ <link type="application/atom+xml" rel="alternate" href="/blog/feed.xml" title="tudor's blog" />
<link rel="stylesheet" href="/blog/css/main.css">
<link rel="icon" href="/blog/favicon.png">
@@ -45,6 +45,26 @@
+
+ <li>
+ <a href="/blog/technical/" class="">
+
+ Nerd stuff
+
+ </a>
+ </li>
+
+
+
+ <li>
+ <a href="/blog/writing/" class="">
+
+ Writings
+
+ </a>
+ </li>
+
+
</ul>
</nav>
diff --git a/_site/feed.xml b/_site/feed.xml
index 50628a8..e09a624 100644
--- a/_site/feed.xml
+++ b/_site/feed.xml
@@ -1 +1,217 @@
-<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.1.1">Jekyll</generator><link href="http://localhost:4000/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="http://localhost:4000/blog/" rel="alternate" type="text/html" /><updated>2020-07-14T17:24:50+00:00</updated><id>http://localhost:4000/blog/feed.xml</id><title type="html">tudor’s blog</title><subtitle>the blog of Tudor Roman</subtitle><author><name>Tudor Roman</name></author></feed> \ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.1.1">Jekyll</generator><link href="/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="/blog/" rel="alternate" type="text/html" /><updated>2021-01-26T11:27:56+02:00</updated><id>/blog/feed.xml</id><title type="html">tudor’s blog</title><subtitle>the blog of Tudor Roman</subtitle><author><name>Tudor Roman</name></author><entry><title type="html">Should You Write a Wayland Compositor?</title><link href="/blog/technical/2021/01/26/the-wayland-experience/" rel="alternate" type="text/html" title="Should You Write a Wayland Compositor?" /><published>2021-01-26T10:59:23+02:00</published><updated>2021-01-26T10:59:23+02:00</updated><id>/blog/technical/2021/01/26/the-wayland-experience</id><content type="html" xml:base="/blog/technical/2021/01/26/the-wayland-experience/">&lt;p&gt;&lt;a href=&quot;https://wayland.freedesktop.org/&quot;&gt;Wayland&lt;/a&gt; is the “new” (13 years old already) display server technology on Linux, which is supposed to replace
+the antiquated X11. It promises better security, performance, portability, everything, compared to X11, and
+it sure does deliver, provided that you’re not using &lt;a href=&quot;https://www.nvidia.com/&quot;&gt;unsupported graphics cards&lt;/a&gt;.
+You can watch &lt;a href=&quot;https://www.youtube.com/watch?v=RIctzAQOe44&quot;&gt;this talk / rant about X11&lt;/a&gt; to get an idea about how bad it is.&lt;/p&gt;
+
+&lt;p&gt;Some power users also haven’t switched to Wayland because their gimmicky window manager doesn’t have a
+Wayland equivalent (XMonad, Awesome, Bspwm and the others). Some may wonder “hmm maybe I can port it myself”. I wrote this post to make you (re)consider that.&lt;/p&gt;
+
+&lt;p&gt;One of the main ideas of Wayland is that it’s merely a specialised IPC protocol, and the communication is
+strictly between the clients (applications on your screen mainly) and the server.&lt;/p&gt;
+
+&lt;p&gt;The server is what we are interested in now. Unlike X11, where the server is X.Org and the window manager
+is just an X11 client with special privileges, on Wayland the window manager is also a server and a compositor. In fact, the correct terminology is “Wayland compositor”. This piece of software has the
+task of doing everything it wants with the clients, which is usually among the lines of showing them
+on the screen and giving them inputs events from your keyboards, mice, touchscreens etc. In fact, &lt;a href=&quot;https://mir-server.io/&quot;&gt;no one
+stops you from leaving window management as an API&lt;/a&gt;.&lt;/p&gt;
+
+&lt;p&gt;On my laptop, I prefer using GNOME under Wayland, because it has the most stable, fully-featured and “just works”
+experience there is. However, I wanted a better way of organising windows on my screen than leaving them
+shuffled around. Tiling was also not good, because every time you launch a new window, the others get
+shrunk to fit, which is not good on a small laptop display.&lt;/p&gt;
+
+&lt;p&gt;Luckily I discovered &lt;a href=&quot;https://github.com/paperwm/PaperWM&quot;&gt;PaperWM&lt;/a&gt;. It’s perfect for laptops, instead of
+shrinking your windows, it just renders them off-screen. To switch between one or the other you can just
+flick three fingers on the touchpad. It’s great. It’s also “glossy” and polished and has great UI and animations.&lt;/p&gt;
+
+&lt;p&gt;It has some disadvantages though, mainly concerning its speed. I find it sluggish, and it’s no surprise that
+like all other GNOME Shell extensions, it’s a JavaScript behemoth. PaperWM still uses the GNOME Tweener
+framework for its animations, which is entirely written in JS. Because of that, it needs to communicate with the main
+GNOME compositor process on each operation. And because we’re talking animations, said operations happen
+for &lt;em&gt;every frame&lt;/em&gt;. That means there is JS executed for every frame, 60 times a second. It’s horrible!&lt;/p&gt;
+
+&lt;p&gt;BTW nowadays GNOME uses animations implemented in C (since GNOME 3.34), the “GNOME runs JS on every frame” statement is for the most part false today.&lt;/p&gt;
+
+&lt;p&gt;Because of the inefficiency of PaperWM, it also means that it drains my battery quickly, which is bad for a laptop!&lt;/p&gt;
+
+&lt;p&gt;So, because I am a student with a lot of free time, together with &lt;a href=&quot;https://alexge50.com/&quot;&gt;Alex&lt;/a&gt; we figured we shall develop a scrollable tiling (PaperWM-like) Wayland compositor. It’s called &lt;a href=&quot;https://gitlab.com/cardboardwm/cardboard&quot;&gt;Cardboard&lt;/a&gt; (get it??). It also has the nicety of
+being controlled and configured by a remote control program, like bspwm.
+Neither Alex nor I had experience with any kind of Wayland development. However, I wrote &lt;a href=&quot;https://tudorr.ro/software/windowchef/&quot;&gt;an X11 window manager&lt;/a&gt;,
+&lt;a href=&quot;https://github.com/tudurom/disputils/&quot;&gt;some XRandR querying utilities&lt;/a&gt; and &lt;a href=&quot;https://github.com/tudurom/ruler&quot;&gt;a window rule daemon&lt;/a&gt;. Alex has experience
+with &lt;a href=&quot;https://git.alexge50.com/a/gie&quot;&gt;graphics programming&lt;/a&gt; and &lt;a href=&quot;https://github.com/alexge50/libwatchpoint&quot;&gt;C++ devilry&lt;/a&gt;.&lt;/p&gt;
+
+&lt;p&gt;Our “tech stack” was &lt;a href=&quot;https://github.com/swaywm/wlroots&quot;&gt;wlroots&lt;/a&gt;, which is an exceptional Wayland compositor library (apart from its lack of documentation). The description from the README is spot on:&lt;/p&gt;
+
+&lt;blockquote&gt;
+ &lt;p&gt;About 50,000 lines of code you were going to write anyway.&lt;/p&gt;
+&lt;/blockquote&gt;
+
+&lt;p&gt;I (because I wrote most of the Wayland-interfacing code) realised soon that there is still a lot of boilerplate involved in writing a compositor, much more than for an X11 window manager,
+namely setting up your own rendering code, registering and storing input devices and screens in your own data structures, passing input events to windows, calculating bounds for bars
+and other overlays (courtesy of &lt;a href=&quot;https://github.com/swaywm/wlr-protocols/blob/master/unstable/wlr-layer-shell-unstable-v1.xml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;layer-shell&lt;/code&gt;&lt;/a&gt;) and others. X11 handles all of this for you,
+the window manager just reacts to events regarding window inputs to establish its behaviour. With Wayland, you handle everything, even with wlroots. The upside is that if you don’t like the way X11 does something
+(which is a given), not only that you can do it in your own way on Wayland, you are required to do so.&lt;/p&gt;
+
+&lt;p&gt;Because we weren’t really prepared for what writing a compositor involved, we thought that it must be approached like a “normal” program: split code into modules, each with their own responsibilities, call wlroots to do its thing, the usual stuff.
+We are writing a program in our way and wlroots lets us “interface with Wayland”. Or so we thought.&lt;/p&gt;
+
+&lt;p&gt;We were as careful as possible to separate responsibilities into different structures / classes, yet we ended up with most functions taking a pointer to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&lt;/code&gt; as their first parameter.
+&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&lt;/code&gt; is a singleton holding all the other “sub-modules” in it. It represents the compositor itself. The reason most functions need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&lt;/code&gt; is that everything is related to everything,
+not because of a mistake in structuring the code, but by design. This is what a compositor requires, the problem of writing a compositor is somewhat complex because it has a great deal variables ranging
+from input events, drawing on the screen, creating behaviours that the user leverages, reacting to application events. All of them can affect the other, you can not separate the thing into modules.
+The best you can do is separate the code in different files and folders based on some criteria, like grouping data structures with related routines.&lt;/p&gt;
+
+&lt;p&gt;Excerpt from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Seat.h&lt;/code&gt;:&lt;/p&gt;
+
+&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// Hides the \a view from the screen without unmapping. Happens when a Workspace is deactivated.&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hide_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;c1&quot;&gt;/// Gives keyboard focus to a plain surface (OR xwayland usually)&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;focus_surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;wlr_surface&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;surface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;cm&quot;&gt;/**
+ * \brief Sets the focus state on \a view. Auto-scrolls the Workspace if it's tiled.
+ *
+ * If \a view is null, the previously focused view will be unfocused and no other view will be focused.
+ */&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;focus_view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OptionalRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;condense_workspace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;c1&quot;&gt;/// Marks the layer as receiving keyboard focus from this seat.&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;focus_layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;wlr_layer_surface_v1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;cm&quot;&gt;/**
+ * \brief Focus the most recently focused view on \a column.
+ *
+ * \param column - must be from this workspace
+ */&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;focus_column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Workspace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;c1&quot;&gt;/// Removes the \a view from the focus stack.&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;remove_from_focus_stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;begin_move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;begin_resize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;begin_workspace_scroll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Workspace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process_cursor_motion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process_cursor_move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GrabState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Move&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;move_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process_cursor_resize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GrabState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Resize&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resize_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process_swipe_begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fingers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process_swipe_update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fingers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;process_swipe_end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;end_interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;end_touchpad_swipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+
+&lt;span class=&quot;c1&quot;&gt;/// Updates the scroll of the workspace during three-finger swipe, taking in account speed and friction.&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_swipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+
+&lt;span class=&quot;c1&quot;&gt;/// Returns true if the \a view is currently in a grab operation.&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_grabbing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;View&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+
+&lt;span class=&quot;c1&quot;&gt;/// Returns the workspace under the cursor.&lt;/span&gt;
+&lt;span class=&quot;n&quot;&gt;OptionalRef&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Workspace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_focused_workspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+
+&lt;span class=&quot;c1&quot;&gt;/// Moves the focus to a different workspace, if the workspace is already on a monitor, it focuses that monitor&lt;/span&gt;
+&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Workspace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// TODO: yikes, passing Server*&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
+
+&lt;p&gt;As an example, let’s take damage tracking. For starters, because the compositor is also tasked with rendering the content and displaying it on the screen, we have some rendering code that runs on every frame.
+Damage tracking is tracking which areas of the screen have changed in an amount of time. An example would
+be typing a letter in the terminal. The place where the cursor is changes to the letter you typed, and the cursor advances.
+If there is no change, the frame is not rendered, as it would look
+exactly the same as the previous one, which would be a waste of processor time.
+This way, instead of re-rendering everything 60 times a second (I assume that you use a common display), we can render and paint as little as possible to account for the changed region.
+You can read &lt;a href=&quot;https://emersion.fr/blog/2019/intro-to-damage-tracking/&quot;&gt;an introduction to damage tracking&lt;/a&gt; written by one of the main developers of wlroots.&lt;/p&gt;
+
+&lt;p&gt;I have just implemented the most basic form of damage tracking: do not render the frame if nothing on the screen changes.
+It doesn’t track the damage itself, just that it exists.
+To do this, I first added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlr_output_damage&lt;/code&gt; object to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Output&lt;/code&gt; structure:&lt;/p&gt;
+
+&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;struct Output {
+&lt;/span&gt; struct wlr_output* wlr_output;
+ struct wlr_output_damage* wlr_output_damage;
+&lt;span class=&quot;gi&quot;&gt;+ struct wlr_box usable_area;
+&lt;/span&gt; ...
+&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
+
+&lt;p&gt;This structure tracks the damage accumulated per-output, as rendering is also per-output (this means that you can use screens of different refresh rates, yay!). However, to make this initial attempt at
+damage tracking easier, I decided to trigger rendering for all attached outputs. I added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_dirty()&lt;/code&gt; function to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputManager&lt;/code&gt; class that does just that:&lt;/p&gt;
+
+&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OutputManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_dirty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
+&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
+ &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
+ &lt;span class=&quot;n&quot;&gt;wlr_output_damage_add_whole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wlr_output_damage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
+ &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
+&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
+
+&lt;p&gt;This marks every output as entirely damaged, and as such triggers a render.&lt;/p&gt;
+
+&lt;p&gt;With this function set into place, I had to identify when does the “screen” change, namely when a window “commits” (changes its contents) and when a window is moved. The window move is one example
+of the way everything is related to everything in the compositor. Before damage tracking, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;View::move()&lt;/code&gt; (we call windows “views”) method just changed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y&lt;/code&gt; fields of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;View&lt;/code&gt; structures.
+Now, a move must call a method of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputManager&lt;/code&gt;, so we need to give that as a parameter. This is almost like giving &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&lt;/code&gt; as a parameter, as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputManager&lt;/code&gt; is a singleton inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&lt;/code&gt;.&lt;/p&gt;
+
+&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;-void View::move(int x_, int y_)
+&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+void View::move(OutputManager&amp;amp; output_manager, int x_, int y_)
+&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
+ x = x_;
+ y = y_;
+&lt;span class=&quot;gi&quot;&gt;+ output_manager.set_dirty();
+&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
+&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
+
+&lt;p&gt;That’s when it hit me that wlroots is more of a framework and the compositor is one of its modules. Thinking that wlroots is an “interface to Wayland” is plain wrong, as the Wayland server is the
+program that I am writing. The next refactor is going to make the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Server&lt;/code&gt; instance global…&lt;/p&gt;
+
+&lt;p&gt;Now that we have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlr_output_damage&lt;/code&gt; object in place and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_dirty()&lt;/code&gt; calls where they’re needed, we only need to call the render function when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlr_output_damage&lt;/code&gt; tells us instead of
+every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1/60&lt;/code&gt; seconds:&lt;/p&gt;
+
+&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@@ -46,11 +46,12 @@&lt;/span&gt; void register_output(Server&amp;amp; server, Output&amp;amp;&amp;amp; output_)
+ server.output_manager-&amp;gt;outputs.emplace_back(output_);
+ auto&amp;amp; output = server.output_manager-&amp;gt;outputs.back();
+ output.wlr_output-&amp;gt;data = &amp;amp;output;
+&lt;span class=&quot;gi&quot;&gt;+ output.wlr_output_damage = wlr_output_damage_create(output.wlr_output);
+&lt;/span&gt;
+ register_handlers(server,
+ &amp;amp;output,
+ {
+&lt;span class=&quot;gd&quot;&gt;- { &amp;amp;output.wlr_output-&amp;gt;events.frame, Output::frame_handler },
+&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ { &amp;amp;output.wlr_output_damage-&amp;gt;events.frame, Output::frame_handler },
+&lt;/span&gt; { &amp;amp;output.wlr_output-&amp;gt;events.present, Output::present_handler },
+ { &amp;amp;output.wlr_output-&amp;gt;events.commit, Output::commit_handler },
+ { &amp;amp;output.wlr_output-&amp;gt;events.mode, Output::mode_handler },
+&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
+
+&lt;p&gt;This is not complete code for a basic damage tracking implementation with wlroots. You can see &lt;a href=&quot;https://gitlab.com/cardboardwm/cardboard/-/commit/f2ef2ff076ddbbd23994553b8eff131f9bd0207f&quot;&gt;the whole commit here&lt;/a&gt;.&lt;/p&gt;
+
+&lt;p&gt;This is an example of how wlroots provides yet another “module” that we can use in the grand scheme of the compositor. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlr_output_damage&lt;/code&gt; accumulates damaged rectangles in time and even turns these
+numerous small rectangles into a big one as an optimisation. It also calls the frame handler when it’s needed, and this is not only just when something on the screen changed, but also when the underlying “backend”
+of the compositor changes. The simplest situation is when the compositor starts: it needs to render an initial frame so the screen isn’t pitch black.&lt;/p&gt;
+
+&lt;p&gt;All in all, I do not recommend writing your own compositor if you only want some gimmicky user interface.
+In the X world there is a WM for every single way of window tiling plus a couple more. It doesn’t work like
+that with Wayland, you will spend more time implementing basic compositor duties than
+on your compositor’s unique features. Instead, if I were to rewrite Cardboard, I would rather do it
+as a &lt;a href=&quot;https://github.com/WayfireWM/wayfire/wiki/Plugin-architecture&quot;&gt;Wayfire plugin&lt;/a&gt; or maybe as a KWin script. However, I think Wayfire is more “enthusiast-friendly”, as it uses &lt;a href=&quot;https://github.com/swaywm/wlr-protocols&quot;&gt;protocols from
+wlroots&lt;/a&gt; such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;layer-shell&lt;/code&gt; (for panels, overlays and backgrounds), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gamma-control&lt;/code&gt; (for Redshift), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;screencopy&lt;/code&gt; (for screenshots) and others,
+allowing people to write tools that are not specific to the compositor.&lt;/p&gt;
+
+&lt;p&gt;Nevertheless, if you want to do it for the learning experience, I definitely recommend writing
+a “full-fledged” compositor with wlroots, learning from other compositors (&lt;a href=&quot;https://github.com/swaywm/sway&quot;&gt;Sway&lt;/a&gt;,
+&lt;a href=&quot;https://github.com/hjdskes/cage&quot;&gt;cage&lt;/a&gt;, &lt;a href=&quot;https://github.com/WayfireWM/wayfire&quot;&gt;Wayfire&lt;/a&gt;, &lt;a href=&quot;https://hikari.acmelabs.space/&quot;&gt;hikari&lt;/a&gt; and others; cage is the simplest, hikari second simplest) and from their creators on
+IRC (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#sway-devel&lt;/code&gt; on Freenode), they are very kind and knowledgeable.&lt;/p&gt;
+
+&lt;p&gt;There is also a &lt;a href=&quot;https://github.com/swaywm/wlroots/issues/1826&quot;&gt;discussion about introducing a high-level scene API in wlroots&lt;/a&gt;. Maybe when it will arrive, I will change my opinion.&lt;/p&gt;
+
+&lt;p&gt;Recommended lectures:&lt;/p&gt;
+&lt;ul&gt;
+ &lt;li&gt;&lt;a href=&quot;https://drewdevault.com/2018/02/17/Writing-a-Wayland-compositor-1.html&quot;&gt;Writing a Wayland Compositor&lt;/a&gt;, by Drew DeVault, the author of Sway and wlroots&lt;/li&gt;
+ &lt;li&gt;&lt;a href=&quot;https://wayland-book.com/&quot;&gt;The Wayland Protocol Book&lt;/a&gt;, also by Drew DeVault&lt;/li&gt;
+ &lt;li&gt;&lt;a href=&quot;https://wayland.freedesktop.org/docs/html/&quot;&gt;The Wayland Protocol&lt;/a&gt;, the actual protocol (larger than the book)&lt;/li&gt;
+ &lt;li&gt;Posts from Simon Ser’s blog, maintainer of Sway and wlroots:
+ &lt;ul&gt;
+ &lt;li&gt;&lt;a href=&quot;https://emersion.fr/blog/2018/wayland-rendering-loop/&quot;&gt;Writing a Wayland rendering loop&lt;/a&gt;&lt;/li&gt;
+ &lt;li&gt;&lt;a href=&quot;https://emersion.fr/blog/2019/intro-to-damage-tracking/&quot;&gt;Introduction to damage tracking&lt;/a&gt;&lt;/li&gt;
+ &lt;li&gt;&lt;a href=&quot;https://emersion.fr/blog/2020/wayland-clipboard-drag-and-drop/&quot;&gt;Wayland clipboard and drag &amp;amp; drop&lt;/a&gt;&lt;/li&gt;
+ &lt;/ul&gt;
+ &lt;/li&gt;
+ &lt;li&gt;&lt;a href=&quot;https://github.com/WayfireWM/wayfire/blob/cb7920187d2546ca375f00e7ef0a71d7a4995ba6/src/core/core.cpp#L173&quot;&gt;The init routine of Wayfire&lt;/a&gt;. GTK is rather picky when it comes to the order of advertised Wayland protocols.
+Don’t waste hours of your life trying to figure out whether you did something wrong, it’s GTK…&lt;/li&gt;
+&lt;/ul&gt;
+
+&lt;p&gt;Also, I suggest you write the compositor in either C, C++ or Zig (with &lt;a href=&quot;https://github.com/swaywm/zig-wlroots&quot;&gt;zig-wlroots&lt;/a&gt; or just doing your thing, Zig is C compatible).
+See this article on why the &lt;a href=&quot;http://way-cooler.org/blog/2019/04/29/rewriting-way-cooler-in-c.html&quot;&gt;Rust bindings failed&lt;/a&gt;.&lt;/p&gt;</content><author><name>Tudor Roman</name></author><category term="technical" /><summary type="html">Wayland is the “new” (13 years old already) display server technology on Linux, which is supposed to replace the antiquated X11. It promises better security, performance, portability, everything, compared to X11, and it sure does deliver, provided that you’re not using unsupported graphics cards. You can watch this talk / rant about X11 to get an idea about how bad it is.</summary></entry></feed> \ No newline at end of file
diff --git a/_site/index.html b/_site/index.html
index ed60006..43c29eb 100644
--- a/_site/index.html
+++ b/_site/index.html
@@ -15,7 +15,7 @@
<meta property="og:site_name" content="tudor's blog">
<!-- jekyll-feed gem required -->
- <link type="application/atom+xml" rel="alternate" href="http://localhost:4000/blog/feed.xml" title="tudor's blog" />
+ <link type="application/atom+xml" rel="alternate" href="/blog/feed.xml" title="tudor's blog" />
<link rel="stylesheet" href="/blog/css/main.css">
<link rel="icon" href="/blog/favicon.png">
@@ -45,14 +45,42 @@
+
+ <li>
+ <a href="/blog/technical/" class="">
+
+ Nerd stuff
+
+ </a>
+ </li>
+
+
+
+ <li>
+ <a href="/blog/writing/" class="">
+
+ Writings
+
+ </a>
+ </li>
+
+
</ul>
</nav>
</header>
+ <div class="home">
+ <ul class="posts">
+
+
+ <li onmouseover="this.children[0].innerHTML='2021-01-26'" onmouseout="this.children[0].innerHTML='1611651563'">
+ <span>1611651563</span>
+ <a href="/blog/technical/2021/01/26/the-wayland-experience/">Should You Write a Wayland Compositor?</a>
+ </li>
-<h1 id="under-construction">Under construction!</h1>
+ </ul>
+</div>
-<p>Check back soon!</p>
<footer>
<hr />
diff --git a/_site/technical/2021/01/26/the-wayland-experience/index.html b/_site/technical/2021/01/26/the-wayland-experience/index.html
new file mode 100644
index 0000000..e4bd28b
--- /dev/null
+++ b/_site/technical/2021/01/26/the-wayland-experience/index.html
@@ -0,0 +1,311 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="description" content="the blog of Tudor Roman">
+ <meta name="author" content="Tudor Roman">
+ <meta name="keywords" content="tudor roman, tudor's blog, blog, linux, unix">
+ <meta name="theme-color" content="#333">
+
+ <meta property="og:title" content="Should You Write a Wayland Compositor?">
+ <meta property="og:type" content="article">
+ <meta property="og:description" content="the blog of Tudor Roman">
+ <meta property="og:author" content="Tudor Roman">
+ <meta property="og:site_name" content="tudor's blog">
+
+ <!-- jekyll-feed gem required -->
+ <link type="application/atom+xml" rel="alternate" href="/blog/feed.xml" title="tudor's blog" />
+
+ <link rel="stylesheet" href="/blog/css/main.css">
+ <link rel="icon" href="/blog/favicon.png">
+ <title>Should You Write a Wayland Compositor? - tudor's blog</title>
+</head>
+<body>
+ <main>
+ <header class="site-header">
+ <h1 class="site-title"><a href="/blog/">tudor's blog</a></h1>
+ <nav class="site-nav">
+ <ul>
+
+
+
+
+
+ <li>
+ <a href="/blog/about/" class="">
+
+ About
+
+ </a>
+ </li>
+
+
+
+
+
+
+
+ <li>
+ <a href="/blog/technical/" class="">
+
+ Nerd stuff
+
+ </a>
+ </li>
+
+
+
+ <li>
+ <a href="/blog/writing/" class="">
+
+ Writings
+
+ </a>
+ </li>
+
+
+
+ </ul>
+ </nav>
+ </header>
+ <article class="post">
+ <h2 class="post-header">Should You Write a Wayland Compositor?</h2>
+ <p class="post-subtitle"></p>
+ <p class="post-meta">2021-01-26 10:59:23+02:00</p>
+ <p><a href="https://wayland.freedesktop.org/">Wayland</a> is the “new” (13 years old already) display server technology on Linux, which is supposed to replace
+the antiquated X11. It promises better security, performance, portability, everything, compared to X11, and
+it sure does deliver, provided that you’re not using <a href="https://www.nvidia.com/">unsupported graphics cards</a>.
+You can watch <a href="https://www.youtube.com/watch?v=RIctzAQOe44">this talk / rant about X11</a> to get an idea about how bad it is.</p>
+
+<p>Some power users also haven’t switched to Wayland because their gimmicky window manager doesn’t have a
+Wayland equivalent (XMonad, Awesome, Bspwm and the others). Some may wonder “hmm maybe I can port it myself”. I wrote this post to make you (re)consider that.</p>
+
+<p>One of the main ideas of Wayland is that it’s merely a specialised IPC protocol, and the communication is
+strictly between the clients (applications on your screen mainly) and the server.</p>
+
+<p>The server is what we are interested in now. Unlike X11, where the server is X.Org and the window manager
+is just an X11 client with special privileges, on Wayland the window manager is also a server and a compositor. In fact, the correct terminology is “Wayland compositor”. This piece of software has the
+task of doing everything it wants with the clients, which is usually among the lines of showing them
+on the screen and giving them inputs events from your keyboards, mice, touchscreens etc. In fact, <a href="https://mir-server.io/">no one
+stops you from leaving window management as an API</a>.</p>
+
+<p>On my laptop, I prefer using GNOME under Wayland, because it has the most stable, fully-featured and “just works”
+experience there is. However, I wanted a better way of organising windows on my screen than leaving them
+shuffled around. Tiling was also not good, because every time you launch a new window, the others get
+shrunk to fit, which is not good on a small laptop display.</p>
+
+<p>Luckily I discovered <a href="https://github.com/paperwm/PaperWM">PaperWM</a>. It’s perfect for laptops, instead of
+shrinking your windows, it just renders them off-screen. To switch between one or the other you can just
+flick three fingers on the touchpad. It’s great. It’s also “glossy” and polished and has great UI and animations.</p>
+
+<p>It has some disadvantages though, mainly concerning its speed. I find it sluggish, and it’s no surprise that
+like all other GNOME Shell extensions, it’s a JavaScript behemoth. PaperWM still uses the GNOME Tweener
+framework for its animations, which is entirely written in JS. Because of that, it needs to communicate with the main
+GNOME compositor process on each operation. And because we’re talking animations, said operations happen
+for <em>every frame</em>. That means there is JS executed for every frame, 60 times a second. It’s horrible!</p>
+
+<p>BTW nowadays GNOME uses animations implemented in C (since GNOME 3.34), the “GNOME runs JS on every frame” statement is for the most part false today.</p>
+
+<p>Because of the inefficiency of PaperWM, it also means that it drains my battery quickly, which is bad for a laptop!</p>
+
+<p>So, because I am a student with a lot of free time, together with <a href="https://alexge50.com/">Alex</a> we figured we shall develop a scrollable tiling (PaperWM-like) Wayland compositor. It’s called <a href="https://gitlab.com/cardboardwm/cardboard">Cardboard</a> (get it??). It also has the nicety of
+being controlled and configured by a remote control program, like bspwm.
+Neither Alex nor I had experience with any kind of Wayland development. However, I wrote <a href="https://tudorr.ro/software/windowchef/">an X11 window manager</a>,
+<a href="https://github.com/tudurom/disputils/">some XRandR querying utilities</a> and <a href="https://github.com/tudurom/ruler">a window rule daemon</a>. Alex has experience
+with <a href="https://git.alexge50.com/a/gie">graphics programming</a> and <a href="https://github.com/alexge50/libwatchpoint">C++ devilry</a>.</p>
+
+<p>Our “tech stack” was <a href="https://github.com/swaywm/wlroots">wlroots</a>, which is an exceptional Wayland compositor library (apart from its lack of documentation). The description from the README is spot on:</p>
+
+<blockquote>
+ <p>About 50,000 lines of code you were going to write anyway.</p>
+</blockquote>
+
+<p>I (because I wrote most of the Wayland-interfacing code) realised soon that there is still a lot of boilerplate involved in writing a compositor, much more than for an X11 window manager,
+namely setting up your own rendering code, registering and storing input devices and screens in your own data structures, passing input events to windows, calculating bounds for bars
+and other overlays (courtesy of <a href="https://github.com/swaywm/wlr-protocols/blob/master/unstable/wlr-layer-shell-unstable-v1.xml"><code class="language-plaintext highlighter-rouge">layer-shell</code></a>) and others. X11 handles all of this for you,
+the window manager just reacts to events regarding window inputs to establish its behaviour. With Wayland, you handle everything, even with wlroots. The upside is that if you don’t like the way X11 does something
+(which is a given), not only that you can do it in your own way on Wayland, you are required to do so.</p>
+
+<p>Because we weren’t really prepared for what writing a compositor involved, we thought that it must be approached like a “normal” program: split code into modules, each with their own responsibilities, call wlroots to do its thing, the usual stuff.
+We are writing a program in our way and wlroots lets us “interface with Wayland”. Or so we thought.</p>
+
+<p>We were as careful as possible to separate responsibilities into different structures / classes, yet we ended up with most functions taking a pointer to <code class="language-plaintext highlighter-rouge">Server</code> as their first parameter.
+<code class="language-plaintext highlighter-rouge">Server</code> is a singleton holding all the other “sub-modules” in it. It represents the compositor itself. The reason most functions need <code class="language-plaintext highlighter-rouge">Server</code> is that everything is related to everything,
+not because of a mistake in structuring the code, but by design. This is what a compositor requires, the problem of writing a compositor is somewhat complex because it has a great deal variables ranging
+from input events, drawing on the screen, creating behaviours that the user leverages, reacting to application events. All of them can affect the other, you can not separate the thing into modules.
+The best you can do is separate the code in different files and folders based on some criteria, like grouping data structures with related routines.</p>
+
+<p>Excerpt from <code class="language-plaintext highlighter-rouge">Seat.h</code>:</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// Hides the \a view from the screen without unmapping. Happens when a Workspace is deactivated.</span>
+<span class="kt">void</span> <span class="nf">hide_view</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="n">View</span><span class="o">&amp;</span> <span class="n">view</span><span class="p">);</span>
+<span class="c1">/// Gives keyboard focus to a plain surface (OR xwayland usually)</span>
+<span class="kt">void</span> <span class="nf">focus_surface</span><span class="p">(</span><span class="k">struct</span> <span class="nc">wlr_surface</span><span class="o">*</span> <span class="n">surface</span><span class="p">);</span>
+<span class="cm">/**
+ * \brief Sets the focus state on \a view. Auto-scrolls the Workspace if it's tiled.
+ *
+ * If \a view is null, the previously focused view will be unfocused and no other view will be focused.
+ */</span>
+<span class="kt">void</span> <span class="nf">focus_view</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="n">OptionalRef</span><span class="o">&lt;</span><span class="n">View</span><span class="o">&gt;</span> <span class="n">view</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">condense_workspace</span> <span class="o">=</span> <span class="nb">false</span><span class="p">);</span>
+<span class="c1">/// Marks the layer as receiving keyboard focus from this seat.</span>
+<span class="kt">void</span> <span class="nf">focus_layer</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="k">struct</span> <span class="nc">wlr_layer_surface_v1</span><span class="o">*</span> <span class="n">layer</span><span class="p">);</span>
+<span class="cm">/**
+ * \brief Focus the most recently focused view on \a column.
+ *
+ * \param column - must be from this workspace
+ */</span>
+<span class="kt">void</span> <span class="nf">focus_column</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="n">Workspace</span><span class="o">::</span><span class="n">Column</span><span class="o">&amp;</span> <span class="n">column</span><span class="p">);</span>
+<span class="c1">/// Removes the \a view from the focus stack.</span>
+<span class="kt">void</span> <span class="nf">remove_from_focus_stack</span><span class="p">(</span><span class="n">View</span><span class="o">&amp;</span> <span class="n">view</span><span class="p">);</span>
+
+<span class="kt">void</span> <span class="nf">begin_move</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="n">View</span><span class="o">&amp;</span> <span class="n">view</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">begin_resize</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="n">View</span><span class="o">&amp;</span> <span class="n">view</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">edges</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">begin_workspace_scroll</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="n">Workspace</span><span class="o">&amp;</span> <span class="n">workspace</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">process_cursor_motion</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">time</span> <span class="o">=</span> <span class="mi">0</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">process_cursor_move</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span><span class="p">,</span> <span class="n">GrabState</span><span class="o">::</span><span class="n">Move</span> <span class="n">move_data</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">process_cursor_resize</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span><span class="p">,</span> <span class="n">GrabState</span><span class="o">::</span><span class="n">Resize</span> <span class="n">resize_data</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">process_swipe_begin</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">fingers</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">process_swipe_update</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="kt">uint32_t</span> <span class="n">fingers</span><span class="p">,</span> <span class="kt">double</span> <span class="n">dx</span><span class="p">,</span> <span class="kt">double</span> <span class="n">dy</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">process_swipe_end</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">end_interactive</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">);</span>
+<span class="kt">void</span> <span class="nf">end_touchpad_swipe</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">);</span>
+
+<span class="c1">/// Updates the scroll of the workspace during three-finger swipe, taking in account speed and friction.</span>
+<span class="kt">void</span> <span class="nf">update_swipe</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">);</span>
+
+<span class="c1">/// Returns true if the \a view is currently in a grab operation.</span>
+<span class="kt">bool</span> <span class="nf">is_grabbing</span><span class="p">(</span><span class="n">View</span><span class="o">&amp;</span> <span class="n">view</span><span class="p">);</span>
+
+<span class="c1">/// Returns the workspace under the cursor.</span>
+<span class="n">OptionalRef</span><span class="o">&lt;</span><span class="n">Workspace</span><span class="o">&gt;</span> <span class="n">get_focused_workspace</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">);</span>
+
+<span class="c1">/// Moves the focus to a different workspace, if the workspace is already on a monitor, it focuses that monitor</span>
+<span class="kt">void</span> <span class="nf">focus</span><span class="p">(</span><span class="n">Server</span><span class="o">&amp;</span> <span class="n">server</span><span class="p">,</span> <span class="n">Workspace</span><span class="o">&amp;</span> <span class="n">workspace</span><span class="p">);</span> <span class="c1">// TODO: yikes, passing Server*</span>
+</code></pre></div></div>
+
+<p>As an example, let’s take damage tracking. For starters, because the compositor is also tasked with rendering the content and displaying it on the screen, we have some rendering code that runs on every frame.
+Damage tracking is tracking which areas of the screen have changed in an amount of time. An example would
+be typing a letter in the terminal. The place where the cursor is changes to the letter you typed, and the cursor advances.
+If there is no change, the frame is not rendered, as it would look
+exactly the same as the previous one, which would be a waste of processor time.
+This way, instead of re-rendering everything 60 times a second (I assume that you use a common display), we can render and paint as little as possible to account for the changed region.
+You can read <a href="https://emersion.fr/blog/2019/intro-to-damage-tracking/">an introduction to damage tracking</a> written by one of the main developers of wlroots.</p>
+
+<p>I have just implemented the most basic form of damage tracking: do not render the frame if nothing on the screen changes.
+It doesn’t track the damage itself, just that it exists.
+To do this, I first added a <code class="language-plaintext highlighter-rouge">wlr_output_damage</code> object to my <code class="language-plaintext highlighter-rouge">Output</code> structure:</p>
+
+<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">struct Output {
+</span> struct wlr_output* wlr_output;
+ struct wlr_output_damage* wlr_output_damage;
+<span class="gi">+ struct wlr_box usable_area;
+</span> ...
+</code></pre></div></div>
+
+<p>This structure tracks the damage accumulated per-output, as rendering is also per-output (this means that you can use screens of different refresh rates, yay!). However, to make this initial attempt at
+damage tracking easier, I decided to trigger rendering for all attached outputs. I added a <code class="language-plaintext highlighter-rouge">set_dirty()</code> function to the <code class="language-plaintext highlighter-rouge">OutputManager</code> class that does just that:</p>
+
+<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">OutputManager</span><span class="o">::</span><span class="n">set_dirty</span><span class="p">()</span>
+<span class="p">{</span>
+ <span class="k">for</span> <span class="p">(</span><span class="k">auto</span><span class="o">&amp;</span> <span class="n">output</span> <span class="o">:</span> <span class="n">outputs</span><span class="p">)</span> <span class="p">{</span>
+ <span class="n">wlr_output_damage_add_whole</span><span class="p">(</span><span class="n">output</span><span class="p">.</span><span class="n">wlr_output_damage</span><span class="p">);</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+</code></pre></div></div>
+
+<p>This marks every output as entirely damaged, and as such triggers a render.</p>
+
+<p>With this function set into place, I had to identify when does the “screen” change, namely when a window “commits” (changes its contents) and when a window is moved. The window move is one example
+of the way everything is related to everything in the compositor. Before damage tracking, the <code class="language-plaintext highlighter-rouge">View::move()</code> (we call windows “views”) method just changed the <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> fields of the <code class="language-plaintext highlighter-rouge">View</code> structures.
+Now, a move must call a method of <code class="language-plaintext highlighter-rouge">OutputManager</code>, so we need to give that as a parameter. This is almost like giving <code class="language-plaintext highlighter-rouge">Server</code> as a parameter, as <code class="language-plaintext highlighter-rouge">OutputManager</code> is a singleton inside <code class="language-plaintext highlighter-rouge">Server</code>.</p>
+
+<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gd">-void View::move(int x_, int y_)
+</span><span class="gi">+void View::move(OutputManager&amp; output_manager, int x_, int y_)
+</span><span class="err">{</span>
+ x = x_;
+ y = y_;
+<span class="gi">+ output_manager.set_dirty();
+</span><span class="err">}</span>
+</code></pre></div></div>
+
+<p>That’s when it hit me that wlroots is more of a framework and the compositor is one of its modules. Thinking that wlroots is an “interface to Wayland” is plain wrong, as the Wayland server is the
+program that I am writing. The next refactor is going to make the <code class="language-plaintext highlighter-rouge">Server</code> instance global…</p>
+
+<p>Now that we have a <code class="language-plaintext highlighter-rouge">wlr_output_damage</code> object in place and <code class="language-plaintext highlighter-rouge">set_dirty()</code> calls where they’re needed, we only need to call the render function when <code class="language-plaintext highlighter-rouge">wlr_output_damage</code> tells us instead of
+every <code class="language-plaintext highlighter-rouge">1/60</code> seconds:</p>
+
+<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">@@ -46,11 +46,12 @@</span> void register_output(Server&amp; server, Output&amp;&amp; output_)
+ server.output_manager-&gt;outputs.emplace_back(output_);
+ auto&amp; output = server.output_manager-&gt;outputs.back();
+ output.wlr_output-&gt;data = &amp;output;
+<span class="gi">+ output.wlr_output_damage = wlr_output_damage_create(output.wlr_output);
+</span>
+ register_handlers(server,
+ &amp;output,
+ {
+<span class="gd">- { &amp;output.wlr_output-&gt;events.frame, Output::frame_handler },
+</span><span class="gi">+ { &amp;output.wlr_output_damage-&gt;events.frame, Output::frame_handler },
+</span> { &amp;output.wlr_output-&gt;events.present, Output::present_handler },
+ { &amp;output.wlr_output-&gt;events.commit, Output::commit_handler },
+ { &amp;output.wlr_output-&gt;events.mode, Output::mode_handler },
+</code></pre></div></div>
+
+<p>This is not complete code for a basic damage tracking implementation with wlroots. You can see <a href="https://gitlab.com/cardboardwm/cardboard/-/commit/f2ef2ff076ddbbd23994553b8eff131f9bd0207f">the whole commit here</a>.</p>
+
+<p>This is an example of how wlroots provides yet another “module” that we can use in the grand scheme of the compositor. <code class="language-plaintext highlighter-rouge">wlr_output_damage</code> accumulates damaged rectangles in time and even turns these
+numerous small rectangles into a big one as an optimisation. It also calls the frame handler when it’s needed, and this is not only just when something on the screen changed, but also when the underlying “backend”
+of the compositor changes. The simplest situation is when the compositor starts: it needs to render an initial frame so the screen isn’t pitch black.</p>
+
+<p>All in all, I do not recommend writing your own compositor if you only want some gimmicky user interface.
+In the X world there is a WM for every single way of window tiling plus a couple more. It doesn’t work like
+that with Wayland, you will spend more time implementing basic compositor duties than
+on your compositor’s unique features. Instead, if I were to rewrite Cardboard, I would rather do it
+as a <a href="https://github.com/WayfireWM/wayfire/wiki/Plugin-architecture">Wayfire plugin</a> or maybe as a KWin script. However, I think Wayfire is more “enthusiast-friendly”, as it uses <a href="https://github.com/swaywm/wlr-protocols">protocols from
+wlroots</a> such as <code class="language-plaintext highlighter-rouge">layer-shell</code> (for panels, overlays and backgrounds), <code class="language-plaintext highlighter-rouge">gamma-control</code> (for Redshift), <code class="language-plaintext highlighter-rouge">screencopy</code> (for screenshots) and others,
+allowing people to write tools that are not specific to the compositor.</p>
+
+<p>Nevertheless, if you want to do it for the learning experience, I definitely recommend writing
+a “full-fledged” compositor with wlroots, learning from other compositors (<a href="https://github.com/swaywm/sway">Sway</a>,
+<a href="https://github.com/hjdskes/cage">cage</a>, <a href="https://github.com/WayfireWM/wayfire">Wayfire</a>, <a href="https://hikari.acmelabs.space/">hikari</a> and others; cage is the simplest, hikari second simplest) and from their creators on
+IRC (<code class="language-plaintext highlighter-rouge">#sway-devel</code> on Freenode), they are very kind and knowledgeable.</p>
+
+<p>There is also a <a href="https://github.com/swaywm/wlroots/issues/1826">discussion about introducing a high-level scene API in wlroots</a>. Maybe when it will arrive, I will change my opinion.</p>
+
+<p>Recommended lectures:</p>
+<ul>
+ <li><a href="https://drewdevault.com/2018/02/17/Writing-a-Wayland-compositor-1.html">Writing a Wayland Compositor</a>, by Drew DeVault, the author of Sway and wlroots</li>
+ <li><a href="https://wayland-book.com/">The Wayland Protocol Book</a>, also by Drew DeVault</li>
+ <li><a href="https://wayland.freedesktop.org/docs/html/">The Wayland Protocol</a>, the actual protocol (larger than the book)</li>
+ <li>Posts from Simon Ser’s blog, maintainer of Sway and wlroots:
+ <ul>
+ <li><a href="https://emersion.fr/blog/2018/wayland-rendering-loop/">Writing a Wayland rendering loop</a></li>
+ <li><a href="https://emersion.fr/blog/2019/intro-to-damage-tracking/">Introduction to damage tracking</a></li>
+ <li><a href="https://emersion.fr/blog/2020/wayland-clipboard-drag-and-drop/">Wayland clipboard and drag &amp; drop</a></li>
+ </ul>
+ </li>
+ <li><a href="https://github.com/WayfireWM/wayfire/blob/cb7920187d2546ca375f00e7ef0a71d7a4995ba6/src/core/core.cpp#L173">The init routine of Wayfire</a>. GTK is rather picky when it comes to the order of advertised Wayland protocols.
+Don’t waste hours of your life trying to figure out whether you did something wrong, it’s GTK…</li>
+</ul>
+
+<p>Also, I suggest you write the compositor in either C, C++ or Zig (with <a href="https://github.com/swaywm/zig-wlroots">zig-wlroots</a> or just doing your thing, Zig is C compatible).
+See this article on why the <a href="http://way-cooler.org/blog/2019/04/29/rewriting-way-cooler-in-c.html">Rust bindings failed</a>.</p>
+
+
+</article>
+
+ <footer>
+ <hr />
+ <nav class="footer-nav">
+ <ul>
+
+ <a href="/blog/" id="back_link">Back</a>
+
+ <a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width: 0; float: right;" src="/blog/assets/cc.png" /></a>
+ </ul>
+ </nav>
+ </footer>
+ </main>
+
+</body>
+</html>
diff --git a/_site/technical/index.html b/_site/technical/index.html
new file mode 100644
index 0000000..8d8104c
--- /dev/null
+++ b/_site/technical/index.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="description" content="the blog of Tudor Roman">
+ <meta name="author" content="Tudor Roman">
+ <meta name="keywords" content="tudor roman, tudor's blog, blog, linux, unix">
+ <meta name="theme-color" content="#333">
+
+ <meta property="og:title" content="Nerd stuff">
+ <meta property="og:type" content="article">
+ <meta property="og:description" content="the blog of Tudor Roman">
+ <meta property="og:author" content="Tudor Roman">
+ <meta property="og:site_name" content="tudor's blog">
+
+ <!-- jekyll-feed gem required -->
+ <link type="application/atom+xml" rel="alternate" href="/blog/feed.xml" title="tudor's blog" />
+
+ <link rel="stylesheet" href="/blog/css/main.css">
+ <link rel="icon" href="/blog/favicon.png">
+ <title>Nerd stuff - tudor's blog</title>
+</head>
+<body>
+ <main>
+ <header class="site-header">
+ <h1 class="site-title"><a href="/blog/">tudor's blog</a></h1>
+ <nav class="site-nav">
+ <ul>
+
+
+
+
+
+ <li>
+ <a href="/blog/about/" class="">
+
+ About
+
+ </a>
+ </li>
+
+
+
+
+
+
+
+ <li>
+ <a href="/blog/technical/" class="active">
+
+ Nerd stuff
+
+ </a>
+ </li>
+
+
+
+ <li>
+ <a href="/blog/writing/" class="">
+
+ Writings
+
+ </a>
+ </li>
+
+
+
+
+
+ </ul>
+ </nav>
+ </header>
+ <div class="home">
+ <ul class="posts">
+
+
+ <li onmouseover="this.children[0].innerHTML='2021-01-26'" onmouseout="this.children[0].innerHTML='1611651563'">
+ <span>1611651563</span>
+ <a href="/blog/technical/2021/01/26/the-wayland-experience/">Should You Write a Wayland Compositor?</a>
+ </li>
+
+ </ul>
+</div>
+
+
+ <footer>
+ <hr />
+ <nav class="footer-nav">
+ <ul>
+
+ <a href="/" id="back_link">Root</a>
+
+ <a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width: 0; float: right;" src="/blog/assets/cc.png" /></a>
+ </ul>
+ </nav>
+ </footer>
+ </main>
+
+</body>
+</html>
diff --git a/_site/writing/index.html b/_site/writing/index.html
new file mode 100644
index 0000000..5b57505
--- /dev/null
+++ b/_site/writing/index.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<html lang="en" prefix="og: http://ogp.me/ns#">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="description" content="the blog of Tudor Roman">
+ <meta name="author" content="Tudor Roman">
+ <meta name="keywords" content="tudor roman, tudor's blog, blog, linux, unix">
+ <meta name="theme-color" content="#333">
+
+ <meta property="og:title" content="Writings">
+ <meta property="og:type" content="article">
+ <meta property="og:description" content="the blog of Tudor Roman">
+ <meta property="og:author" content="Tudor Roman">
+ <meta property="og:site_name" content="tudor's blog">
+
+ <!-- jekyll-feed gem required -->
+ <link type="application/atom+xml" rel="alternate" href="/blog/feed.xml" title="tudor's blog" />
+
+ <link rel="stylesheet" href="/blog/css/main.css">
+ <link rel="icon" href="/blog/favicon.png">
+ <title>Writings - tudor's blog</title>
+</head>
+<body>
+ <main>
+ <header class="site-header">
+ <h1 class="site-title"><a href="/blog/">tudor's blog</a></h1>
+ <nav class="site-nav">
+ <ul>
+
+
+
+
+
+ <li>
+ <a href="/blog/about/" class="">
+
+ About
+
+ </a>
+ </li>
+
+
+
+
+
+
+
+ <li>
+ <a href="/blog/technical/" class="">
+
+ Nerd stuff
+
+ </a>
+ </li>
+
+
+
+ <li>
+ <a href="/blog/writing/" class="active">
+
+ Writings
+
+ </a>
+ </li>
+
+
+
+
+
+ </ul>
+ </nav>
+ </header>
+ <div class="home">
+ <ul class="posts">
+
+
+
+ </ul>
+</div>
+
+
+ <footer>
+ <hr />
+ <nav class="footer-nav">
+ <ul>
+
+ <a href="/" id="back_link">Root</a>
+
+ <a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width: 0; float: right;" src="/blog/assets/cc.png" /></a>
+ </ul>
+ </nav>
+ </footer>
+ </main>
+
+</body>
+</html>
diff --git a/index.md b/index.md
index ac860b8..ddc20f9 100644
--- a/index.md
+++ b/index.md
@@ -2,10 +2,4 @@
layout: default
---
-{% comment %}
{% include posts.html %}
-{% endcomment %}
-
-# Under construction!
-
-Check back soon!