movwin: My (unpublished) TUI framework
blog - git - desktop - contact
movwin: My (unpublished) TUI framework
2026-05-29
Making programs with some sort of GUI (or TUI) has been a bit<br>unsatisfactory for me for a very long time now. Libraries come and go,<br>trends come and go. You constantly have to chase upstream's new<br>decisions and adjust your code. Sometimes you don't agree with<br>upstream's decisions at all and then you have to find a new framework.<br>It's a bit tiring. It's not rare that I keep my projects alive for 5 or<br>10 years or more, and a lot can change in that time.
So, after last Advent of Code in late December 2025, I decided to start<br>making my own TUI framework. This wasn't an easy decision, because I<br>knew that it was going to be a lot of work. I looked for alternatives<br>but couldn't find any that I liked -- or that were fast enough.<br>Performance really appears to have tanked lately, with some frameworks<br>requiring two seconds just to initialize.
This blog post is nothing but a little tour of the current state of<br>movwin, because I've decided that I won't publish this code for now.<br>The situation isn't too great at the moment: Everything I publish will<br>get sucked up by an "AI" company and then they will sell it,<br>disregarding any licensed attached to the code. I'm not okay with that.
The basics
It's a Python library. I'm not the biggest fan of Python anymore, but it<br>does have its advantages, mainly a big standard library that lets me<br>easily do a lot of things.
movwin -- which stands for "movq's windows and widgets" -- sits on top of<br>ncurses. ncurses does the heavy lifting when it comes to terminal<br>compability. What movwin does not make use of is ncurses' subwindows or<br>pads. Instead, ncurses acts as some kind of (intelligent) framebuffer<br>that I can draw to, and it acts as a source of keyboard and mouse input.
One major goal is "acceptable" Unicode support. It's highly unlikely<br>that movwin will ever support things like right-to-left, but I do not<br>want to put an emoji somewhere and see the entire layout explode. In<br>other words, movwin must know how many cells in the terminal a Unicode<br>sequence will (probably) occupy. The big problem here is that this<br>depends on the terminal, so it can't ever be perfect.
There is only one dependency (besides Python 3.14 itself):<br>wcwidth and its wcswidth()<br>function. This is used to measure the "apparent size" of text: "♀️" is<br>two cells wide, for example.
From very early on, movwin had the notion of a "Window" and a<br>"Window Manager". What I had in mind while making the whole thing was<br>old DOS TUIs:
It would have been lovely to truly recreate this experience, and to some<br>degree I have, but mouse support in UNIX terminals is not that great. In<br>my tests, no terminal reported mouse motion events by default (only<br>"mouse down / up") and I had to tweak terminfo files. Worse, in large<br>terminal windows, some mouse events don't register at all. As such,<br>mouse support in movwin is quite limited at the moment. Maybe I'll even<br>remove it entirely, because, well, being keyboard-driven is actually a<br>good thing after all. For some things like moving windows or controlling<br>scroll areas having mouse support is very nice, though.
Another major goal is "acceptable" performance, meaning: "Around 200-300<br>ms of startup time on my tiny little 10-year-old Intel NUC with a<br>Celeron CPU is okay, but it shouldn't be more than that." Startup time<br>really is an issue with Python, because import is super slow. I had to<br>make some sacrifices here, like not using dataclasses. This is on the<br>NUC where the import times alone are a killer:
$ time python -c 'exit(0)'<br>real 0m0.061s<br>user 0m0.048s<br>sys 0m0.011s
$ time python -c 'from dataclasses import dataclass'<br>real 0m0.151s<br>user 0m0.115s<br>sys 0m0.027s
Applications
What started this whole thing was a little program called tracktivity:<br>I use it to track activities and events, like caffeine consumption or<br>weather events. It operates on CSV files:
rfc3339datetime,Food[coffee;blacktea;greentea],Comment<br>2026-05-29T14:04:00+00:00,coffee,at home<br>2026-05-29T14:04:43+00:00,blacktea,just some dummy entry :-)<br>2026-05-29T14:04:51+00:00,blacktea,more tea
The Food column is configured to have several options to choose from,<br>the Comment column is free-form text. tracktivity builds a UI form<br>based on that file, it looks like this:
It's nothing fancy and some widget types are still missing, for example<br>there's no proper table or list widget yet.
tracktivity is a simple single-window fullscreen program, but it still<br>uses the Window class (like all movwin programs should), so if I<br>wanted to I could resize and move this window around (click for a<br>video):
"Popups" are also implemented as new windows. movwin comes with a couple<br>of stock popups, like a "yes/no box", an "input box", or a "message<br>box".
bine is another program that uses movwin: It's a basic hex editor.<br>What I really wanted to have is a simple hex editor with good<br>performance but also that little info...