ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/docs/pws2006/rxvtperl.pod
Revision: 1.5
Committed: Thu Feb 1 16:24:36 2007 UTC (19 years, 8 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.4: +2 -2 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 Rxvt-Unicode: jetzt mit Mega-Perl!
2
3 =head2 Zusammenfassung
4
5 Seit Anfang des Jahres besitzt der X11 Terminal-Emulator "Rxvt-Unicode"
6 einen eingebetteten Perl-Interpreter.
7
8 Dieser wird jetzt schon benutzt, um z.B. die Selection intelligenter zu
9 gestalten, um mehrere Terminals durch Reiter zu kombinieren, Popup-Menüs
10 zu implementieren, Text zu formatieren, den Scrollback-Puffer durchsuchbar
11 zu machen, OnTheSpot-Editing für Eingabemethoden (XIM, X Input Methods) zu
12 implementieren und vieles, vieles mehr.
13
14 Dies erfüllt meinen lang gehegten Traum, meine Hauptschnittstelle
15 zum Rechner noch besser anpassen zu können.
16
17 =head1 Einführung
18
19 Rxvt-Unicode ist Ende 2003 aus dem Frust heraus entstanden, daß es keine
20 guten und Unicode-fähigen Terminals gab. Mlterm war der beste Kandidat
21 (sehr schlank, sehr gute Rendering-Fähigkeiten, gutes internes Design),
22 aber mein Problem der gleichzeitigen Darstellung von Umlauten und Kanji
23 hat es leider nicht gelöst: Dafür braucht man zwei Schriftarten, und
24 mlterm konnte dies nur für die Kombination ASCII/sonstiges.
25
26 Aber philosophische Gedanken beiseite: Als erstes wurde rxvt in C++
27 "übersetzt" und die richtige Interpretation von Locales beigebracht: Das
28 "Unicode" bezieht sich auf die interne Speicherung der Zeichen, nicht
29 auf die Festlegung von z.B. UTF-8 als Ein-/Ausgabeencoding, da hält
30 sich Rxvt-Unicode an die vom Benutzer eingestellte Locale (wie auch bei
31 Dingen wie die Breite der Zeichen, aber das geht schon wieder in Richtung
32 Philosophie).
33
34 Lange Jahre hindurch wünschte ich mir aber mehr
35 Eingriffsmöglichkeiten: Ich dachte immer, wie schön es wäre, wenn man
36 z.B. durch einen externen Prozess intelligentere Steuerungen oder
37 Feedback-Möglichkeiten hätte. Erst Anfang des Jahres kam mir die Idee,
38 nicht eine aufwendige sprachenunabhängige Schnittstelle zu implementieren,
39 sondern einfach Perl einzubetten, womit ich mehr Erfahrung hatte und was
40 auch eine einfachere Implementierung zuließ.
41
42 Ein wichtiger Punkt ist der erhöhte Speicherbedarf, da Rxvt-Unicode
43 den Ruf hat, sehr ressourcenschonend zu sein: Statt 3MB
44 für ein full-featured Terminal sind es nun eher 7MB, dank
45 Perl-Speicherverbrauchstechnologie. Aber da I<ein> Rxvt-Unicode Prozess
46 beliebig viele Fenster öffnen kann (wobei die Kosten von Perl auf diese
47 verteilt werden), ein Xft-Font mit 10MB+ und gängige Terminals wie KDEs
48 Konsole durchaus mal mit 60MB+ zuschlagen können, dachte ich "no big
49 deal", und es war eine gute Entscheidung :).
50
51 =head1 Das "Wie" der Perl-Erweiterungen
52
53 Rxvt-Unicode kommt von Hause aus mit einer Menge Perl-Erweiterugen. Hat
54 man das Paket heruntergeladen, kann man sich diese in F<src/perl>
55 anschauen (sie sind zur Zeit alle in der urxvtperl(7)-Manpage
56 beschrieben). Ist das Paket schon installiert, muss man im
57 F<../lib/urxvt/perl>-Verzeichnis nachschauen.
58
59 Eigene Erweiterungen kann man ganz einfach erstellen und benutzen:
60
61 mkdir extensions
62 vi extentions/myext
63 urxvt --perl-lib $PWD/extensions -pe myext
64
65 Die Perl-Erweiterung (Methode etc.) ist I<komplett> in der
66 urxvtperl(7)-Manpage dokumentiert.
67
68 =head1 Beispiele, Beispiele... Beispiele!
69
70 =head2 Pseudo-Transparenz (C<-pe automove-background>)
71
72 Viele Leute wünschen sich pseudotransparente Terminals. Ich nicht. Daher
73 hatte ich keinen Bock, eine häufig gewünschte Erweiterung zu
74 implementieren (schon garnicht in C++): da es stets Probleme macht, das
75 aktuelle Hintergrundbild zu identifizieren, könnte man da nicht ein
76 normales Terminal-Hintergrundbild angeben, dieses aber je nach Position
77 des Fensters auf dme Schirm verschieben? Das hätte sogar den Vorteil,
78 viel schneller zu sein, da man Effekte wie Schatten u.ä. vorher in seinem
79 Malprogramm erreichen kann.
80
81 Der erste Versuch in Perl war schnell geschreiben:
82
83 sub on_configure_notify {
84 my ($self, $event) = @_;
85
86 $self->cmd_parse ("\033]20;;=+$event->{x}+$event->{y}\007");
87 }
88
89 Ja, das ist die komplette Erweiterung: Jede Erweiterung ist (intern)
90 eine Perl-Klasse, und für jedes Terminal-Fenster wird ein neues Objekt
91 für jede daran gebundene Erweiterung erzeugt, das C<$self> ist also ein
92 echtes Erweiterungs-"Objekt", daß von der C<urxvt::term>-Klasse (indirekt)
93 abgeleitet ist.
94
95 Rxvt-Unicode selbst ruft nur Methoden mit Namen auf, die mit C<on_>
96 beginnen, und das immer bei bestimmten Ereignissen. Im obigen Beispiel,
97 wenn ein B<ConfigureNotify>-Event eintrifft, der bei Größenänderungen und
98 Verschiebungen des Fensters aufgerufen wird.
99
100 Danach wird eine Escape-Sequenz generiert, die das Hintergrundbild
101 verschiebt (alle Escape-Sequenzen sind in der urxvt(7)-Manpage
102 dokumentiert) und dem Terminal per C<cmd_parse> eingegeben, wodurch sie
103 so interpretiert werden, als ob das im Terminal laufende Programm sie
104 ausgegeben hätte.
105
106 Das klappte schon so halb, leider stimmen die Koordinaten im
107 ConfigureNotify-event nicht immer, daher muss man explizit den server
108 fragen, wo das Fenster denn nun gerade ist:
109
110 sub on_configure_notify {
111 my ($self, $event) = @_;
112
113 my ($x, $y) = $self->XTranslateCoordinates (
114 $self->vt, $self->DefaultRootWindow,
115 0, 0
116 );
117
118 $self->cmd_parse ("\033]20;;=+$x+$y\007");
119 }
120
121 Das klappt in allen Fällen.
122
123 =head2 Eine Digitaluhr im Terminal (C<-pe digital-clock>)
124
125 Klar, braucht jeder. Umso erstaunlicher ist es, daß dies die erste
126 Erweiterung war, die existierte, weil sie recht viel Funktionalität
127 testet. Tatsächlich soll es Leute geben, die sie benutzen.
128
129 Hier ist sie:
130
131 sub on_start {
132 my ($self) = @_;
133
134 $self->{overlay} = $self->overlay (-1, 0, 8, 1, urxvt::OVERLAY_RSTYLE, 0);
135 $self->{timer} = urxvt::timer
136 ->new
137 ->start (1 + int urxvt::NOW) # make sure we update "on" the second
138 ->interval (1)
139 ->cb (sub {
140 $self->{overlay}->set (0, 0,
141 sprintf "%2d:%02d:%02d", (localtime urxvt::NOW)[2,1,0]);
142 });
143
144 ()
145 }
146
147 Die C<on_start>-Methode wird aufgerufen, nachdem das Terminal komplett
148 initialisiert wurde und daher komplett funktionsfähig ist.
149
150 Die obige C<on_start>-Methode erzeugt zuerst ein I<Overlay>. Das ist ein
151 rechteckiger Kasten, der "über" dem normalen Terminal-Inhalt liegt und
152 für Menüs und andere Dinge genutzt werden kann. Die Position ist C<-1, 0>
153 (rechts, oben), die Größe ist C<8, 1> (8 Spalten, eine Zeile), und die
154 initiale Farbgebung ist C<urxvt::OVERLAY_RSTYLE>, schwarz auf grellgelb.
155
156 Danach wird ein Timer erzeugt, der einmal pro Sekunde am Sekundenanfang
157 feuert. Der Callback ersetzt den Text im Overlay durch eine einfache
158 Digitalanzeige.
159
160 Wichtig ist, daß Overlays, Timer usw. direkt in C<%$self> gespeichert
161 werden. Der Grund ist nicht sofort ersichtlich: Solange noch irgendwelche
162 Event-Watcher (Zeitgeber, Datei-Watcher, Prozess-Watcher usf.) aktiv sind,
163 endet der Prozess nicht.
164
165 Merkt man sich den Timer in einer C<my>-Variable, wird er beim Löschen des
166 Fensters vielleicht nicht freigegeben und läuft weiter, und er Benutzer
167 wundert sich, warum Rxvt-Unicode weiterläuft, obwohl er das Fenster geschlossen
168 hat.
169
170 C<%$self> wird dagegen automatisch geleert, was Timer und andere Objekte
171 automatisch zerstört.
172
173 Im Prinzip kann man Rxvt-Unicode als Perl-Daemon betrachten, der I<auch>
174 Terminal-Fenster aufmachen kann. Ach ja, er unterstützt natürlich
175 auch C<AnyEvent>, allerdings nur non-blocking (wäre doof, wenn das
176 Terminal Minuten oder auch nur Sekunden "steht"). Näheres im Beitrag
177 "Ereignisgesteuerte Programmierung".
178
179 =head2 URLs markieren und anklickbar gestalten (C<-pe mark-urls>)
180
181 Noch ein Feature, daß nur existiert weil es so simpel zu implementieren
182 war, ist die C<mark-urls> Erweiterung: Sie unterstreicht alle URLs (naja,
183 zumindest viele) im Terminal-Fenster. Wenn man draufklickt, startet sie
184 einen Browser.
185
186 Zuerst zu dem Teil, der die Sache mit der Unterstreichung regelt:
187
188 my $url =
189 qr{(
190 (?:https?|ftp|news|mailto|file)://[ab-zA-Z0-9\-\@;\/?:&=%\$_.+!*\x27(),~]+
191 [ab-zA-Z0-9\-\@;\/?:&=%\$_+!*\x27()~] # exclude some trailing characters (heuristic)
192 )}x;
193
194 Diese horrende Regex versucht, "übliche" URLs zu matchen. Muss man nicht
195 lesen können, klappt aber gut.
196
197 sub on_line_update {
198 my ($self, $row) = @_;
199
200 Die C<on_line_update>-Methode wird (meistens) aufgerufen, wenn eine Zeile
201 dargestellt wird, die bisher noch nicht durch Perl gefiltert wurde. Das
202 bedeutet schlicht, daß man jeden angezeigten Text filtern kann. Wird
203 eine Zeile verändert, wird die Methode nochmals aufgerufen. Man muss
204 nur aufpassen, daß die Zeile eventuell schon Änderungen von vorherigen
205 Aufrufen enthält, so daß man zwar Unterstreichung gefahrlos anschalten,
206 aber nicht Togglen darf.
207
208 Das "langsame" Perl behindert übrigens nicht das Scrolling: Obwohl Perl so
209 "langsam" ist, hat es erstaunlich wenig Einfluß auf die Geschwindigkeit,
210 obwohl es pro Bildschirmrefresh 30 mal oder mehr aufgerufen wird (je
211 nach Zeilenzahl). Außerdem ruft Rxvt-Unicode Perl nicht auf, wenn es an
212 der Grenze des technisch Möglichen (Last!) scrollt. Anders als andere
213 Terminals (die kleinen Wichtel, äh Gnome, kommen mir da in den Sinn)
214 gibt es im Normalfall aber auch keine Zeitverzögerung, d.h. die URLs
215 erscheinen, sofern die Last es zuläßt, sofort unterstrichen und nicht erst
216 nach kurzer Verzögerung.
217
218 Eine "kleine" Bemerkung zum Begriff "Zeile" (line): Ein Terminal ist
219 ein rechteckiges Raster aus vielen, immer gleich großen, "Zellen",
220 die einzelne Zeichen (oder Teile derselben) enthalten können. Diese
221 werden in "Reihen" (eigentlich Zeilen) ("rows") und Spalten ("columns")
222 eingeteilt. Darüberhinaus gibt es aber Absätze, die aus langen Zeilen
223 bestehen, die in mehrere "Reihen" aufgespalten wurden. Diese Absätze
224 werden "Zeilen" (lines) genannt, wobei die Unterscheidung von Zeilen
225 vs. Reihen (lines vs. rows) im Deutschen etwas künstlich ist: korrekt
226 wäre es, von Absätzen zu sprechen, wenn man potentiell lange, mehrzeilige
227 "Zeilen" meint, aber ich werfe das immer durcheinander: Zeilen kann also
228 mal Absatz, mal Einzelzeile/Reihe bedeutet.
229
230 C<on_line_update> wirds jedenfalls pro Absatz, nicht pro Terminal-Zeile
231 aufgerufen. Zurück zum Quelltext, der als allererstes die entsprechende
232 Zeile ausliest:
233
234 # fetch the line that has changed
235 my $line = $self->line ($row);
236 my $text = $line->t;
237
238 C<$row> ist immer die oberste Zeile, und C<< $self->line >> erzeugt ein
239 "Absatz-Objekt", daß man manipulieren kann.
240
241 Nun müssen noch alle URLs in der Zeile gefunden werden:
242
243 # find all urls (if any)
244 while ($text =~ /$url/g) {
245
246 Und unterstrichen werden:
247
248 my $rend = $line->r;
249
250 $_ |= urxvt::RS_Uline
251 for @{$rend}[ $-[1] .. $+[1] - 1];
252
253 $line->r ($rend);
254 }
255
256 Zum Unterstreichen werden die Zeichenattribute ("rendition")
257 der Zeile gelesen, für die entsprechenden Zeichen wird das
258 C<urxvt::RS_Uline>-Attribut (naja, "Bit") gesetzt, und wieder
259 geschrieben. Ergebnis: URL wird unterstrichen. Fertig:
260
261 ()
262 }
263
264 Das C<()> sorgt dafür, daß nicht aus Versehen etwas zurückgegeben wird,
265 was nicht zurückgegeben werden soll: Alle C<on_>*-Methoden sollten nichts
266 zurückliefern, sofern sie nicht das Ereignis "fressen": Wenn z.B. ein
267 Mausklick verarbeitet wird, entscheidet der Rückgabewert darüber, ob
268 Rxvt-Unicode ihn Intern weiterverarbeitet (C<()>) oder ignoriert (C<1>).
269
270 Die URL zusätzlich anklickbar zu machen, erfordert einen gedanklichen
271 Spagat und etwas Vorsicht, ist aber prinzipiell der gleiche Vorgang:
272
273 sub on_start {
274 my ($self) = @_;
275
276 $self->{browser} = $self->x_resource ("urlLauncher") || "x-www-browser";
277
278 ()
279 }
280
281 Zuerst schaut die Erweiterung nach der C<urlLauncher>-Resource. Auf diese
282 Weise kann man konfigurieren, welchen "Browser" man denn gerne hätte:
283
284 URxvt.urlLauncher: links -g
285
286 Dies sollte man unbedingt in C<on_start> machen, da es nur eine
287 Resourcedatenbank für alle Terminals gibt und diese nur beim Start
288 aktuell ist. Keinsfalls sollte man globale Variablen verwenden, da es
289 durchaus Benutzer gibt, die verschiedene Terminal-Fenster verschieden
290 konfigurieren, und globale Variablen sind für alle Terminal-Fenster eines
291 Prozesses gleich.
292
293 Für den "Klick" muss man noch die C<on_button_release>-Methode implementieren:
294
295 sub on_button_release {
296 my ($self, $event) = @_;
297
298 my $mask = $self->ModLevel3Mask | $self->ModMetaMask
299 | urxvt::ShiftMask | urxvt::ControlMask;
300
301 if ($event->{button} == 2 && ($event->{state} & $mask) == 0) {
302
303 Das C<if> sorgt dafür, daß nur bei "normalen" Klicks (keine wichtigen
304 Modifer-Tasten wie Ctrl gedrückt), und nur mit der mittleren Maustaste
305 angeklickt werden kann (ich finde das nicht intuitiv mit der mittleren
306 Taste, aber mir wurde gesagt, gnome-terminal würde es ebenfalls so machen,
307 und gnome-terminal hat bei solchen Features per Definition recht, solange
308 es sich an das hält, was KDEs Konsole auch tut und es I<nicht> hilfreich
309 beim Arbeiten ist. Hmmja).
310
311 In C<($row,$col)> steht die Koordinate des Klicks. Doch welche URL
312 befindet sich darunter? Da man (noch) keine Daten mit Zeilen verknüpfen
313 kann, muss man - wie beim Markieren - auch alle URLs suchen und diejenige
314 finden, die sich unterhalb des Maus-Cursors befindet:
315
316 my $row = $event->{row};
317 my $col = $event->{col};
318
319 my $line = $self->line ($row);
320 my $text = $line->t;
321
322 while ($text =~ /$url/g) {
323 if ($-[1] <= $col && $+[1] >= $col) {
324 $self->exec_async ($self->{browser}, $1);
325 return 1;
326 }
327 }
328 }
329
330 ()
331 }
332
333 Die C<exec_async>-Methode startet ein externes Programm, hier den
334 Browser. Sie ist besser als ein einfaches C<system>, daß sie das richtige
335 %ENV setzt (wie schon bemerkt, kann ein einzelner Prozess viele Fenster
336 mit unterschiedlichen Environment-Variablen haben).
337
338 =head2 Blockgrafik-nach-ASCII (C<-pe block-graphics-to-ascii>)
339
340 Dies ist ein einfacher Filter, um alle Blockgrafikzeichen durch
341 ASCII-Zeichen zu ersetzen:
342
343 sub on_add_lines {
344 my ($self, $str) = @_;
345
346 $str =~ s/([\x{2500}-\x{259f}])/substr $rep_unicode, (ord $1) - 0x2500, 1/ge;
347
348 $self->scr_add_lines ($str);
349
350 1
351 }
352
353 (In Wahrheit etwas komplexer, da es in Vor-Unicode-Zeichen etwas anders
354 gemacht wurde). Eine Erklärung ist fast nicht notwendig: C<on_add_lines>
355 wird aufgerufen, wenn Text ausgegeben werden soll. In diesem werden
356 Unicode-Blockgrafikzeichen durch ASCII-Äquivalente in C<$rep_unicode>
357 (Definition steht nur in der Quelldatei, da der Perlworkshop noch nicht
358 Unicode-fähig ist) ersetzt und ausgegeben. Die C<1> am Ende sorgt dafür,
359 daß Rxvt-Unicode den Text nicht mehr ausgibt.
360
361 =head1 Weitere Erweiterungen
362
363 Es gibt noch eine Menge weiterer Erweiterungen, die z.B. asynchrone
364 Kommunikation mit Pipes benutzen, um eine Art Distributed Shared Clipboard
365 zu implementieren (C<-pe remote-clipboard>), Popups implementieren (C<-pe
366 selection-popup>), die übrigens nichts anderes als kleine Terminal-Fenster
367 sind, inkrementelles Suchen im Scrollback-Buffer ermöglichen (C<-pe
368 scrollback-search>), "Tabbed Terminals" (C<-pe tabbed>), Cursor-Movement
369 per Mausklick (C<-pe readline>) und einiges andere mehr.
370
371 Natürlich gibt es auch einfach nur "Nützliches", wie die intelligentere
372 Selection, die auch bei hartnäckigen Fällen korrekt Shell-Argumente
373 parsed...
374
375 cerebro ~/src/rxvt-unicode/src# ls da"t ei"\ .txt
376 ^^^^^^^^^^^^^^
377
378 oder eine vom Benutzer getätige Selection sofort transformiert:
379
380 syntax error at /opt/rxvt/lib/urxvt/urxvt.pm line 957, near "_exit 255"
381 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
382
383 Selektiert man das Unterstrichene, landet bei mir folgendes in der
384 Selection:
385
386 :e \/opt\/rxvt\/lib\/urxvt\/urxvt\.pm
387 :957
388
389 Das muss ich nur noch in meinen VI[M] pasten und bin sofort an der richtigen
390 Stelle.
391
392 Ich hoffe, dem Ein- oder Anderen habe ich etwas Lust gemacht, sein
393 bevorzugtes Man-Machine-Interface an die eigenen Bedürfnisse anzupassen
394 und noch intelligenter zu gestalten.
395
396 =head1 Danke...
397
398 ... an Ralf Valerien für seine Kommentare und Korrekturen.
399
400
401