plot aus Common Lisp mit SBCL ansteuern

plot aus Common Lisp mit SBCL ansteuern

🌐 Read in English
📅 2026-10-18 ✍ Andreas Wittmann đŸ‘ïž ... lisp sbcl graphics

Der plot-Artikel endet mit einer kurzen ErwĂ€hnung, dass es bereits einen Common-Lisp-Client fĂŒr das Socket-Protokoll von plot_server.cpp gibt, ohne ihn zu behandeln — das war bewusst einem eigenen Artikel vorbehalten. Das ist dieser Artikel: den Client in SBCL laden, mit dem Server verbinden, und ein paar Beispiele durchgehen.

Was hier SBCL-spezifisch ist

Der Client (package.lisp, plot.lisp, plot.asd — alle vom plot-Artikel aus verlinkt) ist gegen SBCLs eigene, implementierungsspezifische Pakete geschrieben, nicht gegen portables Common Lisp: sb-bsd-sockets fĂŒr den Unix-Domain-Socket, sb-thread:make-mutex, um gleichzeitige Aufrufe auf eine Verbindung zu serialisieren, und sb-ext:run-program/sb-ext:string-to-octets, um den Server zu starten und UTF-8-Text zu kodieren. Eine Portierung auf eine andere Implementierung wĂŒrde bedeuten, diese gegen usocket, bordeaux-threads beziehungsweise uiop:run-program auszutauschen — nichts davon ist hier gemacht, dieser Artikel handelt also speziell von SBCL.

Voraussetzungen

SBCL selbst:

apt install sbcl

Die native plot-server-BinÀrdatei, gebaut im selben Verzeichnis (siehe den entsprechenden Abschnitt im plot-Artikel):

make plot-server

plot:open startet diesen Server bei Bedarf automatisch, falls noch keiner lĂ€uft — allerdings ĂŒber sb-ext:run-program ... :search t, was plot-server auf $PATH sucht. Stelle sicher, dass das Verzeichnis mit der frisch gebauten BinĂ€rdatei auf PATH liegt, oder kopiere/verlinke sie irgendwohin, wo das bereits der Fall ist.

Das System laden

ASDF muss wissen, wo es das plot-System findet. Der schnellste Weg fĂŒr eine einmalige Sitzung ist, das Verzeichnis direkt auf asdf:*central-registry* zu pushen:

(require :asdf)
(push #P"/pfad/zu/posts/plot/common-lisp/plot/" asdf:*central-registry*)
(asdf:load-system "plot")

FĂŒr etwas Dauerhafteres das plot/-Verzeichnis unter ~/.local/share/common-lisp/source/ verlinken (oder dort ablegen), was im Standard-Suchpfad von ASDFs Source-Registry liegt — dann findet es ein einfaches (asdf:load-system "plot") ohne den push-Schritt.

So oder so bekommst du damit ein plot-Package mit (unter anderem) plot:open, plot:clear, plot:color, plot:pixel, plot:line, plot:rectangle, plot:filled-rectangle, plot:triangle, plot:cross, plot:text, plot:present, plot:auto-present, plot:mode, plot:close sowie das Makro plot:with-frame — eine direkte Abbildung auf die im plot-Artikel dokumentierten Socket-Protokoll-Befehle.

Eine erste Sitzung

(plot:open)                     ; verbindet, startet plot-server bei Bedarf

(plot:clear 255 255 255)        ; weißer Hintergrund
(plot:color 200 0 0)
(plot:line -50 0 50 0)

(plot:color 0 150 0)
(plot:rectangle -30 -30 30 30)

(plot:color 0 0 200 128)        ; das optionale 4. Argument ist Alpha
(plot:filled-rectangle 10 10 40 40)

(plot:cross)                    ; zeichnet die x/y-Achsen

(plot:color 0 0 0)
(plot:triangle 0 -40 -20 -10 20 -10)

(plot:text 5 5 "HĂ€llo, λ!")     ; Nicht-ASCII-Text funktioniert — intern ist alles UTF-8

(plot:present)                  ; bis hierher erscheint nichts im Fenster
(plot:close)

plot:open ohne Argumente verbindet sich ĂŒber den Standard-Socket-Pfad, in der StandardgrĂ¶ĂŸe 800×600 — dieselben Vorgaben, die plot-server selbst verwendet. Mit (plot:open breite höhe pfad) lĂ€sst sich jede davon ĂŒberschreiben.

Jeder dieser Aufrufe geht ĂŒber den Socket hin und zurĂŒck und wartet auf eine OK/ERR-Antwort (die genaue Zuordnung zu den Textbefehlen steht in der Protokoll-Tabelle im plot-Artikel); eine ERR-Antwort oder jeder tiefer liegende Socket-Fehler löst einen Lisp-Fehler aus, statt still zu scheitern.

with-frame und auto-present

Nach jeder einzelnen Form (plot:present) zu tippen, wird außerhalb eines kurzen REPL-Experiments schnell lĂ€stig. Zwei verschiedene Komfortfunktionen decken zwei verschiedene Situationen ab.

plot:with-frame umschließt einen Block aus Zeichenaufrufen und spĂŒlt am Ende einmal — gut fĂŒr eine in sich geschlossene Momentaufnahme:

(plot:with-frame (:clear t)
  (plot:color 128 0 128)
  (plot:line -10 -10 10 10))

plot:auto-present dreht stattdessen das Verhalten des Servers fĂŒr jeden folgenden Befehl um, bis man es wieder abschaltet — gut, um interaktiv aus dem REPL heraus am Canvas herumzuprobieren und jede Form sofort erscheinen zu sehen:

(plot:auto-present t)
(plot:pixel 1 1)                ; erscheint sofort
(plot:auto-present nil)         ; zurĂŒck zu manuellem plot:present

Eine einfache Animation

Hier ist nichts Lisp-Spezifisches — dieselbe Idee wie beim Ansteuern des Sockets aus jeder anderen Sprache, nur mit Lisps dotimes statt einer for-Schleife. Ein Rechteck, das von links nach rechts wandert und bei jedem Schritt den gesamten Frame neu zeichnet:

(plot:open)
(plot:mode 1)          ; PLOT_MODE_CROSS: Ursprung in der Mitte
(plot:auto-present t)

(dotimes (i 20)
  (plot:clear 240 240 240)
  (plot:color 200 0 0)
  (plot:filled-rectangle (- (* i 5) 100) -5
                          (+ (- (* i 5) 100) 10) 5))

(plot:close)

Mit aktiviertem auto-present spĂŒlt jeder filled-rectangle-Aufruf in der Schleife von selbst ins Fenster — kein explizites present innerhalb der Schleife nötig.

Fazit

Die Lisp-Seite davon fĂŒgt dem Protokoll selbst nichts hinzu — jeder Aufruf hier ist nur ein dĂŒnner Wrapper, der eine Textzeile formatiert, sie an den Socket schreibt und auf OK wartet. Was man dadurch gewinnt, ist ein REPL: einmal verbinden, eine Form zeichnen, sie sofort sehen, eine Zahl Ă€ndern, erneut zeichnen, ohne irgendetwas neu zu kompilieren oder neu zu starten. Genau diese Unmittelbarkeit ist der eigentliche Punkt daran, plot aus Lisp statt aus C++ anzusteuern — das Protokoll im plot-Artikel wurde genau fĂŒr diese Art von externem, möglicherweise langlebigem Client entworfen.