In my last post I talked about how I have been using Ansible for my new laptop configuration, and shared my configuration for notion.
So far, I've been extremely happy with using Ansible for configuring my machine. Prior to using Ansible, I'd spend a fair amount of time creating detailed notes that described what I did. I estimate that creating Ansible recipes takes about the same time as keeping good notes, and maybe even less. That's because there are many existing roles for common settings and software that can be reused. As with any ecosystem, the quality of such roles varies.
The big difference between my notes and Ansible, though, is that Ansible playbooks can be played in minutes, whilst manually following my notes can take hours to set up an entire new machine. I used to dread the idea of configuring a new machine. But now it's fairly effortless.
I just publicly shared my Ansible configuration. I don't expect that anyone will use my configuration as is, any more than I expect anyone to use my notion configuration! I'm extremely opinionated and picky. But I do hope that it might give people some ideas, like how to install llvm, nvidia drivers and so on. I know I personally found other people's repositories to be helpful.
In a very similar vein, I've started using
dorothy, which claims to allow you to "...
bring your dotfile commands and configuration to any shell." Since I usually
but not always use fish, I've always been hesistant to
write my own commands in fish. Plus, I have been writing bash scripts for long
enough that I'm decent at it, so it tends to be one of my go-tos. Dorothy makes
it easy to define variables and commands in such a way that they magically
appear in all shells. (Again, this is very useful for fish, which is not a
posix-compliant shell.) There's also a fair number of useful built-in commands.
Dorothy encourages users to split their dotfiles into public and private
portions, and you can view my public dotfile
here. Specifically, here are my
custom commands. Some
of these might be useful, such as setup-util-ghidra
and
setup-util-ghidrathon
. I've found that having a designated spot for these
types of utility commands encourages me to write them, which ultimately saves me
time. Usually.
Sometime while I was in graduate school, I started using the notion window manager. (Actually, at the time, I think it was ion3.) Notion is a tiling window manager that is geared towards keyboard usage instead of mouse usage. But let's be honest: I've been using notion for so long that I simply prefer it over anything else.
Notion, like most minor window managers, is a bit spartan. It does not provide
a desktop environment. It really just manages windows. There are some
features of a desktop environment that I don't need, such as a launcher. I know
all the commands that I use; I don't need a GUI to list them for me. But it's
often the little things that get you, such as locking the screen, or using the
media keys on your keyboard to adjust the volume. I used to be (more of) a hardcore nerd
and relished in my ability to craft a super-complex .xsessionrc
file with all
kinds of bells, whistles and utilities connected as if with duct tape. But as I
grow older, sometimes I just want my computer to work.
For a long while now, I've found that running notion alongside GNOME for "desktop stuff" to work pretty well. For a long time, I followed an old Wiki post about how to combine GNOME with Awesome WM. This worked really welll with GNOME 2.
Many people say that you can't use GNOME 3 with window managers other than GNOME
Shell. I've actually had pretty good luck copying the Ubuntu gnome-session and
replacing Gnome Shell with notion. The above Awesome WM Wiki also shows how to
do it. Unfortunately, I've found that some features do not work, such as the
keyboard media keys, much to my dismay. Do media keys matter that much? Yes,
yes, they do. This apparently broke when GNOME Shell started binding the media
keys instead of gnome-settings-daemon
. There
used to be a gnome-fallback-media-keys-helper
utility around that would
simulate this behavior, but it seems to have disappeared.
As I was trying to fix this problem, I came across a blog post and an unrelated but similar github repo both describing how to use the i3 window manager with GNOME. TLDR: GNOME Flashback is a still supported variant of GNOME that is explicitly designed to support third-party window managers. Whereas GNOME Shell incorporates both the window manager and other stuff such as handling media keys, GNOME Flashback has the "other stuff" in a separate component that is designed to be used with a window manager such as metacity. But it works just fine with notion! Best of all, both my media keys and screen locking work. Hurray!
Because I hate setting up stuff like this, I've actually been hard at work packaging up my Linux computer configuration into reusable ansible components. It takes a little longer than doing it manually of course, but it's not too bad and it's pretty easy to read. I'm making my notion role available here in case anyone wants to try out my setup. Most of the logic is here if you are curious what is involved. Below are a few snippets to show how Ansible makes it relatively easy to manipulate configuration files.
# Same thing, but for gnome-flashback
- name: Copy gnome-flashback-metacity.session to notion-gnome-flashback.session
copy:
src: /usr/share/gnome-session/sessions/gnome-flashback-metacity.session
dest: /usr/share/gnome-session/sessions/notion-gnome-flashback.session
- name: 'notion-gnome-flashback.session: Change metacity to notion and add stalonetray'
replace:
path: /usr/share/gnome-session/sessions/notion-gnome-flashback.session
regexp: 'metacity'
replace: notion;stalonetray
- name: 'notion-gnome-flashback.session: Remove gnome-panel'
replace:
path: /usr/share/gnome-session/sessions/notion-gnome-flashback.session
regexp: ';gnome-panel'
- name: Symlink systemd target for notion-gnome-flashback session to gnome-flashback-metacity session
file:
src: /usr/lib/systemd/user/gnome-session@gnome-flashback-metacity.target.d
dest: /usr/lib/systemd/user/gnome-session@notion-gnome-flashback.target.d
state: link
- name: Install gconf override for notion-gnome-flashback
copy:
src: notion-gnome-flashback.gschema.override
dest: /usr/share/glib-2.0/schemas/01_notion-gnome-flashback.gschema.override
notify: Compile glib schemas
- name: Set META
lineinfile:
path: /usr/local/etc/notion/cfg_notion.lua
regexp: '^--META='
line: META="Mod4+"
backup: true
- name: Set ALTMETA
lineinfile:
path: /usr/local/etc/notion/cfg_notion.lua
regexp: '^--ALTMETA='
line: ALTMETA="Mod1+"
backup: true
- name: Disable mod_dock
lineinfile:
path: /usr/local/etc/notion/cfg_defaults.lua
state: absent
line: 'dopath("mod_dock")'
backup: true
- name: Enable mod_statusbar
lineinfile:
path: /usr/local/etc/notion/cfg_notion.lua
regexp: '^--dopath("mod_statusbar")'
line: 'dopath("mod_statusbar")'
backup: true
Sometimes it is necessary to use a bleeding edge Linux kernel such as
drm-intel-next
to debug hardware issues. In this post, I'll discuss how to do
this on Ubuntu Jammy.
Ubuntu has a pretty cool automated mainline kernel build
system that also tracks branches like
drm-tip
and drm-intel-next
. Sadly, it's usually based off whatever Ubuntu
release is under development, and may not be compatible with the most recent LTS
release. This is currently the case with Ubuntu Jammy.
I want to run drm-intel-next
, which is available
here. But if you
attempt to install this kernel on Jammy, and have DKMS modules, you'll run into
an error because Jammy's glibc version is too old. The solution is to build the
kernel from scratch. But since there aren't any source debs, and there doesn't
really seem to be any documentation, I always forget how to do this.
So here's how to do it. The first step is that we'll checkout the kernel source from git. Right now, at the top of the drm-intel-next mainlage page it says:
To obtain the source from which they are built fetch the commit below:
git://git.launchpad.net/~ubuntu-kernel-test/ubuntu/+source/linux/+git/mainline-crack cod/mainline/cod/tip/drm-intel-next/2023-10-13
So we'll do that:
~/kernels $ git clone --depth 1 -b cod/mainline/cod/tip/drm-intel-next/2023-10-13 https://git.launchpad.net/~ubuntu-kernel-test/ubuntu/+source/linux/+git/mainline-crack drm-intel-next
Cloning into 'drm-intel-next'...
remote: Enumerating objects: 86818, done.
remote: Counting objects: 100% (86818/86818), done.
remote: Compressing objects: 100% (82631/82631), done.
remote: Total 86818 (delta 10525), reused 21994 (delta 3264)
Receiving objects: 100% (86818/86818), 234.75 MiB | 2.67 MiB/s, done.
Resolving deltas: 100% (10525/10525), done.
Note: switching to '458311d2d5e13220df5f8b10e444c7252ac338ce'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
Updating files: 100% (81938/81938), done.
Side note: The --depth 1
will speed the clone up, because we don't really care
about the entire git history.
Next, cd drm-intel-next
.
As a sanity check, verify that the debian
directory, which contains the Ubuntu
build scripts, is present.
To start, you need to run fakeroot debian/rules clean
which will generate a
few files including debian/changelog
.
Next, run fakeroot debian/rules binary
and wait a few seconds. I got an error:
~/k/drm-intel-next $ fakeroot debian/rules binary
[... snip ...]
make ARCH=x86 CROSS_COMPILE=x86_64-linux-gnu- HOSTCC=x86_64-linux-gnu-gcc-13 CC=x86_64-linux-gnu-gcc-13 KERNELVERSION=6.6.0-060600rc2drmintelnext20231013- CONFIG_DEBUG_SECTION_MISMATCH=y KBUILD_BUILD_VERSION="202310130203" LOCALVERSION= localver-extra= CFLAGS_MODULE="-DPKG_ABI=060600rc2drmintelnext20231013" PYTHON=python3 O=/home/ed/kernels/drm-intel-next/debian/tmp-headers INSTALL_HDR_PATH=/home/ed/kernels/drm-intel-next/debian/linux-libc-dev/usr -j16 headers_install
make[1]: Entering directory '/home/ed/kernels/drm-intel-next'
make[2]: Entering directory '/home/ed/kernels/drm-intel-next/debian/tmp-headers'
HOSTCC scripts/basic/fixdep
/bin/sh: 1: x86_64-linux-gnu-gcc-13: not found
make[4]: *** [/home/ed/kernels/drm-intel-next/scripts/Makefile.host:114: scripts/basic/fixdep] Error 127
make[3]: *** [/home/ed/kernels/drm-intel-next/Makefile:633: scripts_basic] Error 2
make[2]: *** [/home/ed/kernels/drm-intel-next/Makefile:234: __sub-make] Error 2
make[2]: Leaving directory '/home/ed/kernels/drm-intel-next/debian/tmp-headers'
make[1]: *** [Makefile:234: __sub-make] Error 2
make[1]: Leaving directory '/home/ed/kernels/drm-intel-next'
make: *** [debian/rules.d/2-binary-arch.mk:559: install-arch-headers] Error 2
Naturally, gcc-13
isn't available on Jammy. This is why I hate computers.
gcc-13
appears hard-coded into the build scripts:
~/k/drm-intel-next $ fgrep -R gcc-13 .
./init/Kconfig:# It's still broken in gcc-13, so no upper bound yet.
./debian/control: gcc-13, gcc-13-aarch64-linux-gnu [arm64] <cross>, gcc-13-arm-linux-gnueabihf [armhf] <cross>, gcc-13-powerpc64le-linux-gnu [ppc64el] <cross>, gcc-13-riscv64-linux-gnu [riscv64] <cross>, gcc-13-s390x-linux-gnu [s390x] <cross>, gcc-13-x86-64-linux-gnu [amd64] <cross>,
./debian/rules.d/0-common-vars.mk:export gcc?=gcc-13
./debian.master/config/annotations:CONFIG_CC_VERSION_TEXT policy<{'amd64': '"x86_64-linux-gnu-gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0"', 'arm64': '"aarch64-linux-gnu-gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0"', 'armhf': '"arm-linux-gnueabihf-gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0"', 'ppc64el': '"powerpc64le-linux-gnu-gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0"', 'riscv64': '"riscv64-linux-gnu-gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0"', 's390x': '"s390x-linux-gnu-gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0"'}>
Well, let's change them to gcc-12
and see what happens. Be prepared to wait a lot longer this time...
~/k/drm-intel-next $ sed -i -e 's/gcc-13/gcc-12/g' debian/{control,rules.d/0-common-vars.mk} debian.master/config/annotations
~/k/drm-intel-next $ fakeroot debian/rules binary
[... lots of kernel compilation output ...]
# Compress kernel modules
find debian/linux-image-unsigned-6.6.0-060600rc2drmintelnext20231013-generic -name '*.ko' -print0 | xargs -0 -n1 -P 16 zstd -19 --quiet --rm
stdout is a console, aborting
make: *** [debian/rules.d/2-binary-arch.mk:622: binary-generic] Error 123
Well, that's annoying. It seems that the general build logic calls zstd
to
compress any found files. But if none are found, then the rule fails. Perhaps
newer versions of zstd
don't fail when called on no arguments. Anyway, apply
the following patch to use xargs -r
to fix this:
--- a/debian/rules.d/2-binary-arch.mk
+++ b/debian/rules.d/2-binary-arch.mk
@@ -568,7 +568,7 @@ define dh_all
dh_installdocs -p$(1)
dh_compress -p$(1)
# Compress kernel modules
- find debian/$(1) -name '*.ko' -print0 | xargs -0 -n1 -P $(CONCURRENCY_LEVEL) zstd -19 --quiet --rm
+ find debian/$(1) -name '*.ko' -print0 | xargs -r -0 -n1 -P $(CONCURRENCY_LEVEL) zstd -19 --quiet --rm
dh_fixperms -p$(1) -X/boot/
dh_shlibdeps -p$(1) $(shlibdeps_opts)
dh_installdeb -p$(1)
Re-run fakeroot debian/rules binary
and if all goes well, you should end up
with a bunch of .deb
files in the parent directory, and something like:
~/k/drm-intel-next $ fakeroot debian/rules binary
[... lots of kernel compilation output ...]
dpkg-deb: building package 'linux-source-6.6.0' in '../linux-source-6.6.0_6.6.0-060600rc2drmintelnext20231013.202310130203_all.deb'.
dpkg-deb: building package 'linux-headers-6.6.0-060600rc2drmintelnext20231013' in '../linux-headers-6.6.0-060600rc2drmintelnext20231013_6.6.0-060600rc2drmintelnext20231013.202310130203_all.deb'.
dpkg-deb: building package 'linux-tools-common' in '../linux-tools-common_6.6.0-060600rc2drmintelnext20231013.202310130203_all.deb'.
dpkg-deb: building package 'linux-cloud-tools-common' in '../linux-cloud-tools-common_6.6.0-060600rc2drmintelnext20231013.202310130203_all.deb'.
dpkg-deb: building package 'linux-tools-host' in '../linux-tools-host_6.6.0-060600rc2drmintelnext20231013.202310130203_all.deb'.
dpkg-deb: building package 'linux-doc' in '../linux-doc_6.6.0-060600rc2drmintelnext20231013.202310130203_all.deb'.
Now install the desired packages with sudo dpkg -i <paths to .deb files>
.
Some days I hate computers. Today is one of those days. My work computer froze over the weekend (which is another long, frustrating story that I won't go into right now), so I had to reboot. As usual, I logged into our Pulse Secure VPN, and opened up Chrome. And Chrome can't resolve anything. I can't get to regular internet sites or intranet sites. What the heck?
My first thought is that this is somehow proxy related. But no, even when disabling the proxy, I still can't resolve internal hostnames.
But tools like dig
and ping
work. I open up Firefox, and that works too.
OK, that's weird. I open up chrome://net-internals/#dns
in Chrome and confirm
that it can't resolve anything. I try flushing the cache, but that doesn't
work. I try a few other things, like disabling DNS prefetching and safe
browsing, but none of those help either.
I take a look at /etc/resolv.conf
, which contains a VPN DNS server presumably
added by Pulse Secure, and 127.0.0.53
for the systemd-resolved
resolver. I
confirm that resolvectl
does not know about the Pulse Secure DNS server. I
add it manually with resolvectl dns tun0 <server>
, and Chrome starts working
again. OK, well that's good. But how do we fix it permanently?
This seems relevant: PulseSecure VPN does not work with
systemd-resolved. Oh, maybe
not. The "fix" is to publish documentation that the Pulse Secure developers
should read. Sigh. After reading more closely, I see something about the
resolvconf
command, which they do already support. I don't seem to have that
command, but that is easily fixed by a apt install resolvconf
, and I confirm
that after reconnecting to the VPN, systemd-resolved
knows of the VPN DNS
servers. And Chrome works. Yay!
So what happened that this suddenly became a problem? I'm not sure. One
possibility is that Chrome started ignoring /etc/resolv.conf
and directly
using systemd-resolved
if it appears to be available.
I really hate when my computer stops working, so I hope that if you are affected by this problem and find this blog post, it helps you out.
I'm a long time user of weechat. I don't really use IRC anymore, but I use it to connect to bitlbee for IMs, slack, and so on.
I really like weechat. But for a long time, I've had strange screen corruption problems, such as in the second to last line of:
That's a mild example. Sometimes weechat is all but unusable. Now, I can press Ctrl+L to redraw the screen and make the corruption go away. But it really bugged me one day, so I started investigating.
It took me a while, but eventually I realized that running weechat with a fresh configuration did not exhibit the problem. From there, it was straight-forward (but annoying) to isolate the problematic configuration option. It turned out to be an option called eat_newline_glitch.
According to the weechat user manual:
if set, the eat_newline_glitch will be set to 0; this is used to not add new line char at end of each line, and then not break text when you copy/paste text from WeeChat to another application (this option is disabled by default because it can cause serious display bugs)
If eat_newline_glitch is not turned on, then links to wrap from one line to another are not properly detected by gnome-terminal. It will detect the portion on the first line as a link only. In the modern days of long, automatically generated URLs, this is really annoying to say the least.
Anyway, at some point I must have turned eat_newline_glitch on and
forgot about it, and never associated it with the screen corruption.
I could have just turned the option off, but I obsess over things
like this like to know why things don't work. ๐
The first problem was being able to replicate the problem on demand. I eventually realized that the problem seemed to be related to lines that are exactly the width of the terminal. After some fighting with weechat scripting, I managed to write the following script which prints a command-line that reliably triggers the problem for me:
#!/usr/bin/env python
import subprocess
import time
prefixlen = len("13:52:08 | ")
collen = int(subprocess.check_output("tput cols", shell=True))
n = collen - prefixlen
s = "A" * n
commands = []
commands.append("/set weechat.look.eat_newline_glitch on")
commands.append("/bar hide buflist")
commands.append("/print %s" % s)
commands.append("/print %s" % s)
for _ in range(20):
commands.append("/exec -buffer new echo This text should not be visible in buffer 1")
commands.append("/buffer exec.new")
commands.append("/wait 1 /buffer 1")
print ("weechat -d /tmp -r '%s'" % "; ".join(commands))
The problem is pretty easy to spot. If you're in buffer 1 and you see "This text should not be visible in buffer 1", the bug was present! Here's an example:
Now that I could reliably replicate the problem, how to figure out what was going on? Weechat uses the ncurses library to write the UI to the screen. And interestingly, eat_newline_glitch corresponds to a capability name in terminfo, which is used by ncurses to draw the screen. The official description is not very helpful:
Newline ignored after 80 columns (Concept)
After perusing the ncurses source code and documentation, I found that it has a trace mode. Since ncurses maintains an internal representation of the terminal, it can print out what it thinks the screen should look like.
newscr[ 0] -1 -1 ='WeeChat 2.9-dev (C) 2003-2020 - https://weechat.org/ '
colors[ 0] ='222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222'
newscr[ 1] -1 -1 ='11:11:30 | ___ __ ______________ _____ '
colors[ 1] ='11611611177kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk '
newscr[ 2] -1 -1 ='11:11:30 | __ | / /___________ ____/__ /_______ __ /_ '
colors[ 2] ='11611611177kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk '
newscr[ 3] -1 -1 ='11:11:30 | __ | /| / /_ _ \ _ \ / __ __ \ __ `/ __/ '
colors[ 3] ='11611611177kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk '
newscr[ 4] -1 -1 ='11:11:30 | __ |/ |/ / / __/ __/ /___ _ / / / /_/ // /_ '
colors[ 4] ='11611611177kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk '
newscr[ 5] -1 -1 ='11:11:30 | ____/|__/ \___/\___/\____/ /_/ /_/\__,_/ \__/ '
colors[ 5] ='11611611177kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk '
newscr[ 6] -1 -1 ='11:11:30 | WeeChat 2.9-dev [compiled on Apr 1 2020 10:32:22] '
colors[ 6] ='1161161117799999999999999997888888888888888888888888888888887 '
newscr[ 7] -1 -1 ='11:11:30 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '
colors[ 7] ='11611611177111111111111111111111111111111111111111111111111111111111111111 '
newscr[ 8] -1 -1 ='11:11:30 | Plugins loaded: alias, buflist, charset, exec, fifo, fset, irc, logger, python, relay, script, trigger, xfer '
colors[ 8] ='11611611177111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 '
newscr[ 9] -1 -1 ='11:11:30 | WARNING: this option can cause serious display bugs, if you have such problems, you must turn off this option. '
colors[ 9] ='1161161117711111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 '
newscr[10] -1 -1 ='11:11:30 | Option changed: weechat.look.eat_newline_glitch = on (default: off) '
colors[10] ='1161161117711111111111111111111111111111111111111111111111777887771111111118887 '
newscr[11] -1 -1 ='11:11:30 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
colors[11] ='116116111771111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[12] -1 -1 ='11:11:30 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
colors[12] ='116116111771111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[13] -1 -1 =' '
newscr[14] -1 -1 =' '
colors[14] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[15] -1 -1 =' '
colors[15] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[16] -1 -1 =' '
colors[16] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[17] -1 -1 =' '
colors[17] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[18] -1 -1 =' '
colors[18] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[19] -1 -1 =' '
colors[19] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[20] -1 -1 =' '
colors[20] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[21] -1 -1 =' '
colors[21] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[22] -1 -1 =' '
colors[22] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[23] -1 -1 =' '
colors[23] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[24] -1 -1 =' '
colors[24] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[25] -1 -1 =' '
colors[25] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[26] -1 -1 =' '
colors[26] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[27] -1 -1 =' '
colors[27] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[28] -1 -1 =' '
colors[28] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[29] -1 -1 =' '
colors[29] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[30] -1 -1 =' '
colors[30] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[31] -1 -1 =' '
colors[31] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[32] -1 -1 =' '
colors[32] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
newscr[33] 5 5 ='[11:14] [2] [core] 1:weechat '
colors[33] ='322222323232322223243555555522222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222'
newscr[34] -1 -1 =' '
colors[34] ='111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
Ncurses' internal representation of the screen does not show the "This text should not be visible" messages, which implies that something ncurses is doing to update the screen is not having the desired effect.
After digging through the ncurses code some more, I found a very useful define called POSITION_DEBUG:
Enable checking to see if doupdate and friends are tracking the true cursor position correctly. NOTE: this is a debugging hack which will work ONLY on ANSI-compatible terminals!
Since eat_newline_glitch was related to the cursor position, this sounded promising! And indeed, with this option turned on, the trace log contained the following messages:
position seen (10, 188) doesn't match expected one (11, 0) in wrap_cursor
position seen (11, 188) doesn't match expected one (12, 0) in wrap_cursor
Not only does it tell us that something is wrong, but also the function it is in, wrap_cursor (with some debug code removed):
/*
* Wrap the cursor position, i.e., advance to the beginning of the next line.
*/
static void
wrap_cursor(NCURSES_SP_DCL0)
{
if (eat_newline_glitch) {
/*
* xenl can manifest two different ways. The vt100 way is that, when
* you'd expect the cursor to wrap, it stays hung at the right margin
* (on top of the character just emitted) and doesn't wrap until the
* *next* graphic char is emitted. The c100 way is to ignore LF
* received just after an am wrap.
*
* An aggressive way to handle this would be to emit CR/LF after the
* char and then assume the wrap is done, you're on the first position
* of the next line, and the terminal out of its weird state. Here
* it's safe to just tell the code that the cursor is in hyperspace and
* let the next mvcur() call straighten things out.
*/
SP_PARM->_curscol = -1;
SP_PARM->_cursrow = -1;
} else if (auto_right_margin) {
SP_PARM->_curscol = 0;
SP_PARM->_cursrow++;
/*
* We've actually moved - but may have to work around problems with
* video attributes not working.
*/
if (!move_standout_mode && AttrOf(SCREEN_ATTRS(SP_PARM))) {
VIDPUTS(SP_PARM, A_NORMAL, 0);
} else {
SP_PARM->_curscol--;
}
position_check(NCURSES_SP_ARGx
SP_PARM->_cursrow,
SP_PARM->_curscol,
"wrap_cursor");
}
The explanation of xenl (the capability name for eat_newline_glitch) is the best I've ever seen. Basically, it means that the cursor's location is unknown as soon as the right-most column is output. Ncurses handles this by marking the cursor location as unknown, which then forces a conservative mechanism to reset the cursor location the next time it is moved. OK, that makes sense, but then why are we getting screen corruption when we set eat_newline_glitch in weechat?
Because setting weechat's eat_newline_glitch to x
sets ncurses'
eat_newline_glitch to !x
. I'm embarrassed to admit this took me
quite a long time to notice.
Ok, so to refresh, I set eat_newline_glitch to true
in weechat,
which sets it to false
in ncurses. So when we execute wrap_cursor,
we take the branch for else if (auto_right_margin)
, which assumes
that the terminal moves the cursor to the left-most column of the next
row. Since this is not what gnome-terminal does, screen corruption
results!
So where is the bug? Weechat surely has a bug, in that screen corruption can occur when you set eat_newline_glitch. But they do warn you about this in a very vague way. In my opinion, ncurses does not have a bug. My terminal has weird behavior when reaching the right-most column, and thus it is no surprise that when we effectively tell ncurses the glitch does not exist, it results in screen corruption.
That being said, I think there is some room for improvement. The problem with eat_newline_glitch is that the behavior is ambiguous by definition; it describes two types of behaviors. If ncurses knew the actual behavior of the terminal, it wouldn't need to manually move the cursor or insert a line break, which wouldn't interfere with the terminal's ability to detect a wrapped URL. So one solution would be to create two capabilities based on the two different behaviors that comprise eat_newline_glitch.
Another option would be to query for the cursor's location the first time wrap_cursor is invoked, and use the result to predict the cursor location in the future.
Anyway, that is my long trip down the rabbit hole of multi-line URLs. Unlike most trips of this nature, this one did not have a very satisfying ending. Hopefully I can save at least one other person from going down the same rabbit hole.
Powered with by Gatsby 5.0