💾 Archived View for log.aviary.biz › 2022-01-06.gmi captured on 2023-09-08 at 15:52:20. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2022-01-08)
-=-=-=-=-=-=-
one of my favorite parts of visiting grandma's house was her N64. somehow my cousins and i all absorbed this common wisdom that, if something went wrong with the game, we could fix it with this procedure:
invariably, this worked.
and inevitably, with the passage of years, i now believe this procedure would have worked just as frequently:
i use a terminal-based text editor called kakoune (it's manual page is kak(1), so that's how i'll refer to it).
i use kak on my personal computer (kak + kitty terminal + OpenBSD on a Thinkpad) and on my job computer (kak + iTerm2 terminal + macOS on a MacBook). kak has a sweet feature, "rotate contents of selections," that you activate with Alt+Shift+( and Alt+Shift+). the feature itself doesn't really matter; what matters is that didn't work on my personal computer.
i have ignored this problem for months, and i finally decided to try to figure it out.
my initial problem statement was, "rotating selections doesn't work." at the outset, the problem could be anywhere between my physical keyboard and the kak process. i asked, "is kak receiving my keys?" i could answer that question with `kak -debug keys`, where kak prints all key events to the *debug* buffer.
Client 'client0' got key '<a-s-9>'
the docs, however, list a slightly different key combination.
<a-(> rotate selections content backward
i read some source code, and i saw that these two representations really did correspond to different keypresses. if i had my job computer around, i would have checked the debug output there, too. even without that confirmation, though, i had enough evidence to continue the journey.
my problem statement changed to, "kak gets Alt+Shift+9 when i want it to get Alt+(". i then asked, "is my terminal sending the right keys?" i didn't know yet know what "right" meant, so i decided to read the kak key handling code.
% pkg_info kakoune | head -1 Information for inst:kakoune-2021.08.28 % cd ~/src/kakoune % git remote get-url origin https://github.com/mawww/kakoune.git % git checkout v2021.08.28
kak is written in C++. i know a little C, but C++ is a whole different world. all i knew was to start at the function called "main" and go from there. i figured that, just like every interactive program, there had to be a loop somewhere that listened for input. that led me to src/terminal_ui.cc, where TerminalUI::get_next_key() is defined. until today i had willfully ignored the implementation details of terminal emulators and the travails of programs that run in them. to put it mildly, i now have a better understanding of the problems at this interface boundary.
i don't know enough and frankly shit is so fucked that it's not relevant, but here's the gist: i learned there is one channel of communcation between a terminal and its controlling program. this is the same communication model that has existed since the invention of electromechanical terminals like teletype. since then, we've stuffed in a whole bunch of features that enable backspace and bold and colors and modifier keys and mice and dynamic window manager titles and—you get the idea. all these features are implemented as escape sequences.
at this point i started fidgeting and nibbling on my fingernails. i had taken a little peek at the abyss, and i wanted to bail. instead, i decided to edit some code!
i wanted to know which branch of execution my "<a-s-9>" keypresses entered. i remembered a handy "write_to_debug_buffer()" function from my earlier perusal, and i tried to use it in TerminalUI::get_next_key(). i made the edit and tried to compile.
% make 2>&1 | head -2 make -C src all
it looks like kak needs some massaging before building on OpenBSD. i knew enough about the OpenBSD package manager that i already had the ports tree checked out on my computer. i have the git mirror, and i usually just pull the latest.
% cd /usr/ports/ % git remote get-url origin https://github.com/openbsd/ports.git % git pull
the kak port is in editors/kakoune.
% cd editors/kakoune % tree . |-- Makefile |-- distinfo |-- patches | `-- patch-src_Makefile `-- pkg |-- DESCR `-- PLIST 2 directories, 5 files
i know that you can build a port by running the Makefile.
% make ===> kakoune-2021.11.08 depends on: gmake-* -> gmake-4.3 ===> kakoune-2021.11.08 depends on: bzip2-* -> bzip2-1.0.8p0 ===> Verifying specs: c++ c++abi pthread c m ===> found c++.8.0 c++abi.5.0 pthread.26.1 c.96.1 m.10.1 ===> Checking files for kakoune-2021.11.08 `/usr/ports/distfiles/kakoune-2021.11.08.tar.bz2' is up to date. >> (SHA256) kakoune-2021.11.08.tar.bz2: OK ===> Extracting for kakoune-2021.11.08 ===> Patching for kakoune-2021.11.08 ===> Applying OpenBSD patch patch-src_Makefile Hmm... Looks like a unified diff to me... The text leading up to this was: -------------------------- |$OpenBSD: patch-src_Makefile,v 1.1.1.1 2020/06/14 13:07:04 solene Exp $ | |Remove optimization flags for debug or release builds | |Index: src/Makefile |--- src/Makefile.orig |+++ src/Makefile -------------------------- Patching file src/Makefile using Plan A... Hunk #1 succeeded at 12. done ===> Compiler link: clang -> /usr/bin/clang ===> Compiler link: clang++ -> /usr/bin/clang++ ===> Compiler link: cc -> /usr/bin/cc ===> Compiler link: c++ -> /usr/bin/c++ ===> Generating configure for kakoune-2021.11.08 ===> Configuring for kakoune-2021.11.08 ===> Building for kakoune-2021.11.08 gmake -C src Makefile gmake[1]: Entering directory '/usr/ports/pobj/kakoune-2021.11.08/kakoune-2021.11.08/src' # ...snip... the compilation continues here
from this output, you can see that the ports system downloads the source code to some directory, patches it, and then compiles it with the right toolchain. we can edit the files in /usr/ports/pobj/kakoune-2021.11.08/kakoune-2021.11.08 and then run "make build" in /usr/ports/editors/kakoune to build our custom version of the software.
after making several silly C++ coding errors, i finally got the code to compile with some of my debugging statements. i launched the program, tried the key combination, and...
...the feature worked. wat.
i could rotate selections, and i saw that kak interpreted my keypresses as <a-(>.
i put my computer aside, stood up, looked out the window. it's sunny, cold, and a little breezy. i put on some music.
and then i realized i was building a different version of kak than the one i had installed with `pkg_add kakoune`. i'm on OpenBSD 7.0 (the "-stable" release), but the latest code in ports (for the "-current" release) also had the latest version of kak. i looked up the diff between the two versions.
% git diff --stat v2021.08.28..v2021.11.08 | tail -1 146 files changed, 1394 insertions(+), 815 deletions(-) % git diff --stat v2021.08.28..v2021.11.08 -- src/terminal_ui.cc src/terminal_ui.cc | 228 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 139 insertions(+), 89 deletions(-) % git log --oneline v2021.08.28..v2021.11.08 -- src/terminal_ui.cc a70dbda5 use shifted key codes on kitty 0f688656 Fix DECRPM parsing for 2026 874f3e07 Fix terminal underline color not being properly reset 60165bac Use DECRQM/DECRPM to detect support for synchronized output 3acf85f2 Restore diff based terminal output optimization when synchronized b3a1017a Remove scrolling detection/optimization in terminal output 3fc8e29d Add support for curly underline and separate underline color 5c6c58ae Remove unnecessary c_str() calls b841f3a2 Remove terminal_wheel_up/down_button UI option
huh, that most recent commit seems mighty interesting! it's got "shift" and "kitty" in its subject. hey, i use kitty! hey, i have a problem with a shifted key! this commit doesn't have any more explanation than the subject line, so i must read the code to understand what changed. it looks like in the "CSI" escape sequence, kitty might send more than one character when the shift key is pressed. after reading some kitty docs, it seems this behavior only happens when you enable kitty's "progressive enhancement" of the terminal communication protocol. indeed, this kak commit does enable the extended protocol.
i spent several hours poking at this problem, and at the end of it i felt a little deflated. someone else swooped in and fixed my bug! i'm grateful, of course, for the work some stranger did to make my life a little better, but i wanted to see a problem and fix it. upon reflection, i did do just that! i followed a fairly rigorous debugging procedure, i narrowed down the fault, and i (in the wrong order) gained a good-enough understanding of the problem to fix it.
i hardly ever follow this process rigorously, and every time i manage to be disciplined i end up telling myself, "why don't i do this every time!? this is magic!"
computers are electromechanical machines, after all, and they almost always follow the rules we give them.