blog · git · desktop · images · contact & privacy · gopher


Linux-VT via Softcursor erträglicher gestalten (Update)

KMS ist auf ATi-Karten seit Linux 2.6.33 in Arch wieder standardmäßig aktiv und, wie ich irgendwo auf der Mailingliste las, soll das jetzt auch erstmal so bleiben. Da das jetzt endlich auch auf meiner Mobility Radeon X1300 ganz gut funktioniert und dvtm neben GNU/Screen und GPM sehr schöne Möglichkeiten eröffnet, werden die VTs langsam richtig attraktiv. Am Laptop die native Auflösung fahren zu können, ist schon von Vorteil.

Nun gibt es aber etwas, das mich an den Linux-VTs extrem stört: Der Hardware-Cursor. Dieses kleine hysterisch blinkende Ding macht mich auf Dauer wirklich nervös. ;) Bei den BSDs hat man standardmäßig einen nicht-blinkenden Blockcursor. In meinen XTerms habe ich den auch, aber eben nicht an einem VT.

Unter Linux gibt es sowas auch, es ist aber nicht per Default aktiv. Außerdem muss man noch etwas Hirnschmalz investieren, bevor das so funktioniert, wie man es gerne hätte. Mir ist weiterhin aufgefallen, dass der Linux-Softcursor eigentlich nur an Framebuffer-Konsolen gut läuft -- das ist zum Glück gegeben, wenn man KMS nutzt.

Was den Softcursor aktiviert, ist eine Escape-Sequenz, ähnlich derer zum Wechseln der Farben. Einen nicht-blinkenden, weißen Blockcursor, der die Zeichen unter ihm in schwarzer Farbe darstellt, bekommt man hierdurch:

$ echo $'\e[?16;7;112c'

Wirkt ein wenig abstrus, aber wenn man erstmal diesen Artikel zum Softcursor gelesen hat, ist das gar nicht mehr so schwer.

Jetzt hat man aber im Wesentlich zwei Probleme zu bewältigen:

– Update: Der folgende Ausflug in die Terminfo-Welt ist bestimmt auch ganz interessant und für ältere Systeme auch notwendig, für das eigentliche Problem hat sich aber eine schönere Lösung ergeben. Siehe Ende des Postings.

Weg über Terminfo-Einträge

Das entscheidende Stichwort ist schon gefallen: Man muss/sollte einen neuen Terminfo-Eintrag anlegen. Alternativ könnte man das auch jeder einzelnen Anwendung beibringen -- sofern sie das unterstützt und man zu viel Zeit hat. Eine dritte Möglichkeit wäre, den existieren Terminfo-Eintrag ("linux") abzuändern. Das bringt aber viel Arbeit mit sich, denn man muss hierfür unter Arch das Paket "ncurses" patchen und in der Folge selbst warten. Ein komplett neuer Eintrag kann parallel existieren.

Was Terminfo übrigens macht, ist, die ganzen Escape-Sequenzen und sonstige Optionen aller Terminals zu verwalten. Das macht es abhängig von der "$TERM"-Umgebungsvariablen. Viele Anwendungen fragen diese Datenbank ab, um zu erfahren, wie sie am Terminal Text fett drucken können oder ähnliches.

Terminfo ist zum Glück hierarchisch aufgebaut oder zumindest kann man existierende Einträge erweitern, ohne den Eintrag nochmal komplett selbst zu schreiben. So sieht das aus:

linux-block-cursor|linux console with block cursor,
    use=linux,
    cvvis=\E[?16;7;112c,
    civis=\E[?16;0;0c,
    cnorm=\E[?16;7;112c,

Diese Datei macht folgende Dinge:

Der Vollständigkeit zuliebe: Das Arch-Paket "ncurses" stellt die normale Terminfo-Datenbank bereit. Dort im Quellcode (PKGBUILD über's ABS besorgen und "makepkg -o") befindet sich unter "misc/terminfo.src" der komplette Quelltext für die Datenbank. Daran kann man sich orientieren. Weiterhin steht in "man terminfo", welche Einträge es gibt und was sie bedeuten.

Die neu erstellte Datei kann man mit "tic" kompilieren. Tut man das als normaler User, wird unter "~/.terminfo" eine kleine Verzeichnisstruktur ähnlich der unter "/usr/share/terminfo" angelegt. Hier befindet sich der kompilierte Eintrag, der auch ohne weiteres Zutun genutzt wird ...

... sofern die "$TERM"-Umgebungsvariable entsprechend auf "linux-block-cursor" gesetzt wurde. "tput" kann man nutzen, um Einträge aus der Terminfo-DB abzufragen und auszugeben. An einem Linux-VT würde -- wenn man obige Datei kompiliert hat -- also das hier den Softcursor aktivieren:

$ export TERM=linux-block-cursor
$ tput cnorm

Ob die richtigen Zeichen ausgegeben werden, kann man verifizieren, indem man "tput" nach Vim schickt:

$ tput cnorm | vim -R -

Wenn das geklappt hat, dann kann man auch schon ncurses-Anwendungen und andere nutzen, ohne dass sie den Cursor zurücksetzen. Sei es nun mutt, Vim, newsbeuter oder nano -- der Softcursor bleibt, die erste Hürde ist genommen. :)

Um den Softcursor nun zum Standard zu machen, muss man etwas tricksen. Zuerst kann man den Standardwert für "$TERM" unter Arch in der "/etc/inittab" über den letzten Parameter für "agetty" setzen:

c1:2345:respawn:/sbin/agetty -8 38400 tty1 linux-block-cursor
c2:2345:respawn:/sbin/agetty -8 38400 tty2 linux-block-cursor
c3:2345:respawn:/sbin/agetty -8 38400 tty3 linux-block-cursor
c4:2345:respawn:/sbin/agetty -8 38400 tty4 linux-block-cursor
c5:2345:respawn:/sbin/agetty -8 38400 tty5 linux-block-cursor
c6:2345:respawn:/sbin/agetty -8 38400 tty6 linux-block-cursor

Würde man nun nach dem Login Vim starten, sähe man den Softcursor. Um ihn aber von Anfang an zu haben, könnte man die Escape-Sequenz zur Aktivierung noch einmal in "/etc/issue" schreiben:

^[[?16;7;112c
Arch Linux \r  (\n) (\l)

Das "^[" ist diesmal ein echtes Escape-Zeichen. In Vim kann man das über "Strg-V" und danach "Esc" eingeben.

Ganz so schön finde ich die Lösung über "/etc/issue" nicht, da ich die Sequenz nun zweimal verwalten muss: Einmal in Terminfo und einmal in dieser Datei. Wenn irgendwer dort draußen weiß, wie man das besser machen könnte -- immer her damit! :)

Das war's dann. Nach all diesen Änderungen hat man auch nach einem Reboot immer einen Softcursor an den VTs. Falls Interesse besteht, kann ich ein PKGBUILD ins AUR stellen, das systemweit einen Terminfo-Eintrag anlegt.

Achtung: "$TERM" wird auch übernommen, wenn man sich mit SSH auf einer anderen Maschine einloggt. Existiert in deren Terminfo-Datenbank aber der erweiterte Eintrag nicht, dann werden viele Anwendungen etwas böse -- von less bis Vim meckern alle, dass sie das Terminal nicht kennen. Das sollte man bedenken und entweder die andere Datenbank auch erweitern oder "$TERM" vorher zurücksetzen.

Kurze Zusammenfassung:

Weg über Kernel-Parameter

Linux wäre nicht Linux, wenn es nicht auch einfacher ginge. Man muss es nur wissen und ein bisschen Glück haben. ;)

In Documentation/kernel-parameters.txt sind alle Standard-Kernel-Parameter aufgelistet. Hier findet sich auch der Parameter "vt.cur_default", der -- wie sollte es anders sein -- die Standard-Form des Cursors setzt. Zitat:

vt.cur_default= [VT] Default cursor shape.
                Format: 0xCCBBAA, where AA, BB, and CC are the same as
                the parameters of the <Esc>[?A;B;Cc escape sequence;
                see VGA-softcursor.txt. Default: 2 = underline.

Also genau das, was ich will. Da ich gemerkt habe, dass der Cursor namens "^[[?17;7;112c" auch ein bisschen besser funktioniert, steht also nun in meiner "/boot/grub/menu.lst":

# (0) Arch Linux
title  Arch Linux - Softcursor
root   (hd0,4)
kernel /boot/vmlinuz26 root=/dev/disk/... ro vt.cur_default=0x700711
initrd /boot/kernel26.img

Das erspart einem auch die Probleme mit SSH und all das andere Gebastel.

Dass sich diese Lösung nicht auf Anhieb im Internet findet, liegt vermutlich daran, dass es erst seit dem 16. Dezember 2009 funktioniert. Wenn ich mich jetzt grade nicht täusche, dann heißt das auch, dass es das erst seit Linux-2.6.33-rc1 gibt. Und der 2.6.33er ist ja in Arch erst vor ein paar Tagen aus testing rausgerutscht. ;)

Kommentare

Pil fragt nach:

Sehr schön der Blog-Eintrag zum Einstellen eines gewünschten Kursors. Ich habe das (auch im englischsprachigen) Netz sonst nirgends gefunden.

Leider wird nicht erläutert, wie man von

\u201e^[[?17;7;112c\u201c

zu

0x700711

gelangt. Ausgehend von Documentation/kernel-parameters.txt würde ein Normalo wohl zunächst so vorgehen:

0x112112771717

Aber das ist natürlich falsch.

Um meinen blauen, nicht-blinkenden Block-Kursor zu berechnen, der so erreicht wird

echo -e '\033[?17;0;16c'

bin ich dann auf

http://clrcds.com/

gestoßen. Und sofort hatte ich dann meinen gewünschten Kursor systemweit.

Meine Antwort:

Hi,

der Weg vom Escape-Code zum Kernel-Parameter erfordert, dass du die Werte von dezimaler Basis (10) in hexadezimale Basis (16) umrechnest. Im Beispiel "^[[?17;7;112c" ist A = 17, B = 7, C = 112. Zur Umrechnung kannst du den Taschenrechner deiner Wahl verwenden, zum Beispiel "bc":

$ echo 'obase = 16; 17' | bc
11
$ echo 'obase = 16; 7' | bc
7
$ echo 'obase = 16; 112' | bc
70

Also haben wir jetzt A' = 0x11, B' = 0x7, C' = 0x70.

Dass diese Umrechnung notwendig ist, kann man aus dem "0x" in "Format: 0xCCBBAA" der Kernel-Doku schließen. "0x" ist ganz allgemein der Präfix, um hexadezimale Zahlen zu kennzeichnen. Deswegen habe ich auch "A' = 0x11" geschrieben anstatt "A' = 11", um klarzumachen, dass A' eine hexadezimale Zahl ist.

Etwas missverständlich ist, dass die Bezeichnier hier doppelt vorkommen. Du hast das so verstanden, dass man die Werte zweimal hinschreiben muss, gemeint ist aber ein "Padding" auf zwei Stellen. So wird aus B' = 0x7 dann eine "07".

Die finale Zahl hat außerdem nur noch einen gemeinsamen 0x-Präfix.

Jetzt musst du nur noch die Reihenfolge umdrehen, also C', B', A' (mitsamt Padding) aneinanderreihen und den gemeinsamen Präfix davorschreiben. So entsteht dann:

 0x  70  07  11  = 0x700711
└─┬┘└─┬┘└─┬┘└─┬┘
  │   │   │   │
  │   │   │   └ A' = 0x11, ehemals A = 17, jetzt in hexadezimaler Basis.
  │   │   │
  │   │   └ B' = 0x07, ehemals B = 7, jetzt in hexadezimaler Basis.
  │   │
  │   └ C' = 0x70, ehemals C = 112, jetzt in hexadezimaler Basis.
  │
  └ Gemeinsamer Präfix.

Hoffe, dass das etwas weiterhilft. :)

Die Verbindung zu http://clrcds.com/ verstehe ich jetzt wiederum nicht.