[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Lost] [Patch] vterm: textterm in vterm integriert



Am Freitag, 28. Dezember 2007 16.59:25 schrieb Kevin Wolf:
> > +    if (request_ports(PORT_HW_CURSOR, 5) == FALSE) {
> > +        puts("[ vterm ] Konnte Port fuer Hardware-Cursor nicht
> > registrieren."); +        exit(-1);
> > +    }
>
> Sollten zwei Ports nicht reichen?

Hm doch...

> > +/**
> > + * Ausgabe fuer ein vterminal vorbereiten
> > + */
> > +bool vterm_output_init(vterminal_t* vterm, size_t buffer_lines)
> > +{
> > +    size_t buffer_size;
> > +    size_t i;
> > +    videomem_cell_t c;
> > +    vterm_output_t* out = &(vterm->output);
> > +
> > +    // Struktur initialisieren
>
> screen_redraw und vt100_backup_valid werden nirgends initialisiert. Ich
> weiß nicht, ob das ein Problem darstellen kann.
Hm bei screen_redraw macht es sicherlich nichts.. Aber sauberer ist es schon.

> > +/**
> > + * Aktuelle Position auf dem Bildschirm errechnen. Das klappt natuerlich
> > nur, + * wenn die aktuelle Position im Puffer auch irgendwo auf dem
> > Bildschirm ist. + *
> > + * @param position Pointer auf die Positionsstruktur, in der das
> > Ergebnis + *                  abgelegt werden soll.
> > + *
> > + * @return TRUE wenn die Position auch auf dem Bildschirm liegt, FALSE
> > sonst. + */
> > +bool screen_position(vterm_output_t* out, position_t buffer_pos,
> > +    position_t* dest_pos)
>
> Prototyp und Kommentar passen nicht mehr zusammen

Gefixt.
 
> > +    // Wenn die Position auf dem Bildschirm liegt, kann sie ganz einfach
> > +    // errechnet werden:
> > +    if (screen_bottom > out->screen_topline) {
> > +        dest_pos->line = buffer_pos.line - out->screen_topline;
> > +    } else {
> > +        dest_pos->line = out->buffer_lines - out->screen_topline +
> > +            buffer_pos.line;
>
> Fehlt da nicht ein % out->buffer_lines?

Doch.

> > +/**
> > + * Eine einzelne Zeile auf den Bildschirm bringen
> > + *
> > + * @param line Zeilennummer
>
> Auf dem Bildschirm, nicht im Puffer. Wenn ich es richtig sehe, ist das
> nicht die einzige Stelle, wo das erst durch den Code klar wird. Das
> könnte man ruhig in den Kommentaren erwähnen, was jeweils gemeint ist.

Okay, ich versuche mal die Stellen alle zu erwischen. ;-)

> > +    for (i = 0; i < out->screen_width; i++) {
> > +        // Position aktualisieren
> > +        screen_pos.column = i;
> > +
> > +        // Bufferposition aktualisieren und Zeichen aus dem Buffer
> > auslesen +        buffer_position(out, screen_pos, &buffer_pos);
> > +        c = buffer_cell_get(out, out->buffer, buffer_pos);
> > +
> > +        // Zeichen in den Videospeicher kopieren
> > +        buffer_cell_set(out, video_memory, screen_pos, c);
> > +    }
>
> Diese ganzen Funktionsaufrufe für ein einzelnes Zeichen kommen mir
> relativ langsam vor... Du hast immerhin teilweise inline drin, aber das
> schlägt ja erst ab -O2 (?) an.

Für direkte Vorschläge bin ich jederzeit offen. ;-) Ich habe nur versucht, das 
ganze so verständlich wie möglich umzusetzen...

> > +/**
> > + * Bildschirmfenster um @lines Zeilen herunterschieben und
> > Bildschirminhalt + * entsprechend anpassen.
> > + *
> > + * @param lines Anzahl der Zeilen um die der Inhalt verschoben werden
> > soll. + */
> > +void screen_scroll(vterm_output_t* out, int lines)
> > +{
> > +    // TODO: Optimierungspotential ;-)
> > +    out->screen_topline += lines + out->buffer_lines;
> > +    out->screen_topline %= out->buffer_lines;
>
> Wozu soll denn das + out->buffer_lines gut sein?

Das machte sonst bei negativen Werten fuer lines Probleme und so tut es, also 
hab ich es halt drin gelassen.

> > +void buffer_position(vterm_output_t* out, position_t screen_pos,
> > +    position_t* dest_pos)
>
> Sollte inline sein (siehe oben)

Habe ich bis jetzt bleiben lassen, weil es in vt100.c auch benutzt wird, und 
ich es nicht in den Header kopieren wollte. Aber inline ist wohl schon 
besser.

>
> > +/**
> > + * Ausgaben durch vt100-Emulation verarbeiten
> > + */
> > +void vt100_process_output(vterminal_t* vterm, char* data, size_t length)
> > +{
> > +    char c;
> > +    int i;
> > +    char* last_flush = data;
> > +    size_t text_len = 0;
> > +    
> > +    // Der String wird jetzt Zeichenweise durchsucht. Nach einem
> > Escape-Zeichen +    // werden die Zeichen so lange gepuffert, bis die
> > Sequenz fertig ist, oder +    // feststeht, dass sie ungueltig ist. In
> > diesem Fall, werden alle Zeichen +    // ausser dem Escape-Zeichen
> > ausgegeben.
> > +    for (i = 0; i < length; i++) {
> > +        c = data[i];
> > +        
> > +        // Die vorherigen Zeichen waren alle normaler Text, imd auch das
> > +        // Aktuelle ist kein Beginn einer Sequenz. Die Zeichen werden
> > also +        // aufbewahrt, bis wir am Ende sind, oder eine
> > Escape-Sequenz anfaengt. +        if ((vterm->vt100_buffer_offset == 0)
> > && (c != '\033')) { +            text_len++;
> > +        } else if (c == '\033') {
> > +            if (vterm->vt100_buffer_offset == 0) {
> > +                // Der Beginn einer Sequenz. Jetzt wird ausgegeben, was
> > bis +                // jetzt gekommen ist.
> > +                if (text_len != 0)  {
> > +                    vterm_output_text(vterm, last_flush, text_len);
> > +                }
> > +            } else {
> > +                // Wir haben noch eine Sequenz im Puffer, die allerdings
> > nich +                // gueltig ist.
> > +                puts("ff");
>
> Ist das übriggebliebener Debugcode?

Ja, ist es.

> > +/**
> > + * Pfuefen der VT100-Escapesequenz. Falls sie vollstaendig ist, wird sie
> > + * ausgefuehrt.
> > + *
> > + * @return TRUE falls die Sequenz gueltig ist/war, FALSE sonst
> > + */
> > +static bool vt100_buffer_validate(vterminal_t* vterm)
> > +{
> > +    int i;
> > +    int j;
> > +    bool have_n1;
> > +    bool have_n2;
> > +    bool have_sep = FALSE;
> > +    int n1;
> > +    int n2;
> > +    bool matches;
> > +    char* cur_pos= vterm->vt100_buffer;
> > +    size_t offset = 0;
> > +    size_t sequences = sizeof(handler_list) / sizeof(struct
> > +        vt100_sequence_handler);
>
> Ah ja, alles klar, sind ja nur elf Bezeichner und alle total sprechend. ;-)

Na klar... *g*


Hier nun die korrigierte Version.
Index: src/modules/vterm/output.c
===================================================================
--- src/modules/vterm/output.c	(Revision 0)
+++ src/modules/vterm/output.c	(Revision 0)
@@ -0,0 +1,509 @@
+/*  
+ * Copyright (c) 2007 The LOST Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the LOST Project
+ * by Antoine Kaufmann.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the LOST Project
+ *     and its contributors.
+ * 4. Neither the name of the LOST Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <types.h>
+#include <syscall.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <rpc.h>
+#include <lostio.h>
+#include "vterm.h"
+#include <sleep.h>
+#include <ports.h>
+
+#define PORT_HW_CURSOR 0x3D4
+
+out_buffer_t* video_memory;
+
+static void screen_draw(vterm_output_t* out);
+static void screen_draw_line(vterm_output_t* out, size_t line);
+static void screen_adjust(vterm_output_t* out, position_t pos);
+static void screen_cursor_update(vterm_output_t* out);
+static inline void buffer_cell_set(vterm_output_t* out, out_buffer_t* buffer,
+    position_t position, videomem_cell_t c);
+static inline videomem_cell_t buffer_cell_get(vterm_output_t* out,
+    out_buffer_t* buffer, position_t position);
+static void buffer_line_clear(vterm_output_t* out, out_buffer_t* buffer,
+    size_t line);
+static void output_add_cell(vterm_output_t* out, videomem_cell_t c);
+static void output_newline(vterm_output_t* out);
+
+
+/**
+ * Ausgabetreiber allgemein vorbereiten
+ */
+void init_output()
+{
+    video_memory = mem_allocate_physical(SCREEN_WIDTH_MAX * SCREEN_HEIGHT_MAX *
+        sizeof(videomem_cell_t), 0xb8000, 0);
+
+    if (request_ports(PORT_HW_CURSOR, 2) == FALSE) {
+        puts("[ vterm ] Konnte Port fuer Hardware-Cursor nicht registrieren.");
+        exit(-1);
+    }
+}
+
+
+/**
+ * Ausgabe fuer ein vterminal vorbereiten
+ */
+bool vterm_output_init(vterminal_t* vterm, size_t buffer_lines)
+{
+    size_t buffer_size;
+    size_t i;
+    videomem_cell_t c;
+    vterm_output_t* out = &(vterm->output);
+
+    // Struktur initialisieren
+    out->buffer_lines = buffer_lines;
+    out->buffer_pos.line = 0;
+    out->buffer_pos.column = 0;
+    
+    out->active = FALSE;
+    
+    out->screen_redraw = FALSE;
+    out->screen_width = 80;
+    out->screen_height = 25;
+    out->screen_topline = 0;
+    
+    out->scroll_lock = FALSE;
+    
+    // Standardschriftart: Grau auf schwarz
+    out->current_color.raw = 0x7;
+    
+    out->vt100_backup_valid = FALSE;
+
+    // Puffer vorbereiten
+    buffer_size = sizeof(videomem_cell_t) * out->screen_width * buffer_lines;
+    out->buffer = malloc(buffer_size);
+    if (out->buffer == NULL) {
+        return FALSE;
+    }
+
+
+    c.color = out->current_color;
+    c.ascii = ' ';
+    for (i = 0; i < out->screen_width * buffer_lines; i++) {
+        out->buffer[i] = c;
+    }
+
+    return TRUE;
+}
+
+/**
+ * Wird beim Wechsel zu einem neuen virtuellen Terminal aufgerufen
+ */
+void vterm_output_change(vterminal_t* old, vterminal_t* new)
+{
+    // Nur wenn vorher ueberhaupt ein terminal aktiviert war, muss es jetzt auf
+    // inaktiv gesetzt werden
+    if (old != NULL) {
+        old->output.active = FALSE;
+    }
+
+    new->output.active = TRUE;
+    screen_draw(&new->output);
+    screen_cursor_update(&new->output);
+}
+
+/**
+ * Ausgaben eines Prozesses fuer ein Virtuelles Terminal verarbeiten
+ * 
+ * @param data Pointer auf die auszugebenden Daten
+ * @param length Laenge der auszugebenden Daten
+ */
+void vterm_process_output(vterminal_t* vterm, char* data, size_t length)
+{
+    vterm_output_t* out = &(vterm->output);
+
+    // Ausgaben durch vt100-Emulation taetigen
+    vt100_process_output(vterm, data, length);
+
+    // Anzeige aktualisieren falls es sich um aenderungen auf dem aktiven
+    // Terminal handelt.
+    screen_adjust(out, vterm->output.buffer_pos);
+    if (out->active) {
+        screen_cursor_update(out);
+        
+        if (out->screen_redraw) {
+            out->screen_redraw = FALSE;
+            screen_draw(out);
+        }
+    }
+
+}
+
+/**
+ * Ausgaben eines Prozesses in Form von reinem Text ausgeben
+ * 
+ * @param data Pointer auf die auszugebenden Daten
+ * @param length Laenge der auszugebenden Daten
+ */
+void vterm_output_text(vterminal_t* vterm, char* data, size_t length)
+{
+    size_t i;
+    videomem_cell_t c;
+    vterm_output_t* out = &(vterm->output);
+    
+
+    c.color = out->current_color;
+
+    // Zeichen einzeln ausgeben
+    for (i = 0; i < length; i++) {
+        
+        if (data[i] == '\n') {
+            output_newline(out);
+        } else if (data[i] == '\r') {
+    
+        output_set_position(out, out->buffer_pos.line, 0);
+        } else {
+            c.ascii = data[i];
+            output_add_cell(out, c);
+        }
+    }
+}
+
+
+
+/**
+ * Position auf dem Bildschirm anhand einer beliebigen Position im Puffer
+ * errechnen. Das klappt natuerlich nur, wenn die Position im Puffer auch
+ * irgendwo auf dem Bildschirm ist.
+ *
+ * @param buffer_pos Position im Puffer
+ * @param dest_pos Pointer auf die Positionsstruktur, in der das Ergebnis
+ *                  abgelegt werden soll.
+ *
+ * @return TRUE wenn die Position auch auf dem Bildschirm liegt, FALSE sonst.
+ */
+bool screen_position(vterm_output_t* out, position_t buffer_pos,
+    position_t* dest_pos)
+{
+    size_t screen_bottom = (out->screen_topline + out->screen_height - 1) %
+        out->buffer_lines;;
+
+    // Wenn die Position ausserhalb des angezeigten Bereiches liegt, ist der
+    // Fall klar.
+    if (((screen_bottom > out->screen_topline) && (
+            (buffer_pos.line < out->screen_topline) || 
+            (buffer_pos.line > screen_bottom))
+        ) ||
+        ((screen_bottom < out->screen_topline) &&
+            (buffer_pos.line > screen_bottom) &&
+            (buffer_pos.line < out->screen_topline)
+        ))
+        
+    {
+        return FALSE;
+    }
+
+    // Wenn die Position auf dem Bildschirm liegt, kann sie ganz einfach
+    // errechnet werden:
+    if (screen_bottom > out->screen_topline) {
+        dest_pos->line = buffer_pos.line - out->screen_topline;
+    } else {
+        dest_pos->line = (out->buffer_lines - out->screen_topline +
+            buffer_pos.line) % out->buffer_lines;
+    }
+    dest_pos->column = buffer_pos.column;
+
+    return TRUE;
+}
+
+/**
+ * Position des Hardware-Cursors anpassen
+ */
+static void screen_cursor_update(vterm_output_t* out)
+{
+    uint16_t position = 0;
+    position_t pos;
+
+    // Position errechnen
+    if (screen_position(out, out->buffer_pos, &pos) == TRUE) {
+        position = pos.line * out->screen_width + pos.column;
+    }
+
+    // Hardware Cursor verschieben
+    outb(PORT_HW_CURSOR, 15);
+    outb(PORT_HW_CURSOR + 1, position);
+    outb(PORT_HW_CURSOR, 14);
+    outb(PORT_HW_CURSOR + 1, position >> 8);
+}
+
+/**
+ * Den richtigen Teil des Buffers auf in den Videospeicher kopieren.
+ */
+static void screen_draw(vterm_output_t* out)
+{
+    size_t i;
+    for (i = 0; i < out->screen_height; i++) {
+        screen_draw_line(out, i);
+    }
+}
+
+/**
+ * Eine einzelne Zeile aus dem Puffer direkt in den Videospeicher kopieren
+ *
+ * @param line Zeilennummer
+ */
+static void screen_draw_line(vterm_output_t* out, size_t line)
+{
+    size_t i;
+    position_t screen_pos;
+    position_t buffer_pos;
+    videomem_cell_t c;
+
+    // Nein groessere Zeilennummern als die Bildschirmhoehe gibts nicht.
+    if (line >= out->screen_height) {
+        return;
+    }
+    
+    screen_pos.line = line;
+    
+    for (i = 0; i < out->screen_width; i++) {
+        // Position aktualisieren
+        screen_pos.column = i;
+
+        // Bufferposition aktualisieren und Zeichen aus dem Buffer auslesen
+        buffer_position(out, screen_pos, &buffer_pos);
+        c = buffer_cell_get(out, out->buffer, buffer_pos);
+
+        // Zeichen in den Videospeicher kopieren
+        buffer_cell_set(out, video_memory, screen_pos, c);
+    }
+}
+
+/**
+ * Bildschirmfenster um @lines Zeilen herunterschieben und Bildschirminhalt
+ * entsprechend anpassen.
+ * 
+ * @param lines Anzahl der Zeilen um die der Inhalt verschoben werden soll.
+ */
+void screen_scroll(vterm_output_t* out, int lines)
+{
+    // TODO: Optimierungspotential ;-)
+    out->screen_topline += lines + out->buffer_lines;
+    out->screen_topline %= out->buffer_lines;
+
+    // Der Bildschrim muss garantiert neu gezeichnet werden
+    // TODO: Da sie Funktion auch von aussen aufgerufen wird, muss hier im
+    // Moment neu gezeichnet werden. Hier waere vielleicht eine eigene Funktion
+    // dafuer nicht schlecht.
+    // out->screen_redraw = TRUE;
+    screen_draw(out);
+}
+
+/**
+ * Dafuer sorgen, dass das Bildschirmfenster so angepasst wird, dass die
+ * Pufferposition pos sichtbar ist.
+ *
+ * @param pos
+ */
+static void screen_adjust(vterm_output_t* out, position_t pos)
+{
+    position_t screen_pos;
+    // Wenn position noch nicht sichtbar ist, wird jetzt entsprechend
+    // gescrollt, falls das moeglich ist.
+    if ((screen_position(out, pos, &screen_pos) == FALSE) && (out->scroll_lock
+        == FALSE))
+    {
+        // Berechnen wieviele Zeilen gescrollt werden muss
+        int diff = pos.line - out->screen_topline - out->screen_height + 1;
+
+        // Negative Werte umgehen, die machen irgendwie probleme
+        diff += out->buffer_lines;
+        screen_scroll(out, diff);
+    }
+}
+
+
+
+/**
+ * Diese Funktion ist das Gegenstueck zu screen_position. Sie errechnet anhand
+ * einer gegebenen Bildschirm-Position die Position im Puffer. Im Gegensatz zu
+ * screen_position kann diese Funktion aber nicht fehlschlagen, da der
+ * Bildschirm immer irgendwo innerhalb des Puffers sein muss.
+ *
+ * @param screen_pos Position auf dem Bildschirm
+ * @param dest_pos Pointer auf die Positionsstruktur, in der das Ergebnis
+ *                  abgelegt werden soll.
+ */
+inline void buffer_position(vterm_output_t* out, position_t screen_pos,
+    position_t* dest_pos)
+{
+    dest_pos->line = out->screen_topline + screen_pos.line;
+    dest_pos->line %= out->buffer_lines;
+    dest_pos->column = screen_pos.column;
+}
+
+/**
+ * Ein einzelnes Zeichen in einem Buffer setzen
+ * 
+ * @param buffer Puffer in den das Zeichen gesetzt werden soll
+ * @param positon Position an die das Zeichen gesetzt werden soll
+ * @param c Das Zeichen mit Farbinformation
+ */
+static inline void buffer_cell_set(vterm_output_t* out, out_buffer_t* buffer,
+    position_t position, videomem_cell_t c)
+{
+    buffer[position.line * out->screen_width + position.column] = c;
+}
+
+/**
+ * Einzelnes Zeichen aus einem Buffer auslesen
+ *
+ * @param buffer
+ * @param position
+ * 
+ * @return Das Zeichen
+ */
+static inline videomem_cell_t buffer_cell_get(vterm_output_t* out,
+    out_buffer_t* buffer, position_t position)
+{
+    return buffer[position.line * out->screen_width + position.column];
+}
+
+/**
+ * Eine Zeile in einem Puffer leeren
+ *
+ * @param line Zeilennummer
+ */
+static void buffer_line_clear(vterm_output_t* out, out_buffer_t* buffer,
+    size_t line)
+{
+    position_t position;
+    videomem_cell_t c;
+    size_t i;
+    
+    c.color.raw = 0x7;
+    c.ascii = ' ';
+
+    position.line = line;
+    
+    // Jetzt wird sie Zeichenweise geleert
+    for (i = 0; i < out->screen_width; i++) {
+        position.column = i;
+        buffer_cell_set(out, buffer, position, c);
+    }
+}
+
+
+
+
+
+
+/**
+ * Aktuelle Position im Puffer aktualisieren
+ */
+void output_set_position(vterm_output_t* out, size_t line,
+    size_t column)
+{
+    out->buffer_pos.column = column;
+    out->buffer_pos.line = line;
+    
+    // Zeilenumbruch?
+    if (column >= out->screen_width) {
+        out->buffer_pos.line += out->buffer_pos.column / out->screen_width;
+        out->buffer_pos.column %= out->screen_width;
+    }
+
+    out->buffer_pos.line %= out->buffer_lines;
+}
+
+/**
+ * Einzelnes Zeichen hinzufuegen und Bildschirminhalt ggf aktualisieren.
+ *
+ * @param c Zeichen
+ */
+static void output_add_cell(vterm_output_t* out, videomem_cell_t c)
+{
+    position_t screen_pos;
+    
+    // Zeichen setzen
+    buffer_cell_set(out, out->buffer, out->buffer_pos, c);
+    
+    // Zeichen gegebenenfalls auch noch auf dem Bildschirm ausgeben
+    if ((out->active) &&  (screen_position(out, out->buffer_pos, &screen_pos)
+        == TRUE))
+    {
+        // Zeichen in den Videospeicher schreiben
+        buffer_cell_set(out, video_memory, screen_pos, c);
+    }
+
+    // Position aktalisieren
+    if (out->buffer_pos.column != out->screen_width - 1) {
+        // Kein Zeilenumbruch
+        output_set_position(out, out->buffer_pos.line, out->buffer_pos.column
+            + 1);
+    } else {
+        // Zeilenumbruch
+        output_newline(out);
+    }
+}
+
+/**
+ * Den festgelegten Bereich im Puffer und auf dem Bildschirm leeren
+ *
+ * @param count Anzahl der zu loeschenden Zeichen
+ */
+void output_clear(vterm_output_t* out, size_t count)
+{
+    size_t i;
+    videomem_cell_t c;
+    // Position retten
+    position_t pos = out->buffer_pos;
+    
+    c.color = out->current_color;
+    c.ascii = ' ';
+    for (i = 0; i < count; i++) {
+        output_add_cell(out, c);
+    }
+
+    // Position wiederherstellen
+    out->buffer_pos = pos;
+}
+
+/**
+ * Neue Zeile in Ausgabe anfangen
+ */
+static void output_newline(vterm_output_t* out)
+{
+    output_set_position(out, out->buffer_pos.line + 1, 0);
+
+    // Zeile leeren
+    buffer_line_clear(out, out->buffer, out->buffer_pos.line);
+}
+
Index: src/modules/vterm/vterm.h
===================================================================
--- src/modules/vterm/vterm.h	(Revision 675)
+++ src/modules/vterm/vterm.h	(Arbeitskopie)
@@ -40,28 +40,96 @@
 #define LOSTIO_TYPES_OUT 255
 #define LOSTIO_TYPES_IN 254
 
-// Filehandles fuer Eingabe- und Ausgabekanal
-extern FILE* input;
-extern FILE* output;
+#define SCREEN_WIDTH_MAX 80
+#define SCREEN_HEIGHT_MAX 25
 
+#define VT100_BUFFER_LEN 16
 
 /// LostIO-Interface vorbereiten
 void init_lostio_interface();
 
+/// Output-Treiber vorbereiten
+void init_output();
 
+// Farbe im Videospeicher
+typedef union {
+    byte raw;
+    struct {
+        unsigned char foreground : 3;
+        unsigned char bold : 1;
+        unsigned char background : 3;
+        unsigned char blink : 1;
+    } __attribute__((packed));
+} __attribute__((packed)) con_color_t;
+
+// Zelle im Videospeicher
+typedef struct {
+    char ascii;
+    con_color_t color;
+} __attribute__((packed)) videomem_cell_t;
+
+
+
+typedef videomem_cell_t out_buffer_t;
+typedef struct position {
+    /// Zeile
+    int line;
+
+    /// Spalte
+    int column;
+} position_t;
+
+typedef struct vterm_output {
+    /// Groesse des Puffers in Zeilen
+    int buffer_lines;
+
+    /// Position im Puffer
+    struct position buffer_pos;
+    
+    /// Aktuelle Farbe
+    con_color_t current_color;
+
+    /// Der eigentliche Puffer
+    videomem_cell_t* buffer;
+    
+    /// TRUE falls der Buffer gerade auf dem Bildschirm ist.
+    bool active;
+    
+    /// Groesse des Bildschirms
+    size_t screen_width;
+    size_t screen_height;
+
+    /// Oberste Zeile auf dem Bildschirm
+    int screen_topline;
+
+    /// Wird auf TRUE gesetzt falls der Bildschirm neu gezeichnet werden soll
+    bool screen_redraw;
+
+    /// Scroll-Lock
+    bool scroll_lock;
+    
+
+    /// TRUE falls vt100_pos_backup und vt100_color_backup gueltig sind
+    bool vt100_backup_valid;
+
+    /// Backupposition fuer vt100-Emulation
+    position_t vt100_pos_backup;
+
+    /// Kopie der Farbe fuer vt100
+    con_color_t vt100_color_backup;
+} vterm_output_t;
+
+
 /// Typ fuer ein  Terminal
 typedef struct {
     /// Name des Terminals
     char* name;
 
     /// Shortcut mit dem das Terminal aktiviert werden kann
-    char* shortcut;
+    char shortcut;
 
-    /// Groesse des Ausgabenpuffers
-    size_t out_buffer_size;
-
-    /// Pointer auf den Ausgabenpuffer
-    char* out_buffer;
+    /// Struktur fuer den ganzen Ausgabekram
+    vterm_output_t output;
     
     /// LostIO-Node fuer out-Datei
     vfstree_node_t* out_node;
@@ -74,17 +142,65 @@
 
     /// LostIO-Node fuer in-Datei
     vfstree_node_t* in_node;
+
+    /// Eingegebene Ziffern sofort ausgeben
+    bool input_echo;
+
+    /// vt100-Puffer
+    char vt100_buffer[VT100_BUFFER_LEN];
+
+    /// Anzahl der Zeichen im vt100-Puffer
+    size_t vt100_buffer_offset;
 } vterminal_t;
 
+
 extern vterminal_t* current_vterm;
 
 /// Virtuelle terminals einrichten
 void init_vterminals(unsigned int count);
 
+/// Ausgabe fuer ein virtuelles Terminal initialisieren
+bool vterm_output_init(vterminal_t* vterm, size_t buffer_lines);
+
+/// Wird beim Wechsel zu einem neuen virtuellen Terminal aufgerufen
+void vterm_output_change(vterminal_t* old, vterminal_t* new);
+
 /// Ausgaben in ein virtuelles Terminal verwalten
 void vterm_process_output(vterminal_t* vterm, char* data, size_t length);
 
 /// Eingaben verarbeiten
 void vterm_process_input(char* data, size_t length);
 
+/// Text ohne vt100-Emulation ausgeben
+void vterm_output_text(vterminal_t* vterm, char* data, size_t length);
 
+
+/// Ausgabeposition aebdern
+void output_set_position(vterm_output_t* out, size_t row,
+    size_t column);
+
+
+/// Alle Zeichen loeschen die bis zu count Zeichen von der aktuellen Position
+/// entfernt sind.
+void output_clear(vterm_output_t* out, size_t count);
+
+/// Bildschirmfenster verschieben
+void screen_scroll(vterm_output_t* out, int lines);
+
+/// Pufferposition in Bildischmposition umrechnen
+bool screen_position(vterm_output_t* out, position_t buffer_pos,
+    position_t* position);
+
+/// Das Gegenstueck zu screen_position
+extern inline void buffer_position(vterm_output_t* out, position_t screen_pos,
+    position_t* dest_pos)
+{
+    dest_pos->line = out->screen_topline + screen_pos.line;
+    dest_pos->line %= out->buffer_lines;
+    dest_pos->column = screen_pos.column;
+}
+
+
+/// Ausgabe in vt100-Emulation verarbeiten
+void vt100_process_output(vterminal_t* vterm, char* data, size_t length);
+
Index: src/modules/vterm/term.c
===================================================================
--- src/modules/vterm/term.c	(Revision 675)
+++ src/modules/vterm/term.c	(Arbeitskopie)
@@ -51,7 +51,7 @@
 static list_t* vterm_list;
 
 /// Virtuelles Terminal erstellen
-vterminal_t* vterm_create(const char* shortcut);
+vterminal_t* vterm_create(char shortcut);
 
 /// Virtuelles Terminal wechseln
 void vterm_change(vterminal_t* vterm);
@@ -59,8 +59,8 @@
 void init_vterminals(unsigned int count)
 {
 
-    const char* shortcuts[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9",
-        "0", "'", "^"};
+    char shortcuts[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '\'',
+        '^'};
 
     // Liste fuer alle vterms erstellen
     vterm_list = list_create();
@@ -81,7 +81,7 @@
  *
  * @return Pointer auf die vterm-Struktur
  */
-vterminal_t* vterm_create(const char* shortcut)
+vterminal_t* vterm_create(char shortcut)
 {
     // vterm-Struktur vorbereiten
     vterminal_t* vterm = malloc(sizeof(vterminal_t));
@@ -111,14 +111,19 @@
     memcpy(path + name_len + 1, "/out", 5);
     vfstree_create_node(path, LOSTIO_TYPES_OUT, 0, vterm, 0);
     vterm->out_node = vfstree_get_node_by_path(path);
+    
     // Shortcut zuweisen
-    vterm->shortcut = malloc(strlen(shortcut) + 1);
-    memcpy(vterm->shortcut, shortcut, strlen(shortcut) + 1);
+    vterm->shortcut = shortcut;
+    
+    // Standardmaessig nichts ausgeben
+    vterm->input_echo = FALSE;
 
     // vterm an Liste Anhaengen
     vterm_list = list_push(vterm_list, vterm);
     
-
+    if (vterm_output_init(vterm, 100) == FALSE) {
+        // TODO
+    }
     return vterm;
 }
 
@@ -130,131 +135,112 @@
 void vterm_change(vterminal_t* vterm)
 {
     if (current_vterm != vterm) {
+        vterm_output_change(current_vterm, vterm);
         current_vterm = vterm;
-        
-        // ANSI-Sequenz zum leeren des Bildschirms
-        fwrite("\033[2J", 1, 4, output);
-
-        // Ausgabenkanal-Buffer anzeigen
-        fwrite(vterm->out_buffer, 1, vterm->out_buffer_size, output);
     }
 }
 
+
 /**
- * Ausgaben eines Prozesses fuer ein Virtuelles Terminal verarbeiten
- * 
- * @param data Pointer auf die auszugebenden Daten
- * @param length Laenge der auszugebenden Daten
+ * Dem Eingabepuffer ein Zeichen anhaengen
  */
-void vterm_process_output(vterminal_t* vterm, char* data, size_t length)
+static void input_buffer_append(vterminal_t* vterm, char c)
 {
-    // Wenn es sich um das aktuelle Terminal handelt, werden die Daten gleich
-    // ausgegeben
-    if (vterm == current_vterm) {
-        fwrite(data, 1, length, output);
-    }
-
-    // Die Daten auch Puffer kopieren, dazu muss der zuerst vergroessert werden
-    // FIXME: potentielles memleak
-    vterm->out_buffer_size += length;
-    vterm->out_buffer = realloc(vterm->out_buffer, vterm->out_buffer_size);
-
+    // FIXME; Ineffizienter konnte ich das nicht loesen. ;-)
+    vterm->in_buffer_size += 1;
+    vterm->in_node->size = current_vterm->in_buffer_size;
+    vterm->in_buffer = realloc(vterm->in_buffer, vterm->in_buffer_size);
     // Daten in den Puffer kopieren
-    memcpy(vterm->out_buffer + vterm->out_buffer_size - length, data, length);
+    vterm->in_buffer[vterm->in_buffer_size - 1] = c;
 }
 
 /**
- * Eingaben verarbeiten
- *
- * @param data Pointer auf die eingegebenen Daten
- * @param length Laenge der eingebenen Daten
+ * Ein zeichen der Eingabe verarbeiten
  */
-void vterm_process_input(char* data, size_t length)
+static void input_process_char(vterminal_t* vterm, char c)
 {
-    static bool wait_switch = FALSE;
-    static bool last_switch = FALSE;
-    
-    // Falls die Taste zum Terminalwechsel gedrueckt wurde, wird versucht zu
-    // wechseln
-    if (wait_switch == TRUE) {
-        wait_switch = FALSE;
+    static bool wait_command = FALSE;
 
-        // Verhindert Probleme, wenn Escape gedrueckt wurde, und dann nicht
-        // gewechselt wurde
-        last_switch = TRUE;
-
-        vterminal_t* vterm = NULL;
-        int i = 0;
-        // Pruefe ob ein Passendes Terminal vorhanden ist
-        while ((vterm = list_get_element_at(vterm_list, i++))) {
-            if (length >= strlen(vterm->shortcut) && (strncmp(data, vterm->
-                shortcut, strlen(vterm->shortcut)) == 0))
-            {
+    if (wait_command == TRUE) {
+        wait_command = FALSE;
+        switch (c) {
+            // ESC wird momentan zum einleiten von Befehlen Benutzt
+            case '\033':
+                // Wenn schon einmal ESC gedrueckt wurde, wird ein ESC
+                // weitergeleitet.
+                input_buffer_append(vterm, c);
                 break;
-            }
-        }
         
-        // Wenn nein wird abgebrochen und ganz normal ausgegeben
-        if (vterm == NULL) {
-            vterm_process_input(data, length);
-            return;
-        }
+            // ESC + s => Scroll lock
+            case 's':
+                vterm->output.scroll_lock = !vterm->output.scroll_lock;
+                break;
         
-        // Falls doch etwas gefunden wurde, wird gewechselt.
-        vterm_change(vterm);
-        
-        // Es wurde doch erfolgreich gewechselt, also darf das nächste Escape
-        // wieder benutzt werden
-        last_switch = FALSE;
+            // ESC + u => Scroll up
+            case 'u':
+                screen_scroll(&(vterm->output), -10);
+                break;
 
-        // Wenn noch mehr Daten da sind werden die ausgegeben
-        size_t shortcut_len = strlen(vterm->shortcut);
-        if (length - shortcut_len != 0) {
-            vterm_process_input(data + shortcut_len, length - shortcut_len);
-        }
-    } else {    
-        // Nach aufforderungen zum Terminal-Wechseln suchen
-        char* chg = memchr(data, 27, length);
-        
-        // Wenn escape gedrueckt wurde, und nicht gewechselt wurde, darf ein
-        // weiteres escape nicht benutzt werden
-        if ((last_switch == TRUE) && (chg == data)) {
-            chg = NULL;
-        }
-        last_switch = FALSE;
+            // ESC + d => Scroll down
+            case 'd':
+                screen_scroll(&(vterm->output), 10);
+                break;
 
-        // Falls eine vorhanden ist, wird bis davor ausgegeben
-        if (chg != NULL) {
-            // Wenn noch Daten davor da sind, werden diese verarbeitet
-            if (chg != data) {
-                vterm_process_input(data, chg - data);
-                length -= chg - data;
-                data += chg - data;
-            }
-            
-            wait_switch = TRUE;
-            
-            // Wechselaufforderung ueberspringen
-            data++;
-            length--;
+            // Alles andere => Terminalwechsel
+            default: {
+                vterminal_t* vterm_switch = NULL;
+                int i = 0;
 
-            // Wenn danach auch noch Daten vorhanden sind, werden diese auch
-            // noch verarbeitet
-            if (length != 0) {
-                vterm_process_input(data, length);
+                // Passendes Terminal suchen
+                while ((vterm_switch = list_get_element_at(vterm_list, i++))) {
+                    if (vterm_switch->shortcut == c) {
+                        break;
+                    }
+                }
+
+                // Wenn kein passendes Terminal gefunden werden konnte, wird
+                // das Zeichen normal ausgegeben
+                if (vterm_switch == NULL) {
+                    input_buffer_append(vterm, c);
+                    break;
+                }
+                
+                // Ansonsten wird jetzt gewechselt
+                vterm_change(vterm_switch);
+                break;
             }
-        } else {
-            // Die Daten auch Puffer kopieren, dazu muss der zuerst
-            // vergroessert werden
-            // FIXME: potentielles memleak
-            current_vterm->in_buffer_size += length;
-            current_vterm->in_node->size = current_vterm->in_buffer_size;
-            current_vterm->in_buffer = realloc(current_vterm->in_buffer,
-                current_vterm->in_buffer_size);
-            // Daten in den Puffer kopieren
-            memcpy(current_vterm->in_buffer + current_vterm->in_buffer_size -
-                length, data, length);
         }
+    } else {
+        switch (c) {
+            // ESC wird momentan zum einleiten von Befehlen Benutzt
+            case '\033':
+                // Das naechste Zeichen wird zum identifizieren des neuen
+                // Terminals benutzt.
+                wait_command = TRUE;
+                
+            break;
+        
+            // Alles andere
+            default:
+                input_buffer_append(vterm, c);
+                break;
+        }
     }
 }
 
+/**
+ * Eingaben verarbeiten
+ *
+ * @param data Pointer auf die eingegebenen Daten
+ * @param length Laenge der eingebenen Daten
+ */
+void vterm_process_input(char* data, size_t length)
+{
+    size_t i;
+    
+    // Eingabe wird zeichenweise verarbeitet
+    for (i = 0; i < length; i++) {
+        input_process_char(current_vterm, data[i]);
+    }
+}
+
Index: src/modules/vterm/vt100.c
===================================================================
--- src/modules/vterm/vt100.c	(Revision 0)
+++ src/modules/vterm/vt100.c	(Revision 0)
@@ -0,0 +1,635 @@
+/*  
+ * Copyright (c) 2007 The LOST Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the LOST Project
+ * by Antoine Kaufmann.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the LOST Project
+ *     and its contributors.
+ * 4. Neither the name of the LOST Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <types.h>
+#include <syscall.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rpc.h>
+#include <lostio.h>
+#include "vterm.h"
+#include <sleep.h>
+
+struct vt100_sequence_handler {
+    const char* sequence;
+    void (*handler)(vterminal_t* vterm, bool have_n1, bool have_n2, int n1,
+        int n2);
+};
+
+static void vt100_buffer_flush(vterminal_t* vterm);
+static bool vt100_buffer_validate(vterminal_t* vterm);
+
+static void vt100_cursor_up(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+static void vt100_cursor_down(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+static void vt100_cursor_left(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+static void vt100_cursor_right(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+static void vt100_cursor_bol_down(vterminal_t* vterm, bool have_n1,
+    bool have_n2, int n1, int n2);
+static void vt100_cursor_bol_up(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+static void vt100_cursor_direct(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+static void vt100_cursor_save(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+static void vt100_cursor_restore(vterminal_t* vterm, bool have_n1,
+    bool have_n2, int n1, int n2);
+
+static void vt100_clear_screen(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+static void vt100_clear_line(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+static void vt100_text_attr(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2);
+
+
+static struct vt100_sequence_handler handler_list[] = {
+    // ANSI
+    {"\033[\1A",        vt100_cursor_up},
+    {"\033[\1B",        vt100_cursor_down},
+    {"\033[\1C",        vt100_cursor_right},
+    {"\033[\1D",        vt100_cursor_left},
+    {"\033[\1E",        vt100_cursor_bol_down},
+    {"\033[\1F",        vt100_cursor_bol_up},
+    {"\033[\1;\1f",     vt100_cursor_direct},
+    {"\033[\1;\1H",     vt100_cursor_direct},
+    {"\033[s",          vt100_cursor_save},
+    {"\033[u",          vt100_cursor_restore},
+
+    {"\033[2J",         vt100_clear_screen},
+    {"\033[K",          vt100_clear_line},
+    {"\033[\1;\1m",     vt100_text_attr}
+
+    // VT100
+    // ...
+};
+
+/**
+ * Ausgaben durch vt100-Emulation verarbeiten
+ */
+void vt100_process_output(vterminal_t* vterm, char* data, size_t length)
+{
+    char c;
+    int i;
+    char* last_flush = data;
+    size_t text_len = 0;
+    
+    // Der String wird jetzt Zeichenweise durchsucht. Nach einem Escape-Zeichen
+    // werden die Zeichen so lange gepuffert, bis die Sequenz fertig ist, oder
+    // feststeht, dass sie ungueltig ist. In diesem Fall, werden alle Zeichen
+    // ausser dem Escape-Zeichen ausgegeben.
+    for (i = 0; i < length; i++) {
+        c = data[i];
+        
+        // Die vorherigen Zeichen waren alle normaler Text, imd auch das
+        // Aktuelle ist kein Beginn einer Sequenz. Die Zeichen werden also
+        // aufbewahrt, bis wir am Ende sind, oder eine Escape-Sequenz anfaengt.
+        if ((vterm->vt100_buffer_offset == 0) && (c != '\033')) {
+            text_len++;
+        } else if (c == '\033') {
+            if (vterm->vt100_buffer_offset == 0) {
+                // Der Beginn einer Sequenz. Jetzt wird ausgegeben, was bis
+                // jetzt gekommen ist.
+                if (text_len != 0)  {
+                    vterm_output_text(vterm, last_flush, text_len);
+                }
+            } else {
+                // Wir haben noch eine Sequenz im Puffer, die allerdings nich
+                // gueltig ist.
+                vt100_buffer_flush(vterm);
+            }
+            last_flush = data + i + 1;
+            text_len = 0;
+
+            // Jetzt wird das Escape-Zeichen in den Puffer gelegt.
+            vterm->vt100_buffer[0] = '\033';
+            vterm->vt100_buffer_offset = 1;
+        } else {
+            // Ein Zeichen in einer Escape-Sequenz
+            
+            // Wenn der Puffer voll ist, dann stimmt sowieso was nicht.
+            if (vterm->vt100_buffer_offset == VT100_BUFFER_LEN) {
+                vt100_buffer_flush(vterm);
+
+                // Das aktuelle Zeichen ist dann reiner Text
+                last_flush = data + i;
+                text_len = 1;
+            } else {
+                // Sonst wird das Zeichen an den Buffer angehaengt, und
+                // geprueft, ob die Sequenz schon abgeschlossen, oder noch
+                // gueltig ist.
+                vterm->vt100_buffer[vterm->vt100_buffer_offset] = c;
+                vterm->vt100_buffer_offset++;
+
+                if (vt100_buffer_validate(vterm) == FALSE) {
+                    // Wenn die Sequenz nicht mehr gueltig ist, wird sie
+                    // ausgegeben.
+                    vt100_buffer_flush(vterm);
+                }
+
+                // Fruehstens das naechste Zeichen ist dann vernuenftiger Text
+                last_flush = data + i + 1;
+                text_len = 0;
+            }
+        }
+    }
+
+    // Falls noch etwas uebrig blieb, wird das jetzt ausgegeben
+    if (text_len != 0) {
+        vterm_output_text(vterm, last_flush, text_len);
+    }
+}
+
+/**
+ * VT100-Puffer leeren.
+ */
+static void vt100_buffer_flush(vterminal_t* vterm)
+{
+    // Alles ausser dem Escape-Zeichen ausgeben
+    if (vterm->vt100_buffer_offset > 1) {
+        vterm_output_text(vterm, vterm->vt100_buffer + 1, vterm->
+            vt100_buffer_offset - 1);
+    }
+    vterm->vt100_buffer_offset = 0;
+}
+
+/**
+ * Pfuefen der VT100-Escapesequenz. Falls sie vollstaendig ist, wird sie
+ * ausgefuehrt.
+ *
+ * @return TRUE falls die Sequenz gueltig ist/war, FALSE sonst
+ */
+static bool vt100_buffer_validate(vterminal_t* vterm)
+{
+    // Laufvariable fuer Sequenz
+    int i;
+
+    // Laufvariable fuer Zeichen im Sequenzmuste
+    int j;
+
+    // TRUE wenn der ensprechende Parameter schon gefunden wurde
+    bool have_n1;
+    bool have_n2;
+
+    // TRUE wenn ein Semikolon, das zum trennen von Parametern benutzt wird,
+    // gefunden wurde
+    bool have_sep = FALSE;
+
+    // Die Werte der Parameter; Nur korrekt falls have_nX = TRUE
+    int n1;
+    int n2;
+
+    bool matches;
+
+    // Pointer auf das Zeichen im vt100-Puffer, das gerade verarbeitet wird. 
+    char* cur_pos= vterm->vt100_buffer;
+    // Offset im v100-Puffer
+    size_t offset = 0;
+
+    // Anzahl der Seqenzen im Array.
+    size_t sequences = sizeof(handler_list) / sizeof(struct
+        vt100_sequence_handler);
+
+    // Alle Sequenzen in der Liste durchgehen und schauen ob eine passt.
+    for (i = 0;  i < sequences; i++) {
+        // Initialwerte fuer jeden Durchlauf
+        matches = TRUE;
+        have_n1 = FALSE;
+        have_n2 = FALSE;
+        have_sep = FALSE;
+        cur_pos= vterm->vt100_buffer;
+        offset = 0;
+
+        // Sequenzmuster vergleichen
+        for (j = 0; (j <= strlen(handler_list[i].sequence)) && matches; j++) {
+            char c = handler_list[i].sequence[j]; 
+
+            // Wenn wir am Bufferende angekommen sind, und das Muster noch
+            // nicht zu Ende ist, wird abgebrochen, denn dann ist die Sequenz
+            // noch gueltig.
+            if ((offset >= vterm->vt100_buffer_offset) && (c != '\0')) {
+                break;
+            }
+            switch (c) {
+                case '\1': {
+                    int count;
+                    int* n;
+                    bool* have;
+
+                    // Entscheiden ob wir jetzt an n1 oder n2 rumspielen. Das
+                    // haengt allein davon ab, ob der Seperator schon gekommen
+                    // ist oder nicht.
+                    if (have_sep == FALSE) {
+                        n = &n1;
+                        have = &have_n1;
+                    } else {
+                        n = &n2;
+                        have = &have_n2;
+                    }
+                    
+                    // Initialwerte, falls keine Ziffern gefunden werden
+                    *n = 0;
+                    *have = FALSE;
+                    count = 0;
+
+                    // Ein numerisches Argument darf mehrere Ziffern haben
+                    while ((*cur_pos >= '0') && (*cur_pos <= '9') && (offset <
+                        vterm->vt100_buffer_offset)) 
+                    {
+                        *n = *n * 10 + (*cur_pos - '0');
+
+                        cur_pos++;
+                        offset++;
+                        count++;
+                    }
+
+                    if (count != 0) {
+                        *have = TRUE;
+                    }
+                    break;
+                }
+
+                case ';':
+                    // Der Seperator _muss_ auch in der Sequenz im Puffer
+                    if (*cur_pos != ';') {
+                        matches = FALSE;
+                        break;
+                    }
+
+                    // Mehrere Seperatoren dürfen nicht vorkommen
+                    if (have_sep == TRUE) {
+                        matches = FALSE;
+                    } else {
+                        have_sep = TRUE;
+                    }
+                    cur_pos++;
+                    offset++;
+                    break;
+                    
+                case '\0':
+                    // Ende der Sequenz erreicht, sie ist vollstaendig im
+                    // Puffer und kann verarbeitet werden.
+                    handler_list[i].handler(vterm, have_n1, have_n2, n1, n2);
+                    // Puffer leeren
+                    vterm->vt100_buffer_offset = 0;
+                    return TRUE;
+                    break;
+
+                default:
+                    // Alle anderen Zeichen muessen 1:1 stimmen
+                    if (c != *cur_pos) {
+                        matches = FALSE;
+                    }
+
+                    cur_pos++;
+                    offset++;
+                    break;
+            }
+        }
+        
+        // Diese Seqeuenz ist zwar noch nicht vollstaendig, passt aber bis
+        // jetzt.
+        if (matches == TRUE) {
+            return TRUE;
+        }
+    }
+
+    // Wenn bis hier nichts gefunden wurde, ist die Sequenz ungueltig
+    return FALSE;
+}
+
+/**
+ * ESC[nA
+ * Cursor um n1 Zeilen nach oben bewegen. Wenn n1 nicht angegeben wird, wird
+ * n1 = 1 angenommen.
+ */
+static void vt100_cursor_up(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    position_t pos;
+    vterm_output_t* out = &(vterm->output);
+    
+    // Standardwert fuer n1 setzen
+    if (!have_n1) {
+        n1 = 1;
+    }
+
+    // Versuchen die Position auf dem Bildschirm zu ermitteln
+    if (screen_position(out, out->buffer_pos, &pos)) {
+        if (pos.line >= n1) {
+            // Neue Position setzen
+            output_set_position(out, out->buffer_pos.line - n1,
+                out->buffer_pos.column);
+        }
+    }
+}
+
+/**
+ * ESC[nB
+ * Cursor um n1 Zeilen nach unten bewegen. Wenn n1 nicht angegeben wird, wird
+ * n1 = 1 angenommen.
+ */
+static void vt100_cursor_down(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    position_t pos;
+    vterm_output_t* out = &(vterm->output);
+    
+    // Standardwert fuer n1 setzen
+    if (!have_n1) {
+        n1 = 1;
+    }
+
+    // Versuchen die Position auf dem Bildschirm zu ermitteln
+    if (screen_position(out, out->buffer_pos, &pos)) {
+        if (pos.line < out->screen_height - n1) {
+            // Neue Position setzen
+            output_set_position(out, out->buffer_pos.line + n1,
+                out->buffer_pos.column);
+        }
+    }
+}
+
+/**
+ * ESC[nC
+ * Cursor um n1 Spalten nach links bewegen. Wenn n1 nicht angegeben wird, wird
+ * n1 = 1 angenommen.
+ */
+static void vt100_cursor_left(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    position_t pos;
+    vterm_output_t* out = &(vterm->output);
+    
+    // Standardwert fuer n1 setzen
+    if (!have_n1) {
+        n1 = 1;
+    }
+
+    // Versuchen die Position auf dem Bildschirm zu ermitteln
+    if (screen_position(out, out->buffer_pos, &pos)) {
+        if (pos.column >= n1) {
+            // Neue Position setzen
+            output_set_position(out, out->buffer_pos.line,
+                out->buffer_pos.column - n1);
+        }
+    }
+}
+
+/**
+ * ESC[nD
+ * Cursor um n1 Spalten nach rechts bewegen. Wenn n1 nicht angegeben wird, wird
+ * n1 = 1 angenommen.
+ */
+static void vt100_cursor_right(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    position_t pos;
+    vterm_output_t* out = &(vterm->output);
+    
+    // Standardwert fuer n1 setzen
+    if (!have_n1) {
+        n1 = 1;
+    }
+
+    // Versuchen die Position auf dem Bildschirm zu ermitteln
+    if (screen_position(out, out->buffer_pos, &pos)) {
+        if (pos.column < out->screen_width - n1) {
+            // Neue Position setzen
+            output_set_position(out, out->buffer_pos.line,
+                out->buffer_pos.column + n1);
+        }
+    }
+}
+
+/**
+ * ESC[nE
+ * Den Cursor an den Anfang der Zeile, die n1 Zeilen unterhalb der Aktuellen
+ * ist, setzen. Wenn n1 nicht angegeben wird, wird n1 = 1 angenommen.
+ */
+static void vt100_cursor_bol_down(vterminal_t* vterm, bool have_n1,
+    bool have_n2, int n1, int n2)
+{
+    vterm_output_t* out = &(vterm->output);
+    
+    // Standardwert fuer n1 setzen
+    if (!have_n1) {
+        n1 = 1;
+    }
+
+    // Neue Position setzen
+    output_set_position(out, out->buffer_pos.line + n1, 0);
+}
+
+/**
+ * ESC[nF
+ * Den Cursor an den Anfang der Zeile, die n1 Zeilen oberhalb der Aktuellen
+ * ist, setzen. Wenn n1 nicht angegebern wird, wird n1 = 1 angenommen.
+ */
+static void vt100_cursor_bol_up(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    vterm_output_t* out = &(vterm->output);
+    
+    // Standardwert fuer n1 setzen
+    if (!have_n1) {
+        n1 = 1;
+    }
+
+    // Neue Position setzen
+    output_set_position(out, out->buffer_pos.line - n1, 0);
+}
+
+/**
+ * ESC[n;nf
+ * ESC[n;nH
+ * Cursorposition direkt setzen. n1 = Zeile, n2 = Spalte
+ *
+ * Wenn keiner der Parameter angegeben wird, wird der Cursor in die obere linke
+ * Ecke positioniert.
+ */
+static void vt100_cursor_direct(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    position_t buffer_pos;
+    position_t screen_pos;
+    vterm_output_t* out = &(vterm->output);
+
+    // Standardwerte hernehmen, wenn nichts angegben wurde
+    if (!have_n1) {
+        n1 = 0;
+    }
+    if (!have_n2) {
+        n2 = 0;
+    }
+    
+    // In positionstruktur einfuellen
+    screen_pos.line = n1;
+    screen_pos.column = n2;
+
+    buffer_position(out, screen_pos, &buffer_pos);
+    output_set_position(out, buffer_pos.line, buffer_pos.column); 
+}
+
+/**
+ * ESC[s
+ * Cursorposition abspeichern, um sie spaeter mit ESC[u wiederherstellen zu
+ * koennen.
+ */
+static void vt100_cursor_save(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    position_t pos;
+    vterm_output_t* out = &(vterm->output);
+    
+    // Versuchen die Position auf dem Bildschirm zu ermitteln
+    if (screen_position(out, out->buffer_pos, &pos)) {
+        out->vt100_pos_backup = pos;
+        out->vt100_color_backup = out->current_color;
+        out->vt100_backup_valid = TRUE;
+    }
+}
+
+/**
+ * ESC[u
+ * Cursorposition wiederherstellen
+ */
+static void vt100_cursor_restore(vterminal_t* vterm, bool have_n1,
+    bool have_n2, int n1, int n2)
+{
+    vterm_output_t* out = &(vterm->output);
+
+    // Wenn die Positionen wirklich gespeichert wurden, werden sie jetzt
+    // wiederhergestellt.
+    if (out->vt100_backup_valid) {
+        buffer_position(out, out->vt100_pos_backup, &(out->buffer_pos));
+        out->current_color = out->vt100_color_backup;
+        out->vt100_backup_valid = FALSE;
+    }
+}
+
+/**
+ * ESC[2J
+ * Bildschirm von der Cursorposition an leeren.
+ */
+static void vt100_clear_screen(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    vterm_output_t* out = &(vterm->output);
+
+    // Anzahl der uebrigen Zeichen
+    size_t count = (out->screen_height - out->buffer_pos.line) * out->
+        screen_width - out->buffer_pos.column;
+
+    // Zeichen loeschen
+    if (count != 0) {
+        output_clear(out, count);
+    }
+
+}
+
+/**
+ * ESC[K
+ * Zeile von der Cursorposition an leeren.
+ */
+static void vt100_clear_line(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    vterm_output_t* out = &(vterm->output);
+
+    // Anzahl der uebrigen Zeichen
+    size_t count = out->screen_width - out->buffer_pos.column;
+
+    // Zeichen loeschen
+    if (count != 0) {
+        output_clear(out, count);
+    }
+}
+
+/**
+ * Parameter fuer Textattribute verarbeiten
+ */
+static void vt100_text_attr_param(vterminal_t* vterm, int n)
+{
+    vterm_output_t* out = &(vterm->output);
+    // Zum Umrechnen der ANSI Farbcodes in die VGA Palette.
+    static char colors[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+    switch(n)
+    {
+        case 0: // Normal
+            out->current_color.bold = 0;
+            out->current_color.blink = 0;
+            break;
+        
+        case 1: // Fett
+            out->current_color.bold = 1;
+            break;
+
+        case 5: // Blinken
+            out->current_color.blink = 1;
+            break;
+
+        case 30 ... 37: // Vordergrundfarbe
+            out->current_color.foreground = colors[n - 30];
+            break;
+
+        case 40 ... 47: // Hintergrundfarbe
+            out->current_color.background = colors[n - 40];
+            break;
+    }
+
+}
+
+/**
+ * ESC[n;nm
+ * Textattribute einstellen
+ */
+static void vt100_text_attr(vterminal_t* vterm, bool have_n1, bool have_n2,
+    int n1, int n2)
+{
+    // Die Parameter muessen natuerlich jeweils nur verarbeitet werden, wenn
+    // sie beide angegeben wurden.
+    if (have_n1)  {
+        vt100_text_attr_param(vterm, n1);
+    }
+    if (have_n2)  {
+        vt100_text_attr_param(vterm, n2);
+    }
+}
+
Index: src/modules/vterm/vterm.c
===================================================================
--- src/modules/vterm/vterm.c	(Revision 675)
+++ src/modules/vterm/vterm.c	(Arbeitskopie)
@@ -46,19 +46,17 @@
 #include "init.h"
 
 
-/// Oeffnet Eingabe- und Ausgabedatei
+/// Oeffnet Eingabdatei
 static bool init_io_files();
 /// Hier kommen die Eingaben fuer all die virtuellen Terminals her
 FILE* input = NULL;
-/// Und hier gehen die Ausgaben hin
-FILE* output = NULL;
 
 /// Eingaben einlesen
 static void get_input();
 
 int main(int argc, char* argv[])
 {
-    // Filehandles fuer Ein- und Ausgabe oeffnen
+    // Eingabedatei oeffnen
     if (init_io_files() == FALSE) {
         // Falls das nicht klappt, ist alles sinnlos. ;-)
         return -1;
@@ -67,6 +65,8 @@
     // Schnittstelle fuer die anderen Prozesse einrichten
     init_lostio_interface();
     
+    init_output();
+
     init_vterminals(10);
     
     init_service_register("vterm");
@@ -80,7 +80,7 @@
 }
 
 /**
- * File-Handle fuer die Eingaben und Ausgaben aller Terminals oeffnen
+ * File-Handle fuer die Eingaben aller Terminals oeffnen
  *
  * @return TRUE bei Erfolg, FALSE im Fehlerfall
  */
@@ -88,41 +88,22 @@
 {
     int i;
     
-    // FIXME: Diese Pfade sollten aus einer Konfigurationsdatei oder aus den
-    // Parametern kommen!
-    
     // Falls das öffnen nicht beim ersten Anlauf klappt, wird weiter Probiert
     // 5 Mal im Abstand von einer Sekunde
-    for (i = 0; i < 5; i++) {
+    for (i = 0; (i < 10) && (input == NULL); i++) {
         if (input == NULL) {
             input = fopen("kbc:/keyboard/ascii", "r");
         }
 
-        if (output == NULL) {
-            output = fopen("textterm:/echo", "w");
-        }
-        
         // Pruefen ob schon beide erfolgreich geoeffnet wurden
-        if ((input == NULL) || (output == NULL)) {
-            msleep(1000);
-        } else {
-            break;
+        if ((input == NULL)) {
+            msleep(500);
         }
     }
     
-    // Wenn nicht beide geoeffnet werden konnten, muss abgebrochen werden
-    if ((input == NULL) || (output == NULL)) {
-        if (input == NULL) {
-            puts("Input konnte nicht geoeffnet werden!");
-        } else {
-            fclose(input);
-        }
-
-        if (output == NULL) {
-            puts("Output konnte nicht geoeffnet werden!");
-        } else {
-            fclose(output);
-        }
+    // Wenn input nicht geoeffnet werden konnten, muss abgebrochen werden
+    if (input == NULL) {
+        puts("Input konnte nicht geoeffnet werden!");
         return FALSE;
     }