blog - git - desktop - contact
movwin: My (unpublished) TUI framework
Making programs with some sort of GUI (or TUI) has been a bit unsatisfactory for me for a very long time now. Libraries come and go, trends come and go. You constantly have to chase upstream's new decisions and adjust your code. Sometimes you don't agree with upstream's decisions at all and then you have to find a new framework. It's a bit tiring. It's not rare that I keep my projects alive for 5 or 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 making my own TUI framework. This wasn't an easy decision, because I knew that it was going to be a lot of work. I looked for alternatives but couldn't find any that I liked -- or that were fast enough. Performance really appears to have tanked lately, with some frameworks requiring two seconds just to initialize.
This blog post is nothing but a little tour of the current state of movwin, because I've decided that I won't publish this code for now. The situation isn't too great at the moment: Everything I publish will get sucked up by an "AI" company and then they will sell it, 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 does have its advantages, mainly a big standard library that lets me easily do a lot of things.
movwin -- which stands for "movq's windows and widgets" -- sits on top of ncurses. ncurses does the heavy lifting when it comes to terminal compability. What movwin does not make use of is ncurses' subwindows or pads. Instead, ncurses acts as some kind of (intelligent) framebuffer 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 that movwin will ever support things like right-to-left, but I do not want to put an emoji somewhere and see the entire layout explode. In other words, movwin must know how many cells in the terminal a Unicode sequence will (probably) occupy. The big problem here is that this depends on the terminal, so it can't ever be perfect.
There is only one dependency (besides Python 3.14 itself): wcwidth and its wcswidth() function. This is used to measure the "apparent size" of text: "♀️" is two cells wide, for example.
... continue reading