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

# User Rev Content
1 root 1.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 root 1.4 zu machen, OnTheSpot-Editing für Eingabemethoden (XIM, X Input Methods) zu
12     implementieren und vieles, vieles mehr.
13 root 1.1
14     Dies erfüllt meinen lang gehegten Traum, meine Hauptschnittstelle
15 root 1.3 zum Rechner noch besser anpassen zu können.
16 root 1.1
17     =head1 Einführung
18    
19 root 1.2 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 root 1.3 Aber philosophische Gedanken beiseite: Als erstes wurde rxvt in C++
27 root 1.2 "ü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 root 1.4 Eingriffsmöglichkeiten: Ich dachte immer, wie schön es wäre, wenn man
36 root 1.2 z.B. durch einen externen Prozess intelligentere Steuerungen oder
37     Feedback-Möglichkeiten hätte. Erst Anfang des Jahres kam mir die Idee,
38 root 1.3 nicht eine aufwendige sprachenunabhängige Schnittstelle zu implementieren,
39 root 1.2 sondern einfach Perl einzubetten, womit ich mehr Erfahrung hatte und was
40 root 1.3 auch eine einfachere Implementierung zuließ.
41 root 1.2
42     Ein wichtiger Punkt ist der erhöhte Speicherbedarf, da Rxvt-Unicode
43 root 1.3 den Ruf hat, sehr ressourcenschonend zu sein: Statt 3MB
44 root 1.2 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 root 1.3 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 root 1.2
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 root 1.5 mkdir extensions
62     vi extentions/myext
63 root 1.2 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 root 1.3 Viele Leute wünschen sich pseudotransparente Terminals. Ich nicht. Daher
73 root 1.2 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 root 1.3 des Fensters auf dme Schirm verschieben? Das hätte sogar den Vorteil,
78 root 1.2 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 root 1.3 $self->cmd_parse ("\033]20;;=+$event->{x}+$event->{y}\007");
87 root 1.2 }
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 root 1.3 $self->vt, $self->DefaultRootWindow,
115 root 1.2 0, 0
116     );
117    
118     $self->cmd_parse ("\033]20;;=+$x+$y\007");
119     }
120    
121 root 1.3 Das klappt in allen Fällen.
122 root 1.2
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 root 1.3 Im Prinzip kann man Rxvt-Unicode als Perl-Daemon betrachten, der I<auch>
174 root 1.2 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 root 1.3 Diese horrende Regex versucht, "übliche" URLs zu matchen. Muss man nicht
195     lesen können, klappt aber gut.
196 root 1.2
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 root 1.3 eingeteilt. Darüberhinaus gibt es aber Absätze, die aus langen Zeilen
223 root 1.2 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 root 1.3 Das C<()> sorgt dafür, daß nicht aus Versehen etwas zurückgegeben wird,
265 root 1.2 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 root 1.3 aktuell ist. Keinsfalls sollte man globale Variablen verwenden, da es
289 root 1.2 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 root 1.3 es sich an das hält, was KDEs Konsole auch tut und es I<nicht> hilfreich
309     beim Arbeiten ist. Hmmja).
310 root 1.2
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 root 1.3 mit unterschiedlichen Environment-Variablen haben).
337 root 1.2
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 root 1.3 (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 root 1.2 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 root 1.4 Natürlich gibt es auch einfach nur "Nützliches", wie die intelligentere
372 root 1.2 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 root 1.4 Selektiert man das Unterstrichene, landet bei mir folgendes in der
384     Selection:
385 root 1.2
386     :e \/opt\/rxvt\/lib\/urxvt\/urxvt\.pm
387     :957
388    
389 root 1.4 Das muss ich nur noch in meinen VI[M] pasten und bin sofort an der richtigen
390 root 1.2 Stelle.
391    
392 root 1.3 Ich hoffe, dem Ein- oder Anderen habe ich etwas Lust gemacht, sein
393 root 1.2 bevorzugtes Man-Machine-Interface an die eigenen Bedürfnisse anzupassen
394     und noch intelligenter zu gestalten.
395    
396 root 1.4 =head1 Danke...
397    
398     ... an Ralf Valerien für seine Kommentare und Korrekturen.
399    
400 root 1.2
401 root 1.1