rumpl https://rumpl.dev/ Recent content on rumpl Hugo -- gohugo.io en-us Mon, 29 Dec 2025 23:40:41 +0100 Vibe Year https://rumpl.dev/posts/vibe-year/ Mon, 29 Dec 2025 23:40:41 +0100 https://rumpl.dev/posts/vibe-year/ <p>What a year eh?</p> <p>For research purposes, over the last couple of months I&rsquo;ve started using LLMs heavily for throwaway code. My main goal was to train myself to write good prompts and find what works (and what doesn&rsquo;t) with different agents—since that&rsquo;s going to be my job from now on, after all.</p> <p>My setup was very similar for all of these projects: all were made using <a href="https://github.com/docker/cagent"><code>cagent</code></a>, either with a hand-crafted agent or by simply running <code>cagent run --model ...</code>.</p> <p>Most of the agents only had access to the filesystem and the shell. I&rsquo;ve found that newer LLMs don&rsquo;t really need much else. In the GitHub clone, once I had the issues feature and the CLI working, I would ask the agent to plan its steps, create issues for everything using the CLI, and then implement the feature I asked for. There were zero MCPs involved. MCP as a spec is great, but the implementations out there are almost all pretty bad.</p> <p>I&rsquo;ve mostly used 3 models:</p> <ul> <li>Opus for pretty much everything. This one is a BEAST and can stay focused for a long, long time.</li> <li>GPT 5.1 and 5.2. Sometimes Opus will get confused about a fix that is needed and struggle to find the issue. In these cases I would spin up GPT, and most of the time it would take a <em>looong</em> time to analyze but was pretty much always able to find the issue and fix it with a minimal change.</li> <li>Gemini 3 for frontend things. It&rsquo;s very good at frontend work.</li> </ul> <p>These models are great for everyday work and can do a lot of things very well. However, they all have issues. Opus, for example, would sometimes end up very confused about the codebase, miss things, and end up in a &ldquo;But wait!&rdquo; loop when the feature is big or the bug is nasty. GPT is just very slow and almost always ends up in a strange state where it tells me to <code>Write &quot;do it now&quot; if you want me to do this</code>—very strange. Gemini can be dumb easily and struggles to follow instructions.</p> <p>When I started doing this for fun I did not expect much, but I was proven wrong quickly: the newest models are genuinely useful tools for today&rsquo;s developers.</p> <p>Below I&rsquo;m showing some of the projects I did lately. These include a Linux-like kernel, a GitHub clone, a markdown editor, a GameBoy Color emulator, a container runtime, etc. I got way better results if I knew what I was talking about; I was able to give the agent keywords and provide semi-detailed (I&rsquo;m lazy) instructions on how I wanted it implemented. In cases where I had no idea what I was talking about (GameBoy emulator), we would struggle for a bit because all I could say was pretty much &ldquo;It doesn&rsquo;t work&rdquo;, but we did manage to make something that works in the end, it was a struggle though.</p> <p>Without further ado and in no specific order, here is the list of some of the vibe-coded projects.</p> <h2 id="arm64-kernel">ARM64 kernel</h2> <p>This was a wild and fun one. With the agent, I managed to create a working kernel that was able to boot and run a busybox shell.</p> <p>Features include:</p> <ul> <li>IRQs</li> <li>VMM</li> <li>VFS, with working tarfs and FAT32 filesystems</li> <li>An initrd</li> <li>Character devices</li> <li>Block devices</li> <li>ELF loader</li> <li>A <em>very simple</em> (round-robin) scheduler</li> </ul> <p>Once all of that was implemented, I managed to cross-compile a small subset of busybox and run it as the init process of the system.</p> <p>I plan to slow down LLM usage on this one and continue playing with it manually; I&rsquo;ve already started to implement a very simple <code>proc</code> filesystem. Switching from LLM-based to finger-based coding is quite interesting. Things that I <em>know</em> the LLM could make in a matter of minutes can take me hours, but it&rsquo;s all cool to have a working starting playground.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="go">; make run-disk </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="err"></span><span class="go">Starting QEMU with VirtIO disks... (Press Ctrl+A then X to exit) </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="go">vda = tarfs disk (2MB) </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="go">vdb = FAT32 disk (4MB) </span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="go">================================================================= </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="err"></span><span class="go">--- </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="err"></span><span class="go">| |/ / _ \| \ | | | </span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="go">| &#39; /| |_) | \| | | </span></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="go">| . \| \_ &lt;| |\ | |**_ </span></span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="go">|_|\_\_| \_\_| \_|\_\_\_**| </span></span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="err"></span><span class="gp">#</span> Minimal ARM64 Kernel v0.8 </span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="err"> </span></span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="err"></span><span class="go">[BOOT] Kernel loaded successfully </span></span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="go">[BOOT] DTB not passed in x0, scanning memory... </span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="go">[FDT] Scanning for DTB in memory... </span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="go">[FDT] No DTB found in memory scan </span></span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="go">[BOOT] DTB pointer: (nil) </span></span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="go">[IRQ] GIC initialized </span></span></span><span class="line"><span class="ln"> 23</span><span class="cl"><span class="go">[IRQ] Exception vectors installed at 0xffff000040081000 </span></span></span><span class="line"><span class="ln"> 24</span><span class="cl"><span class="go">[TIMER] Counter frequency: 62500000 Hz </span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="go">[TIMER] Tick interval: 625000 counts (100 Hz) </span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="go">[IRQ] Enabled IRQ 30 with priority 128 </span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="go">[TIMER] Timer started, 100 ticks/second </span></span></span><span class="line"><span class="ln"> 28</span><span class="cl"><span class="go">[PMM] Initializing physical memory manager </span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="go">[PMM] Stack top at: VA 0xffff000040133000 (PA 0x40133000) </span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="go">[PMM] Managed region: PA 0x40133000 - 0x48080000 (127 MB) </span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="go">[PMM] Total pages: 32768 (128 MB) </span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="go">[PMM] Bitmap size: 4096 bytes </span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="go">[PMM] Bitmap at: VA 0xffff000040133000 (PA 0x40133000 - 0x40134000) </span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="go">[PMM] First free page: PA 0x40134000 (index 180) </span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="go">[PMM] Free pages: 32588 (127 MB) </span></span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="go">[PMM] Used pages: 180 (kernel + bitmap) </span></span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="go">[PMM] Physical memory manager initialized! </span></span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="go">[KMALLOC] Initializing kernel heap </span></span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="go">[KMALLOC] Heap at 0xffff000040134000 - 0xffff000040174000 (256 KB) </span></span></span><span class="line"><span class="ln"> 40</span><span class="cl"><span class="go">[KMALLOC] Kernel heap initialized! </span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="go">[MMU] Running at virtual address 0xffff0000400873f0 </span></span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="go">[MMU] Kernel mapped: VA 0xffff000040080000 -&gt; PA 0x40080000 </span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="go">[MMU] Device memory: VA 0xffff000009000000 -&gt; PA 0x9000000 </span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="go">[MMU] Higher-half kernel initialized! </span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="go">[SYSCALL] System call interface initialized </span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"><span class="go">[SYSCALL] Supported: exit, read, write, brk, mmap, munmap, etc. </span></span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="go">[TTY] Console initialized (80x24, 115200 baud) </span></span></span><span class="line"><span class="ln"> 48</span><span class="cl"><span class="go">[VFS] Initializing virtual filesystem... </span></span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="go">[CHARDEV] Initializing character device drivers... </span></span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="go">[VFS] Registered chrdev major 1: mem </span></span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="go">[VFS] Registered chrdev major 5: tty </span></span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="go">[CHARDEV] Character device drivers initialized </span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="go">[RAMFS] RAM filesystem initialized </span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="go">[RAMFS] Mounted ramfs filesystem </span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="go">[VFS] Mounted ramfs as root filesystem </span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="go">[DEVFS] Device filesystem initialized </span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="go">[DEVFS] Created /dev/console (major 5, minor 1) </span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="go">[DEVFS] Created /dev/tty (major 5, minor 0) </span></span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="go">[DEVFS] Created /dev/null (major 1, minor 3) </span></span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="go">[DEVFS] Created /dev/zero (major 1, minor 5) </span></span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="go">[DEVFS] Created /dev/full (major 1, minor 7) </span></span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="go">[VFS] Created /dev with device nodes </span></span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="go">[VFS] Created /tmp directory </span></span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="go">[PROCFS] procfs initialized </span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="go">[VFS] Created /proc directory </span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="go">[VFS] Virtual filesystem initialized </span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="go">[BLKDEV] Block device subsystem initialized </span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"><span class="go">[VIRTIO-BLK] Scanning for VirtIO block devices... </span></span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="go">[VIRTIO-BLK] Found block device at 0xffff00000a003c00 (version 2) </span></span></span><span class="line"><span class="ln"> 70</span><span class="cl"><span class="go">[BLKDEV] Registered: /dev/vda (major 254, minor 0) </span></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="go">[VIRTIO-BLK] vda: 2 MB (4096 sectors, 512 byte blocks) </span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="go">[VIRTIO-BLK] Found block device at 0xffff00000a003e00 (version 2) </span></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="go">[BLKDEV] Registered: /dev/vdb (major 254, minor 1) </span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="go">[VIRTIO-BLK] vdb: 4 MB (8192 sectors, 512 byte blocks) </span></span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="go">[VIRTIO-BLK] Found 2 block device(s) </span></span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="go">[TARFS] TAR filesystem initialized </span></span></span><span class="line"><span class="ln"> 77</span><span class="cl"><span class="go">[FAT32] FAT32 filesystem driver initialized </span></span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="go">[DEVFS] Created /dev/vda (block device, major 254, minor 0) </span></span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="go">[DEVFS] Created /dev/vdb (block device, major 254, minor 1) </span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="go">[BOOT] Mounting tarfs from /dev/vda... </span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="go">[VFS] Mounting /dev/vda on /mnt (type: tarfs) </span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="go">[TARFS] Parsed 30 entries </span></span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="go">[TARFS] Mounted vda (2097152 bytes) </span></span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="go">[VFS] Mounted /dev/vda on /mnt successfully </span></span></span><span class="line"><span class="ln"> 85</span><span class="cl"><span class="go">[BOOT] Mounted tarfs on /mnt </span></span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="go">[BOOT] Mounting FAT32 from /dev/vdb... </span></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="go">[VFS] Mounting /dev/vdb on /fat (type: fat32) </span></span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="go">[FAT32] Mounting filesystem from vdb </span></span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="go">[FAT32] Volume: KRNLDATA, ID: 12345678 </span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="go">[FAT32] 512 bytes/sector, 1 sectors/cluster </span></span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="go">[FAT32] 8032 total clusters, root at cluster 2 </span></span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="go">[FAT32] Filesystem mounted successfully </span></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="go">[VFS] Mounted /dev/vdb on /fat successfully </span></span></span><span class="line"><span class="ln"> 94</span><span class="cl"><span class="go">[BOOT] Mounted FAT32 on /fat </span></span></span><span class="line"><span class="ln"> 95</span><span class="cl"><span class="go">[TASK] Task subsystem initialized </span></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="go">[TASK] Initial task: PID 0 (kernel) </span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="go">[BOOT] Executing /mnt/bin/init... </span></span></span><span class="line"><span class="ln"> 98</span><span class="cl"><span class="go">[USER] Loading ELF executable &#39;init&#39; (88576 bytes) </span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"><span class="go">[ELF] Loading ELF: entry=0x100000, 1 program headers </span></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="go">[ELF] Segment: vaddr=0x100000-0x104364 filesz=16076 memsz=17252 flags=RWX </span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="go">[ELF] Load complete, entry point: 0x100000 </span></span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="go">[TASK] Created task: PID 1 (init), entry=0xffff000040084e40, stack=0xffff000040152f18-0xffff000040153f10 </span></span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="go">[USER] Created ELF process: PID 1, entry=0x100000, brk=0x105000 </span></span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="go">[BOOT] Started init as PID 1 </span></span></span><span class="line"><span class="ln">105</span><span class="cl"><span class="go">[BOOT] Enabling interrupts... </span></span></span><span class="line"><span class="ln">106</span><span class="cl"><span class="go">[BOOT] Interrupts enabled! </span></span></span><span class="line"><span class="ln">107</span><span class="cl"><span class="go">[BOOT] Scheduler enabled! </span></span></span><span class="line"><span class="ln">108</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln">109</span><span class="cl"><span class="err"></span><span class="go">[BOOT] Entering idle loop (task 0) </span></span></span><span class="line"><span class="ln">110</span><span class="cl"><span class="go">[USER] Entering user mode: entry=0x100000, stack=0x7fffffe0 </span></span></span><span class="line"><span class="ln">111</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln">112</span><span class="cl"><span class="err"></span><span class="go">================================= </span></span></span><span class="line"><span class="ln">113</span><span class="cl"><span class="go">KRNL Init Process (PID 1) </span></span></span><span class="line"><span class="ln">114</span><span class="cl"><span class="go">================================= </span></span></span><span class="line"><span class="ln">115</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln">116</span><span class="cl"><span class="err"></span><span class="go">[init] Testing FAT32 file creation... </span></span></span><span class="line"><span class="ln">117</span><span class="cl"><span class="go">[init] Created /fat/testfile successfully! </span></span></span><span class="line"><span class="ln">118</span><span class="cl"><span class="go">[init] Running readdir_test: </span></span></span><span class="line"><span class="ln">119</span><span class="cl"><span class="go">[TASK] Created task: PID 2 (init), entry=0xffff000040084eb0, stack=0xffff00004013d718-0xffff000040141710 </span></span></span><span class="line"><span class="ln">120</span><span class="cl"><span class="go">[FORK] Created child process 2 (parent 1) </span></span></span><span class="line"><span class="ln">121</span><span class="cl"><span class="go">[EXECVE] Failed to open &#39;/mnt/bin/readdir_test&#39;: error 2 </span></span></span><span class="line"><span class="ln">122</span><span class="cl"><span class="go">[SYSCALL] Task 2 (init) exiting with status 127 </span></span></span><span class="line"><span class="ln">123</span><span class="cl"><span class="go">[TASK] Task 2 (init) exiting with status 127 </span></span></span><span class="line"><span class="ln">124</span><span class="cl"><span class="go">[TASK] Destroyed task: PID 2 (init) </span></span></span><span class="line"><span class="ln">125</span><span class="cl"><span class="go">[init] Running ls /mnt test: </span></span></span><span class="line"><span class="ln">126</span><span class="cl"><span class="go">[TASK] Created task: PID 3 (init), entry=0xffff000040084eb0, stack=0xffff00004013e390-0xffff000040142390 </span></span></span><span class="line"><span class="ln">127</span><span class="cl"><span class="go">[FORK] Created child process 3 (parent 1) </span></span></span><span class="line"><span class="ln">128</span><span class="cl"><span class="go">[ELF] Loading ELF: entry=0x400000, 2 program headers </span></span></span><span class="line"><span class="ln">129</span><span class="cl"><span class="go">[ELF] Segment: vaddr=0x400000-0x427100 filesz=160000 memsz=160000 flags=R-X </span></span></span><span class="line"><span class="ln">130</span><span class="cl"><span class="go">[ELF] Segment: vaddr=0x438000-0x438f90 filesz=296 memsz=3984 flags=RW- </span></span></span><span class="line"><span class="ln">131</span><span class="cl"><span class="go">[ELF] Load complete, entry point: 0x400000 </span></span></span><span class="line"><span class="ln">132</span><span class="cl"><span class="go">[EXECVE] Loaded &#39;ls&#39; at entry=0x400000, sp=0x7ffffe80 </span></span></span><span class="line"><span class="ln">133</span><span class="cl"><span class="go">/mnt/bin/ls </span></span></span><span class="line"><span class="ln">134</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln">135</span><span class="cl"><span class="err"></span><span class="go">/mnt: </span></span></span><span class="line"><span class="ln">136</span><span class="cl"><span class="go">hello.txt etc sbin bin </span></span></span><span class="line"><span class="ln">137</span><span class="cl"><span class="go">[SYSCALL] Task 3 (ls) exiting with status 0 </span></span></span><span class="line"><span class="ln">138</span><span class="cl"><span class="go">[TASK] Task 3 (ls) exiting with status 0 </span></span></span><span class="line"><span class="ln">139</span><span class="cl"><span class="go">[TASK] Destroyed task: PID 3 (ls) </span></span></span><span class="line"><span class="ln">140</span><span class="cl"><span class="go">[init] BusyBox found, will use ash shell </span></span></span><span class="line"><span class="ln">141</span><span class="cl"><span class="go">[init] Starting BusyBox ash shell... </span></span></span><span class="line"><span class="ln">142</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln">143</span><span class="cl"><span class="err"></span><span class="go">BusyBox v1.36.1 (2025-12-17 21:48:14 CET) built-in shell (ash) </span></span></span><span class="line"><span class="ln">144</span><span class="cl"><span class="go"></span><span class="err"> </span></span></span><span class="line"><span class="ln">145</span><span class="cl"><span class="err"></span><span class="go">krnl$ ls -l / </span></span></span><span class="line"><span class="ln">146</span><span class="cl"><span class="go">ls: ls </span></span></span><span class="line"><span class="ln">147</span><span class="cl"><span class="go">ls: -l/ </span></span></span><span class="line"><span class="ln">148</span><span class="cl"><span class="go">/: </span></span></span><span class="line"><span class="ln">149</span><span class="cl"><span class="go">fat bin mnt proc tmp dev </span></span></span></code></pre></div><h2 id="lung-language">Lung language</h2> <p>I also made a language (or rather, the LLM did—I just told it my wishes). It looks something like this:</p> <pre tabindex="0"><code>package main import &#34;fmt&#34; import &#34;./other.lung&#34; import &#34;./sub/toto.lung&#34; def main(): void { const message: string = &#34;Hello, Lung!&#34; fmt.println(message) let x: int = 10 let y: int = 20 x += y fmt.println(&#34;x + y =&#34;, x) let arr: [int] = [1, 2, 3, 4, 5] fmt.println(&#34;Array:&#34;, arr) fmt.println(&#34;Length:&#34;, len(arr)) if x &gt; 25 { fmt.println(&#34;x is greater than 25&#34;) } else { fmt.println(&#34;x is not greater than 25&#34;) } let i: int = 0 while i &lt; 3 { fmt.println(&#34;Loop iteration:&#34;, i) i += 1 } fmt.println(&#34;my function is&#34;, my_function()) fmt.println(&#34;sub: &#34;, sub.my_sub_func()) } </code></pre><p>The implementation happened in multiple phases. First, I had a working parser, lexer, and stack-based interpreter. Then came the modules that you can import. Finally, I decided I wanted to have even more fun and ended up compiling binaries using <code>cranelift</code>. Of course, I didn&rsquo;t stop there and ended up with the ability to compile and link to other dynamic libraries. Here&rsquo;s, for example, a mini-game in lung with raylib:</p> <pre tabindex="0"><code>package main // Static raylib FFI sanity check: // - white background // - two rectangles // - some text // --- Minimal raylib FFI surface --- extern &#34;C&#34; def InitWindow(width: int32, height: int32, title: *u8): void extern &#34;C&#34; def CloseWindow(): void extern &#34;C&#34; def WindowShouldClose(): bool extern &#34;C&#34; def BeginDrawing(): void extern &#34;C&#34; def EndDrawing(): void @repr(C) struct Color { r: u8, g: u8, b: u8, a: u8 } extern &#34;C&#34; def ClearBackground(color: Color): void extern &#34;C&#34; def SetTargetFPS(fps: int32): void extern &#34;C&#34; def DrawRectangle(x: int32, y: int32, w: int32, h: int32, color: Color): void extern &#34;C&#34; def DrawText(text: *u8, x: int32, y: int32, fontSize: int32, color: Color): void // Run: // /tmp/lung_raygame_test def main(): int32 { InitWindow(800, 450, cstr(&#34;Lung + raylib (FFI)&#34;)) SetTargetFPS(60) // fixed positions let px: int32 = 100 let py: int32 = 100 let ps: int32 = 32 let cx: int32 = 400 let cy: int32 = 200 let cs: int32 = 14 while WindowShouldClose() == false { BeginDrawing() ClearBackground(Color { r: 245, g: 245, b: 245, a: 255 }) DrawRectangle(px, py, ps, ps, Color { r: 0, g: 255, b: 0, a: 255 }) DrawRectangle(cx, cy, cs, cs, Color { r: 255, g: 215, b: 0, a: 255 }) DrawText(cstr(&#34;Collect the coin!&#34;), 16, 16, 20, Color { r: 0, g: 0, b: 130, a: 255 }) DrawText(cstr(&#34;Score:&#34;), 16, 44, 20, Color { r: 0, g: 0, b: 0, a: 255 }) EndDrawing() } CloseWindow() return 0 } </code></pre><p>It&rsquo;s very far from perfect, but I did learn a bunch because I asked the LLM way too many questions about different parts of its implementation.</p> <h2 id="container-runtime">Container runtime</h2> <p>I work at Docker, so of course I had to try making a container runtime. This time in Rust. The code for this one <a href="https://github.com/rumpl/ross">is available on my GitHub</a>. The architecture is inspired by containerd but, y&rsquo;know, completely generated by an LLM.</p> <p>The project contains a CLI and a daemon that talk to each other over gRPC. The daemon runs natively on Linux or macOS. On Linux, it uses <code>runc</code> directly to create and run containers. On macOS, each container runs inside a micro-VM thanks to <a href="https://github.com/containers/libkrun">libkrun</a>.</p> <p>The neat part of this experiment was the moment I decided I wanted faster networking in the libkrun case. After <em>a lot</em> of back and forth and multiple model switches, I managed to have a user-space networking stack that was ~20 times faster than what I had at the beginning. Don&rsquo;t ask me how it works, but it works.</p> <h2 id="docker-tui">Docker TUI</h2> <p>Speaking of Docker, here&rsquo;s a neat little TUI for your Docker engine.</p> <p><img src="./images/ratata-containers.png" alt="ratata containers"></p> <p><img src="./images/ratata-images.png" alt="ratata images"></p> <p><img src="./images/ratata-volumes.png" alt="ratata volumes"></p> <p><img src="./images/ratata-networks.png" alt="ratata networks"></p> <h2 id="markdown-editor">Markdown editor</h2> <p>Of all the projects listed here, this one is the most useful. And I even use it daily now. It&rsquo;s a simple and beautiful <a href="https://github.com/rumpl/typr">markdown editor</a>. From the readme:</p> <blockquote> <p>I made this just for me, there are plenty of other markdown editors, I&rsquo;m pretty sure others would be better for you and will have less bugs.</p> <p>Typr has one feature I could not find in any other: the ability to freely rearrange the notes in the sidebar. This is literally why typr exists.</p> </blockquote> <p>I actually generated this twice, with the same agent and model. In the first try, I ended up with a big HTML file with JavaScript and CSS shoved inside; it was a huge mess. After a reset I retried, and this time after a couple of minutes I had a neat little Electron project. To my surprise, it was working really well and there wasn&rsquo;t that much code. Basically, I learned of the existence of the <em>excellent</em> <a href="https://tiptap.dev/">tiptap</a> library.</p> <h2 id="raytracer">Raytracer</h2> <p>I guess I was bored and thinking about my university days and how I spent months back in the day implementing a raytracer in Java. These days you can get one in less than an hour.</p> <p><img src="./images/raye.png" alt="raye"></p> <h2 id="gameboy-emulator">GameBoy emulator</h2> <p>Why not, right?</p> <p><img src="./images/smb.png" alt="game boy color"></p> <p><img src="./images/smb1.png" alt="game boy color"></p> <p><img src="./images/smb2.png" alt="game boy color"></p> <h2 id="github-clone">GitHub clone</h2> <p>Another one that goes into the &ldquo;why not&rdquo; category. I made a small GitHub clone, but without the social part. I&rsquo;m kinda planning on cleaning up the code, adding some more tests, and then just deploying this on a server at home so that my code has a second place to call home. I&rsquo;m very happy about the CI that came out of it; it&rsquo;s YAML-based like GitHub Actions, but only uses images, and you can also run it locally with no fuss. The project has a server that listens to git events, syncs its own database, and runs CI when needed. There&rsquo;s a nice UI for it and also a CLI.</p> <p>I was particularly impressed when I decided I didn&rsquo;t want my server to be <a href="https://echo.labstack.com/">Echo</a>-based anymore, but wanted to use <a href="https://connectrpc.com/">ConnectRPC</a> instead. The agent was able to—one-shot—analyze the existing code, generate protobuf files, and convert the server, CLI, and UI to the new library. If I remember correctly, the whole thing took less than an hour and worked on the first try. Truly impressive.</p> <p>Repository view <img src="./images/geet1.png" alt="geet"> <img src="./images/geet2.png" alt="geet"></p> <p>Issues, with epics <img src="./images/geet3.png" alt="geet"></p> <p>Pull requests <img src="./images/geet4.png" alt="geet"></p> <p>GitHub actions inspired CI <img src="./images/geet5.png" alt="geet"> <img src="./images/geet6.png" alt="geet"></p> <p>Commits with diff views <img src="./images/geet7.png" alt="geet"> <img src="./images/geet8.png" alt="geet"></p> <h2 id="sessions-share-for-cagent">Sessions share for cagent</h2> <p>This was an easy and fun one. I pointed the agent to cagent&rsquo;s repository and asked it to add a &ldquo;Share&rdquo; command that would send the current session to a remote server. I then asked the agent to make a React/Tailwind/shadcn/ui frontend that is capable of showing the session. If memory serves, less than 10 minutes later I had everything working and was able to share my sessions from cagent to a server deployed to Fly.io.</p> <p><img src="./images/shagent.png" alt="shagent"></p> <p><img src="./images/shagent-detail.png" alt="shagent"></p> cagent https://rumpl.dev/posts/cagent/ Tue, 18 Nov 2025 09:00:00 +0100 https://rumpl.dev/posts/cagent/ <p>Long time no see.</p> <p>I&rsquo;ve been heads down working on something new, and I couldn&rsquo;t be more excited about it. It&rsquo;s called <a href="https://github.com/docker/cagent">cagent</a>, and it&rsquo;s an open-source project I&rsquo;ve been building at Docker.</p> <p>In short: <strong>cagent is a multi-agent runtime that makes it easy to build, run, and share AI agents.</strong></p> <p>We wanted to create a tool that lets you orchestrate &ldquo;teams&rdquo; of virtual experts: agents with specialized knowledge and tools, without the complexity that usually comes with building AI applications.</p> <h2 id="how-it-works">How it works</h2> <p>The core philosophy is simplicity. You define your agents in a straightforward YAML file. No complex Python frameworks or boilerplate code required.</p> <p>Here is what a basic agent looks like:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">agents</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"> </span><span class="nt">root</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"> </span><span class="nt">model</span><span class="p">:</span><span class="w"> </span><span class="l">openai/gpt-4o</span><span class="w"> </span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"> </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="l">A helpful AI assistant</span><span class="w"> </span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w"> </span><span class="nt">instruction</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd"> </span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="sd"> You are a knowledgeable assistant that helps users with various tasks. </span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="sd"> Be helpful, accurate, and concise in your responses.</span><span class="w"> </span></span></span></code></pre></div><p>You can run this immediately with:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">cagent run agent.yaml </span></span></code></pre></div><h2 id="the-power-of-tools-mcp">The Power of Tools (MCP)</h2> <p>One of the things I&rsquo;m most excited about is the support for the <strong>Model Context Protocol (MCP)</strong>. This allows your agents to connect to the outside world: databases, file systems, web search, you name it.</p> <p>You can even use Docker containers as tools. For example, giving an agent access to DuckDuckGo search via a containerized MCP server is as simple as adding a few lines to your YAML:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">toolsets</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"> </span>- <span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">mcp</span><span class="w"> </span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"> </span><span class="nt">ref</span><span class="p">:</span><span class="w"> </span><span class="l">docker:duckduckgo</span><span class="w"> </span></span></span></code></pre></div><h2 id="agents-as-tools">Agents as Tools</h2> <p>It gets more interesting when you start composing agents. <code>cagent</code> isn&rsquo;t just about running one agent; it&rsquo;s about orchestration. You can have a <code>root</code> agent that delegates tasks to specialized sub-agents (e.g., a coder, a writer, a researcher).</p> <p>Plus, <code>cagent</code> itself can act as an MCP server. This means you can expose your custom agents as tools to be used by <em>other</em> MCP clients!</p> <h2 id="sharing-is-caring">Sharing is Caring</h2> <p>Since we are Docker, we obviously had to make distribution easy. You can push your agent configurations to Docker Hub just like container images:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">cagent push my-namespace/my-agent </span></span></code></pre></div><p>And anyone else can pull and run them:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">cagent run my-namespace/my-agent </span></span></code></pre></div><h2 id="more">More</h2> <p>Is that it, I hear you ask. Well no, of course not. <code>cagent</code> is packed with features, each could have its own blog post. And maybe I will one day, for now here&rsquo;s a rapid fire overview of some of them.</p> <ul> <li>cagent can act as an <a href="https://agentclientprotocol.com/overview/introduction">ACP server</a>, use your agents from your IDE</li> <li>It has an API</li> <li>Its TUI can connect to the API remotely</li> <li>Remote MCPs with OAuth work out of the box</li> <li>A (we think) nice list of builtin tools for your agents</li> <li><a href="https://xbow.com/blog/alloy-agents">Alloy agents</a>, in your YAML file, add multiple LLMs to your agent: <code>model: openai/gpt-5,anthropic/claude-sonnet-4-5</code></li> <li><a href="https://blog.cloudflare.com/code-mode/">Code mode MCP</a></li> <li>Evals</li> <li>Auto session compaction</li> <li><a href="https://github.com/toon-format/toon">TOON</a> encoding of the tool results</li> </ul> <h2 id="the-future">The future</h2> <p>We have <em>a lot</em> of ideas about new features we can add to <code>cagent</code>. We are exploring more complex orchestration patterns, workflows, RAG, you name it, we&rsquo;ll implement it!</p> <h2 id="give-it-a-spin">Give it a spin</h2> <p>I&rsquo;m really proud of how this is shaping up. It&rsquo;s still active development, so things might break, but I&rsquo;d love for you to try it out.</p> <p>You can install it via Homebrew:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">brew install cagent </span></span></code></pre></div><p>Or check out the <a href="https://github.com/docker/cagent">GitHub repository</a> for more details, examples, and binary releases.</p> <p>Let me know what you think!</p> Wasmio 2023 https://rumpl.dev/posts/wasmio23/ Sat, 25 Mar 2023 18:41:49 +0100 https://rumpl.dev/posts/wasmio23/ <p>I just got back from sunny Barcelona where the WasmIO 2023 conference was held. It was purely and simply a <strong>blast</strong>! I want to thank the organizers for their hard work in making this first WasmIO conference awesome. Y&rsquo;all are awesome!</p> <p><a href="https://wasmio.tech/">WasmIO</a> was a two-day, single-track conference, and we had the creme de la creme of the people involved in the Webassembly world. And I was lucky enough to also be a speaker. This was my first conference in a while and boy am I rusty in public speaking, I hope that this will change now that the in-person conferences are back.</p> <p>The organization was flawless, the venue was great, and of course, Barcelona is a wonderful city. They did feed us on spanish time (lunch at 1:30PM), I&rsquo;m not used to eat so late but the food was great so I won&rsquo;t complain (much).</p> <p>My talk was on the first day in the afternoon so it&rsquo;s all kinda fuzzy to me because I was constantly checking my slides&hellip; I do remember seeing some awesome talks, of interest (to me) was the &ldquo;Containers vs WebAssembly&rdquo; panel, do check it out once the videos are out.</p> <p>I gave my talk about the things we&rsquo;ve been working on at Docker, making it easy to run Wasm workload with Docker and on Kubernetes. We also announced the second technical preview of Docker+Wasm in which we added 3 more runtimes for people to use: spin, slight and wasmtime. The first technical preview only had the WasmEdge runtime. On top of that, it&rsquo;s now possible to run Wasm inside the Docker Desktop&rsquo;s kubernetes cluster. <a href="https://www.docker.com/blog/announcing-dockerwasm-technical-preview-2/">Read more about it here</a>.</p> <p>I won&rsquo;t go into details about the talks but from what I&rsquo;ve seen the bar was high! Of course it is, knowing that the speakers are the ones that move the WebAssembly world forward: Dan Gohman, the WASI man, spoke, Fermyon had multiple talks and workshops. Zalim Bashorov and Sébastien Deleuze gave a deep-dive talk about Kotlin and Wasm, I could go on and on&hellip; I&rsquo;m telling you, go check out the videos once they are out!</p> <p>As with any conference, the meat of it was happening in the hallways and in the afterparties. I had too many great conversations, my head is spinning with ideas! This was my first foray into the WebAssembly community and it&rsquo;s all love and everyone is there with a mission: make WebAssembly a great platform for developers. 10/10, would recommend.</p> <p>Next year&rsquo;s WasmIO is already scheduled, it will be on the 21st and 22nd of March 2024, I would urge you to make sure to be there if you are interested in Wasm. I for sure will work hard to earn my place there next year. But for now, I am back home, time for some quiet time.</p> Writing a Window Manager In Rust - Part 2 https://rumpl.dev/posts/writing-a-window-manager-in-rust-part-2/ Tue, 03 Jan 2023 13:09:33 +0100 https://rumpl.dev/posts/writing-a-window-manager-in-rust-part-2/ <p>Welcome to the second installment of this series. In the first part we managed to set up our development environment and finished with a simple window manager that shows windows. This is a great start but let&rsquo;s now make it so that our window manager, you know, manages windows.</p> <h2 id="the-specs">The specs</h2> <p>Before going further let&rsquo;s decide what our window manager will do and look like. There are many ways a window manager can work, here is a list of features I would like the window manager to have. This list is not complete of course and might change in the future, that&rsquo;s what you call a <em>scope creep</em> but that&rsquo;s fine, this is our little playground and we do what we want.</p> <p>On to the list of features.</p> <ul> <li>the window manager will be a tiling window manager. I personally use <a href="https://i3wm.org/">i3</a> and quite like it. The real reason for making it tiling is that I feel like it would be simpler to write a tiling rather than a stacking window manager</li> <li>it will have workspaces like any other window manager, this is a must</li> <li><code>miniwm</code> will be configurable to some extent, we do want to at least be able to configure things that the window manager will launch at startup, this will be a great place to put <code>polybar</code> and other bells and whistles that exist in the Linux desktop world</li> </ul> <p>These are the things we really want our window manager to have. Let&rsquo;s go to the &ldquo;nice to haves&rdquo;, it&rsquo;s always good to have these.</p> <ul> <li>window decorations, like the top bar with the minimize, maximize and close buttons, this would be neat</li> <li>maybe we want the decorations to be themable? Imagine that!</li> <li>partial support for <a href="https://en.wikipedia.org/wiki/Extended_Window_Manager_Hints">EWMH</a></li> </ul> <p>How&rsquo;s that for a feature list? Good? Great, let&rsquo;s get into it!</p> <h2 id="windows">Windows</h2> <p>What is as window anyway? In the X server world a window is a 64 bits unsigned integer. That&rsquo;s right, it&rsquo;s a <code>u64</code> in Rust speak. Now we like types don&rsquo;t we, let&rsquo;s create a new type <code>Window</code> that we will use in our code, in <code>miniwm/mod.rs</code> we can add this</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">type</span> <span class="nc">Window</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kt">u64</span><span class="p">;</span><span class="w"> </span></span></span></code></pre></div><p>Our <code>Window</code> type is nothing more than a <a href="https://doc.rust-lang.org/reference/items/type-aliases.html">type alias</a> for a <code>u64</code>.</p> <p>We want to be able to manage the windows so we need to keep a list of opened windows somewhere, we will use a <code>BTreeSet</code> for this. This collection has all the properties we want:</p> <ul> <li>easy to add/remove items to it</li> <li>is sorted, we really want the windows to be sorted in the order they were added</li> </ul> <p>Our <code>MiniWM</code> struct now becomes:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">MiniWM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"> </span><span class="n">display</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">xlib</span>::<span class="n">Display</span><span class="p">,</span><span class="w"> </span></span></span><span class="line hl"><span class="ln">3</span><span class="cl"><span class="w"> </span><span class="n">windows</span>: <span class="nc">BTreeSet</span><span class="o">&lt;</span><span class="n">Window</span><span class="o">&gt;</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Before heading to the layout part, let&rsquo;s correctly listen to the <code>x11:UnmapNotify</code> event sent from the X server when a window is closed. To do that we need to add the <a href="https://tronche.com/gui/x/xlib/events/processing-overview.html#SubstructureNotifyMask"><code>xlib::SubstructureNotifyMask</code></a>. Selecting this mask will tell the X server that our window manager wants to listen to the unmap event (amongst others).</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">init</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">MiniWMError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">XSelectInput</span><span class="p">(</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">XDefaultRootWindow</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">),</span><span class="w"> </span></span></span><span class="line hl"><span class="ln"> 6</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">SubstructureRedirectMask</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">xlib</span>::<span class="n">SubstructureNotifyMask</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"> </span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"> </span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Next, in our main loop in <code>run</code> we match on the <code>xlib::UnmapNotify</code> event. When we receive that event we want to remove the window.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">run</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">MiniWMError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">event</span>: <span class="nc">xlib</span>::<span class="n">XEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">zeroed</span><span class="p">()</span><span class="w"> </span><span class="p">};</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"> </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">XNextEvent</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">event</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">event</span><span class="p">.</span><span class="n">get_type</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">MapRequest</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">create_window</span><span class="p">(</span><span class="n">event</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line hl"><span class="ln">11</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">UnmapNotify</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line hl"><span class="ln">12</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">remove_window</span><span class="p">(</span><span class="n">event</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"> </span></span></span><span class="line hl"><span class="ln">13</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;unknown event {:?}&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">event</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Upon receiving the unmap event we need to remove the window in question from the <code>self.windows</code> list. Once this is done we can call the <code>self.layout()</code> function we will define later.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">remove_window</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">event</span>: <span class="nc">xlib</span>::<span class="n">XEvent</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">MiniWMError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">event</span>: <span class="nc">xlib</span>::<span class="n">XUnmapEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">From</span>::<span class="n">from</span><span class="p">(</span><span class="n">event</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">windows</span><span class="p">.</span><span class="n">remove</span><span class="p">(</span><span class="o">&amp;</span><span class="n">event</span><span class="p">.</span><span class="n">window</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">layout</span><span class="p">()</span><span class="w"> </span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>With this out of the way we can start working on our layout code.</p> <h2 id="layout">Layout</h2> <p>As per the specs, we want our window manager to be a <a href="https://en.wikipedia.org/wiki/Tiling_window_manager">tiling one</a>. For the sake of simplicity we will only make it tile the windows vertically for now, we might revisit our algorithm later. There are a lot of different ways one can tile windows, we will start small.</p> <p>Before laying out the windows we need to know what screen size we have. For this we can query <a href="https://en.wikipedia.org/wiki/Xinerama">Xinerama</a>. Xinerama is an extension of the X window system that enables applications to use multiple screens as one display. Don&rsquo;t worry, we won&rsquo;t handle multiple screens just yet, we only want to know what&rsquo;s the size of our screen.</p> <p><code>xinerama</code> is a feature of the <code>x11</code> crate so we need to add it to the list of features in Cargo.toml:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="nx">x11</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;2.20.1&#34;</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;xlib&#34;</span><span class="p">,</span> <span class="s2">&#34;xinerama&#34;</span><span class="p">]</span> <span class="p">}</span> </span></span></code></pre></div><p>With that done, without further ado, here is the function to get the screen size</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">get_screen_size</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(</span><span class="kt">i16</span><span class="p">,</span><span class="w"> </span><span class="kt">i16</span><span class="p">),</span><span class="w"> </span><span class="n">MiniWMError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">num</span>: <span class="kt">i32</span> <span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">screen_pointers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">xinerama</span>::<span class="n">XineramaQueryScreens</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">num</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">screens</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">slice</span>::<span class="n">from_raw_parts</span><span class="p">(</span><span class="n">screen_pointers</span><span class="p">,</span><span class="w"> </span><span class="n">num</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">to_vec</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">screen</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">screens</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">screen</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">screen</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">((</span><span class="n">screen</span><span class="p">.</span><span class="n">width</span><span class="p">,</span><span class="w"> </span><span class="n">screen</span><span class="p">.</span><span class="n">height</span><span class="p">))</span><span class="w"> </span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">MiniWMError</span>::<span class="n">ScreenNotFound</span><span class="p">)</span><span class="w"> </span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Nothing much is going on here, on line 4 we ask Xinerama to give us the list of screens, we convert these pointers to a vector on line 5 and finally, on line 6 we get the first screen, remember, no multi-displays (yet).</p> <p>Great, with the list of windows to show and the screen size we can finally layout our windows as we want</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">layout</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">MiniWMError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">windows</span><span class="p">.</span><span class="n">is_empty</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Ok</span><span class="p">(());</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="p">(</span><span class="n">width</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">get_screen_size</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">win_width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">width</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">i32</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">windows</span><span class="p">.</span><span class="n">len</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">i32</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">windows</span><span class="p">.</span><span class="n">iter</span><span class="p">().</span><span class="n">for_each</span><span class="p">(</span><span class="o">|</span><span class="n">window</span><span class="o">|</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">move_window</span><span class="p">(</span><span class="o">*</span><span class="n">window</span><span class="p">,</span><span class="w"> </span><span class="n">start</span><span class="p">,</span><span class="w"> </span><span class="mi">0_</span><span class="k">i32</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">resize_window</span><span class="p">(</span><span class="o">*</span><span class="n">window</span><span class="p">,</span><span class="w"> </span><span class="n">win_width</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">u32</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">win_width</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w"> </span><span class="p">});</span><span class="w"> </span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"> </span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>The <code>move_window</code> and <code>resize_window</code> are one line function that call the appropriate x11 function</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">move_window</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">window</span>: <span class="nc">Window</span><span class="p">,</span><span class="w"> </span><span class="n">x</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="kt">i32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">xlib</span>::<span class="n">XMoveWindow</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">,</span><span class="w"> </span><span class="n">window</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">};</span><span class="w"> </span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">resize_window</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">window</span>: <span class="nc">Window</span><span class="p">,</span><span class="w"> </span><span class="n">width</span>: <span class="kt">u32</span><span class="p">,</span><span class="w"> </span><span class="n">height</span>: <span class="kt">u32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">xlib</span>::<span class="n">XResizeWindow</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">,</span><span class="w"> </span><span class="n">window</span><span class="p">,</span><span class="w"> </span><span class="n">width</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="p">)</span><span class="w"> </span><span class="p">};</span><span class="w"> </span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>With all this code you should be able to see a window that takes the whole screen when alone</p> <p><a href="images/first.png"><img src="images/first.png" alt="Xephyr with one urxvt window open"></a></p> <p>Adding one window splits the screen space between the two windows</p> <p><a href="images/second.png"><img src="images/second.png" alt="Xephyr with two urxvt windows open"></a></p> <p>Adding another window should move the first two to make place for the last one</p> <p><a href="images/third.png"><img src="images/third.png" alt="Xephyr with three urxvt windows open"></a></p> <p>Would you look at that, it works!</p> <p>This must be the simplest tiling manager out there but it&rsquo;s it ours, we should be proud! The code of this article can be found on the <a href="https://github.com/rumpl/miniwm/tree/163828403619c8d4b09b4f45234aab8d04be3e7e">miniwm github repository</a>.</p> <p>Tune in next time for some exciting new features like for example ✨ <em>workspaces</em> ✨.</p> <p><em>This the second part in the &ldquo;Writing a window manager in Rust&rdquo; series</em></p> <ul> <li><a href="https://rumpl.dev/posts/writing-a-window-manager-in-rust-part-1">Part 1</a></li> <li><a href="https://rumpl.dev/posts/writing-a-window-manager-in-rust-part-2">Part 2</a></li> </ul> 2022 in Review https://rumpl.dev/posts/2022-in-review/ Thu, 29 Dec 2022 12:51:50 +0100 https://rumpl.dev/posts/2022-in-review/ <p>Well this is a first for me. I don&rsquo;t know what changed but this time I felt like writing a recap of my year. And boy, this year was an exciting one for me personally and professionally.</p> <p>I won&rsquo;t go over the personal stuff for obvious reasons but there were a lot of good things happen to me this year and of course some bad. Life is like that, you can&rsquo;t have nice things but you can&rsquo;t also only have lemons.</p> <p>Professionally though, that&rsquo;s another story, let&rsquo;s talk about that!</p> <h2 id="the-conferences">The conferences</h2> <p>The year 2022 is the year in-person conferences were a thing once again. I submitted a talk to three conferences and talked to two, I&rsquo;m looking at you Kubeconf, pick me next time!</p> <p>The first conference I talked at was <a href="https://cfp.devoxx.fr/2022/talks.html">Devoxx FR</a>. I actually had two of my talks selected. Unfortunately for me I got sick the week before (yes, it was the big C) and could only attend to one of my talks virtually. The talk went great, I did it with my friend and coworker <a href="https://twitter.com/glours/">Guillaume Lours</a>.</p> <p>The second conference was in my hometown, Rouen. This talk was also with Guillaume. This time I managed not to get sick and got to talk to a bunch of interesting people. Our talk went great again, even though my part of the demo didn&rsquo;t work, curse you cloud!</p> <h2 id="container-stuff">Container stuff</h2> <p>This year I switched teams at Docker and am now in the team that works on the docker cli and the daemon, we call it the <em>runtime</em> team. This is a big deal for me, I started talking about this switch with my manager two years ago and we were trying to find a way for me to switch. It took a long time in part because I wasn&rsquo;t feeling ready and also because they needed me on other projects. But this year it finally happened.</p> <p>I now spend my days working with the people, like <a href="https://github.com/thajeztah/">Sebastiaan</a>, who made this industry-changing technology and it&rsquo;s a humbling and extremely satisfying.</p> <p>Somehow I even managed to get the lead on the huge work of <a href="https://www.docker.com/blog/extending-docker-integration-with-containerd/">switching dockerd&rsquo;s image management code over to containerd</a>. It&rsquo;s long and complex work but also exciting and I&rsquo;m really happy that docker is going in the right direction and embracing containerd more. This will help us in the long run and make maintaining docker easier and will bring features faster to our users.</p> <h2 id="wasm">Wasm</h2> <p>Have you heard of <a href="https://webassembly.org/">webassembly</a>? Did you know that <a href="https://www.docker.com/blog/docker-wasm-technical-preview/">Docker can run Wasm modules too?</a> Late this year I was lucky enough to get on the webassembly bandwagon. I helped making it possible to run Wasm modules with docker. It was great fun, and our work is only starting. Expect Wasm posts in the near future on my website.</p> <p>The people creating the spec are doing a great job and we will hear more about Wasm in the future for sure. I will definitely help bring wasm and containers closer as much as I can in the future.</p> <h2 id="rust">Rust</h2> <p>2022 was the year of Rust for me, this was my fourth or fifth time I tried to learn it and this time it stuck. I am now having more fun with rust than ever and hope I will get to do more in the future. If you are thinking about learning a new language, Rust should be on your list!</p> <h2 id="conclusion">Conclusion</h2> <p>Everything I wrote here is just a small part of what happened to me this year. I&rsquo;m not great at taking notes, there was so much more I did or learned. Think of this as a preview of my 2022.</p> <p>Next year I hope to be at least as productive as this year, but also I think I might try and take more time for myself, I do tend to grind too much when I work on interesting things. I need to be better at slowing down when I&rsquo;m hooked on something I&rsquo;m working on. Not to say these are my new-year&rsquo;s resolutions but next year I will definitely try to take more time to really learn to play the guitar. I live near a forest now so one of the things I want to do is get out more, do more biking.</p> <p>I sincerely hope you had a great 2022 and wish you all the best in 2023 and hope you never stop being curious and explore new things.</p> Writing a Window Manager in Rust - Part 1 https://rumpl.dev/posts/writing-a-window-manager-in-rust-part-1/ Wed, 28 Dec 2022 12:58:11 +0100 https://rumpl.dev/posts/writing-a-window-manager-in-rust-part-1/ <p>This is the first part in writing <code>miniwm</code>, a window manager for X11 in rust.</p> <p>I recently got my Linux machine working again and I thought I would try and make a window manager in rust. This is something I tried many moons ago but back in the day all you had was C, the xlib documentation and code from other window managers. Let me tell you it wasn&rsquo;t easy&hellip;</p> <p>I don&rsquo;t have the list of features defined yet, we can build it as we go I guess. There are things we will need to think about at one point, things like do we want it to be stackable or tiling, how will we configure the window manager etc.</p> <p>For this series you will need these things:</p> <ul> <li>a Linux machine</li> <li>rust</li> <li>Xephyr</li> <li>urxvt (optional)</li> </ul> <h2 id="setup">Setup</h2> <p>First we need to create the project, in rust this is easy, open a terminal and write:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> cargo new miniwm </span></span></code></pre></div><p>This will create rust bin project in the <code>miniwm</code> directory.</p> <p>For our mini window manager we will be using the <a href="https://github.com/AltF02/x11-rs"><code>x11</code></a> crate. There are two options for libraries that you can use when you write a window manager for X11:</p> <ul> <li><a href="https://www.x.org/releases/current/doc/libX11/libX11/libX11.html">Xlib</a></li> <li><a href="https://xcb.freedesktop.org/">XCB</a></li> </ul> <p>Both of these libraries provide an API to talk to the X server but are designed quite differently. I won&rsquo;t get into the details, maybe another time if I decide I want to try and rewrite <code>miniwm</code> using <code>XCB</code>.</p> <p>The first step is to add the <code>x11</code> crate to your project:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">miniwm $ cargo add x11 --features xlib </span></span></span></code></pre></div><p>For nicer error handling let&rsquo;s install <code>thiserror</code> too:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">miniwm $ cargo add thiserror </span></span></span></code></pre></div><p>Right, we have successfully setup our project, we should be able to build it now</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">miniwm $ cargo build </span></span></span><span class="line"><span class="cl"><span class="go">Compiling miniwm v0.1.0 (/home/rumpl/dev/miniwm) </span></span></span><span class="line"><span class="cl"><span class="go">Finished dev [unoptimized + debuginfo] target(s) in 0.77s </span></span></span></code></pre></div><p>If the project doesn&rsquo;t compile you will need to install <code>libx11-dev</code>.</p> <h2 id="connecting-to-the-x-server">Connecting to the X server</h2> <p>On to the good stuff. The first thing a window manager has to do is to connect to the X server. This is done by calling <a href="https://www.x.org/releases/X11R7.5/doc/man/man3/XOpenDisplay.3.html">XOpenDisplay</a>.</p> <p>We will create a <code>struct</code> for our window manager, first create a directory <code>miniwm</code> and a <code>mod.rs</code> inside that directory</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">miniwm $ mkdir src/miniwm </span></span></span><span class="line"><span class="cl"><span class="go">miniwm $ touch src/miniwm/mod.rs </span></span></span></code></pre></div><p>We can now create our struct in that file</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">use</span><span class="w"> </span><span class="n">thiserror</span>::<span class="n">Error</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="cp">#[derive(Error, Debug)]</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">MiniWMError</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"> </span><span class="cp">#[error(</span><span class="s">&#34;display {0} not found&#34;</span><span class="cp">)]</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"> </span><span class="n">DisplayNotFound</span><span class="p">(</span><span class="nb">String</span><span class="p">),</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"> </span><span class="cp">#[error(</span><span class="s">&#34;{0}&#34;</span><span class="cp">)]</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"> </span><span class="n">NulString</span><span class="p">(</span><span class="cp">#[from]</span><span class="w"> </span><span class="n">NulError</span><span class="p">),</span><span class="w"> </span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"></span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">MiniWM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w"></span><span class="k">impl</span><span class="w"> </span><span class="n">MiniWM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">display_name</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="bp">Self</span><span class="p">,</span><span class="w"> </span><span class="n">MiniWMError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">MiniWM</span><span class="w"> </span><span class="p">{})</span><span class="w"> </span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">init</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">MiniWMError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"> </span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">run</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;miniwm running&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Let&rsquo;s see what is going on here, first we define our own error type that we will use later.</p> <p>The MiniWM has three functions:</p> <ul> <li><code>new</code> will connect to the X server and create a new wm instance</li> <li><code>init</code> will instruct the X server that we want to listen to some events</li> <li><code>run</code> will be our infinite loop that listens for X server events</li> </ul> <p>Let&rsquo;s now use our <code>MiniWM</code> in <code>main.rs</code></p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">mod</span> <span class="nn">miniwm</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">std</span>::<span class="n">error</span>::<span class="n">Error</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"></span><span class="k">use</span><span class="w"> </span><span class="n">miniwm</span>::<span class="n">MiniWM</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="nb">Box</span><span class="o">&lt;</span><span class="k">dyn</span><span class="w"> </span><span class="n">Error</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">display_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">std</span>::<span class="n">env</span>::<span class="n">var</span><span class="p">(</span><span class="s">&#34;DISPLAY&#34;</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">wm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MiniWM</span>::<span class="n">new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">display_name</span><span class="p">)</span><span class="o">?</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"> </span><span class="n">wm</span><span class="p">.</span><span class="n">init</span><span class="p">()</span><span class="o">?</span><span class="p">;</span><span class="w"> </span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"> </span><span class="n">wm</span><span class="p">.</span><span class="n">run</span><span class="p">();</span><span class="w"> </span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"> </span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>In the first line we ask for the current value of the <code>$DISPLAY</code> environment variable. On POSIX systems the <code>$DISPLAY</code> environment variable holds the current display name.</p> <p>We can now try and run our window manager. Granted it doesn&rsquo;t do anything for now but this is a great time to check that our setup works.</p> <p>To check this we will need 3 terminals.</p> <p>In the first terminal we will launch <code>Xephyr</code>. Xephyr is a nested X server that runs as an X application. Having a nested X server means we don&rsquo;t have to go out of our own X session to test the window manager we are making, neat!</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> sudo Xephyr :1 -ac -br -noreset -screen 800x600 </span></span></code></pre></div><p>This will open a black window, this is the X server that our window manager will connect to.</p> <p>Note: without <code>sudo</code> I had problems with <code>Xpehyr</code> not detecting the mouse and the keyboard, <code>sudo</code> makes the problems go away, you should try running Xephyr without sudo, it might work for you.</p> <p>In another terminal launch our window manager</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">miniwm $ DISPLAY=:1 ./target/debug/miniwm </span></span></span></code></pre></div><p>Note that we redefine the <code>$DISPLAY</code> variable so that our window manager connects to the right X server.</p> <p>Finally we can try and run an application, I will use <code>urxvt</code>, a VT102 emulator for the X window system.</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> <span class="nv">DISPLAY</span><span class="o">=</span>:1 urxvt </span></span></code></pre></div><p>After all this you should see a <code>miniwm running</code> message in the second terminal and also see the <code>urxvt</code> window should be shown inside the <code>Xephyr</code> window.</p> <p><a href="images/first.png"><img src="images/first.png" alt="Image alt"></a></p> <p>Our window manager doesn&rsquo;t do anything yet, let&rsquo;s add some code shall we?</p> <p>The first thing a window manager should do is to connect to the X server, this is done by calling <a href="https://www.x.org/releases/X11R7.5/doc/man/man3/XOpenDisplay.3.html">XOpenDisplay</a></p> <p>Once we get the reference to the display we will save it in our struct, to do that we add a <code>display</code> field to our struct:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">MiniWM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"> </span><span class="n">display</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">xlib</span>::<span class="n">Display</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>We can then change the <code>new</code> function and add the call to <code>XOpenDisplay</code></p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">display_name</span>: <span class="kp">&amp;</span><span class="kt">str</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="bp">Self</span><span class="p">,</span><span class="w"> </span><span class="n">MiniWMError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">display</span>: <span class="o">*</span><span class="k">mut</span><span class="w"> </span><span class="n">xlib</span>::<span class="n">Display</span><span class="w"> </span><span class="o">=</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">xlib</span>::<span class="n">XOpenDisplay</span><span class="p">(</span><span class="n">CString</span>::<span class="n">new</span><span class="p">(</span><span class="n">display_name</span><span class="p">)</span><span class="o">?</span><span class="p">.</span><span class="n">as_ptr</span><span class="p">())</span><span class="w"> </span><span class="p">};</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">display</span><span class="p">.</span><span class="n">is_null</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">MiniWMError</span>::<span class="n">DisplayNotFound</span><span class="p">(</span><span class="n">display_name</span><span class="p">.</span><span class="n">into</span><span class="p">()));</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">MiniWM</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">display</span><span class="w"> </span><span class="p">})</span><span class="w"> </span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><h2 id="listening-to-events">Listening to events</h2> <p>Next we need to tell the X server that we want to listen to some events. By default the X server doesn&rsquo;t send any events, it&rsquo;s up to the window manager to tell it what events we care about.</p> <p>We will do this in our <code>init</code> function</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">init</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span><span class="w"> </span><span class="n">MiniWMError</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">XSelectInput</span><span class="p">(</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">XDefaultRootWindow</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">),</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">SubstructureRedirectMask</span><span class="p">,</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"> </span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"> </span><span class="nb">Ok</span><span class="p">(())</span><span class="w"> </span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>Let&rsquo;s pause here and see what is going on.</p> <p>The <code>XSelectInput</code> tells the X server that we want to listen to particular events. Here we use the <code>xlib::SubstructureRedirectMask</code> mask. This mask tells the X server that we want to listen to events of type <code>CirculateRequest</code>, <code>ConfigureRequest</code> and <code>MapRequest</code>. We only care about the last one, in X terms <em>mapping</em> a window means creating a window.</p> <p>Let&rsquo;s create our busy loop that will wait for events</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">run</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">event</span>: <span class="nc">xlib</span>::<span class="n">XEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">zeroed</span><span class="p">()</span><span class="w"> </span><span class="p">};</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"> </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">XNextEvent</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">event</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">event</span><span class="p">.</span><span class="n">get_type</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"> </span><span class="n">xlib</span>::<span class="n">MapRequest</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">create_window</span><span class="p">(</span><span class="n">event</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;unknown event {:?}&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">event</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>This is an infinite loop that waits for the next event and then dispatches it. We then call the <code>create_window</code> function if we get a <code>MapRequest</code> from the X server.</p> <h2 id="creating-a-window">Creating a window</h2> <p>The last thing we need to do is to tell the X server to create a window when needed.</p> <p>Here is the <code>create_window</code> function</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="ln">1</span><span class="cl"><span class="w"> </span><span class="k">fn</span> <span class="nf">create_window</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">event</span>: <span class="nc">xlib</span>::<span class="n">XEvent</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;creating a window&#34;</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">event</span>: <span class="nc">xlib</span>::<span class="n">XMapRequestEvent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">From</span>::<span class="n">from</span><span class="p">(</span><span class="n">event</span><span class="p">);</span><span class="w"> </span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">xlib</span>::<span class="n">XMapWindow</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">display</span><span class="p">,</span><span class="w"> </span><span class="n">event</span><span class="p">.</span><span class="n">window</span><span class="p">)</span><span class="w"> </span><span class="p">};</span><span class="w"> </span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w"> </span><span class="p">}</span><span class="w"> </span></span></span></code></pre></div><p>It converts the event to the appropriate <code>xlib::XMapRequestEvent</code> type and then calls the <code>XMapWindow</code> function. Here we could have also used the <code>xlib::XMapRaised</code> function to effectively show the window and push it on the top of the stack.</p> <h2 id="the-end">The end</h2> <p>We have now created a basic window manager that can show windows when it receives the event to do so. There is so much more we can do but we can, I think, stop here for the first part. Tap yourself on the back and take a break.</p> <p>Tune in next time for some exciting window manager explorations. In the meantime you might want to take a look at the <a href="https://www.x.org/releases/current/doc/libX11/libX11/libX11.html">xlib documentation</a> and reading some other tiny window manager code can&rsquo;t hurt, here is a list of some nice ones:</p> <ul> <li><a href="https://github.com/mackstann/tinywm">tinywm</a>, written in C, the &ldquo;ridiculously tiny window manager&rdquo;</li> <li><a href="https://github.com/acmiyaguchi/rust-tinywm">rust-tinywm</a> is a port of the <code>tinywm</code> to rust</li> <li><a href="https://github.com/JLErvin/berry">berry</a>, written in C, has some really clean code inside</li> </ul> <p>You can also see the full code of this post on <a href="https://github.com/rumpl/miniwm/tree/704b855f41e685a661e3adca6d2c6d77b06bf86c">github</a></p> <p>These are just some of the window managers I looked at but the internet is full of tiny window managers, explore!</p> <p><em>This the first part in the &ldquo;Writing a window manager in Rust&rdquo; series</em></p> <ul> <li><a href="https://rumpl.dev/posts/writing-a-window-manager-in-rust-part-1">Part 1</a></li> <li><a href="https://rumpl.dev/posts/writing-a-window-manager-in-rust-part-2">Part 2</a></li> </ul> Add docker/docker to your dependencies https://rumpl.dev/posts/add_docker_to_your_dependencies/ Sun, 10 May 2020 00:00:00 +0000 https://rumpl.dev/posts/add_docker_to_your_dependencies/ <h2 id="the-problem">The problem</h2> <p>If you are using go modules and want to use the client from <a href="https://github.com/moby/moby">docker/docker</a> in your code you would do something like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">package</span> <span class="nx">main</span> </span></span><span class="line"><span class="ln"> 2</span><span class="cl"> </span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">import</span> <span class="p">(</span> </span></span><span class="line"><span class="ln"> 4</span><span class="cl"> <span class="s">&#34;context&#34;</span> </span></span><span class="line"><span class="ln"> 5</span><span class="cl"> <span class="s">&#34;fmt&#34;</span> </span></span><span class="line"><span class="ln"> 6</span><span class="cl"> </span></span><span class="line"><span class="ln"> 7</span><span class="cl"> <span class="s">&#34;github.com/docker/docker/api/types&#34;</span> </span></span><span class="line"><span class="ln"> 8</span><span class="cl"> <span class="s">&#34;github.com/docker/docker/client&#34;</span> </span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">)</span> </span></span><span class="line"><span class="ln">10</span><span class="cl"> </span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="ln">12</span><span class="cl"> <span class="nx">c</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">NewClientWithOpts</span><span class="p">(</span><span class="nx">client</span><span class="p">.</span><span class="nx">FromEnv</span><span class="p">)</span> </span></span><span class="line"><span class="ln">13</span><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="ln">14</span><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> </span></span><span class="line"><span class="ln">15</span><span class="cl"> <span class="k">return</span> </span></span><span class="line"><span class="ln">16</span><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="ln">17</span><span class="cl"> </span></span><span class="line"><span class="ln">18</span><span class="cl"> <span class="nx">containers</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">ContainerList</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">types</span><span class="p">.</span><span class="nx">ContainerListOptions</span><span class="p">{})</span> </span></span><span class="line"><span class="ln">19</span><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span> </span></span><span class="line"><span class="ln">20</span><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> </span></span><span class="line"><span class="ln">21</span><span class="cl"> <span class="k">return</span> </span></span><span class="line"><span class="ln">22</span><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="ln">23</span><span class="cl"> </span></span><span class="line"><span class="ln">24</span><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">container</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">containers</span> <span class="p">{</span> </span></span><span class="line"><span class="ln">25</span><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">container</span><span class="p">.</span><span class="nx">ID</span><span class="p">)</span> </span></span><span class="line"><span class="ln">26</span><span class="cl"> <span class="p">}</span> </span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">}</span> </span></span></code></pre></div><p>If you then try and build your code you will be greeted with a nice error message:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="p">.</span><span class="o">/</span><span class="nx">main</span><span class="p">.</span><span class="k">go</span><span class="p">:</span><span class="mi">12</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span> <span class="nx">undefined</span><span class="p">:</span> <span class="nx">client</span><span class="p">.</span><span class="nx">NewClientWithOpts</span> </span></span><span class="line"><span class="cl"><span class="p">.</span><span class="o">/</span><span class="nx">main</span><span class="p">.</span><span class="k">go</span><span class="p">:</span><span class="mi">12</span><span class="p">:</span><span class="mi">40</span><span class="p">:</span> <span class="nx">undefined</span><span class="p">:</span> <span class="nx">client</span><span class="p">.</span><span class="nx">FromEnv</span> </span></span></code></pre></div><p>What just happened? If you look at your <code>go.mod</code> file you would see something like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-golang" data-lang="golang"><span class="line"><span class="cl"><span class="nf">require</span> <span class="p">(</span> </span></span><span class="line"><span class="cl"> <span class="o">...</span> </span></span><span class="line"><span class="cl"> <span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">docker</span><span class="o">/</span><span class="nx">docker</span> <span class="nx">v1</span><span class="mf">.13.1</span> <span class="c1">// indirect </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">...</span> </span></span><span class="line"><span class="cl"><span class="p">)</span> </span></span></code></pre></div><p>It its great wisdom, <code>go mod</code> decided to take the <code>v1.13.1</code> tag because this is the latest tag that happens to be semver valid. The problem with this tag is that it was created in 2017. And <code>client.NewClientWithOpts</code> did not exist in 2017.</p> <h2 id="the-solution">The solution</h2> <p>Here&rsquo;s how to fix this shenanigans: add <code>github.com/docker/docker master</code> in your go.mod file and execute <code>go mod tidy</code>. If you now look at your <code>go.mod</code> file you will see something like this:</p> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">github</span><span class="p">.</span><span class="nx">com</span><span class="o">/</span><span class="nx">docker</span><span class="o">/</span><span class="nx">docker</span> <span class="nx">v17</span><span class="mf">.12.0</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="nx">rc1</span><span class="mf">.0.20200508181053</span><span class="o">-</span><span class="mi">298</span><span class="nx">ba5b13150</span><span class="o">+</span><span class="nx">incompatible</span> </span></span></code></pre></div><p>I am unsure why it takes the <code>v17.12.0-ce-rc1</code> tag but the <code>298ba5b13150</code> sha is indeed the <a href="https://github.com/moby/moby/commit/298ba5b13150bfffe8414922a951a7a793276d31">sha of the latest commit on docker/docker at this time</a></p> <p>If you now try to build the code above it will work as expected.</p> <p><em>Note</em>: you could have also changed your code to create a client with <code>c, err := client.NewEnvClient()</code> and the code would have worked, this is because the docker team made it so that even an older client can talk to the latest engine, and this is kinda awesome.</p> <h2 id="recap">Recap</h2> <p>Why this happens? The reason is twofold:</p> <ul> <li><code>docker/docker</code> uses calver (calendar versions)</li> <li><code>go mod</code> uses semver (semantic versions)</li> </ul> <p>This means that even if <code>docker/docker</code> released a bunch of new versions since 2017, its way of naming tags (latest tag right now is <code>v19.03.8</code>) makes it incompatible with the way <code>go mod</code> thinks you should name your tags. Fortunately there is a solution, I just wished life was easier sometimes.</p> First https://rumpl.dev/posts/first/ Fri, 11 Oct 2019 21:04:27 +0200 https://rumpl.dev/posts/first/ <h1 id="hello-world">Hello world!</h1> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">&#34;fmt&#34;</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello world&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"><span class="p">}</span> </span></span></code></pre></div>