Main
Main
Artwork
Artwork
Books
Books
Donate
Donate
Licenses
Licenses
Shorts
Shorts
Software
Software
Source
Source


"X Window System via libcaca (and why it'll be a little longer before I'm working on Insanely Witty Stupidity, again)"

2025-10-31

If you've been watching Insanely Witty Stupidity's Slackware pages, then you probably noticed some new packages appear yesterday (Palantir, SDL-1.2, Xbindkeys, and Xvfb-run). And, I introduced these exciting add-ons in the site's changelog (the entry marked "Thu Oct 30 11:06:06 CDT 2025" is what you're looking for). I've considered the prospect of running X11 without writing a root window (meaning-- using it with pure ASCII art in a terminal) for a very, very long time. And, I've finally succeeded at it :D The basic idea is that SDL-1.2 (pretty sure SDL 2 and 3 don't have the feature) can be built against the aalib and libcaca libraries (configure with its "--enable-video-aalib" and "--enable-video-caca" options). Then, it's simply a matter of running a VNC client built against SDL-1.2 and setting SDL_VIDEODRIVER to "aalib" or "caca".

Naturally, I noticed some keyboard quirks right away. I didn't consider how terminals work at first, but I finally figured out what the deal was (and a sensible solution). You see, terminals don't watch system file descriptors. And, staples like SSH rely on this basic standard. Rather, terminals are character devices-- and they receive typing character streams at a specific rate (referred to as "baud"). X Window System on the other hand watches system file descriptors for input and detects keyup and keydown events directly. So, the first problem SDL-1.2's developers faced was converting this input into something sensible (since SDL is for graphical interfaces). And apparently, they decided to use baud rate to dictate sending keyup and keydown events (if they didn't, I don't think my solution would work :o )

So, the way input from Palantir running with SDL_VIDEODRIVER="caca" (or "aalib") works is a bit different than X11 developers expect. For basic things like (say) browsing the web with Firefox or punching up documents with Abiword, a user won't notice any hiccups (a key press is a key press). But when I started trying to play Metroid and Mario 3 with NEStopia, well-- I discovered what I considered to be some weird behavior (at least at the time). So when a user controls a character in NEStopia (and similar emulators), he/ she typically holds at least one key while tapping others for controls. X Window System sends keydown events for held keys and has no problem sending keydowns for additional keys. In the end, it sends a keyup the *precise moment* a user releases any held keys.

Terminals don't work that way-- and (pretty sure I'm getting this right) SDL-1.2 developers compensated for this by sending keyup events for held keys the moment new keydown events occur. The behavior users can expect when leading Samus across Brinstar and instructing her to jump is that-- she'll stop dead and jump straight up in the air. Rather, you gotta position the character near the spot you want her to land, press your "jump" key, then use your movement keys to guide her into place (lol). And when it comes to using Mario's turbo mode (*you* know, button "B"), my CACA implementation will always fail you :( So, I patched Palantir's "vnc-display-sdl.cc" file and added a feature that can wait to purge held keys and another that skims some environment variables ($keyup_ignore, $keyup_purge, $keyup_purge_ignore).

The way it works is that-- a user can set "$keyup_ignore" and "$keyup_purge" with comma separated lists (say "a,period,G,return,z" for $keyup_ignore and "z,space,H,j" for $keyup_purge). And, users can also dictate whether purge keys will send keyup and keydown events at all by setting "$keyup_purge_ignore" with "true" or "false" (by default, $keyup_purge_ignore is "false"-- meaning purge keys send keyup and keydown events as well). For a complete list of key names, download the Palantir snapshot I built from and the patch I wrote, apply the patch to "vnc-display-sdl.cc" (patch vnc-display-sdl.cc vnc-display-sdl.cc.patch -o vnc-display-sdl-patched.cc), and read the resulting file (maybe use your text editor to search for "escape" or "return", both of which are key aliases in the switch I wrote).

With the patched Palantir clients I built (all five Slackware-15.0 pages have links: x86, x86 64-bit, arm soft-float, arm hard-float, and aarch64), a user can add their "B" button key alias to $keyup_ignore and set a purge key for all keyup ignored keys. And then, start Mario 3. And, start the first level of grassland (let's say). And when the level starts, pop the key being used for button "B". And when the user is needing to kick a Koopa shell (or would rather not pick it up at all), simply "fire" it by pressing the "purge" key. Then press the turbo key again (to hold it closed).

I feel like this leaves Mario 3 and Metroid working reasonably well (although, I'm still experimenting with leading Samus up Brinstar shafts by delaying left and right keys and purging after jumping to the next riser). Another tip: you *definitely* wanna use kbdrate to set the timeout below your terminal's baud rate ("kbdrate -d 100 -r 30" for NEStopia, "kbdrate -d 200 -r 30" to reset). Otherwise, initial keypresses will send a keyup event before your keyboard repeater kicks in (kinda fucks up your character's jump height, if you get what I mean):

 X888SS88X%88@tS;888888888t88X%:   .    
t88;888:; S;@8X@888;XXS ; S8888.; S ; S 
8X 8@%t8 : ; : X8X . ; : : . : . : . :  
X ; X ; X;8@@.; X ; XX8%X ; X ; X ; X ; 
   .   . :8888 .   %;8@@8  .   .   .    
; S ; S ;.8@@;S ; S%888:; S ; S ; S ; S 
 : . : . t8Xt8 . : S888% : . : . : . :  
X ; X ; X;8@8%; X ;S888.X ; X ; X ; X ; 
   .   . tS88X .   8888S   .   .   .    
; S ; S ; @;8 S@@ S:8;8.; S ; S ; S ; S 
 : . : . : . : %8; . : . : . : . : . :  
X ; X ; X%;8.8t%;8t8:8t8X ; X ; X ; X ; 
888S8@8S8S88888@888@@8888888@88%   .    
S8t88;SX888XXX8X888S8.888S8%888:; S ; S 

When it comes to mice, I found I wasn't able to activate X11's pointerkeys feature like I normally do. I figured this was probably because my fullscreen terminal is unable to send "shift + num-lock". But, even Xdotool couldn't activate pointerkeys?? I finally settled for building Xbindkeys and using a key combination to restart Xbindkeys with a separate file that contained xdotool mouse movements and presses (which would use the same key combination to switch back). However-- I found Xbindkeys wouldn't recognize key combinations, either (lol). I eventually decided to sacrifice my keyboard's "grave" key, as Xbindkeys refers to it (you know, the accent/ tilde key near the end of the number row?) *I* never use that key, anyway. And, it seemed to do the trick. I also added key aliases to send control key events to Firefox ("t" sends "ctrl + t" and "q" sends "ctrl + q", etc).

I finished writing the first short story for my sister site (which I am still not comfortable naming here on Insanely Witty Stupidity for fear of retaliation by potential advertisers). In the last Witty News article I linked, I suggested I would begin rewriting Hailey's Comet once I finished the first story. But instead, I decided to write a second story and publish the site. I'm curious if I can generate some revenue (and have no intention of continuing the project if I don't-- what a waste of my time that would be). So, that's what I've been doing for the past three weeks. The second story I'm writing will take another, oh-- two to three weeks to complete. And then, *then* I will continue working on Hailey's Comet. Until then, enjoy browsing the web with Javascript support and playing Mario 3 in a terminal :D

Update: I found that using Palantir via SDL-1.2's aa mode (SDL_VIDEODRIVER="aalib") resulted in some issues with user input. Specifically: SDL_KEYDOWN was set for all keys, but never SDL_KEYUP. The result? A keypress would repeat indefinitely until another was registered (and began repeating). Additionally, pressed keys could never send events again Dx It took me poking around a couple of days to work everything out. But, I finally added a feature to "impose" keyups (lol) the same way SDL does for CACA that got aalib working properly (set a Uint8 pointer from SDL_GetKeyState(), set values _false_ when imposing keyups). The improved patch adds several new environment variables: $aloof_event (default: "50"), $aloof_keyup (default: "200"), $keyup_impose (default: "false"), $spam_delay (default: "0"), $spam_interval (default: "0").

The "spam" values are for setting SDL's keyboard repeater (and can both be set "sdl" to impart SDL's default values or any other delay and interval values when desired). It shouldn't be necessary to enable SDL's repeater (I mean-- assuming you're already using a system with a keyboard repeater). But if you feel like enabling it (or trying different repeater rates), then have at it. The "aloof" values are for setting the timeout when polling events and waiting to send a keyup (read above-- terminals NEVER DO THAT). I also rebuilt all of Insanely Witty Stupidity's Palantir packages (armv5, armv7, aarch64, x86, and x86 64-bit) against the new patch and updated the patch archive. You're welcome, dick-head. :D

Update, part 2 (lol): I gave Palantir two additional environment variables-- $key_prism and $key_rainbow. *These* allow sending simultaneous key events with a single key. For example, let's say you're using SNES-9x to play Mortal Kombat II for Super Nintendo using SDL-1.2's caca or aalib. And, you find you're unable to send up and over (key "w" plus key "d" or key "w" plus key "a") at the same time or fire Sub-Zero's slide attack (requires sending "j" plus "k" plus "l"). You can set a list of keys you would like to send these simultaneous events (key_prism="q,e,h") and a list of key sets you would like those keys to send (key_rainbow="a,w:d,w:j,k,l").

The assigned keys will be unable to send their *own* key events this way (although, a user could include THOSE keynames in the $key_rainbow list: "a,q,w:d,e,w:h,j,k,l"). And, don't expect them to honor $keyup_ignore settings (I have no intention of implementing this, simply set all keys to be repeated until purged-- they will all-READY be repeated simultaneously). At this point, I consider my Palantir additions and patch to be feature complete. Additional work might entail something like-- adding an EvDev mode (I'm unable to get AdvanceMAME to work on a terminal, and I assume it's using EvDev for direct input access). But, this would leave Palantir on a server unusable from an ssh client (kinda defeats the purpose of my project). I may try building AdvanceMAME against SDL-1.2, instead (which should give me a way to use it). But for now, I plan on targeting DGen and SNES-9x with Palantir. ;)

Back

______________________________________________

Random Fact: Insanely Witty Stupidity has its own chatterbot (Candice). You can speak with her any time you feel like talking to someone (just-- be polite). There is even a PHP version of Candice for internet browsers that don't support JavaScript (although, JavaScript browsers will switch to the JavaScript version by default-- and vice versa).

html revised 2025-11-20 by Michael Atkins.

The maintainer of insanelywittystupidity.com does not care if people duplicate this page or any part of it-- as long as this notice remains intact.