Open Nexus OS: my Rust/RISC-V microkernel finally has a UI
Posted: Wed Jun 24, 2026 9:01 am
Hello everyone — a progress update on my OS.

As you can see, I finally have a UI. Please don't overrate it, though. The only dropdown that actually works is the "Apps" one, I have no session management and no real app runtime yet, and a lot of it is still hand-stitched. For the last couple of months I've basically been building and improving my input/output pipeline on the same target tests I didn't want to see anymore, so what you're looking at is just arranged into nicer looking rectangles that I find a bit more comfortable to look at now.
Here's the story. Like I mentioned last time, most of the system foundation was already done, but it only ever lived in UART output. So this whole stretch has really been about the input/output pipeline.
I started with a simple scanout and a CPU renderer, and got a single moving rectangle. Then came the mouse and the target tests I mentioned (hover, click, scroll, key press) — that was my baseline, and everything grew from there. The mouse was painfully slow at first, so I built an early fast path that handled positioning and told the different input types apart. Once that was working and my target tests passed, I swapped the background for a real JPEG and the cursor for a simple vector graphic. That already showed the limits of the approach, because everything was still scanline-based and re-drawing the whole frame every time — but with some optimization it actually held up.
The next step was opacity, shadows and blur. Those who know, know I'm a bit insane — but for everyone else: this isn't because I love Apple. Blur is about the most expensive thing you can render, so it gives you very honest optimization feedback.
To get the UI usable again I started by caching the area under the cursor and only updating the target lines instead of the whole screen. Didn't help much. So I moved to dirty rectangles and pre-caching the target state, and that was a big improvement — though I still had artifacts bleeding over the blur. I also did a lot of work with 9-slice and SDF to cut down rendering cost. After that I built a scrollable list with a filter input. (Side note: I still don't have IME — right now I've hardcoded five keys just to test the filter.) To my surprise, that worked too.
The point where it all fell apart was animation. That's where I started questioning a few life decisions. I rebuilt the pipeline for virtio-gpu, went to a 4-plane VMO setup (still didn't help much), and finally moved to virgl. Do I now have an amazing GPU driver you can game on? Not even close. But it renders shadows and blur, which already feels like a win.
A bit of architecture context. I'm never going to write something like the Linux graphics stack — 30 years of drivers for every card ever made, I think that beast is around two million loc. So I asked myself: what is a driver actually made of? You've got scheduling, an SDK-like layer (think Vulkan), and the actual hardware communication — and then that gets multiplied by per-device if/else branches (oversimplified), all baked into one driver. So what if I split it: a general "driver accelerator" for the reusable IRQ-type plumbing, a very minimal actual driver for device-specific talk, and a Metal-like userspace graphics SDK on top? Then in theory I keep only small, specialized drivers and can maintain the rest independently — and when a new device shows up, I can build another specialized driver in a few weeks instead of starting over (obviously I only have a minimal version of that idea working on qemu right now).
Right now animation runs smoothly, and I have a virtual list with more elements, though I'm still fighting to make scrolling truly smooth. It works — I just need to sit down and focus on that specifically.
Anyway, the I/O pipeline mostly works now. I've still got a few days of cleanup, refactoring, documentation and tests ahead of me, but I felt like sharing already.

As you can see, I finally have a UI. Please don't overrate it, though. The only dropdown that actually works is the "Apps" one, I have no session management and no real app runtime yet, and a lot of it is still hand-stitched. For the last couple of months I've basically been building and improving my input/output pipeline on the same target tests I didn't want to see anymore, so what you're looking at is just arranged into nicer looking rectangles that I find a bit more comfortable to look at now.
Here's the story. Like I mentioned last time, most of the system foundation was already done, but it only ever lived in UART output. So this whole stretch has really been about the input/output pipeline.
I started with a simple scanout and a CPU renderer, and got a single moving rectangle. Then came the mouse and the target tests I mentioned (hover, click, scroll, key press) — that was my baseline, and everything grew from there. The mouse was painfully slow at first, so I built an early fast path that handled positioning and told the different input types apart. Once that was working and my target tests passed, I swapped the background for a real JPEG and the cursor for a simple vector graphic. That already showed the limits of the approach, because everything was still scanline-based and re-drawing the whole frame every time — but with some optimization it actually held up.
The next step was opacity, shadows and blur. Those who know, know I'm a bit insane — but for everyone else: this isn't because I love Apple. Blur is about the most expensive thing you can render, so it gives you very honest optimization feedback.
To get the UI usable again I started by caching the area under the cursor and only updating the target lines instead of the whole screen. Didn't help much. So I moved to dirty rectangles and pre-caching the target state, and that was a big improvement — though I still had artifacts bleeding over the blur. I also did a lot of work with 9-slice and SDF to cut down rendering cost. After that I built a scrollable list with a filter input. (Side note: I still don't have IME — right now I've hardcoded five keys just to test the filter.) To my surprise, that worked too.
The point where it all fell apart was animation. That's where I started questioning a few life decisions. I rebuilt the pipeline for virtio-gpu, went to a 4-plane VMO setup (still didn't help much), and finally moved to virgl. Do I now have an amazing GPU driver you can game on? Not even close. But it renders shadows and blur, which already feels like a win.
A bit of architecture context. I'm never going to write something like the Linux graphics stack — 30 years of drivers for every card ever made, I think that beast is around two million loc. So I asked myself: what is a driver actually made of? You've got scheduling, an SDK-like layer (think Vulkan), and the actual hardware communication — and then that gets multiplied by per-device if/else branches (oversimplified), all baked into one driver. So what if I split it: a general "driver accelerator" for the reusable IRQ-type plumbing, a very minimal actual driver for device-specific talk, and a Metal-like userspace graphics SDK on top? Then in theory I keep only small, specialized drivers and can maintain the rest independently — and when a new device shows up, I can build another specialized driver in a few weeks instead of starting over (obviously I only have a minimal version of that idea working on qemu right now).
Right now animation runs smoothly, and I have a virtual list with more elements, though I'm still fighting to make scrolling truly smooth. It works — I just need to sit down and focus on that specifically.
Anyway, the I/O pipeline mostly works now. I've still got a few days of cleanup, refactoring, documentation and tests ahead of me, but I felt like sharing already.