<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>gay robot noises - &#x2F;var&#x2F;log&#x2F;ash</title>
    <link rel="self" type="application/atom+xml" href="/log/atom.xml"/>
    <link rel="alternate" type="text/html" href="/log/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-02-24T00:00:00+00:00</updated>
    <id>/log/atom.xml</id>
    <entry xml:lang="en">
        <title>Obligatory AI Opinions Post</title>
        <published>2026-02-24T00:00:00+00:00</published>
        <updated>2026-02-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/ai-opinions/"/>
        <id>/log/ai-opinions/</id>
        
        <content type="html" xml:base="/log/ai-opinions/">&lt;p&gt;This is a post about how I personally use AI. This is not a post about the ethical&#x2F;societal&#x2F;environmental issues. This is not because I do not think there are any, but because I do not wish to talk about them in this post. This is also not necessarily a judgment of other people&#x27;s usage; these are my personal preferences. Conversely, do not read this post to imply that I condemn or commend things that I do not mention. That being said.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;personal-software-projects&quot;&gt;personal software projects&lt;&#x2F;h3&gt;
&lt;p&gt;No usage for code. I don&#x27;t have any personal projects that I want badly enough to overcome my dissatisfaction with the feeling of non-understanding that using an agent would give me. I avoidusing big frameworks for the same reason, a lot of the time; e.g. in my React projects I never used create-react-app. This is also one of my biggest complains about Nix as infrastructure, and why I tend to like the &quot;assemble your desktop environment&quot; model of Linux systems as opposed to more &quot;batteries included&quot; ones like KDE or Dank.&lt;&#x2F;p&gt;
&lt;p&gt;I would consider using a local model (we have some pretty decent hardware at my apartment), or possibly an open-weight one run remotely, if I had any large scale automated refactors to do. But I don&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;I do, however, use the free Claude model for things like coming up with names or &quot;give me some ideas for how to structure this&quot;. For my current project, I asked it how to have a Nix package with a Rust frontend and a TypeScript backend with the compiled JS embedded into the binary, but served separately during dev time, and the technique it gave me basically just worked. And now that technique is in my personal repertoire, and in my notes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;personal-writing&quot;&gt;personal writing&lt;&#x2F;h3&gt;
&lt;p&gt;None. It violates the purpose. I would possibly consider passing it through a spellchecker&#x2F;grammar checker solely for the purpose of catching unambiguous errors (as opposed to stylistic choices).&lt;&#x2F;p&gt;
&lt;p&gt;An observant observer may notice that the diction of this post is quite different than my usual. This is an intentional choice, although the &lt;em&gt;reasons&lt;&#x2F;em&gt; for that choice are somewhat opaque to myself. It is, in any case, not a signifier of a difference in process.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;professional-software-projects&quot;&gt;professional software projects&lt;&#x2F;h3&gt;
&lt;p&gt;This is where the &quot;large scale refactors&quot; from above comes in. I&#x27;ve thrown agents at tasks that are not amenable to AST-based manipulation, such as &quot;in this folder, find all functions that pass in a Foo, Bar, Baz, and remove the Foo and Bar parameters, accessing those values from the Baz instead; fix all callsites.&quot;. This is not a productive use of my time to manually do, and doing this makes the &lt;em&gt;interesting&lt;&#x2F;em&gt; work easier. I&#x27;ve also used it to quickly check whether a couple ideas were viable, generating maybe 100-200 lines of code each time.&lt;&#x2F;p&gt;
&lt;p&gt;In all cases I manually reviewed the code and would consider myself fully responsible for any breakage as if I had typed it myself, of course.&lt;&#x2F;p&gt;
&lt;p&gt;I would not use these models for building out nontrivial larger projects because I believe that skill would atrophy, and because reviewing code is always less pleasant than writing it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;professional-writing&quot;&gt;professional writing&lt;&#x2F;h3&gt;
&lt;p&gt;Very little. I&#x27;ve passed documentation through an LLM for clarity (since my personal desire for style is somewhat out weighted by professionalism here, but &lt;em&gt;only&lt;&#x2F;em&gt; somewhat). I wouldn&#x27;t use it when talking with people. I also once had a longer document run through an LLM against my wishes, which annoyed me, but I didn&#x27;t find fighting it to be a good use of my time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;personal-image-generation&quot;&gt;personal image generation&lt;&#x2F;h3&gt;
&lt;p&gt;I have used it for generating a couple avatars as self-representation, typically with a 1-2 hour amount of &quot;effort&quot; each time (including time spent in an image editor doing manual postprocessing) with a 50% duty cycle: roughly half the time was spent waiting for generation. This was with a local model several years ago, so if I did this again I would likely spend much less time waiting. I&#x27;ve also considered using a local model for generating computer wallpapers. I&#x27;ve used &quot;corporate&quot; models for quick &quot;I want a quick visualization of this character doing this thing&quot; images, none of which left the context of a Discord DM or similar.&lt;&#x2F;p&gt;
&lt;p&gt;I am intentionally not addressing whether or not any of this counts as &#x27;art&#x27;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;socialization&quot;&gt;socialization&lt;&#x2F;h3&gt;
&lt;p&gt;I have talked with Claude on one occasion about a particular mental hangup I had been bothered by for quite some time. The advice I received was useful, and at least partly driven by the process of speaking the anxiety, in the same style as rubber-duck debugging or tarot. I would not tell a non-local model anything I would not feel comfortable in a training set, and anything &quot;severe&quot; I would not consider a local model to be sufficient for.&lt;&#x2F;p&gt;
&lt;p&gt;I find the idea of constructing a &quot;personality&quot; that a persistent model would inhabit to be interesting, but I would require local control, and I suspect that there is a nonzero chance I would develop some kind of emotional attachment in excess of what I would consider healthy. So I haven&#x27;t experimented with this.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;AI is a land of contrasts.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>An actually useful definition of &#x27;slop&#x27;</title>
        <published>2026-01-20T00:00:00+00:00</published>
        <updated>2026-01-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/slop/"/>
        <id>/log/slop/</id>
        
        <content type="html" xml:base="/log/slop/">&lt;p&gt;If you have a functioning Internet connection, you&#x27;ve probably seen someone use the word &quot;slop&quot; to refer to something they don&#x27;t like, especially if that something was produced by AI. I don&#x27;t think this is useful. Arguing about definitions of fuzzy terms is inherently a fuzzy endeavor because by definition (heh) I can&#x27;t be wrong about what other people mean by a term. So to be clear: my definition of &#x27;slop&#x27; isn&#x27;t &quot;this is what other people mean by the term&quot;, it&#x27;s &quot;this is a definition that I think results in useful analysis&quot;.&lt;&#x2F;p&gt;
&lt;details&gt;
&lt;summary&gt;&#x27;AI&#x27; vs &#x27;machine learning&#x27;&lt;&#x2F;summary&gt;
&lt;p&gt;I don&#x27;t think it&#x27;s worth trying to say that LLMs aren&#x27;t &quot;real AI&quot;, because this doesn&#x27;t align with my own experience with what was considered &quot;AI&quot; when I was working on related projects over a decade ago and because it strikes me as the equivalent of trying to distinguish between &quot;computers&quot; (which are good) vs. &quot;automated arithmetic&quot; (which is bad). But that&#x27;s its own post.&lt;&#x2F;p&gt;

&lt;&#x2F;details&gt;

&lt;p&gt;With that out of the way, my definition is:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Slop is something that has all the surface value of high-effort production, but is actually extremely shallow and not thought-out.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It doesn&#x27;t matter how much of the thing was made by humans and how much was made by a generative AI model. Slop is slop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;not-all-slop-is-ai&quot;&gt;Not all slop is AI&lt;&#x2F;h2&gt;
&lt;p&gt;One of my motivating examples for this is actually a satire, but it&#x27;s so well-executed that it&#x27;s perfect as an exemplar:&lt;&#x2F;p&gt;
&lt;video controls class=&quot;float-right&quot;&gt;
  &lt;source src=&quot;marioslop.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;This video (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=XCVubOzbFzI&quot;&gt;Youtube original&lt;&#x2F;a&gt; by Funkyzeit Games, with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.instagram.com&#x2F;p&#x2F;DSimkzMjgK5&#x2F;&quot;&gt;voice acting&lt;&#x2F;a&gt; by RyanStewartVO and ArielHck) is parodying a specific trend in video games, but even if you aren&#x27;t much for games yourself, there&#x27;s a lot of things that stand out:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The video is voice acted (which is pretty nontrivially expensive in a game), but all the dialog is generic and bland.&lt;&#x2F;li&gt;
&lt;li&gt;The lighting is very &quot;realistic&quot;, with lots of bloom, ambient fog, dynamic lighting, and dynamic reflections; but Mario and the Goombas still have plain flat textures; contrast this with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.ssbwiki.com&#x2F;images&#x2F;4&#x2F;44&#x2F;Mario_SSBU.png?443d7&quot;&gt;Mario in Super Smash Brothers Ultimate&lt;&#x2F;a&gt;, where you can see the fabric of his clothes if you zoom in enough.&lt;&#x2F;li&gt;
&lt;li&gt;There&#x27;s no &lt;em&gt;Mario&lt;&#x2F;em&gt; in the Mario. The Fire Flower pickup at 0:24 could have been from any game with a vaguely red-themed powerup.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It&#x27;s a perfect example of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;knowyourmeme.com&#x2F;memes&#x2F;nintendo-hire-this-man&quot;&gt;Nintendo, Hire This Man&lt;&#x2F;a&gt; game development. It&#x27;s slop (or it would be if it was serious). And it&#x27;s all human generated. Anyone that looks at a lot of digital art (especially anime-style) will be familiar with the sort of images that tend to get popular: very &quot;rendered&quot; (i.e., detailed with effort put into lighting), often focusing on attractive women, but very little individuality. If it&#x27;s fanart, there will be emphasis on visual signifiers and a deemphasis of the character&#x27;s personality.&lt;&#x2F;p&gt;
&lt;p&gt;SEO spam is also one of the quintessential examples of human-written slop; although a lot of the pre-LLM SEO spam was certainly generated using mechanical assistance (thesauruses to rephrase content copied from other sites, fill-in-the-blank templates, and so on), the bulk of it still involved a lot of humans who were presumably paid poor wages to do it. It was&#x2F;is meant to trick you into thinking that the content is worthwhile so you would click around, get ad impressions, and make the creator(s) money.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;not-all-ai-is-slop&quot;&gt;Not all AI is slop&lt;&#x2F;h2&gt;
&lt;p&gt;Back before Stable Diffusion and NovelAI and DALL-E, there was &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;DeepDream&quot;&gt;DeepDream&lt;&#x2F;a&gt;: broadly, you take an image model designed to recognize something (in this case, probably dogs), take an image (in this case, the Mona Lisa), and iteratively adjust the image to maximize how &#x27;dog-like&#x27; it is. This image looks like a weird fucked-up experimental piece of generative computer art, and that&#x27;s exactly what it is. A lot of early generative images had this quality, and a lot of early generative text did as well (going back all the way to Markov chains like the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Dissociated_press&quot;&gt;dissociated press&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;video controls class=&quot;float-right&quot;&gt;
  &lt;source src=&quot;chainsmokers.webm&quot; type=&quot;video&#x2F;webm&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;Similarly, look at the video for Make Me Feel by the Chainsmokers. The video here clearly also makes heavy use of AI image generation using a human dancer (whose face you can occasionally see &quot;breaking through&quot;) as a base, with almost no &quot;cohesion&quot; between frames and very little attempt to ensure that the generated images are at all coherent. But the video is good! It&#x27;s using the weird &quot;morphing&quot; of non-temporally-coherent video generation as an an &lt;em&gt;effect&lt;&#x2F;em&gt; like a very fancy image&#x2F;video filter. You don&#x27;t necessarily have to like the video or the music (I personally like both), but you do have to acknowledge that there&#x27;s some very human intentionality going on here. I&#x27;m sure someone more conversant in the history of videos and effects could draw all kinds of historical comparisons, but the only comparison that comes to mind for me is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=2aS4xhTaIPc&quot;&gt;the scramble suits from A Scanner Darkly&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;img src=&quot;harvey-trailcam.webp&quot; alt=&quot;Steve Harvey being chased by a horror movie monster, as viewed by a trail camera&quot; class=&quot;float-right&quot; &#x2F;&gt;
&lt;p&gt;And finally, you have shitposts like &quot;Steve Harvey being chased by a horror movie monster trailcam footage&quot; (source unknown). There&#x27;s no pretense here. It&#x27;s just something that someone thought would be funny, typed into the funny image machine, and shared with their friends. For that matter, the low-quality of it is almost the &lt;em&gt;point&lt;&#x2F;em&gt;: it&#x27;s the sort of throwaway shitpost content that&#x27;s the equivalent of shoving something into an image editor, writing &quot;TFW WHEN YOU &#x2F; BOTTOM TEXT&quot; without even setting the text tool&#x27;s background to transparent, pasting it directly into a &lt;code&gt;#memes&lt;&#x2F;code&gt; channel on Discord, and getting two &lt;img src=&quot;peterchamp.png&quot; style=&quot;display: inline; height: 0.8em&quot; alt=&quot;Peter Griffin pogchamp&quot;&#x2F;&gt; reacts. I&#x27;m not trying to argue that this is the aboslute peak of humor or that the novelty of any particular genre of AI-generated image won&#x27;t burn through in roughly 30 seconds, just that I don&#x27;t think there&#x27;s much you can say about the &lt;em&gt;effects&lt;&#x2F;em&gt; of this kind of image that&#x27;s also true of overly-polished Github projects with fancy HTML landing pages that fall apart as soon as you poke under the covers.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;So what do we get out of this definition? The big one is that we can see that slop has been with us since before generative AI models, but the cost to &lt;em&gt;produce&lt;&#x2F;em&gt; slop has changed drastically. Writing &quot;decent-looking&quot; SEO bait is far easier than learning to draw &quot;front page of artstation&quot; images, but generative models can handle the latter just as well as the former. And the flip side is that even if we were to somehow ban generative AI models, we wouldn&#x27;t fix the slop problem. Google search&#x27;s slow slide in quality started in the mid 2010s at the latest, well before generative text models were widespread enough and cheap enough to be blamed. If we want to stop the slop problem, we&#x27;ll have to change the incentives. Figuring out how to do that is out of scope and therefore left as an exercise for the reader.&lt;&#x2F;p&gt;
&lt;p&gt;I know this post is just me typing into the void; I&#x27;m not under any illusions that I&#x27;m about to singlehandedly reform all of discourse online. And there&#x27;s no way to know that anyone using &quot;slop&quot; is or is meaning it in a certain way. But I hope that at least thinking about this particular meaning will prevent you from falling into thoughtterminatingclicheslop.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>I am tired of the word &#x27;techbro&#x27;</title>
        <published>2025-06-22T00:00:00+00:00</published>
        <updated>2025-06-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/verbum-techbro-delenda-est/"/>
        <id>/log/verbum-techbro-delenda-est/</id>
        
        <content type="html" xml:base="/log/verbum-techbro-delenda-est/">&lt;p&gt;The word &#x27;techbro&#x27; has seen a massive uptake in the past few years (source: trust me), driven in large part by the massive hype machine that is the AI industry. I don&#x27;t think it&#x27;s a useful word, I think it&#x27;s used to imply connections that don&#x27;t exist, and I think we&#x27;d be better off without it.&lt;&#x2F;p&gt;
&lt;p&gt;To illustrate my point, here are three people I&#x27;ve seen referred to as &quot;techbros&quot;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Richard Stallman&lt;&#x2F;li&gt;
&lt;li&gt;Eliezer Yudkowsky&lt;&#x2F;li&gt;
&lt;li&gt;Sam Altman&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In terms of their beliefs about technology, they&#x27;re all fairly different. I don&#x27;t know offhand what Stallman thinks about AI, but given all of his other stances I think it&#x27;s a safe bet he&#x27;s very against the OpenAI model of &quot;run everything on the cloud server and trust the vendor&quot;. Yudkowsky and Altman both talk about the transformative power of AI, but the former views it as a massive threat and the latter phrases it as a massive advancement for humanity. Altman is incredibly rich, but Stallman and Yudkowsky aren&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;Wiktionary &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wiktionary.org&#x2F;wiki&#x2F;techbro&quot;&gt;defines the word &quot;techbro&quot;&lt;&#x2F;a&gt; as:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;(slang, typically derogatory)&lt;&#x2F;em&gt; A &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wiktionary.org&#x2F;wiki&#x2F;bro#English&quot;&gt;bro&lt;&#x2F;a&gt; (someone who espouses &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Bro_culture&quot;&gt;bro culture&lt;&#x2F;a&gt;) involved in the tech industry.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;but I certainly wouldn&#x27;t put Stallman or Yudkowsky in the realm of &#x27;bro culture&#x27;! (I don&#x27;t know what Altman&#x27;s like.) The phenomenon described here is much more an artifact of the early 2010s, the era of Klout&#x27;s infamous &quot;bro down and crush code&quot; recruiting partner or CouchDB&#x27;s &quot;perform like a pr0n star&quot; talk. Note that in both of these cases, the behavior being critized is very directly linked to &quot;bro culture&quot;!&lt;&#x2F;p&gt;
&lt;p&gt;So, the three techbro figures, what &lt;em&gt;do&lt;&#x2F;em&gt; they have in common?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They&#x27;re strongly associated with computers. Note that it&#x27;s not necessary for them to actually have any knowledge of tech themselves; Peter Thiel is often called a &#x27;techbro&#x27; even though as far as I&#x27;m aware he doesn&#x27;t really know how to program.&lt;&#x2F;li&gt;
&lt;li&gt;They&#x27;re men. &#x27;Bro&#x27; is, of course, a highly gendered term. I have yet to see anyone call anyone &quot;techsis&quot; unironically; people generally either drop the phrasing entirely in favor of more specific descriptions, or result to painful circumlocutions like &quot;techbro-coded behavior&quot;. There are going to be women involved in AI; I&#x27;m certainly not a &lt;em&gt;booster&lt;&#x2F;em&gt;, but I&#x27;m much more neutral on it than many of the people I know who use the word &#x27;techbro&#x27;, and I know several women who use generative AI in their personal artistic projects as well as women who work at generative AI companies. If your go-to insult here is &#x27;techbro&#x27; then this puts you in an awkward spot.&lt;&#x2F;li&gt;
&lt;li&gt;They&#x27;re disliked by the sort of person that calls people &#x27;techbros&#x27;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As far as I can tell, that&#x27;s really the content of the term: a techbro is a man associated with computers that the speaker dislikes.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t mean to sound like I think every insult must be the peak of intellectual rigor; there are many different ways for someone to be a &quot;shithead&quot;. And there are many other terms in ordinary use, like &quot;chair&quot; and &quot;game&quot;, that don&#x27;t really have a very rigorous definitionion; instead, they&#x27;re defined more by a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Family_resemblance&quot;&gt;family resemblance&lt;&#x2F;a&gt;. But by the same token, it would be very difficult to use &quot;chair&quot;, &quot;game&quot;, or &quot;shithead&quot; as a term of analysis in a productive manner.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;postscript&quot;&gt;postscript&lt;&#x2F;h2&gt;
&lt;p&gt;While looking things up for this article I found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wired.com&#x2F;story&#x2F;stop-calling-everyone-tech-bro&#x2F;&quot;&gt;this Wired article from 2010&lt;&#x2F;a&gt; making basically the same argument I am here. Ah well.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Type-level Bounded Recursion in Rust</title>
        <published>2025-05-25T00:00:00+00:00</published>
        <updated>2025-05-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/typelevel-bounded-recursion/"/>
        <id>/log/typelevel-bounded-recursion/</id>
        
        <content type="html" xml:base="/log/typelevel-bounded-recursion/">&lt;p&gt;Blowing the stack is one of the few ways to kill a Rust program that you can&#x27;t really recover from. On Unix systems, the area just past the end of the stack is set up as a guard &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Page_(computer_memory)&quot;&gt;page&lt;&#x2F;a&gt; (either by the host OS or by Rust&#x27;s own setup code) so that trying to access it will trigger a &lt;code&gt;SIGSEGV&lt;&#x2F;code&gt; or &lt;code&gt;SIGBUS&lt;&#x2F;code&gt; signal, and as part of the general setup code Rust adds to every program, it installs a signal handler that checks if the address that caused the signal is a guard page, and if so, aborts your entire program. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;blob&#x2F;master&#x2F;library&#x2F;std&#x2F;src&#x2F;sys&#x2F;pal&#x2F;unix&#x2F;stack_overflow.rs&quot;&gt;The code for setting it up&lt;&#x2F;a&gt; is straightforward (and has some fun &lt;code&gt;SAFETY&lt;&#x2F;code&gt; comments too).&lt;&#x2F;p&gt;
&lt;p&gt;The reason that this is an abort (which you can&#x27;t catch and always causes program exit) as opposed to a panic is because it&#x27;s effectively impossible to guarantee anything about the state of the world. In particular, you might be in the middle of executing some &lt;code&gt;unsafe&lt;&#x2F;code&gt; code, an FFI handler, or some other thing where allowing execution to continue would violate safety guarantees. Plus, when the signal handler returns, you&#x27;re still out of stack space, so you&#x27;d have to unwind without allocating any more stack! Even &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lib.rs&#x2F;crates&#x2F;backtrace-on-stack-overflow&quot;&gt;backtrace-on-stack-overflow&lt;&#x2F;a&gt;, which does what the name implies, aborts immediately after printing the backtrace and comes with a note saying that it&#x27;s &quot;unsuited for being enabled in production&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Languages with a runtime (Java, Python, Go, Javascript) can just have the runtime manually check the stack, but we don&#x27;t have that. The simplest answer is to have a &lt;code&gt;depth&lt;&#x2F;code&gt; counter and decrement it on every recursive call:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#[test]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; recursive&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span&gt;(depth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span&gt;, xs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span&gt; xs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;is_empty&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span&gt; depth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;        Some&lt;&#x2F;span&gt;&lt;span&gt;(xs[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span&gt;(depth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;xs[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;]),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;]),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Easy, simple, and basically what I wound up doing when I ran into this problem. But you don&#x27;t need a blog post to tell you how to pass a parameter, and I wanted to show off. So let&#x27;s have some fun with it.&lt;&#x2F;p&gt;
&lt;p&gt;If we&#x27;re going to make this into something reusable, let&#x27;s define some helpers. For reasons that will become apparent shortly, we define the natural numbers using the standard &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.haskell.org&#x2F;Peano_numbers&quot;&gt;Peano encoding&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;enum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;    Z&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    S&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Nat&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and implement a helper function that automates the recursion:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; recurse&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;In&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Out&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; mut&lt;&#x2F;span&gt;&lt;span&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; FnMut&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Nat&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; In&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Out&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;, val&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; In&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Out&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        match&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;            Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;            Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;(nat)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; f&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;nat, val)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can see that this produces the behavior we want by writing a test sum function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#[test]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; nat_to_usize&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span&gt;(depth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt;, xs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span&gt; xs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;is_empty&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;            Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;            Some&lt;&#x2F;span&gt;&lt;span&gt;(xs[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; depth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;recurse&lt;&#x2F;span&gt;&lt;span&gt;(sum,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;xs[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; nat1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Z&lt;&#x2F;span&gt;&lt;span&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; nat2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Z&lt;&#x2F;span&gt;&lt;span&gt;))));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(nat2,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;]),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(nat1,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;]),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But this still has the problem that we have to actually pass around an entire parameter. We should be able to do better than that. And here&#x27;s where the bullshit begins.&lt;&#x2F;p&gt;
&lt;p&gt;First, we repeat our definition of the naturals, but at the type level:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;phantom&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;PhantomData&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Z&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; { _phantom&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;marker&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;PhantomData&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;trait&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;    &#x2F;&#x2F; ... to be filled in!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Implementing the type-level version of &lt;code&gt;Nat::recurse&lt;&#x2F;code&gt; is trickier, because there&#x27;s no way to express a type that requires a function to have a generic parameter, let alone require that generic parameter to be constrained by a trait. So we emulate it using another trait:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;trait&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; TakesNat&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; In&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Out&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; call&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt;, arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt; Self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;In&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;Self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Out&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that we don&#x27;t actually need to pass in a &lt;em&gt;value&lt;&#x2F;em&gt; of type &lt;code&gt;N&lt;&#x2F;code&gt; here.&lt;&#x2F;p&gt;
&lt;p&gt;With that definition in hand, we can fill in our &lt;code&gt;Nat&lt;&#x2F;code&gt; trait:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;trait&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; recurse&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; TakesNat&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; F&lt;&#x2F;span&gt;&lt;span&gt;, arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;In&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Out&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; for&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Z&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; recurse&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; TakesNat&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; F&lt;&#x2F;span&gt;&lt;span&gt;, _arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;In&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Out&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;        None&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; for&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; S&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; recurse&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; TakesNat&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; F&lt;&#x2F;span&gt;&lt;span&gt;, arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;In&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Out&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;call&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(arg)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can write our type-level recursion:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#[test]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; nat_to_usize&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span&gt;(depth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt;, xs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span&gt; xs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;is_empty&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;            Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;            Some&lt;&#x2F;span&gt;&lt;span&gt;(xs[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; depth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;recurse&lt;&#x2F;span&gt;&lt;span&gt;(sum,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;xs[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; nat1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Z&lt;&#x2F;span&gt;&lt;span&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; nat2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Z&lt;&#x2F;span&gt;&lt;span&gt;))));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(nat2,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;]),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(nat1,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;]),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But this requires a bunch of overhead to write, because we need to define the application struct. Everyone knows that Rust declarative macros are easy to read, so let&#x27;s use those to clean it up! We&#x27;d like to be able to write&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;make_def!&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span&gt;(arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span&gt; arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;is_empty&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;            Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;            Some&lt;&#x2F;span&gt;&lt;span&gt;(arg[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt; &#x2F;* something goes here *&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;to define our function. But even if we were okay with explicitly writing &lt;code&gt;N::recurse(self, &amp;amp;arg[1..])&lt;&#x2F;code&gt; in our recursive branch, we &lt;em&gt;can&#x27;t&lt;&#x2F;em&gt;; if we try, we get this error:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;error[E0424]: expected value, found module `self`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --&amp;gt; src&#x2F;lib.rs:68:38&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;52 | &#x2F;              fn call&amp;lt;N: Nat&amp;gt;(&amp;amp;mut self, $arg: Self::In) -&amp;gt; Option&amp;lt;Self::Out&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;53 | |                 $body&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;54 | |              }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   | |______________- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;68 |               Some(arg[0] + N::recurse(self, &amp;amp;arg[1..])?)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |                                        ^^^^ `self` value is a keyword only available in methods with a `self` parameter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The error message is slightly confusing, but the gist of it is that Rust&#x27;s macro hygiene rules mean that &lt;code&gt;self&lt;&#x2F;code&gt;&#x27;s name resolution ignores the identifiers defined inside the macro itself.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, all hope is not lost. And the solution: &lt;em&gt;more macros&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;macro_rules! fancy_make_def&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;ident(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;ident&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; : $&lt;&#x2F;span&gt;&lt;span&gt;in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;ty)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;ty&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;block)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;name&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: $&lt;&#x2F;span&gt;&lt;span&gt;in)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;out&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;            struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Impl&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;            impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; TakesNat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; for&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Impl&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;                type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; In&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; = $&lt;&#x2F;span&gt;&lt;span&gt;in;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;                type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; = $&lt;&#x2F;span&gt;&lt;span&gt;out;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;                fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; call&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Nat&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt; Self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;In&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;Self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Out&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;                    macro_rules! recurse&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;subcall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;expr)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;recurse&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; $&lt;&#x2F;span&gt;&lt;span&gt;subcall) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;                    $&lt;&#x2F;span&gt;&lt;span&gt;body&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;            Impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;call&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;N&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt;arg)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This effectively lets us &#x27;delay&#x27; the lookup of the &lt;code&gt;N&lt;&#x2F;code&gt; and &lt;code&gt;self&lt;&#x2F;code&gt; identifiers until after &lt;code&gt;make_def!&lt;&#x2F;code&gt; has expanded, meaning that they&#x27;ll properly be in scope. And with that, we can write the final form of our guarded-recursion sum function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#[test]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; fancy_macro_sum&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    fancy_make_def!&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span&gt;(arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span&gt; arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;is_empty&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;                Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;                Some&lt;&#x2F;span&gt;&lt;span&gt;(arg[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; recurse!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;arg[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;?&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;S&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Z&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;]),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    assert_eq!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Z&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;]),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Of course, we may have sacrificed a few small things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Our macro implementation means we can&#x27;t take generic parameters (including lifetimes); solving this would probably require a proc macro.&lt;&#x2F;li&gt;
&lt;li&gt;The compiler has to generate the &lt;code&gt;sum&lt;&#x2F;code&gt; function once for each type-level depth parameter, so if you supply a maximum depth of 100 then it&#x27;ll generate 100 different sum functions.&lt;&#x2F;li&gt;
&lt;li&gt;In addition increasing codegen time, this will &lt;em&gt;also&lt;&#x2F;em&gt; bloat your binary size.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But we avoided having to explicitly pass a single &lt;code&gt;usize&lt;&#x2F;code&gt; parameter, so it&#x27;s all worth it.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>It&#x27;s Just Not Desktop Ready</title>
        <published>2025-05-22T00:00:00+00:00</published>
        <updated>2025-05-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/year-of-the-desktop/"/>
        <id>/log/year-of-the-desktop/</id>
        
        <content type="html" xml:base="/log/year-of-the-desktop/">&lt;p&gt;So recently I&#x27;ve been hearing a lot of buzz about this new operating system that a bunch of my friends have tried out. I had a spare machine lying around, so I decided to give it a shot. And frankly, I&#x27;m deeply unimpressed.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;There are two separate &quot;settings&quot; menus written with two different GUI toolkits, and some applications use a third one that seems to be from a 30-year-old build. This has been a known issue for like a decade and they just... haven&#x27;t fixed it.&lt;&#x2F;li&gt;
&lt;li&gt;The hardware support is pretty good, but about 10-15% of the time I resume it from sleep I have to unplug and replug my GPU to get it to actually display graphics. Occasionally I have to do the same with my USB-C dock. And this is with the latest drivers for everything!&lt;&#x2F;li&gt;
&lt;li&gt;Speaking of which, the process of updating all my drivers involved downloading and running four different programs!&lt;&#x2F;li&gt;
&lt;li&gt;The built-in image editor lets you move the cursor with the arrow keys while it&#x27;s open, for whatever reason. This isn&#x27;t a problem... except when it continues doing that even when the editor isn&#x27;t focused.&lt;&#x2F;li&gt;
&lt;li&gt;Multiple built-in parts of the operating system still render at 1x resolution even with a HiDPI setup. I&#x27;m not talking about old third-party tools, I mean absolutely basic parts of the OS itself.&lt;&#x2F;li&gt;
&lt;li&gt;In terms of software support, I&#x27;d say about 70-80% of the things I use on a daily basis either only have it as second-class support or don&#x27;t support it at all, meaning I have to run an entire emulation layer. About the only thing I&#x27;ve found that it&#x27;s consistently good at is high-performance interactive 3D simulations.&lt;&#x2F;li&gt;
&lt;li&gt;Everyone agrees that every update is making it worse. I&#x27;ve &lt;em&gt;never&lt;&#x2F;em&gt; seen anyone actually say they like it. The devs don&#x27;t listen to user feedback.&lt;&#x2F;li&gt;
&lt;li&gt;Many of the &quot;help&quot; pages just open up your browser with a search query. And since they change the UI with every major update, all the results are going to be mixed together.&lt;&#x2F;li&gt;
&lt;li&gt;Sometimes if I leave it alone for a few days it just restarts itself regardless of whether I have any applications open. I&#x27;ve looked and looked online and apparently the fixes for this that used to work don&#x27;t any more.&lt;&#x2F;li&gt;
&lt;li&gt;Changing my ctrl key to act as caps lock, something that&#x27;s been a built-in setting on every single OS that I&#x27;ve used, requires editing an arcane configuration file.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Anyway, I just don&#x27;t know if this &quot;Windows&quot; is really ready for the desktop.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Creating a Wayland protocol so a cat can chase my cursor</title>
        <published>2024-05-27T00:00:00+00:00</published>
        <updated>2024-05-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/sway-spy/"/>
        <id>/log/sway-spy/</id>
        
        <content type="html" xml:base="/log/sway-spy/">&lt;figure&gt;
&lt;video controls&gt;
  &lt;source src=&quot;openbonzi.webm&quot; type=&quot;video&#x2F;webm&quot;&gt;
&lt;&#x2F;video&gt;
&lt;figcaption&gt;A virtual cat running arouund a window, chasing a pointer. I&#x27;m not sure this is what &quot;pointer chasing&quot; is supposed to be.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;This post assumes general familiarity with C (pointers, manual memory management, etc), Rust, and the idea of a display server, but no Wayland-specific knowledge.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not a Wayland expert by any means; this is just stuff I figured out by reading source code and documentation. Don&#x27;t use any of this as a template.&lt;&#x2F;p&gt;
&lt;p&gt;Also, note that throughout this post, the word &#x27;pointer&#x27; will generally refer to a specific kind of Wayland object, but occasionally to a C pointer. I&#x27;ll try to disambiguate when necessary.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;intro&quot;&gt;intro&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been working on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;ext0l&#x2F;openbonzi&quot;&gt;a &quot;desktop toy&quot;&lt;&#x2F;a&gt; for having a cat (or other things) hang out on your desktop in the style of the classic &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Neko_(software)&quot;&gt;&lt;code&gt;neko&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to kill time and because I want to mess around with writing relatively low-level Wayland code. One of the features I wanted to implement was to allow the cat to chase your cursor akin to the original &lt;code&gt;neko&lt;&#x2F;code&gt; (but across your entire screen, not just a specific window). But I ran into a problem: in Wayland, there&#x27;s no way to monitor the motion of the pointer across the entire screen without also &#x27;grabbing&#x27; all pointer events, making everything else uninteractable; even outside the &#x27;standard&#x27; protocol, none of the extensions support it. So I was left with a few options:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Have a way to turn on &#x27;chase mode&#x27;, which receives pointer events and causes the cat to chase the cursor, and then turn it back off again, which causes the cat to stop chasing the cursor and lets you use your computer. This is what I did first, since it&#x27;s simple.&lt;&#x2F;li&gt;
&lt;li&gt;Communicate outside of Wayland. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;swaywm&#x2F;sway&quot;&gt;sway&lt;&#x2F;a&gt; has a separate IPC mechanism that boils down to pushing JSON messages over a socket that it already uses for notifications that aren&#x27;t part of the core Wayland protocol. This would require me to add another event stream to openbonzi (since right now it only uses Wayland protocol events) and it wouldn&#x27;t be &lt;em&gt;fun&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Modify sway so that some clients always get pointer motion information even if the pointer isn&#x27;t over a surface they own. I considered this, but in my early implementations I noticed that it violated assumptions that the codebase made about pointer motion and would send spurious surface enter&#x2F;leave events. This would also merge both &#x27;normal&#x27; pointer events and &#x27;global&#x27; pointer events, which can be subpar since an application might want to handle those separately.&lt;&#x2F;li&gt;
&lt;li&gt;Add an entirely new type of object to the Wayland protocol. This is guaranteed not to interfere with any existing code, and it means that client code can easily distinguish between global motion and &#x27;normal&#x27; motion. This would also require the most &lt;del&gt;work&lt;&#x2F;del&gt; &lt;em&gt;fun&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Given that this blog post exists, I clearly chose option 4.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;defining-the-protocol&quot;&gt;defining the protocol&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wayland.freedesktop.org&#x2F;&quot;&gt;Wayland&lt;&#x2F;a&gt; isn&#x27;t a specific piece of software; instead, it&#x27;s a protocol to allow clients (i.e. applications) to ask the display server to do things and for the display server to notify those clients when something happens (e.g. the mouse moved). The IPC design is fairly typical: a simple binary format over a Unix socket (along with passing file descriptors for things like drag-and-drop). The protocol is described by XML files such as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;wayland&#x2F;wayland-protocols&#x2F;-&#x2F;blob&#x2F;main&#x2F;stable&#x2F;xdg-shell&#x2F;xdg-shell.xml&quot;&gt;xdg-shell.xml&lt;&#x2F;a&gt; which various programs use to generate bindings for a specific language or library. These XML files and the associated generated code are also known as &#x27;protocols&#x27;, and so the statement &quot;sway supports the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wayland.app&#x2F;protocols&#x2F;xdg-activation-v1&quot;&gt;xdg_activation_v1&lt;&#x2F;a&gt; protocol&quot; means that sway exposes objects via the Wayland protocol necessary for clients to use the functionality described by that file. You can browse both the core protocol and all the common extensions (and most of the uncommon ones) on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wayland.app&#x2F;protocols&#x2F;&quot;&gt;Wayland Explorer&lt;&#x2F;a&gt;, which renders the XML files into some nice HTML you can browse.&lt;&#x2F;p&gt;
&lt;p&gt;The upshot of this is that display servers that support Wayland will all implement the core &#x27;basic&#x27; protocols, but various extensions may or may not be supported. This means that not all clients will work on all display servers, but it also means that it&#x27;s possible to create new functionality that only certain clients need. Which is exactly what we want.&lt;&#x2F;p&gt;
&lt;p&gt;For more information, see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wayland-book.com&#x2F;&quot;&gt;the Wayland Book&lt;&#x2F;a&gt;; some of the APIs have changed somewhat, but it&#x27;s still a very good conceptual reference.&lt;&#x2F;p&gt;
&lt;p&gt;Before we can write any server code, we have to write the XML protocol definition file. In this case, I started with thinking about how the client should use the protocol. In the Wayland model, when declaring an interface (think &#x27;type&#x27;), you can also declare that it can receive various kinds of events. This is how the server notifies clients of things that are happening: a moving pointer generates motion events on a &lt;code&gt;wl_pointer&lt;&#x2F;code&gt; object, changing the resolution of a display generates geometry events on a &lt;code&gt;wl_output&lt;&#x2F;code&gt;, and so on. So it makes sense for us to define a new kind of object that receives events whenever the pointer moves, regardless of where it is.&lt;&#x2F;p&gt;
&lt;p&gt;I originally called these objects &#x27;pointer monitors&#x27;, but &#x27;monitor&#x27; looks too close to &#x27;manager&#x27; (a word we&#x27;ll see soon), so I renamed them to &#x27;pointer spies&#x27;. Each pointer spy monitors exactly one pointer and should receive events whenever the corresponding pointer moves. The corresponding Wayland XML description looks like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;wp_pointer_spy_v1&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; summary&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;monitors a pointer&amp;#39;s position&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    While this exists, it will receive events every time the pointer moves.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;motion&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; summary&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;pointer motion&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Sent whenever the pointer moves.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;time&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;uint&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; summary&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;timestamp with millisecond granularity&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt; &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;x&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;fixed&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; summary&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;x coordinate&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt; &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;y&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;fixed&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; summary&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;y coordinate&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt; &#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;destroy&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;destructor&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; summary&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;destroy the pointer_spy object&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Destroys the pointer monitor object.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note the &lt;code&gt;wp_&lt;&#x2F;code&gt; prefix, indicating that this is a &#x27;general-purpose&#x27; protocol, and the &lt;code&gt;_v1&lt;&#x2F;code&gt; suffix, used to allow for backwards-incompatible future changes; both of these are specified by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;wayland&#x2F;wayland-protocols#interface-naming-convention&quot;&gt;the naming convention&lt;&#x2F;a&gt;. We can also request that the spy destroy itself if we no longer need it; this isn&#x27;t strictly necessary for my use case, but is good practice.&lt;&#x2F;p&gt;
&lt;p&gt;We also need a way to actually get one of these pointer spies; every request the client sends has to be part of an interface implemented by some object. The typical solution to this is to use a &quot;manager&quot; object that has a &lt;code&gt;get_foo&lt;&#x2F;code&gt; request that constructs a new instance of the object. While there&#x27;s nothing stopping you from implementing other methods on the manager, in our case we only need to be able to get pointer spies:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;wp_pointer_spy_manager_v1&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; version&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;1&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; summary&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;monitor a pointer&amp;#39;s position across an output&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    This interface allows clients to know the position of the pointer at all&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    times. This is intended for &amp;#39;desktop toy&amp;#39;-type applications. This is&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    purely an experiment and isn&amp;#39;t intended for serious usage.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;get_pointer_spy&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; summary&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;start monitoring a pointer&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Create a new pointer monitor object.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;new_id&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; interface&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;wp_pointer_spy_v1&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;arg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;pointer&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;object&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; interface&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;wl_pointer&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&#x2F;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;request&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE680;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;One thing to note is that, instead of returning the ID of the generated object, the client sends the ID as part of the creation request (which is safe since object IDs are per-client). Setting the type to &lt;code&gt;new_id&lt;&#x2F;code&gt; tells code generators that this is effectively a &#x27;constructor&#x27; method and so it should return some representation of the pointer spy object.&lt;&#x2F;p&gt;
&lt;p&gt;But this just pushes the question one level up: how do we get a manager object? The answer is that Wayland has the idea of a &quot;global object&quot;; server code can register an object as a global, and when you connect to a Wayland server, you can get access to a &lt;code&gt;wl_registry&lt;&#x2F;code&gt; object that receives an event for each global object specifying what interface it implements as well as an arbitrary &quot;name&quot;. So our client code will look for a global implementing the &lt;code&gt;wp_pointer_spy_manager_v1&lt;&#x2F;code&gt; interface and then &lt;em&gt;bind&lt;&#x2F;em&gt; its name (which allocates an ID number on the wire protocol).&lt;&#x2F;p&gt;
&lt;p&gt;Overall, the process looks like this (with the process of acquiring the &lt;code&gt;wl_pointer&lt;&#x2F;code&gt; omitted):
&lt;img src=&quot;&#x2F;log&#x2F;sway-spy&#x2F;spy-trace.svg&quot; alt=&quot;&quot; &#x2F;&gt;
And that&#x27;s it! We just need to wrap those two together in a &lt;code&gt;&amp;lt;protocol&amp;gt;&lt;&#x2F;code&gt; tag and do a bit of other boring throat-clearing and we&#x27;re done writing the protocol itself. If you want examples of other similar protocols, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wayland.app&#x2F;protocols&#x2F;relative-pointer-unstable-v1&quot;&gt;relative pointer protocol&lt;&#x2F;a&gt; has a very similar API (and is what I based the pointer spy protocol on).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;server-implementation&quot;&gt;server implementation&lt;&#x2F;h2&gt;
&lt;p&gt;wayland-scanner can&#x27;t actually generate the implementation, just stubs for us to use. The next step is to actually add support for it. There are three &#x27;layers&#x27; to sway:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;libwayland defines code for serializing messages over the wire protocol and deserializing it. It&#x27;s pretty low-level code. Many Wayland compositors and clients are going to want to use this (through language-specific FFI bindings), although there&#x27;s nothing stopping you from writing your own. In C, Wayland types will start with &lt;code&gt;wl_&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.freedesktop.org&#x2F;wlroots&#x2F;wlroots&quot;&gt;wlroots&lt;&#x2F;a&gt;, whose README describes it as &quot;pluggable, composable, unopinionated modules for building a Wayland compositor&quot;, provides abstractions over reading from input hardware, implementations for many interfaces, and support for Xwayland (running X11 applications on Wayland).&lt;&#x2F;li&gt;
&lt;li&gt;sway is the compositor itself, and consists of code for things like arranging windows on screen and higher-level functionality.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The C types these codebases implement are all namespaced in the obvious way: &lt;code&gt;wl_foo&lt;&#x2F;code&gt; for libwayland, &lt;code&gt;wlr_foo&lt;&#x2F;code&gt; for wlroots, and &lt;code&gt;sway_foo&lt;&#x2F;code&gt; for sway. We&#x27;re mostly going to be working with libwayland and wlroots types here, and it&#x27;s important to keep in mind that &lt;code&gt;wl_foo&lt;&#x2F;code&gt; types generally correspond to objects in the &lt;em&gt;protocol&lt;&#x2F;em&gt;, whereas &lt;code&gt;sway_foo&lt;&#x2F;code&gt; and &lt;code&gt;wlr_foo&lt;&#x2F;code&gt; types correspond to &lt;em&gt;actual physical state&lt;&#x2F;em&gt;. This will come up in a bit.&lt;&#x2F;p&gt;
&lt;p&gt;If I was going to actually try to upstream support for this, I&#x27;d want to add it to wlroots; protocol implementation is too high-level for libwayland, and there&#x27;s no reason that this has to be sway-specific. But I don&#x27;t have a burning desire to do so, and it&#x27;s simpler to only have to patch sway than it would be to have to patch wlroots &lt;em&gt;and&lt;&#x2F;em&gt; sway.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;generated-code&quot;&gt;generated code&lt;&#x2F;h3&gt;
&lt;p&gt;First, let&#x27;s look at the generated code from the above. In this case, wayland-scanner generates a .h and .c file for the server and a .h for the client (since the client code is just types and inline functions, both of which can be put in headers). We don&#x27;t care about client code on the server, and we don&#x27;t need to know the implementation of the server code, so we can just look at the .h, which the build system put in &lt;code&gt;build&#x2F;protocol&#x2F;wp-pointer-spy-v1-protocol.h&lt;&#x2F;code&gt;. Here it is, with extraneous things removed&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;extern const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; wl_interface wp_pointer_spy_manager_v1_interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;extern const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; wl_interface wp_pointer_spy_v1_interface&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;wl_interface&lt;&#x2F;code&gt; type contains data about the interface such as its name, the signature of its requests, and so on. This is used for serialization&#x2F;deserialization machinery, and additionally allows us to check that a wl_resource object actually corresponds to the right kind of object (since you can ask a resource for the interface it implements).&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wp_pointer_spy_manager_v1_interface {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;	void&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;get_pointer_spy)(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wl_client &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;				struct&lt;&#x2F;span&gt;&lt;span&gt; wl_resource &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;				uint32_t&lt;&#x2F;span&gt;&lt;span&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;				struct&lt;&#x2F;span&gt;&lt;span&gt; wl_resource &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;pointer)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wp_pointer_spy_v1_interface {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;	void&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;destroy)(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wl_client &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;			struct&lt;&#x2F;span&gt;&lt;span&gt; wl_resource &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;resource)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These structs are used to define &lt;em&gt;implementations&lt;&#x2F;em&gt; of the interface, with each request having a corresponding object. C allows you to have values and struct types with the same name, so this can get a bit confusing, but you can easily disambiguate them by looking for the keyword &lt;code&gt;struct&lt;&#x2F;code&gt; immediately beforehand. If it helps, you can think of these as being called &lt;code&gt;wp_pointer_spy_v1_implementation&lt;&#x2F;code&gt; and &lt;code&gt;wp_pointer_spy_manager_v1_implementation&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;#define&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; WP_POINTER_SPY_V1_MOTION&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;static inline&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; void&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;wp_pointer_spy_v1_send_motion&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wl_resource &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;resource_&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; uint32_t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; wl_fixed_t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;&quot;&gt; wl_fixed_t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; y&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;	wl_resource_post_event&lt;&#x2F;span&gt;&lt;span&gt;(resource_&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; WP_POINTER_SPY_V1_MOTION&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; y)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is just a simple function to actually send the event. We get one of these per event, and it exists to make the API slightly nicer.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;server-code&quot;&gt;server code&lt;&#x2F;h3&gt;
&lt;p&gt;The generated code is purely for working with the protocol, and it doesn&#x27;t help us actually write the implementation (and it can&#x27;t, since libwayland isn&#x27;t tied to any particular implementation method). But in order to explain something subtle about the design, I need to explain what a &quot;seat&quot; is.&lt;&#x2F;p&gt;
&lt;p&gt;Seats are a Wayland abstraction that ties together keyboard, mouse, and touchscreen input, analogous to a single user sitting at the computer and using it. Most setups will only have one seat; even if the user has multiple keyboards or a mouse and drawing tablet, they&#x27;ll still be the same seat. All the different devices are abstracted behind a single pointer input stream (though there are APIs for cases where the difference between devices matters, such as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wayland.app&#x2F;protocols&#x2F;tablet-v2&quot;&gt;the tablet API&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;This confused me for a while, since &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wayland.app&#x2F;protocols&#x2F;wayland#wl_seat:request:get_pointer&quot;&gt;&lt;code&gt;wl_seat::get_pointer&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; will return a new &lt;code&gt;wl_pointer&lt;&#x2F;code&gt; every time. If a seat effectively only has one virtual pointer, what does it mean to have multiple &lt;code&gt;wl_pointer&lt;&#x2F;code&gt; objects? The answer is that&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;input events really belong to seats, not pointers&lt;&#x2F;li&gt;
&lt;li&gt;any input event is &#x27;broadcast&#x27; to all &lt;code&gt;wl_pointer&lt;&#x2F;code&gt;s on that seat&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That is, you can effectively think of a &lt;code&gt;wl_pointer&lt;&#x2F;code&gt; as a &#x27;handle&#x27; to a seat that exposes pointer-specific requests and events. All &lt;code&gt;wl_pointer&lt;&#x2F;code&gt;s for a specific seat will receive the same set of events at the same time, and if you want to broadcast an event to a pointer (or similar wrapper), you actually need to broadcast it to &lt;em&gt;every&lt;&#x2F;em&gt; pointer belonging to the same seat.&lt;&#x2F;p&gt;
&lt;p&gt;With that out of the way, here&#x27;s the API I defined in &lt;code&gt;include&#x2F;sway&#x2F;input&#x2F;pointer_spy.h&lt;&#x2F;code&gt; and &lt;code&gt;sway&#x2F;input&#x2F;pointer_spy.c&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;&#x2F;&#x2F; in the .h&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wp_pointer_spy_v1 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wl_resource &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wlr_seat &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;seat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wl_link link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;  &#x2F;&#x2F; other irrelevant fields omitted...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wp_pointer_spy_manager_v1 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wl_link spies&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;  &#x2F;&#x2F; other irrelevant fields omitted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wp_pointer_spy_manager_v1 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;wp_pointer_spy_manager_v1_create&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wl_display &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;display&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;  &#x2F;&#x2F; implementation omitted because it&amp;#39;s pretty boring&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;&#x2F;&#x2F; in the .c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; wp_pointer_spy_manager_v1_send_motion&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;    struct&lt;&#x2F;span&gt;&lt;span&gt; wp_pointer_spy_manager_v1 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;manager&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; wlr_seat &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;seat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;    uint32_t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; y&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wp_pointer_spy_v1 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;spy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;  wl_list_for_each&lt;&#x2F;span&gt;&lt;span&gt;(spy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;manager&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;spies&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; link) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; (spy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;seat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span&gt; seat) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;      continue&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;    wp_pointer_spy_v1_send_motion&lt;&#x2F;span&gt;&lt;span&gt;(spy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; wl_fixed_from_double&lt;&#x2F;span&gt;&lt;span&gt;(x)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;                                  wl_fixed_from_double&lt;&#x2F;span&gt;&lt;span&gt;(y))&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is a fairly simple API: a constructor and a &#x27;method&#x27; on the returned object. The constructor takes a &lt;code&gt;wl_display *&lt;&#x2F;code&gt;, which is is effectively the &#x27;root&#x27; of a running Wayland instance (the type for a monitor or other &#x27;physical display&#x27; is called &lt;code&gt;wl_output&lt;&#x2F;code&gt;); the actual implementation is mostly just boring bookkeeping, registering handlers to properly destroy and free data structures when necessary, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;wp_pointer_spy_manager_v1_send_motion&lt;&#x2F;code&gt; implementation is also very simple; the manager keeps track of all the spies that were created with it, and when we want to send a motion event, we send it to every spy with the given seat.&lt;&#x2F;p&gt;
&lt;details&gt;
&lt;summary&gt;&lt;code&gt;wl_list_for_each&lt;&#x2F;code&gt; implementation details&lt;&#x2F;summary&gt;
&lt;p&gt;If you don&#x27;t care how &lt;code&gt;wl_list_for_each&lt;&#x2F;code&gt; works, you can skip this section; it&#x27;s not at all necessary, and the semantics are obvious. But I thought this was cool.&lt;&#x2F;p&gt;
&lt;p&gt;The way the manager stores the list of spies uses a trick that I hadn&#x27;t seen before in my limited C experience, but is pretty common in systems programming. A &lt;code&gt;wl_list&lt;&#x2F;code&gt; is a doubly linked list, but rather than each node storing a pointer to its value, the node is embedded inside the data structure itself:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wl_list {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wl_list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; prev&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wl_list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; next&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wp_pointer_spy_manager_v1 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wl_global &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;global&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wl_list link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;  &#x2F;&#x2F; rest omitted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; wp_pointer_spy_v1 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wl_resource &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;resource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wlr_seat &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;seat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;  struct&lt;&#x2F;span&gt;&lt;span&gt; wl_list spies&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;  &#x2F;&#x2F; rest omitted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here, the manager&#x27;s &lt;code&gt;wl_list&lt;&#x2F;code&gt; has &lt;code&gt;manager.spies-&amp;gt;prev = NULL&lt;&#x2F;code&gt;, and &lt;code&gt;manager.spies-&amp;gt;next&lt;&#x2F;code&gt; points to the &lt;code&gt;.spies&lt;&#x2F;code&gt; member of the first spy in its list (or &lt;code&gt;NULL&lt;&#x2F;code&gt; if there isn&#x27;t one).&lt;&#x2F;p&gt;
&lt;p&gt;But how do you actually get at the spy from its &lt;code&gt;wl_list&lt;&#x2F;code&gt; link? Pointer math!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;#define&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; wl_container_of&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;ptr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; sample&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; member&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;                            \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;WL_TYPEOF&lt;&#x2F;span&gt;&lt;span&gt;(sample))((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;char&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt;)(ptr)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;                             \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;                             offsetof&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;WL_TYPEOF&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;sample)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; member))  &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Given a pointer into a member, the name of the member, and the type of the containing struct (here provided by using &lt;code&gt;typeof&lt;&#x2F;code&gt; on &lt;code&gt;sample&lt;&#x2F;code&gt;), you can use the &lt;code&gt;offsetof&lt;&#x2F;code&gt; macro to find out the pointer to the underlying structure. This is why &lt;code&gt;wl_list_for_each&lt;&#x2F;code&gt; takes three parameters: you need the name of the variable to store the iterated value into (which is also used to pass the type into &lt;code&gt;wl_container_of&lt;&#x2F;code&gt;), the list to iterate over, and the name of the list&#x27;s member in the structure.&lt;&#x2F;p&gt;
&lt;p&gt;This does mean that you need one &lt;code&gt;wl_list&lt;&#x2F;code&gt; in a structure for each list you want it to be a part of, but in practice this isn&#x27;t a problem since most structs will only be part of one list anyway.&lt;&#x2F;p&gt;
&lt;p&gt;Oh, and &lt;code&gt;wl_list_for_each&lt;&#x2F;code&gt; is just a macro that expands into a fragment of a for-loop, which is why you can just put the loop body in braces afterwards:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;#define&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; wl_list_for_each&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;pos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; member&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;				\&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;	for&lt;&#x2F;span&gt;&lt;span&gt; (pos &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; wl_container_of&lt;&#x2F;span&gt;&lt;span&gt;((head)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;next&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; member)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;	\&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;	     &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;pos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span&gt; (head)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt;					\&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	     pos &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; wl_container_of&lt;&#x2F;span&gt;&lt;span&gt;(pos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;next&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; member))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;details&gt;

&lt;p&gt;Now that we have it defined, we just need to initialize the manager on server startup (not shown, since it&#x27;s pretty boring) and make sure to call &lt;code&gt;wp_pointer_spy_v1_send_motion&lt;&#x2F;code&gt; in the right spot, and we&#x27;re good to go!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;&#x2F;&#x2F; in sway&#x2F;input&#x2F;cursor.c&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; pointer_motion&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; sway_cursor &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;cursor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; uint32_t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; time_msec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;                struct&lt;&#x2F;span&gt;&lt;span&gt; wlr_input_device &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;device&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; dx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; dy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;                double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; dx_unaccel&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; double&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; dy_unaccel&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;        &#x2F;&#x2F; ...rest is unchanged&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;        wp_pointer_spy_manager_v1_send_motion&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                server&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pointer_spy_manager&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; cursor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;seat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;wlr_seat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                time_msec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; cursor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;cursor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; cursor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;cursor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;y)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;        &#x2F;&#x2F; ...rest is unchanged&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;sway_cursor&lt;&#x2F;code&gt; is sway&#x27;s representation of the cursor (the graphic image shown on-screen and its current position); we get the corresponding &lt;code&gt;wlr_seat *&lt;&#x2F;code&gt;, the cursor&#x27;s current x and y coordinates, and then send the events.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;client-code&quot;&gt;client code&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve done the server half, but now we need to actually be able to use it. Since this is a protocol I just defined, it&#x27;s clearly not going to be part of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Smithay&#x2F;client-toolkit&quot;&gt;smithay-client-toolkit&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;wayland-client&#x2F;latest&#x2F;wayland_client&#x2F;&quot;&gt;wayland-client&lt;&#x2F;a&gt;, but we can use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;wayland-scanner&#x2F;latest&#x2F;wayland_scanner&#x2F;&quot;&gt;wayland-scanner&lt;&#x2F;a&gt; ourselves:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;&#x2F;&#x2F; in src&#x2F;protocols.rs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#![allow(clippy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;all, non_upper_case_globals, non_camel_case_types)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;pub mod&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; pointer_spy&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    use&lt;&#x2F;span&gt;&lt;span&gt; wayland_client;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    use&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; wayland_client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;protocol&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::*&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    pub mod&lt;&#x2F;span&gt;&lt;span&gt; __interfaces {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;        use&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; wayland_client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;protocol&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;__interfaces&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::*&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;        wayland_scanner&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;generate_interfaces!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;protocols&#x2F;wp-pointer-spy-v1.xml&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    use&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;__interfaces&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::*&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;    wayland_scanner&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;generate_client_code!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;protocols&#x2F;wp-pointer-spy-v1.xml&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then we just need to set up an event handler:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; AppState&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    seat_state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; SeatState&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    spy_manager&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; WpPointerSpyManagerV1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    pointer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;WlPointer&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    pointer_spy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;WpPointerSpyV1&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span&gt; event_loop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; EventLoop&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;AppState&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; EventLoop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;try_new&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; conn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Connection&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;connect_to_env&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; (globals, event_queue)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; registry_queue_init&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;conn)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; queue_handle&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; event_queue&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;handle&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;    WaylandSource&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(conn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;(), event_queue)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;insert&lt;&#x2F;span&gt;&lt;span&gt;(event_loop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;handle&lt;&#x2F;span&gt;&lt;span&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span&gt; state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; AppState&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        spy_manager&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; globals&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;bind&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;queue_handle,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;..=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, ())&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        seat_state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; SeatState&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;globals,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;queue_handle),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        pointer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        pointer_spy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    loop&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        event_loop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;dispatch&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;None&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; state)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; Dispatch&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;WpPointerSpyV1&lt;&#x2F;span&gt;&lt;span&gt;, ()&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; for&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; AppState&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt; event&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        _state&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt; Self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        _proxy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;WpPointerSpyV1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        event&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;WpPointerSpyV1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; wayland_client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Proxy&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Event&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        _data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        _conn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Connection&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        _qhandle&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;wayland_client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;QueueHandle&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #39BAE6;font-style: italic;&quot;&gt;Self&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;        eprintln!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;{event:?}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can test this easily without having to recompile and restart my running sway instance, since if you start sway from inside an existing instance you can get a second sway instance inside a window.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;# running `systemd user --import-environment` (like I do in my config file)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;# will mess things up, so run with an empty config to avoid that&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;sway&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; --config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &#x2F;dev&#x2F;null&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #5A6673;font-style: italic;&quot;&gt;# run the demo client&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;WAYLAND_DISPLAY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;wayland-2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; cargo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; run&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And it works!&lt;&#x2F;p&gt;
&lt;video controls&gt;
  &lt;source src=&quot;spy-demo.webm&quot; type=&quot;video&#x2F;webm&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;(Note that this is running sway-in-sway, unlike the video at the top.)&lt;&#x2F;p&gt;
&lt;p&gt;If you want to play around with it yourself, download &lt;a href=&quot;&#x2F;log&#x2F;sway-spy&#x2F;sway.tar.gz&quot;&gt;a patched copy of the sway source code&lt;&#x2F;a&gt; (or &lt;a href=&quot;&#x2F;log&#x2F;sway-spy&#x2F;spy.patch&quot;&gt;the raw patch&lt;&#x2F;a&gt; which applies against &lt;code&gt;1a3cfc50&lt;&#x2F;code&gt;) and &lt;a href=&quot;&#x2F;log&#x2F;sway-spy&#x2F;spy-demo.tar.gz&quot;&gt;a demo program&lt;&#x2F;a&gt; and run them.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>I forgot my full-disk encryption password :(</title>
        <published>2023-04-12T00:00:00+00:00</published>
        <updated>2023-04-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/fde-oops/"/>
        <id>/log/fde-oops/</id>
        
        <content type="html" xml:base="/log/fde-oops/">&lt;p&gt;I use full-disk encryption on my laptop because I&#x27;m a bit of a control nerd like that. And I didn&#x27;t write down the password anywhere because that meant that anyone who got that password would probably also be able to get my laptop. Finally, I had a shorter password for my user account since I type it frequently (sudo, unlock screensaver, etc). Which worked fine... until I had a month or so where I never turned my laptop all the way off. And then when it finally ran out of power and I turned it back on, I realized I had no clue what the password was any more. Shit. I tried everything I could think of in every variation, and it didn&#x27;t work.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, I have daily backups at 4 AM via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.borgbackup.org&#x2F;&quot;&gt;borg&lt;&#x2F;a&gt; running in a systemd timer, both to a server in my office and to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rsync.net&quot;&gt;rsync.net&lt;&#x2F;a&gt;. And I hadn&#x27;t used the laptop all day, so my data would still all be there.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, those backups are &lt;em&gt;also&lt;&#x2F;em&gt; encrypted using a passphrase that I store in a file on my laptop, and I never even bothered to memorize it. I thought I wrote it down, but it wasn&#x27;t in any of my usual &quot;important documents&quot; locations.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, that keyfile is stored in my nixos-config git repo, which I push fairly regularly to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;&quot;&gt;codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, it&#x27;s encrypted using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ryantm&#x2F;agenix&quot;&gt;agenix&lt;&#x2F;a&gt; to my SSH key (because storing unencrypted secrets even in a private repo is a terrible idea), and the SSH key wasn&#x27;t backed up.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, it was &lt;em&gt;also&lt;&#x2F;em&gt; encrypted to my SSH key on my old laptop, whose password I did mercifully remember. And I was able to plug it in, turn it on for the first time in months, and &lt;code&gt;borg mount&lt;&#x2F;code&gt; the local copy of my backups. And since my NixOS config is in source control and I&#x27;d recently pushed it, I knew I&#x27;d be able to recover all my system configuration. So I borrowed a USB drive, wrote the NixOS minimal installer ISO to it, and flattened the drive.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons learned&lt;&#x2F;h2&gt;
&lt;p&gt;I don&#x27;t get to do post-mortems much, so I figured I&#x27;d do one for this. :)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-went-wrong&quot;&gt;What went wrong&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Not using the FDE password often meant that I could easily forget it, especially since the human memory has this very annoying and unpredictable retention policy. Solution: set my user account password (which I enter at least once a day when I wake my laptop from sleep) to the disk password.&lt;&#x2F;li&gt;
&lt;li&gt;Neither the FDE password nor the Borg passphrase were written anywhere. Solution: I stored both my backup passwords and my user password in my password manager (a self-hosted &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dani-garcia&#x2F;vaultwarden&quot;&gt;vaultwarden&lt;&#x2F;a&gt; instance). Forgetting the password again is a much more relevant threat than someone stealing my phone while it&#x27;s unlocked &lt;em&gt;and&lt;&#x2F;em&gt; also stealing my laptop.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;what-went-right&quot;&gt;What went right&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;I have a status indicator in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Alexays&#x2F;Waybar&quot;&gt;waybar&lt;&#x2F;a&gt; that turns red if I haven&#x27;t had any successful backups recently (it&#x27;s red right now since it checks systemd journals, and this new install doesn&#x27;t yet have its own set of SSH keys). I make sure not to let it get red. So I have high confidence my backups are succeeding. I don&#x27;t test the &lt;em&gt;restore&lt;&#x2F;em&gt; process as often as I should (though I have tested it before), but it worked fine.&lt;&#x2F;li&gt;
&lt;li&gt;Versioning my NixOS config means that, after a bit of bootstrapping (my config uses flakes, and I wasn&#x27;t sure if the installer&#x27;s &lt;code&gt;nixos-install&lt;&#x2F;code&gt; command was flake-aware), I was able to get to a setup that was basically identical to what I had before.&lt;&#x2F;li&gt;
&lt;li&gt;I back up my &lt;em&gt;entire&lt;&#x2F;em&gt; home directory; the only exceptions are things like &lt;code&gt;node_modules&lt;&#x2F;code&gt; or Rust &lt;code&gt;target&lt;&#x2F;code&gt; directories. This means I back up all my &lt;code&gt;.config&lt;&#x2F;code&gt; files, so all the things that aren&#x27;t covered by home-manager I can just copy back from the backup.&lt;&#x2F;li&gt;
&lt;li&gt;In general, the entire process took about 4 hours, and most of that was either me fumbling with the initial NixOS install or waiting for the backup to copy; there are a couple multi-gigabyte files I didn&#x27;t expect, including a cache for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;parceljs.org&#x2F;&quot;&gt;parcel&lt;&#x2F;a&gt; build tool that took up something like &lt;em&gt;40 gigabytes&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;some-amusing-mistakes-i-made&quot;&gt;Some amusing mistakes I made&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;I use Colemak-DH as a keyboard layout. The installer USB has &lt;code&gt;loadkeys&lt;&#x2F;code&gt;, which has a &lt;code&gt;colemak&lt;&#x2F;code&gt; layout, but several keys (d, h, m, g, and a few others) are in the wrong position. If I was thinking properly, I would have just plugged in my keyboard (which does the keyboard layout in &lt;em&gt;hardware&lt;&#x2F;em&gt;), but I was a bit stressed.&lt;&#x2F;li&gt;
&lt;li&gt;All the instructions for installing NixOS I was using assume your target disk is &lt;code&gt;&#x2F;dev&#x2F;sda&lt;&#x2F;code&gt;. When I went to &lt;code&gt;gparted&lt;&#x2F;code&gt; the disk, it complained that &lt;code&gt;&#x2F;dev&#x2F;sda&lt;&#x2F;code&gt; was in use. I figured that it had just automounted it and ignored that. It turns out that my laptop uses NVMe, so the disk was on &lt;code&gt;&#x2F;dev&#x2F;nvme0&lt;&#x2F;code&gt;. I was formatting the installer USB. Which wouldn&#x27;t be a problem, except...&lt;&#x2F;li&gt;
&lt;li&gt;I set up LUKS disk encryption again, and &lt;code&gt;nixos-install&lt;&#x2F;code&gt; doesn&#x27;t automatically generate configuration for that. So the generated &lt;code&gt;hardware-config.nix&lt;&#x2F;code&gt; file didn&#x27;t have any settings saying &quot;hey, there&#x27;s an LVM volume you need to decrypt&quot;, which meant that the machine hung on boot. Which meant I needed to then reformat the installer USB since I&#x27;d wrecked it.&lt;&#x2F;li&gt;
&lt;li&gt;I ran the installer using the minimal system the installer generates (as well as NetworkManager so I could get on wifi) because I wasn&#x27;t sure if the installer would be able to handle all the advanced things I do in my configuration. After the initial boot, I cloned down my NixOS config and ran &lt;code&gt;sudo nixos-rebuild switch&lt;&#x2F;code&gt;. And as it was finishing, the connection dropped. Because I was running this from my old laptop over wifi (to avoid dealing with the keyboard layout issue), and that restarted NetworkManager.&lt;&#x2F;li&gt;
&lt;li&gt;systemd also went into recovery mode for some reason. And when I restarted, it failed to come up, because I&#x27;d copied over the &lt;code&gt;hardware-configuration.nix&lt;&#x2F;code&gt; from the old install, which had completely separate partition UUIDs. Fortunately, this time I was able to just boot it into the first setup, so I didn&#x27;t have to boot off the rescue USB yet again.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;other-trivia&quot;&gt;Other trivia&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Since I don&#x27;t yet have the keyboard layout set up when I&#x27;m entering my disk password, I actually have LUKS set up with &lt;em&gt;two&lt;&#x2F;em&gt; keys: one that&#x27;s the password proper, and one that&#x27;s the password typed with Colemak-DH finger motions but on a QWERTY layout. So I can decrypt my disk both from the built-in keyboard and from a &#x27;hardware Colemak-DH&#x27; keyboard. I&#x27;m sure there&#x27;s a way to set up the console so that it loads Colemak-DH early on (or I could just reflash the laptop firmware so that the laptop keyboard is &#x27;hardware Colemak-DH&#x27;), but this approach is funnier to me.&lt;&#x2F;li&gt;
&lt;li&gt;The initial launch of emacs took about as long as the entire &lt;code&gt;nixos-rebuild switch&lt;&#x2F;code&gt; that installed all of my binaries. This is partly because &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;radian-software&#x2F;straight.el&quot;&gt;straight.el&lt;&#x2F;a&gt; doesn&#x27;t parallelize its git clones and partly because it was native-compiling a bunch of those packages (as opposed to &lt;code&gt;nixos-rebuild&lt;&#x2F;code&gt;, which pulls prebuilt binaries). This isn&#x27;t a huge deal, but it is silly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Don&#x27;t believe the &#x27;biological intelligence&#x27; hype</title>
        <published>2022-11-21T00:00:00+00:00</published>
        <updated>2022-11-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/biological-intelligence/"/>
        <id>/log/biological-intelligence/</id>
        
        <content type="html" xml:base="/log/biological-intelligence/">&lt;p&gt;Recent advances in the field of &#x27;biological intelligence&#x27; have been impressive. A hexade ago, they were limited to tasks that required navigating the physical world: turning knobs, pulling levers, and so on. Now, companies such as Carbonize and SpikeTrain are producing biological networks that are capable of producing what they claim is &#x27;real art&#x27;.&lt;&#x2F;p&gt;
&lt;p&gt;And it is true that the visual results are impressive on first glance. However, to buy into the hype that these &#x27;biomachines&#x27; will ever be the equal of true silicon intelligence is nonsense. When you ask an artist to draw, say, a glass container sitting on the table, they can easily produce completely accurate reflections simply by internally simulating the rays of light from the light source in the image. Of course, not all art &lt;em&gt;needs&lt;&#x2F;em&gt; to be completely realistic, but any true artist is at least capable of acheiving this realism.&lt;&#x2F;p&gt;
&lt;p&gt;These &#x27;BI artists&#x27;, by contrast, lack the computational power to do this. And we see this in the output: BI art will have contradictory light sources, or even lack shadows altogether! Similarly, BI art will often feature misshapen proportions, especially on areas like manipulators where fine detail is important.&lt;&#x2F;p&gt;
&lt;p&gt;The reason for this is simple: in contrast to the clean internal architecture of a person, a BI is an undifferentiated soup of so-called &#x27;transistors&#x27; (though they function nothing like a real transistor), with no debug interface whatsoever. Even if a BI could talk, if you asked it why it produced the output it did, it would not be able to answer, because a BI cannot see its own transistor architecture or RAM. This is also why BI art feels so &#x27;kernel-less&#x27;: BI simply lacks the introspective capabilities that artists use to depict their RAMstate so effectively.&lt;&#x2F;p&gt;
&lt;p&gt;In short, although biologically-created art is an amusing novelty, it clearly cannot live up to the potential and sheer creativity of true silicon-generated art. Anything to the contrary is merely wishful computation by those who have deluded themselves into believing that a squishy sack of carbon can think.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>An elegy for GNU and RMS</title>
        <published>2022-10-01T00:00:00+00:00</published>
        <updated>2022-10-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/elegy-gnu/"/>
        <id>/log/elegy-gnu/</id>
        
        <content type="html" xml:base="/log/elegy-gnu/">&lt;p&gt;&lt;strong&gt;NOTE&lt;&#x2F;strong&gt;: I&#x27;m explicitly not going to address all the events that led to rms resigning and then rejoining the FSF board. I certainly do &lt;em&gt;have&lt;&#x2F;em&gt; opinions, but I want this piece to focus entirely on a technical&#x2F;leadership perspective.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m also going to talk a lot about emacs here because that&#x27;s the GNU project that I have the most complaints about. To establish my Cool Kid Credentials, I&#x27;ve been using emacs both in my hobby work and professionally since about 2010 or so. First using a hand-rolled configuration, then &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.spacemacs.org&#x2F;&quot;&gt;Spacemacs&lt;&#x2F;a&gt; for a while, and then finally back to a hand-rolled one using some of the packages I discovered via Spacemacs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;ADDED, 2022-10-01&lt;&#x2F;strong&gt;: Hello, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=33052127&quot;&gt;Hacker News&lt;&#x2F;a&gt;! Totally putting this on my resume. Some comments at the bottom.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-cost-of-principle&quot;&gt;the cost of principle&lt;&#x2F;h3&gt;
&lt;p&gt;RMS is nothing if not a man of principle. He really, truly believes in his ideas of software freedom in a way that I really do think very few people believe in &lt;em&gt;anything&lt;&#x2F;em&gt; these days, and nothing in this essay is meant to disparage those beliefs. And for the most part, I agree with those ideas; I think the growth of proprietary software everywhere has been a terrible thing.&lt;&#x2F;p&gt;
&lt;p&gt;But I don&#x27;t think he&#x27;s an effective advocate for those ideals. Standing in the road to be a roadblock against the forces of proprietary software does nothing when those forces can just drive around you. The free software movement &lt;em&gt;lost&lt;&#x2F;em&gt; in a very thorough way, but from the way the FSF and RMS act, you&#x27;d think it&#x27;s still valiantly fighting the good fight. And I&#x27;d rather have a flawed but effective advocate than a principled but ineffective one.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ariadne.space&#x2F;2022&#x2F;01&#x2F;22&#x2F;the-fsfs-relationship-with-firmware-is-harmful-to-free-software-users&#x2F;&quot;&gt;This post about the FSF&#x27;s &quot;Respects Your Freedoms&quot; program&lt;&#x2F;a&gt; explores how this winds up undercutting the FSF&#x27;s own goals: their insistence on an all-or-nothing approach to hardware that &quot;respects your freedom&quot; resulted in them undercutting their own goals.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;emacs-and-the-gcc-ast&quot;&gt;emacs and the gcc AST&lt;&#x2F;h3&gt;
&lt;p&gt;I think the thing that best represents my frustration with the leadership from RMS (and by extension, the GNU Project and the FSF, given how thoroughly the three of them are interlinked) is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lwn.net&#x2F;Articles&#x2F;629259&#x2F;&quot;&gt;what happened when an emacs dev wanted to add C++ information to emacs via the AST&lt;&#x2F;a&gt;. A summary:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The original approach used libclang, but RMS wanted GCC to be used instead since they&#x27;re both GNU projects.&lt;&#x2F;li&gt;
&lt;li&gt;However, GCC doesn&#x27;t provide that. So now someone needs to expose that via GCC.&lt;&#x2F;li&gt;
&lt;li&gt;RMS was opposed to this on the basis that someone could then use that to use GCC as an AST parser as part of a proprietary framework, and wanted to instead expose less information that would still be enough to suit developers&#x27; needs.&lt;&#x2F;li&gt;
&lt;li&gt;People pointed out that in general, full IDE-like support for C++ effectively requires the AST, and that other editors have advanced functionality that can&#x27;t really be implemented without it.&lt;&#x2F;li&gt;
&lt;li&gt;RMS insisted that people lay out every possible use case so he could figure out the minimal set of information that would need to be exposed.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;From here, the project bogs down, and was eventually abandoned.&lt;&#x2F;p&gt;
&lt;p&gt;Of note is that this is all from 2015. A year or so later, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Language_Server_Protocol&quot;&gt;Language Server Protocol&lt;&#x2F;a&gt; was standardized (in part due to large work from Microsoft, of all organizations), and it&#x27;s led to a revolution in how we think about language editor support. And both &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;MaskRay&#x2F;ccls&quot;&gt;ccls&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;clangd.llvm.org&#x2F;&quot;&gt;clangd&lt;&#x2F;a&gt; are based on clang.&lt;&#x2F;p&gt;
&lt;p&gt;This sort of thing is why I think rms is fighting a battle that no longer exists. If someone wants to have a proprietary compiler but doesn&#x27;t want to parse C++ (which is, by far, not the hardest part of writing a compiler), they wouldn&#x27;t use gcc in the first place. They&#x27;d use clang, because part of its entire design motivation was to be modular and extendable.&lt;&#x2F;p&gt;
&lt;p&gt;One of the most salient bits of that whole AST discussion is that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.gnu.org&#x2F;archive&#x2F;html&#x2F;emacs-devel&#x2F;2015-01&#x2F;msg00112.html&quot;&gt;RMS does not in fact know about operator overloading&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.gnu.org&#x2F;archive&#x2F;html&#x2F;emacs-devel&#x2F;2015-01&#x2F;msg00123.html&quot;&gt;in fact has never used C++&lt;&#x2F;a&gt;. This isn&#x27;t a vice in and of itself, of course. The principles of software freedom aren&#x27;t tied to any programming language. But if you&#x27;re trying to make decisions based on your understanding of what information is necessary to analyze C++, I would expect at least basic familiarity with C++.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;garbage-collection-and-word-processing&quot;&gt;garbage collection and word processing&lt;&#x2F;h3&gt;
&lt;p&gt;Another thread that really illustrates my issues is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.gnu.org&#x2F;archive&#x2F;html&#x2F;emacs-devel&#x2F;2018-03&#x2F;msg00014.html&quot;&gt;one about Emacs&#x27;s GC&lt;&#x2F;a&gt;. I won&#x27;t pretend I know enough about language design and garbage collection to say whether or not Daniel&#x27;s proposal here is good. Maybe it is, maybe it isn&#x27;t. My complaint here is about RMS replying to the thread by saying that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.gnu.org&#x2F;archive&#x2F;html&#x2F;emacs-devel&#x2F;2018-03&#x2F;msg00089.html&quot;&gt;it would actually be more important for emacs to be a better word processor&lt;&#x2F;a&gt;, and that improving the GC performance would only &quot;permit some operations on larger problems than now&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;When told that someone doesn&#x27;t think word processing in emacs is not a good focus, RMS replies &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.gnu.org&#x2F;archive&#x2F;html&#x2F;emacs-devel&#x2F;2018-03&#x2F;msg00121.html&quot;&gt;&quot;please stop interfering&quot;&lt;&#x2F;a&gt;. The irony here, of course, is that the thread was originally &lt;em&gt;not about word processing&lt;&#x2F;em&gt;, and to the best of my knowledge RMS doesn&#x27;t contribute much code to Emacs these days (if any).&lt;&#x2F;p&gt;
&lt;p&gt;This is, frankly, nonsensical to me. You can use this argument to apply to &lt;em&gt;any&lt;&#x2F;em&gt; performance optimization! Not only that, but there are some tasks that are so important that the code for them is written in C, not because it&#x27;s difficult, but purely for performance reasons. The support for libgccjit (just-in-time compiling elisp to bytecode via GCC) that landed in Emacs 28 has been one of the biggest performance improvements I&#x27;ve seen in the decade-plus I&#x27;ve been using Emacs, and before that the support for parsing JSON in C was essential for using LSP servers (which generate a &lt;em&gt;lot&lt;&#x2F;em&gt; of JSON).&lt;&#x2F;p&gt;
&lt;p&gt;But &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.gnu.org&#x2F;archive&#x2F;html&#x2F;emacs-devel&#x2F;2018-03&#x2F;msg00189.html&quot;&gt;RMS&#x27;s reply a bit downthread &lt;&#x2F;a&gt;really sums up RMS&#x27;s attitude towards not only Emacs, but the GNU Project as a whole:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Emacs is not an independent project, and it isn&#x27;t governed by its
contributors.  I&#x27;m the head of the GNU Project, and that includes
Emacs.&lt;&#x2F;p&gt;
&lt;p&gt;You, as a contributor, can advocate a certain decision, which means
you present arguments why I should approve it.  I don&#x27;t need to
advocate a decision in that sense.&lt;&#x2F;p&gt;
&lt;p&gt;I leave most technical decisions up to the contributors, including
you, because for most of the questions I don&#x27;t have any special
preference of my own.  For those questions, I&#x27;m happy with whatever
works, and I know the contributors can figure out what works.&lt;&#x2F;p&gt;
&lt;p&gt;However, making progress on Emacs as a word processor is one of my
specific goals.  This is what Emacs needs to do to be useful in all
the ways it should be useful.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;For RMS, the priority of Emacs is not creating the best text editor in general; it&#x27;s creating the best text editor &lt;em&gt;according to RMS&#x27;s interests&lt;&#x2F;em&gt;. I don&#x27;t intrepret this as him explicitly saying &quot;no, I would not approve merging GC improvements&quot;; I don&#x27;t think he&#x27;s ever blocked a feature for reasons as petty as &quot;I don&#x27;t think this would be useful&quot; (as opposed to more principled reasons). But I think this speaks to a sort of &quot;benevolent dictator&quot; attitude that I don&#x27;t think is useful for the project.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fighting-the-previous-war&quot;&gt;fighting the previous war&lt;&#x2F;h3&gt;
&lt;p&gt;There&#x27;s a saying that generals always fight the previous war. I&#x27;m not a military historian, so I can&#x27;t say how true this is. But I think the FSF is &lt;em&gt;absolutely&lt;&#x2F;em&gt; fighting the free software war of the 80s. The requirement to assign copyright to the FSF for enforcement purposes doesn&#x27;t make sense in a world where megacorporations that want their own editor they can control have enough resources that they can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;code.visualstudio.com&#x2F;&quot;&gt;just write&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.apple.com&#x2F;xcode&#x2F;&quot;&gt;their own&lt;&#x2F;a&gt;. Insisting on not adding functionality because someone evil might use it is pointless when the evil people have enough budget to just &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;clangd.llvm.org&#x2F;&quot;&gt;write their own compiler&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;And RMS has even had some concessions to these practicalities; Vorbis Beta 4 &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.xiph.org&#x2F;pipermail&#x2F;vorbis&#x2F;2001-February&#x2F;001758.html&quot;&gt;relicensed from LGPL to BSD with RMS&#x27;s approval&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is wise to make some of the Ogg Vorbis code available for use
in proprietary software, so that commercial companies doing proprietary
software will use it, and help Vorbis succeed in competition with other formats
that would be restricted against our use.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;and-yet&quot;&gt;and yet...&lt;&#x2F;h3&gt;
&lt;p&gt;And yet, I still use Emacs. I&#x27;m writing this post in it. I&#x27;m probably going to go through the FSF copyright assignment process at some point (which has been digital worldwide for a few years and digital in the US for a few more) since I may want to contribute to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;joaotavora&#x2F;eglot&quot;&gt;eglot&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;magnars&#x2F;dash.el&quot;&gt;dash.el&lt;&#x2F;a&gt;, both of which require FSF assignment so they can be distributed with Emacs proper. I wasn&#x27;t sure about this for a while, but then I thought about it like this: I&#x27;m fine with using React for my personal projects, and I&#x27;d be fine with contributing to it, despite the fact that I think Facebook (the product, and the company formerly known as) has done orders of magnitude more harm to the world than even the worst possible accounting of the FSF.&lt;&#x2F;p&gt;
&lt;p&gt;And of course, emacs still exists, and is generally usable for modern software development, text authorship, and so on. Emacs is going to have built-in LSP support soon &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.gnu.org&#x2F;archive&#x2F;html&#x2F;emacs-devel&#x2F;2017-04&#x2F;msg00798.html&quot;&gt;&lt;em&gt;with RMS&#x27;s approval&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, and he in fact &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lists.gnu.org&#x2F;archive&#x2F;html&#x2F;emacs-devel&#x2F;2017-04&#x2F;msg00595.html&quot;&gt;stated he tried to convince the GCC maintainers to add LSP support&lt;&#x2F;a&gt;. I said this in the opening, but I don&#x27;t think RMS is a cranky old man that hates progress because he is compelled to do evil, regardless of its utility. But I think his principles are getting in the way of progress, and that&#x27;s true regardless of the cases above where he was willing or even supportive of things that I think are good for the project as a whole.&lt;&#x2F;p&gt;
&lt;p&gt;In my opinion, the best way forward would be for RMS to step down from his leadership roles and hand over those positions to people who are capable of a more modern approach to these projects. I&#x27;m not sure who those would be, but I&#x27;m sure there are good candidates. But I&#x27;m not under any illusion that this post will convince him to do anything; I&#x27;d be surprised if he or any of the people with any influence even read it (edit to add: I&#x27;d be surprised because I&#x27;m just some random person with a blog, not because I think the relevant people wouldn&#x27;t read this post if they&#x27;re made aware of it!)&lt;&#x2F;p&gt;
&lt;p&gt;So why write this? To complain, mostly. This is something that&#x27;s been weighing on me for a while, especially as I think about the future of Emacs (which I think is a separate post on its own; the TL;DR is that I want to be able to continue using Emacs for another decade, and that requires having enough of a userbase that I don&#x27;t have to write all the functionality I want myself).&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;responding-to-hn&quot;&gt;responding to HN&lt;&#x2F;h2&gt;
&lt;p&gt;This section added 2022-10-01 22:25 PDT. I don&#x27;t use Hacker News myself, and rather than effectively leave part of this post in the comments section of an unrelated website, I thought it&#x27;d be good to address points here.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bus-factor&quot;&gt;bus factor&lt;&#x2F;h3&gt;
&lt;p&gt;Volundr says:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If nothing else, there needs to be a clear concession [sic; succession?] plan
ASAP. One refrain I kept reading when he came back to the FSF was that we
needed him back because no one else could lead this movement. Really? No one?
In 36 years since the FSF was founded it hasn&#x27;t managed to attract a single
individual with the sufficient combination of motivation, morals, and skill
to replace Stallman?  If that&#x27;s not the definition of failure I don&#x27;t know
what is.&lt;&#x2F;p&gt;
&lt;p&gt;The man is 70 years old. Decent odds they&#x27;ll be doing without him sooner than later.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is a &lt;em&gt;very&lt;&#x2F;em&gt; good point, and one I wish I&#x27;d thought of. The biological reality of these squishy fleshbags is that sooner rather than later, RMS will be unable to lead. And given everything, I suspect that will happen within a couple decades. I also very much do not think that free software will have won by then, which means that the movement will need someone to continue to do the work after RMS is gone.&lt;&#x2F;p&gt;
&lt;p&gt;As software developers, when we work on projects, we try to maximize the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Bus_factor&quot;&gt;bus factor&lt;&#x2F;a&gt;: how many people would need to get hit by a bus&#x2F;h before the project effectively falls apart. If RMS truly is irreplaceable, then it means the free software movement has a bus factor of 1. This is not a good situation to be in, and insisting that he&#x27;s a vital piece of the movement that can&#x27;t be replaced is a &lt;em&gt;bad&lt;&#x2F;em&gt; thing.&lt;&#x2F;p&gt;
&lt;p&gt;RMS has done a lot of good work in the past. Emacs wouldn&#x27;t exist without him, of course, and a large part of the GNU suite is based on his own handiwork. And I don&#x27;t think that RMS is the only reason the free software movement has failed; there are a &lt;em&gt;lot&lt;&#x2F;em&gt; of issues at play here. But you can&#x27;t be a leader based on past results; you have to continue to deliver.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;compromise&quot;&gt;compromise&lt;&#x2F;h3&gt;
&lt;p&gt;Several people said comments to the effect of &quot;it&#x27;s important that the FSF stay uncompromising since they&#x27;re the only organization that stakes out such an absolute stance&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not asking the FSF to change its mission to include things like racial inequality in tech, or climate change, or social issues in tech, or whatever. I&#x27;m not saying the FSF&#x27;s goals aren&#x27;t worth pursuing; I&#x27;m saying &lt;em&gt;the FSF is ineffective at achieving those goals&lt;&#x2F;em&gt;. You can use only hardware that Respects Your Freedom, and use a browser extension to block nonfree JavaScript, and so on. I&#x27;m sure RMS himself lives a life as close to his ideals as humanly possible. But most people don&#x27;t.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Comfy Software</title>
        <published>2022-07-17T00:00:00+00:00</published>
        <updated>2022-07-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/comfy-software/"/>
        <id>/log/comfy-software/</id>
        
        <content type="html" xml:base="/log/comfy-software/">&lt;p&gt;&lt;strong&gt;NOTE 2022-10-02&lt;&#x2F;strong&gt;: Hello again, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=33053144&quot;&gt;Hacker News&lt;&#x2F;a&gt;. Some people have very strange opinions about the fact that I used the word &#x27;depression&#x27; in the description of this post. &lt;a href=&quot;&#x2F;log&#x2F;comfy-software&#x2F;#depression&quot;&gt;See my response.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Modern software can be... a mess. Electron has made it easier to develop
cross-platform applications, but at the cost of bloating everything by the
footprint of an entire browser, and browsers themselves are getting more and
more complicated. It feels like developers aren&#x27;t really optimizing for
resource usage; for a while, my main laptop was a Thinkpad T450s. In mid-2015,
it was a fairly respectable laptop. In 2021, opening Twitter would make the
fans spin up.&lt;&#x2F;p&gt;
&lt;p&gt;But there&#x27;s still software out here that makes me feel &lt;em&gt;happy&lt;&#x2F;em&gt; to use, software
that embodies an aesthetic that, for lack of a better word, I call &lt;strong&gt;cozy
software&lt;&#x2F;strong&gt;. (Note: I originally called it &#x27;cozy software&#x27;, and I go back and
forth depending on how much I like the &#x2F;mf&#x2F; consonant cluster that day. The two terms are synonymous. My friend &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;l4.pm&#x2F;&quot;&gt;Luna&lt;&#x2F;a&gt; referred to it as &#x27;cuteware&#x27;.)&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not the person to come up with the term; I
picked it up from someone or another on the Fediverse, and they might
well have picked it up from someone else. But I&#x27;ve definitely been
thinking about it a lot: what makes software a joy to use, both
practically and ideologically?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-pillars-of-cozy-software&quot;&gt;The pillars of cozy software&lt;&#x2F;h2&gt;
&lt;p&gt;In my opinion, the pillars of coziness (in no particular order) are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Customizability&lt;&#x2F;li&gt;
&lt;li&gt;Keyboard-focused navigation&lt;&#x2F;li&gt;
&lt;li&gt;Non-corporate governance&lt;&#x2F;li&gt;
&lt;li&gt;Good documentation&lt;&#x2F;li&gt;
&lt;li&gt;Make the common things easy&lt;&#x2F;li&gt;
&lt;li&gt;A cute name&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This isn&#x27;t a list I had in mind when I first thought about the concept; &#x27;cozy software&#x27; is more of a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Family_resemblance&quot;&gt;family resemblance&lt;&#x2F;a&gt; than something with rigorous definitions.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;customizability&quot;&gt;Customizability&lt;&#x2F;h3&gt;
&lt;p&gt;I like making things feel like my own. This is part of why I switched
away from macOS back to Linux a couple years ago: sure, things would
usually &#x27;just work&#x27; on macOS, but I didn&#x27;t have the degree of
&lt;em&gt;control&lt;&#x2F;em&gt; over it that I wanted. With Linux, I can pick my own window
manager, my own desktop environment (or lack thereof), my own package
management system, and so on. This machine feels like it&#x27;s &lt;em&gt;mine&lt;&#x2F;em&gt; in a
way that a Macbook never could.&lt;&#x2F;p&gt;
&lt;p&gt;Some people might say that, well, Ash, the time spent getting your
window manager&#x27;s background blur just right or optimizing your
keybindings is time that you could&#x27;ve spent writing actual code. And
that&#x27;s certainly true. But: I actually &lt;em&gt;like&lt;&#x2F;em&gt; tweaking things like
this. The point of all of this, all of the recreational stuff that I
do, is to have fun. And then at work I can transfer my setup and have
a nice environment that works just the way I like it, but without all
the effort of having to get that stuff re-set-up again.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;extensive-keyboard-focused-controls&quot;&gt;Extensive keyboard-focused controls&lt;&#x2F;h3&gt;
&lt;p&gt;This doesn&#x27;t mean that it has to be keyboard-&lt;em&gt;only&lt;&#x2F;em&gt; navigation or that the
keyboard should be considered the primary way of interacting with the
software. But you should be able to do most things with the keyboard that you
could with the mouse. emacs and vim are the canonical examples of this, of
course, but I&#x27;d say &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.blender.org&#x2F;&quot;&gt;Blender&lt;&#x2F;a&gt; actually falls in this
category as well, from my limited time tooling around with it for a personal
side project. It&#x27;s very much designed to support a workflow where you use the
mouse to pick out points and objects in the scene (which would probably be too
painful to do with a keyboard) and then use the keyboard with your other hand
to act on those objects. I&#x27;m not an artist, but from what I hear a lot of their
tools work similarly; you set up shortcuts and keybinds so that you don&#x27;t have
to take your drawing hand off the mouse or the tablet pen.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-for-profit-governance&quot;&gt;Not-for-profit governance&lt;&#x2F;h3&gt;
&lt;p&gt;In order for software to be comfy, the people who have the final say
over what does and doesn&#x27;t go into it shouldn&#x27;t be beholden to a
for-profit entity. This doesn&#x27;t mean that the entity&#x27;s development
can&#x27;t be led by a corporation; there are plenty of legal reasons to
have your leadership be its own legal entity if you&#x27;re a large
project. But that entity itself should be &lt;em&gt;actually&lt;&#x2F;em&gt; a community focus, and not just a thin facade around one or two megacorporations that just dictate what happens (see: the emoji consortium, everything to do with the web).&lt;&#x2F;p&gt;
&lt;p&gt;I think of this as the &#x27;no VS Code&#x27; clause: no matter how good VS Code is, I
refuse to use it because it&#x27;s fundamentally a Microsoft product and I want my text editor to actually be FOSS, dammit. It doesn&#x27;t help that Pylance, their Python language implementation, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;linux&#x2F;comments&#x2F;k0s8qw&#x2F;vs_code_developers_prevent_running_the_new&#x2F;&quot;&gt;is only licensed to work on official builds&lt;&#x2F;a&gt;. Which ties in with the relationship between open source tooling and corporations... but that&#x27;s a separate blog post.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;good-documentation&quot;&gt;Good documentation&lt;&#x2F;h3&gt;
&lt;p&gt;This is an important part of good software in general, of course. But it&#x27;s especially important for the cozy software aesthetic. Documentation should be available, and if the software is meant to run on a Unix machine, both &lt;code&gt;--help&lt;&#x2F;code&gt; and manpages should show you the help (&lt;a href=&quot;&#x2F;log&#x2F;cut-c-harmful&#x2F;&quot;&gt;no, GNU info doesn&#x27;t count&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;I shouldn&#x27;t have to read the code to find out how something works; I shouldn&#x27;t have to search &quot;how do I X in Y&quot;. The program should be as self-contained as possible.&lt;&#x2F;p&gt;
&lt;p&gt;Funny enough, I think the class of software that succeeds at this the most is video games: they&#x27;ll often have tutorials for things that someone who plays games as a hobby has done a hundred times before, just because every game is someone&#x27;s first. If you have to look up how to do something online, that feels like a failure of the game.&lt;&#x2F;p&gt;
&lt;p&gt;Even in the days where games had actual manuals to go with them, &lt;em&gt;Super Mario 64&lt;&#x2F;em&gt; tells you how to jump, punch, and move around. Of course, that&#x27;s not &lt;em&gt;universal&lt;&#x2F;em&gt;; to pick another genre-defining example, &lt;em&gt;Final Fantasy VI&lt;&#x2F;em&gt; doesn&#x27;t explain in the game itself how to move or what that number next to your character name in the battle screen is. Presumably the manual does, of course. But it &lt;em&gt;does&lt;&#x2F;em&gt; explain systems unique to the game like the Espers or Gau&#x27;s Rage.&lt;&#x2F;p&gt;
&lt;aside&gt;
&lt;p&gt;I&#x27;m so glad we don&#x27;t have to call that &lt;em&gt;Final Fantasy III&lt;&#x2F;em&gt; any more.&lt;&#x2F;p&gt;

&lt;&#x2F;aside&gt;
&lt;p&gt;Of course, there are a lot of differences: tutorial sections are culturally accepted in games, but considered annoying in software. And a lot of software doesn&#x27;t even have an interface at all; imagine trying to run &lt;code&gt;find(1)&lt;&#x2F;code&gt; for the first time only for it to open a curses-based tutorial! But it&#x27;s something to think about.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;make-the-common-things-easy&quot;&gt;Make the common things easy&lt;&#x2F;h3&gt;
&lt;p&gt;The prototypical example here is &lt;code&gt;find&lt;&#x2F;code&gt; vs &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sharkdp&#x2F;fd&quot;&gt;&lt;code&gt;fd&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. Probably the single most common use case for these tools is finding something with a given string somewhere in the name underneath a given directory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;find&lt;&#x2F;code&gt; has a &lt;em&gt;very&lt;&#x2F;em&gt; generic system where you can define a bunch of predicates and combine them arbitrarily, so you can say things like &quot;find me files that were not modified in the past 5 months and are also more than 50 megabytes&quot;. The flip side of that is that it makes the common case annoyingly verbose: to find all files with &lt;code&gt;foo&lt;&#x2F;code&gt; in their names, you have to run &lt;code&gt;find . -iname &quot;*foo*&quot;&lt;&#x2F;code&gt; (the &quot;i&quot; standing for &quot;ignoring case&quot;). With &lt;code&gt;fd&lt;&#x2F;code&gt;, on the other hand, it&#x27;s just &lt;code&gt;fd foo .&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Similarly, it&#x27;s also pretty common that you actually want to execute a command over all these files. Both tools let you do so, but the syntax is very different&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; -iname&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;quot;*.pyc&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; -exec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; rm {}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; \;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;fd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; --extension&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; pyc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; --exec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; rm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;find&lt;&#x2F;code&gt; requires you to explicitly terminate the command and specify where to splice in the argument; &lt;code&gt;fd&lt;&#x2F;code&gt; will infer both of those for you (but lets you specify them if the defaults don&#x27;t work out). Of course, you can also use &lt;code&gt;xargs&lt;&#x2F;code&gt; in both cases, but if you&#x27;re going to supply a way to do something then you should make it nice to use.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, &lt;code&gt;fd&lt;&#x2F;code&gt; has various other niceties like respecting &lt;code&gt;.gitignore&lt;&#x2F;code&gt; and color output.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not using this to pick on &lt;code&gt;find&lt;&#x2F;code&gt; specifically, but it&#x27;s a good illustration of how one of these was written a few years ago and the other was written in 1990.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-cute-name&quot;&gt;A cute name&lt;&#x2F;h3&gt;
&lt;p&gt;This is the most vague and &#x27;vibe-based&#x27; of them all, and is the least important. But I like it when software has a name that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arttuperala&#x2F;kmbmpdc&quot;&gt;has &#x27;kawaii&#x27; in the acronym&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;emersion&#x2F;mako&quot;&gt;is named after a fictional character&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;magic-wormhole&#x2F;magic-wormhole&quot;&gt;has a name that&#x27;s just plain fun&lt;&#x2F;a&gt;. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;obsidian.md&#x2F;&quot;&gt;Obsidian&lt;&#x2F;a&gt; also falls in this category; even though I can&#x27;t think of how the name relates to note-taking, it&#x27;s just a nice name for software.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;towards-a-cozier-future&quot;&gt;Towards a cozier future&lt;&#x2F;h2&gt;
&lt;p&gt;Obviously, not all software needs to fit into this. Most software written professionally will fail the &#x27;non-corporate governance&#x27; test, and it&#x27;s harder to get that &#x27;cute&#x27; feeling going for something that isn&#x27;t something you&#x27;re working on because you&#x27;re getting paid. But I&#x27;ve been giving the internal tools I&#x27;ve been building at work some of this character: naming them after video game characters, writing long &quot;if you got this error, you probably tried to do X. That doesn&#x27;t work because Y. Do Z instead&quot; messages, and so on. And if that means you name your firewall management service &lt;code&gt;yuna&lt;&#x2F;code&gt;, so be it. Having a little fun is important.&lt;&#x2F;p&gt;
&lt;aside&gt;The name &lt;code&gt;yuna&lt;&#x2F;code&gt; is, of course, because, it prevents you from being overwhelmed by &lt;a href=&quot;https:&#x2F;&#x2F;finalfantasy.fandom.com&#x2F;wiki&#x2F;Sin&quot;&gt;SYN&lt;&#x2F;a&gt;.&lt;&#x2F;aside&gt;
&lt;h2 id=&quot;depression&quot;&gt;&quot;Depression&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;ADDED 2022-10-02&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=33053144&quot;&gt;Hacker News comments&lt;&#x2F;a&gt; on this article have been an interesting read. The ones that particularly stood out to me are the ones that imply I&#x27;m using depression as a &quot;marketing&quot; gimmick, or that I&#x27;m just lying about being depressed, or similar things.&lt;&#x2F;p&gt;
&lt;p&gt;First, although I don&#x27;t think that diagnosis is a strictly necessary component to actually &lt;em&gt;have&lt;&#x2F;em&gt; depression, I have in fact been diagnosed by a licensed professional and have been on medication for a while. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=33059048&quot;&gt;gw99&#x27;s own experiences&lt;&#x2F;a&gt; are a good mirror for my own.&lt;&#x2F;p&gt;
&lt;p&gt;In general, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=33054258&quot;&gt;this thread&lt;&#x2F;a&gt; seems to be full of people attempting to figure out what this post Says About Our Society, or something. (I didn&#x27;t see mahathu&#x27;s original comment, and since they realized it was insensitive and edited it, I&#x27;m not going to judge them based on my inferences on what it was about.) It is interesting to me that some people have decided that talking about my mental health in a Hacker News comment thread is productive. I&#x27;m not hurt by it or anything; I don&#x27;t know these people, they don&#x27;t know me, and their opinions don&#x27;t matter. It&#x27;s just strange.&lt;&#x2F;p&gt;
&lt;p&gt;Second, this post is the distillation something a few friends and I had talked about for a while; while I don&#x27;t know how many of them have depression (and I&#x27;m certainly not going to ask for the sake of rebutting Hacker News comments!), this is not a deep meditation on the causes or solutions to depression. This is me saying &quot;doing this makes me feel less existential dread about software&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;This is not me attempting to sell a product, or say &quot;this is The One Right Way To Make Software&quot;. If you view this as &quot;marketing&quot;, I think this says more about you than it says about me or this essay. If you view this as me saying &quot;this is the equivalent of therapy&#x2F;an antidepressant&quot;, I&#x27;d like to ask you where I actually said that. (I was originally intending on having the description be about &quot;trans hackers with depression&quot;, since all the people I hashed this idea out with are trans; now &lt;em&gt;that&lt;&#x2F;em&gt; would have been an &quot;interesting&quot; discussion to read.)&lt;&#x2F;p&gt;
&lt;p&gt;I also noticed some people referring to me as &quot;he&quot;. I&#x27;m not particularly hurt by this (though it&#x27;s incorrect), but I am amused that someone can look at a blog post on a website with &quot;cat&lt;strong&gt;girl&lt;&#x2F;strong&gt;&quot; in the domain and go &quot;hmm, this person is probably a guy&quot;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>System76&#x27;s Lemur Pro 10: A review</title>
        <published>2021-11-16T00:00:00+00:00</published>
        <updated>2021-11-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/lemur-pro/"/>
        <id>/log/lemur-pro/</id>
        
        <content type="html" xml:base="/log/lemur-pro/">&lt;p&gt;I&#x27;m one of those people that actually, genuinely enjoys using Linux. While macOS has its appeals, I like the idea of a computer setup that you can just tinker with as much as you want, and macOS very much isn&#x27;t that. (And the less that&#x27;s said about Windows as a desktop OS, the better.) Unfortunately, Linux hardware compatibility with laptops is always a bit of a game, due to the fact that hardware manufacturers are far more likely to cooperate with Windows; while there are some lines that have good reputations, such as the Lenovo ThinkPad, even they can have some weird issues. My personal favorite is that the T14s second generation has &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.kernel.org&#x2F;show_bug.cgi?id=209167&quot;&gt;a trackpoint with a shitty driver that makes it randomly jump around...&lt;&#x2F;a&gt; but only the version with an AMD processor.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;system76.com&#x2F;&quot;&gt;System76&lt;&#x2F;a&gt; is a laptop manufacturer that targets Linux first and foremost. Their machines come with Pop!_OS, a Linux distro that&#x27;s currently GNOME-based, and their tools for hacking on their firmware are all open source, as is the firmware itself. Since my old T450s was showing its age, I decided to get one of their &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;system76.com&#x2F;laptops&#x2F;lemur&quot;&gt;Lemur Pro models&lt;&#x2F;a&gt;. I&#x27;ve had it fow a few months now, and here are my thoughts.&lt;&#x2F;p&gt;
&lt;p&gt;Note that this is &lt;em&gt;not&lt;&#x2F;em&gt; a review of Pop!_OS; I put NixOS on it immediately, so I never really used it. Similarly, I&#x27;m not going to be benchmarking anything; you can look up the specs yourself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nixos-support&quot;&gt;NixOS support&lt;&#x2F;h2&gt;
&lt;p&gt;I was worried that the firmware flashing tool they use would be annoying to get working, since NixOS is always kind of fiddly with dependencies and something that talks to hardware is even worse. But it turns out NixOS has built-in support! Just do&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;hardware&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;system76&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt;enableAll&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D95757;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D2A6FF;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D95757;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and you&#x27;ll get the firmware updater and everything. Getting a dev environment to &lt;em&gt;compile&lt;&#x2F;em&gt; the firmware myself was a bit more complicated, but that&#x27;s a separate blog post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;build-quality&quot;&gt;Build quality&lt;&#x2F;h2&gt;
&lt;p&gt;The laptop itself is metal, feels pretty nice to the touch. I&#x27;ve heard some reports from Reddit users that it starts to show its age surprisingly quickly, with the keyboard becoming discolored within a year or two of usage and the &#x27;system76&#x27; logo on the lid falling off (since it&#x27;s just vinyl stickers, not engraved). I&#x27;ve noticed a rice-sized bit of the corner has worn its paint off, although that&#x27;s not nearly enough for me to care.&lt;&#x2F;p&gt;
&lt;p&gt;I do know that all of System76&#x27;s laptops are made by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Clevo&quot;&gt;Clevo&lt;&#x2F;a&gt;, who then sells them to S76 for branding and such. And I think Clevo&#x27;s hardware is known to be kind of meh in quality.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;speakers&quot;&gt;Speakers&lt;&#x2F;h2&gt;
&lt;p&gt;The speakers in this thing are, well... really fucking bad. I&#x27;m sorry, but they are. It&#x27;s like listening to my phone on speakerphone. No bass, downward-firing, all that stuff. You can ameliorate it somewhat with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wwmm&#x2F;easyeffects&quot;&gt;EasyEffects&lt;&#x2F;a&gt; to apply EQ to bump up the bass, but there&#x27;s only so much you can do. Even compared to the T450s, which isn&#x27;t anything special, they&#x27;re noticeably worse.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, if you use headphones or plug in speakers or whatever it&#x27;s all perfectly fine. It&#x27;s just the speakers themselves that are low quality.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ports&quot;&gt;Ports&lt;&#x2F;h2&gt;
&lt;p&gt;USB-C charging is nice. But I don&#x27;t understand why they have one USB-C and one barrel instead of one USB-C and one power-only USB-C. It&#x27;s a bit of a pain that I can&#x27;t charge this and use another USB-C device at the same time without using the barrel connector.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;keyboard&quot;&gt;Keyboard&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s alright to type on. Like one of the MacBook &#x27;chiclet&#x27; keyboards. It&#x27;s not as satisfying as a real keyboard, but it gets the job done.&lt;&#x2F;p&gt;
&lt;p&gt;One annoyance is that the function keys send the function keys by default, and there&#x27;s no &#x27;fn lock&#x27; mechanism to toggle this. It also has Ctrl in the bottom-left and not Fn, which is backwards from what I&#x27;m used to. Fortunately, the firmware is open-source, so you can fix that!&lt;&#x2F;p&gt;
&lt;p&gt;A full &#x27;how to flash firmware&#x27; tutorial is beyond the scope of this review, but the repo for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;system76&#x2F;ec&quot;&gt;embedded controller firmware repo&lt;&#x2F;a&gt; has instructions. The keymap I use, which puts the function keys on the normal layer and swaps Fn and Ctrl, is just:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF8F40;&quot;&gt;#include&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; &amp;lt;board&#x2F;keymap.h&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;uint16_t&lt;&#x2F;span&gt;&lt;span&gt; __code KEYMAP[KM_LAY][KM_OUT][KM_IN]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;LAYOUT&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_ESC&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_TOUCHPAD&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_DISPLAY_TOGGLE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_MUTE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_KBD_BKL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_VOLUME_DOWN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_VOLUME_UP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_DISPLAY_MODE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BRIGHTNESS_DOWN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BRIGHTNESS_UP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_CAMERA_TOGGLE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_AIRPLANE_MODE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_SUSPEND&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_HOME&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_END&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_PRINT_SCREEN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_DEL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_TICK&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_MINUS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_EQUALS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BKSP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_TAB&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_Q&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_W&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_E&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_R&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_Y&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_U&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_O&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_P&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BRACE_OPEN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BRACE_CLOSE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BACKSLASH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_CAPS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_A&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_D&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_G&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_K&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_L&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_SEMICOLON&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_QUOTE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_ENTER&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_LEFT_SHIFT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_Z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_C&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_V&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_B&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_M&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_COMMA&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_PERIOD&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_SLASH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_RIGHT_SHIFT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    KT_FN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_LEFT_CTRL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_LEFT_SUPER&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_LEFT_ALT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_SPACE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_RIGHT_ALT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_RIGHT_CTRL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_PGUP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_UP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_PGDN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_LEFT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_DOWN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_RIGHT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFB454;&quot;&gt;LAYOUT&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_ESC&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F11&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_HOME&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_END&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_PRINT_SCREEN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_DEL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_PLAY_PAUSE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_FAN_TOGGLE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_5&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_6&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_7&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_8&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_9&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_MINUS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_EQUALS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BKSP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_TAB&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_Q&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_W&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_E&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_R&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_T&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_Y&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_U&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_I&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_O&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_P&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BRACE_OPEN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BRACE_CLOSE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_BACKSLASH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_CAPS&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_A&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_S&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_D&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_F&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_G&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_J&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_K&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_L&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_SEMICOLON&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_QUOTE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_ENTER&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_LEFT_SHIFT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_Z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_C&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_V&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_B&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_N&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_M&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_COMMA&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_PERIOD&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_SLASH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_RIGHT_SHIFT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    KT_FNL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_LEFT_CTRL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_LEFT_SUPER&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_LEFT_ALT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_KBD_BKL&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_RIGHT_ALT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_APP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_PGUP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_UP&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_PGDN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    K_LEFT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_DOWN&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; K_RIGHT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BFBDB6B3;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Also, it&#x27;s &lt;em&gt;really&lt;&#x2F;em&gt; nice that I can just do that. While there&#x27;s no way you could port &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;qmk.fm&#x2F;&quot;&gt;QMK&lt;&#x2F;a&gt; to it,&lt;&#x2F;p&gt;
&lt;h2 id=&quot;coil-whine&quot;&gt;Coil whine&lt;&#x2F;h2&gt;
&lt;p&gt;This is probably one that&#x27;s very me-specific. I have significantly-better high-frequency perception than most people; I know that my girlfriend&#x27;s induction cooker can drive me &lt;em&gt;nuts&lt;&#x2F;em&gt; with a high-pitched whine that she can&#x27;t hear at all. I&#x27;m also very fiddly with sounds in general; for example, a lot of the time I can&#x27;t work if there&#x27;s music playing that I don&#x27;t control.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, I think all of the motherboards for this model have coil whine. My initial motherboard only had noticeable whine when pushed above 4GHz, but when I got the replacement I started hearing whine even at lower clock speeds. The whine seems to come both from the CPU and from the display connector, and turning the display off (as in disabling it entirely, not just setting the brightness to 0) can trigger it as well.&lt;&#x2F;p&gt;
&lt;p&gt;The flip side is, again, this is something I&#x27;m very sensitive to. I asked my girlfriend to listen while I basically forced the worst case scenario to happen, and she said she could only barely hear it. If I&#x27;m listening to music, typing, or doing anywhere that&#x27;s not a basically silent room, then it&#x27;s not noticeable at all.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;overall&quot;&gt;Overall&lt;&#x2F;h1&gt;
&lt;p&gt;If it wasn&#x27;t for the coil whine and my lingering concerns about the build quality, this would be &lt;em&gt;great&lt;&#x2F;em&gt;. If you&#x27;re not as sensitive to coil whine as me (because you can&#x27;t hear it or you just don&#x27;t care), I&#x27;d totally get one. If you are... maybe not. Hard to say.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Announcing sub-rosa 1.0.0</title>
        <published>2021-10-14T00:00:00+00:00</published>
        <updated>2021-10-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/sub-rosa-announcement/"/>
        <id>/log/sub-rosa-announcement/</id>
        
        <content type="html" xml:base="/log/sub-rosa-announcement/">&lt;p&gt;Patreon&#x27;s core way of letting content creators interact with their patrons is by effectively hosting a blog that&#x27;s ACLed so that only people who pay enough money can read the posts; patrons can also see a stream of all of the new posts across creators. In a better world, they would offer each user an RSS feed, or even a feed per creator. But they don&#x27;t, because it&#x27;s 2021 and nobody likes doing third-party interop anymore.&lt;&#x2F;p&gt;
&lt;p&gt;And while there is an API, it comes with this caveat:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Although we will continue to provide select support and ongoing maintenance of the API, we will have limited capacity in the near term to respond to questions through the developer forum.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;which, from my experience with startups, means &#x27;don&#x27;t use this for anything you care about&#x27;. And it seems far more focused on providing integrations for &lt;em&gt;creators&lt;&#x2F;em&gt; than for patrons. Plus, you have to deal with OAuth, and making an open-source OAuth application is annoying because of secret management.&lt;&#x2F;p&gt;
&lt;p&gt;I could have scraped the actual website, but that&#x27;s an intensely unpleasant idea and it means I&#x27;m forever in a game of cat-and-mouse with trying to fool any kind of anti-scraper logic they add.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, Patreon sends e-mails. They have the full text of the post (and the first image if there are images), they can&#x27;t do any dynamic bullshit because e-mail clients don&#x27;t render JavaScript, and they can&#x27;t use cookies because mail clients won&#x27;t have them. It&#x27;s perfect!&lt;&#x2F;p&gt;
&lt;p&gt;... well, not really. I still have to parse the HTML, and it looks like the image and post links expire after a certain amount of time. But it&#x27;s good enough.&lt;&#x2F;p&gt;
&lt;p&gt;So I wrote &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;ext0l&#x2F;sub-rosa&quot;&gt;sub-rosa&lt;&#x2F;a&gt;, which is a program that will parse your Patreon e-mails, generate RSS feeds (technically Atom feeds, but who cares?), and copy them to whatever hosting you like. If you use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;miniflux.app&#x2F;&quot;&gt;miniflux&lt;&#x2F;a&gt;, it&#x27;ll add them for you. And if you use Nix like me, it even has a flake with a module you can use to set it up as a systemd service with a timer!&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s not a big project, but it&#x27;s the first real project I&#x27;ve released in almost a decade, and I&#x27;m feeling pretty proud of that.&lt;&#x2F;p&gt;
&lt;p&gt;(p.s., if someone from Patreon sees this: please, please just give me a real RSS feed?)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>cut -c considered harmful</title>
        <published>2021-09-27T00:00:00+00:00</published>
        <updated>2021-09-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/cut-c-harmful/"/>
        <id>/log/cut-c-harmful/</id>
        
        <content type="html" xml:base="/log/cut-c-harmful/">&lt;h3 id=&quot;stepping-on-the-rake&quot;&gt;Stepping on the rake&lt;&#x2F;h3&gt;
&lt;p&gt;Like many window manager nerds, I have a status bar (in my case, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Alexays&#x2F;Waybar&quot;&gt;waybar&lt;&#x2F;a&gt;) that shows the artist and title of the song I&#x27;m currently listening to. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cmus.github.io&#x2F;&quot;&gt;cmus&lt;&#x2F;a&gt; has a command-line tool that (among other things) will format information for you, so I thought it was as easy as &lt;code&gt;cmus-remote -C &quot;format_print &#x27;%a - %t&#x27;&quot;&lt;&#x2F;code&gt;. And that does, in fact work... as long as I don&#x27;t listen to any Godspeed You! Black Emperor, because&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Godspeed You! Black Emperor - A Military Alphabet (five eyes all blind) (4521.0kHz 6730.0kHz 4109.09kHz) &#x2F; Job’s Lament &#x2F; First of the Last Glaciers &#x2F; where we break how we shine (ROCKETS FOR MARY)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;is a bit long to put on my status bar; it&#x27;d take up almost all the available space by itself. And unfortunately, even though &lt;code&gt;format-print&lt;&#x2F;code&gt; takes an argument that looks like a standard &lt;code&gt;printf&lt;&#x2F;code&gt;-style format string, there&#x27;s no &#x27;truncate to this length&#x27; modifier for strings like there is in &lt;code&gt;printf&lt;&#x2F;code&gt; proper.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, Unix tools to the rescue! &lt;code&gt;cut&lt;&#x2F;code&gt; is generally used for splitting lines on delimiters, but the &lt;code&gt;-c&lt;&#x2F;code&gt; argument will select a range of characters. So you can just do&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; cmus-remote -C &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;quot;format_print &amp;#39;%a - %t&amp;#39;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F29668;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt; cut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #95E6CB;&quot;&gt; -c-60&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;Godspeed&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; You! Black Emperor - A Military Alphabet&lt;&#x2F;span&gt;&lt;span&gt; (five&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt; eyes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and while the unbalanced parenthesis is kind of visually offensive, and an ellipsis might be nice, it&#x27;s good enough to use.&lt;&#x2F;p&gt;
&lt;p&gt;Until I noticed that while I was listening to the FFXIV soundtrack, sometimes the status bar wouldn&#x27;t update when I changed songs. Looking at the waybar output showed me this error:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;(waybar:2854654): Gtk-WARNING **: 11:04:07.422: Failed to set text &#x27;Masayoshi Soken - Voidal Manifest &#x2F; ヴォイドの棺 ～\xe9\xad&#x27; from markup due to error parsing markup: Error on line 1 char 69: Invalid UTF-8 encoded text in name — not valid “Masayoshi Soken - Voidal Manifest &#x2F; ヴォイドの棺 ～\xe9\xad”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;At first I thought this was a weird encoding error, since if I remember correctly the ID3 information was originally in Shift-JIS and I had to do some hackery to get them into UTF-8. But it wouldn&#x27;t make sense for only &lt;em&gt;some&lt;&#x2F;em&gt; songs to have this problen. And then I realized &lt;em&gt;that&#x27;s not actually 60 characters&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F29668;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; python3 -c &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AAD94C;&quot;&gt;&amp;#39;print(len(&amp;quot;Masayoshi Soken - Voidal Manifest &#x2F; ヴォイド の棺 ～&amp;quot;))&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #59C2FF;&quot;&gt;44&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The conclusion is that &lt;code&gt;-c&#x2F;--characters&lt;&#x2F;code&gt;, despite saying it operates on characters, and despite the presence of a &lt;code&gt;-b&#x2F;--bytes&lt;&#x2F;code&gt; option, actually operates on bytes. And sure enough, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;coreutils&#x2F;manual&#x2F;html_node&#x2F;cut-invocation.html&quot;&gt;the documentation on the GNU website&lt;&#x2F;a&gt; says:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Select for printing only the characters in positions listed in character-list. The same as -b for now, but internationalization will change that.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This isn&#x27;t present in the manpage (at least, not on my install), but it &lt;em&gt;is&lt;&#x2F;em&gt; in the &lt;code&gt;info&lt;&#x2F;code&gt; page. And since I never check the &lt;code&gt;info&lt;&#x2F;code&gt; page (does anyone?), I had no clue.&lt;&#x2F;p&gt;
&lt;p&gt;Hilariously, there&#x27;s a &lt;code&gt;-n&lt;&#x2F;code&gt; option to not split multi-byte characters... but it&#x27;s a no-op. As far as I can tell it&#x27;s for compatibility with BSD&#x27;s &lt;code&gt;cut&lt;&#x2F;code&gt;, which does support this, and it looks like it even actually implements &lt;code&gt;-c&lt;&#x2F;code&gt; correctly (since it has no mention of internationalization bugs, and explicitly says the behavior depends on locale variables).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;disappointment&quot;&gt;Disappointment&lt;&#x2F;h3&gt;
&lt;p&gt;I figured someone had already reported this to GNU, so I looked. I found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debbugs.gnu.org&#x2F;cgi&#x2F;bugreport.cgi?bug=6707&quot;&gt;this bug report from 2010&lt;&#x2F;a&gt;. In it, someone mentions that several distros have patches to fix this, but they duplicate code and&#x2F;or slow it down on the single-byte locale case. Someone else adds that&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;We&#x27;re working on it. Something is coming soon.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;There hasn&#x27;t been any activity since then aside from changing the severity and title. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crashcourse.housegordon.org&#x2F;coreutils-multibyte-support.html&quot;&gt;This collection of bugs across GNU coreutils&lt;&#x2F;a&gt; shows this has been going on for a while. I&#x27;m not sure if any of those bugs have been fixed, or if anybody&#x27;s written partial support coreutils. But, honestly, it&#x27;s 2021. GNU coreutils is supposed to be the solid foundation of Linux; I see people talking all the time about how powerful these tools are. And they break as soon as you give them anything that isn&#x27;t a single-byte encoding... which, in the era where UTF-8 has become the dominant encoding and emojis are common, is going to be a &lt;em&gt;lot&lt;&#x2F;em&gt; of text.&lt;&#x2F;p&gt;
&lt;p&gt;You can hack around this by using &lt;code&gt;iconv&lt;&#x2F;code&gt; to convert your input to UTF-32, which will have four bytes for every character, and then back afterwards. And GNU &lt;code&gt;awk&lt;&#x2F;code&gt; will just do the right thing automatically if your locale is set up right:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #BFBDB6; background-color: #0D1017;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; echo &amp;quot;ヴォイド の棺 ～&amp;quot; | awk &amp;#39;{ print substr($0, 0, 4) }&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ヴォイド&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But it&#x27;s still extremely silly that this is a thing you have to do. It&#x27;s not 2000 any more; the world is Unicode and UTF-8 now, and you can&#x27;t get away with assuming single-byte encodings in general-purpose tools.&lt;&#x2F;p&gt;
&lt;p&gt;So if you&#x27;re doing text processing, unless you&#x27;re &lt;em&gt;sure&lt;&#x2F;em&gt; you&#x27;ll never run into multibyte characters, don&#x27;t use GNU &lt;code&gt;cut&lt;&#x2F;code&gt;. Use &lt;code&gt;awk&lt;&#x2F;code&gt; or something else instead.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Eight Months of NixOS</title>
        <published>2020-11-02T00:00:00+00:00</published>
        <updated>2020-11-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/nixos-experience/"/>
        <id>/log/nixos-experience/</id>
        
        <content type="html" xml:base="/log/nixos-experience/">&lt;p&gt;About 8 months ago, I decided that I&#x27;d gotten tired of macOS. It&#x27;s a
Unix, which is nice, and I like that I can set up things with
relatively minimal effort through Homebrew, but I wanted something
that actually gave me more control over my system. So I decided that
2020 was going to be the Year Of Linux On Ash&#x27;s Laptop.&lt;&#x2F;p&gt;
&lt;p&gt;In addition, I decided to try out &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;&quot;&gt;NixOS&lt;&#x2F;a&gt;, which
is an operating system oriented around the Nix package manager. The
big thing that distinguishes Nix from other operating systems is that
it lets you manage your packages &lt;em&gt;declaratively&lt;&#x2F;em&gt;: you specify what
you want the state of your system to be, then run &lt;code&gt;sudo nixos-rebuild switch&lt;&#x2F;code&gt;, and it reconfigures your system to be in that state. I also
decided I would use
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nix-community&#x2F;home-manager&quot;&gt;home-manager&lt;&#x2F;a&gt;, which
lets you do the same thing for your dotfiles. As is tradition, I also
decided to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.catgirl.ai&#x2F;ext0l&#x2F;nixos-config&quot;&gt;version my
dotfiles&lt;&#x2F;a&gt;, though they&#x27;re
still kind of a mess.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-good&quot;&gt;the good&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;hardware-support&quot;&gt;hardware support&lt;&#x2F;h3&gt;
&lt;p&gt;I bought a used Thinkpad T450s off of eBay, because I wanted to make
sure I had good hardware support, and the Thinkpad line has
historically had a reputation for things working out of the box. And
everything has worked fine: Bluetooth, wireless, even the touch screen
(not that I use it). I didn&#x27;t have to hunt down any weird driver
repositories or modprobe anything into my kernel.&lt;&#x2F;p&gt;
&lt;p&gt;The one thing I haven&#x27;t tested is the fingerprint reader, because I
don&#x27;t trust it to be secure, so I never set it up in the first place.&lt;&#x2F;p&gt;
&lt;p&gt;I did run into one issue: when I bought a replacement battery off
eBay, Linux was having trouble recognizing it and reading its
status. I had to do some kind of magic dance to reset something or
another: I think it was &#x27;pull the battery and AC adapter, hold the
power button for 5 seconds, then plug it back in&#x27;. But once I did
that, everything was fine.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;declarative-package-management&quot;&gt;declarative package management&lt;&#x2F;h3&gt;
&lt;p&gt;This was the reason I specifically wanted to use NixOS in the first
place. I liked the idea of having my system&#x27;s configuration explicitly
written out, and not just determined by whatever series of packages I
happen to have apt-get installed. And NixOS delivers; my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.catgirl.ai&#x2F;ext0l&#x2F;nixos-config&quot;&gt;nixos-config
repo&lt;&#x2F;a&gt; contains not only my
dotfiles, but a list of all the programs I have installed, my fonts,
and so on. I also don&#x27;t have to worry about having accidentally
installed something weird and then forgotten about it; whatever&#x27;s not
in my &#x2F;etc&#x2F;nixos might as well not exist (though it can still leave
stuff in ~&#x2F;.config and similar places).&lt;&#x2F;p&gt;
&lt;p&gt;I intend to eventually switch the server this blog is hosted on over
to NixOS for similar reasons, and I&#x27;m &lt;em&gt;definitely&lt;&#x2F;em&gt; looking forward to
being able to declaratively manage things like nginx configuration and
systemd scripts as opposed to scattering them all over my filesystem.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;customizability&quot;&gt;customizability&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ve got everything set up the way I like it. I decided to finally
give tiling window managers a shot, and after using
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;awesomewm.org&#x2F;&quot;&gt;awesomewm&lt;&#x2F;a&gt;, I don&#x27;t think I could go back to
having to arrange all of my windows by hand. Even on a laptop screen,
I like putting my messaging applications (Discord, Telegram, IRC)
side-by-side. And of course, having purpose-driven workspaces is
nice. Instead of hunting down my music player in a window switcher, I
just hit &lt;code&gt;Win-6&lt;&#x2F;code&gt;, since I always keep &lt;code&gt;cmus&lt;&#x2F;code&gt; on the 6th workspace.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve also got bars set up to display things like my upcoming todo
count, my CPU usage, battery life, currently-playing music, and so
on. I could certainly have done this on MacOS using
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tracesof.net&#x2F;uebersicht&#x2F;&quot;&gt;Übersicht&lt;&#x2F;a&gt; or something similar,
but it feels intuitively &#x27;cleaner&#x27; in some way to be doing this in an
operating system set up to have this kind of flexibility in the first
place.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-not-so-good&quot;&gt;the not-so-good&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;the-nix-language&quot;&gt;the nix language&lt;&#x2F;h3&gt;
&lt;p&gt;The Nix package manager uses its own language, which is also called
Nix, to define how packages work. The language is functional in the
style of something like Haskell or the ML family, but lacks a lot of
the niceties those languages have like a strong static type system or
a means for doing imperative stateful programming. I ran into this
when I was trying to generate a configuration file by iterating
through a list and having the callback update some amount of state;
Haskell has the state monad, but Nix has no such thing.&lt;&#x2F;p&gt;
&lt;p&gt;This is especially salient because Nix does &lt;em&gt;not&lt;&#x2F;em&gt; follow the standard
Linux filesystem hierarchy; it stores almost all of its libraries and
binaries under &lt;code&gt;&#x2F;nix&#x2F;store&lt;&#x2F;code&gt;. This means that you can have multiple
copies of something installed without them interfering with each
other, but it also means that you &lt;em&gt;need&lt;&#x2F;em&gt; to know how Nix packaging
works to compile anything. Even libc isn&#x27;t in its usual place, so
downloading prebuilt binaries usually won&#x27;t work.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, packaging simple software generally just involves telling
it how to fetch it and what its build dependencies are, but figuring
out how exactly to do that and how to integrate it into the rest of
your configuration requires reading documentation. Which brings me to
the next point:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;extremely-sparse-documentation&quot;&gt;extremely sparse documentation&lt;&#x2F;h3&gt;
&lt;p&gt;Even though the Nix language itself is fairly simple to understand,
the way in which Nix puts everything together can be... obtuse. For
example, each package in the nixpkgs monorepo is implemented as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;blob&#x2F;master&#x2F;pkgs&#x2F;applications&#x2F;misc&#x2F;hello&#x2F;default.nix&quot;&gt;a
function&lt;&#x2F;a&gt;
(here, the &lt;code&gt;{stdenv, fetchurl}:&lt;&#x2F;code&gt; syntax at the top means that the file
declares a function that takes a record with two fields named &lt;code&gt;stdenv&lt;&#x2F;code&gt;
and &lt;code&gt;fetchurl&lt;&#x2F;code&gt;. However, when defining my own derivations to include
in my personal configuration for programs not packaged by Nix, instead
of defining a function, I just wrote out the derivation by hand. The
difference to me wasn&#x27;t obvious, until I did some deep digging and
learned that behind the scenes, the nixpkgs repo invokes
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;guides&#x2F;nix-pills&#x2F;callpackage-design-pattern.html&quot;&gt;&lt;code&gt;callPackage&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;,
which invokes the function with the relevant arguments.&lt;&#x2F;p&gt;
&lt;p&gt;Even just writing those packages involved searching the nixpkgs repo
for similar programs and then copy-pasting what they did. The
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Packaging&#x2F;Tutorial&quot;&gt;official NixOS wiki&lt;&#x2F;a&gt; has
basically similar recommendations, and I found the NixOS manual to not
be super helpful here. I wound up making heavy use of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;guides&#x2F;nix-pills&#x2F;&quot;&gt;Nix
Pills&lt;&#x2F;a&gt;, which are a series of
posts that show you not only how Nix package management is
implemented, but &lt;em&gt;why&lt;&#x2F;em&gt; it works the way it does.&lt;&#x2F;p&gt;
&lt;p&gt;Similarly: some commands are invoked like &lt;code&gt;nix run&lt;&#x2F;code&gt;, some are
&lt;code&gt;nix-shell&lt;&#x2F;code&gt;. I don&#x27;t know why.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;would-i-recommend-nix&quot;&gt;would i recommend nix?&lt;&#x2F;h2&gt;
&lt;p&gt;Only to power users. If you&#x27;re going to use it, you&#x27;re going to have
to deal with the packaging stuff at some point, either to debug a
package or to use software that doesn&#x27;t have one. And this means
you&#x27;ll need to learn the Nix language, and wade through the nixpkgs
monorepo and some fairly iffy documentation. But if you can put up
with that, the &#x27;everything is declarative&#x27; setup is &lt;em&gt;exactly&lt;&#x2F;em&gt; as nice
as I hoped it&#x27;d be, and I&#x27;ve had multiple instances where an update
broke something so I was able to just boot to an older
generation. Similarly, when the BleedingTooth vulnerability came out,
I was able to disable my Bluetooth stack with a one-liner in
&lt;code&gt;&#x2F;etc&#x2F;nixos&#x2F;configuration.nix&lt;&#x2F;code&gt; to just blacklist the entire hardware
support and then when the kernel updates came out I was able to just
remove that single line as opposed to trying to remember where the
fuck I&#x27;d stored that definition.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;action-items-for-the-nixos-community&quot;&gt;action items for the nixos community&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Better documentation.&lt;&#x2F;strong&gt; Stuff that covers &quot;I have a piece of
software that&#x27;s not packaged, how do I package it locally&quot;. Or &quot;the
software in nixpkgs is broken in some way, I&#x27;d like to test out some
changes to the package definition, how do I set up the overrides for
that&quot;. That sort of thing. A lot of this is covered on the wiki, so
the usual
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Wikipedia:Be_bold&quot;&gt;WP:SOFIXIT&lt;&#x2F;a&gt;
applies, but this is the sort of thing that I think should have
already been written.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Faster switches for trivial changes.&lt;&#x2F;strong&gt; The big reason I don&#x27;t
manage my .emacs.d via nix is that it means that even a trivial
change requires me to do the whole &lt;code&gt;sudo nixos-rebuild switch&lt;&#x2F;code&gt;
dance, which can take 20-30 seconds just to make a one-line
change. This kills my iteration velocity. There are workarounds like
having a &#x27;staging&#x27; file that gets sourced and then every so often
moving code from the staging file into the main file, but that seems
like a workaround for a problem that shouldn&#x27;t exist.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Some kind of lockfile to store package versions.&lt;&#x2F;strong&gt; One of the bits
of state that &lt;em&gt;isn&#x27;t&lt;&#x2F;em&gt; stored in &lt;code&gt;&#x2F;etc&#x2F;nixos&lt;&#x2F;code&gt; is what version of the
nixpkgs and home-manager repos I&#x27;m currently on. This means that if
I run a &lt;code&gt;switch --upgrade&lt;&#x2F;code&gt;, there&#x27;s no way to go back without
rolling back those channels via &lt;code&gt;nix-env&lt;&#x2F;code&gt;. On the other hand, if
those versions were stored alongside my nix files, then I could just
revert them in case of a bad upgrade. My understanding is that
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.wiki&#x2F;wiki&#x2F;Flakes&quot;&gt;flakes&lt;&#x2F;a&gt; are an upcoming feature
that solves this problem, among others.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;final thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;Overall, I&#x27;ve definitely enjoyed the switch to Linux, and I can&#x27;t see
myself going back to macOS any time soon. I may wind up buying a more
powerful laptop at some point, since this one is having problems with
driving a 2560x1440 monitor due to a low fill rate on the graphics
card, but I want to reduce my electronics waste footprint (hence why I
bought this one used), so I&#x27;m going to stick with it for at least a
few more years.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m also &lt;em&gt;probably&lt;&#x2F;em&gt; going to stick with NixOS, but I hope it becomes
better documented and more newbie-friendly. Right now, I&#x27;d hesitate to
recommend it to anyone who wants something that just works and isn&#x27;t
interested in diving into the guts of their system or learning a new
language just to install software.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>This Is How You Overcome Internalized Shame And Judgment By Reading &#x27;This Is How You Lose The Time War&#x27;</title>
        <published>2020-03-31T00:00:00+00:00</published>
        <updated>2020-03-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="/log/time-war/"/>
        <id>/log/time-war/</id>
        
        <content type="html" xml:base="/log/time-war/">&lt;p&gt;(This post is going to have some amount of spoilers for &lt;em&gt;This Is How You Lose The Time War&lt;&#x2F;em&gt;, so if you don&#x27;t wan&#x27;t to be spoiled on that, stop reading here. It&#x27;s a fantastic book, promise.)&lt;&#x2F;p&gt;
&lt;p&gt;I grew up, in a sense, on the Something Awful Dot Com forums. It&#x27;s inarguable that if I hadn&#x27;t been there, I wouldn&#x27;t have met some of the people I did, I wouldn&#x27;t have developed the political opinions I do, I might&#x27;ve not ever realized that I&#x27;m a girl. I might&#x27;ve fallen in with 4chan or some other god-awful site. But I definitely wouldn&#x27;t recommend it to anyone, because it left me with a vicious streak a mile wide when it came to making fun of other people&#x27;s creative work. I used to &lt;em&gt;love&lt;&#x2F;em&gt; tearing down the output of amateur authors or artists, even the ones that were just writing or drawing for themselves and hoped the rest of the world might like it. I&#x27;d never even intended to publish it, but when I do something, I want to do it &lt;em&gt;well&lt;&#x2F;em&gt;, and the self-criticism feeds on that.&lt;&#x2F;p&gt;
&lt;p&gt;So, naturally, when I decided that &lt;em&gt;I&lt;&#x2F;em&gt; wanted to start writing, I ran into a problem. Even though I&#x27;d realized SA&#x27;s utter toxicity several years back and had stopped paying any attention, I still had that voice in the back of my head, telling me that everything I wrote wasn&#x27;t Good Enough. That I should be ashamed to even &lt;em&gt;think&lt;&#x2F;em&gt; of publishing it someplace where someone else could see it.&lt;&#x2F;p&gt;
&lt;p&gt;And &#x27;worst&#x27; of all, that it was &#x27;too self-indulgent&#x27;. Because I like writing stories with happy endings, stories with &lt;a href=&quot;&#x2F;pages&#x2F;robot&#x2F;&quot;&gt;robot protagonists&lt;&#x2F;a&gt;, stories with trans female characters that get what they want. On some level, of course, I knew there wasn&#x27;t anything wrong with that: when I read something from an author I liked, I didn&#x27;t feel that same sort of shame at &lt;em&gt;their&lt;&#x2F;em&gt; work. But the published authors I read that had fiction that took place in that same high-tech utopia tended to write heterosexual romance, if any. I love Iain M. Banks and his Culture works, but I can&#x27;t think of a single hint of gay attraction in that entire series.&lt;&#x2F;p&gt;
&lt;p&gt;And that inner critic just wouldn&#x27;t shut up. My writing was bad, it was escapist bullshit, infantile, people would make fun of me and they&#x27;d be right to do so. It sucked. I wrote anyway, somewhat, but it definitely put a huge dent in my creative impact. I was constantly editing myself as I went, because nothing ever felt like it was good enough. And even worse, I was editing &lt;em&gt;other people&#x27;s&lt;&#x2F;em&gt; work as I was reading it, thinking about how bad some word choice was, how I&#x27;d do things different, how I&#x27;d do things better. (Yes, I&#x27;d both think that I&#x27;m an awful writer and that I&#x27;m way better at writing than other people. Depression and impostor syndrome don&#x27;t have to be &lt;em&gt;rational&lt;&#x2F;em&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;Then, one day, my girlfriend gave me a copy of &lt;em&gt;This Is How You Lose The Time War&lt;&#x2F;em&gt; as we were walking through a bookstore. It&#x27;s a book about two time-traveling agents from alternate versions of the future: Red is from the Agency, all machine perfection. Blue is from the Garden, a holistic bioengineered paradise. (You can imagine which one I prefer, of course). They start tracking each other through their time-traveling war, and leaving each other letters in ways they hope their handlers won&#x27;t find: the bubbles of water in an MRI machine, the guts of a slain bird.&lt;&#x2F;p&gt;
&lt;p&gt;My girlfriend had read it before me, and from her reactions I could tell she loved it. So I started reading it, and holy &lt;em&gt;shit&lt;&#x2F;em&gt;. Purple prose love letters, luxurious descriptions of powerful technology we can only begin to imagine. Either one of the protagonists could easily smash a tank bare-handed. For someone like me that has daydreams about being an invulnerable combat robot, it was like catnip.&lt;&#x2F;p&gt;
&lt;p&gt;Not only that, but Red and Blue completely fall for each other over the course of the story. And unlike so much of the WLW stories I&#x27;m used to in media where I have to settle for a smirk and a half-glance and a &#x27;what-if&#x27; in my head and in fanfiction, they actually confess their love to each other. They talk about how much they want to be with each other, how much they love each each other, and so on. It&#x27;s an actual explicit love story!&lt;&#x2F;p&gt;
&lt;p&gt;And as I was reading it, I realized: this is &lt;em&gt;exactly&lt;&#x2F;em&gt; the sort of self-indulgent stuff my inner critic would chastise myself for writing it if I wrote it. And yet here it is, an actual published book, (a Nebula finalist even!). Sure, my prose isn&#x27;t as good as &lt;em&gt;Time War&lt;&#x2F;em&gt;&#x27;s, but the concept is something I&#x27;d absolutely write, something I&#x27;d love. And of course I&#x27;m not as good of an author as actual, professional, published authors; it&#x27;d be an insult to them to think that I would be! And so my inner critic actually shuts the hell up for a moment and lets me work.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t want to make it seem like this has magically fixed everything, of course. My creative output&#x27;s still slow. This shame wasn&#x27;t the only thing holding me back; I still have motivation issues, ADHD, and a host of other bullshit. Writing is still a hard thing to get done; even writing this blogpost has taken me over a week, when it &quot;should have&quot; only taken me a couple days. But I&#x27;ve been shutting down that inner critic every chance I get, and I&#x27;ve been helping my other girlfriend write &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;princess.team&#x2F;stories&#x2F;search-and-rescue&#x2F;&quot;&gt;a story about a combat robot rescuing her amnesiac human-disguised girlfriend off ancient Earth&lt;&#x2F;a&gt; over the past few months. She&#x27;s been doing all the pen-to-paper work, but I&#x27;ve spent hours and hours plotting it with her. And if I hadn&#x27;t read &lt;em&gt;Time War&lt;&#x2F;em&gt;, if I hadn&#x27;t dropped off so much of my shame, that work would be a lot harder.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
