diff -Naur qemu/Makefile.target qemu-curses/Makefile.target
--- qemu/Makefile.target	2005-12-06 21:42:17.000000000 +0000
+++ qemu-curses/Makefile.target	2005-12-12 02:47:58.000000000 +0000
@@ -341,6 +341,9 @@
 ifdef CONFIG_SDL
 VL_OBJS+=sdl.o
 endif
+ifdef CONFIG_CURSES
+VL_OBJS+=curses.o
+endif
 ifdef CONFIG_COCOA
 VL_OBJS+=cocoa.o
 COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
@@ -383,7 +386,7 @@
 endif
 
 $(QEMU_SYSTEM): $(VL_OBJS) libqemu.a
-	$(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS)
+	$(CC) $(VL_LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(CURSES_LIBS) $(VL_LIBS)
 
 cocoa.o: cocoa.m
 	$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
@@ -394,6 +397,9 @@
 sdlaudio.o: sdlaudio.c
 	$(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
 
+curses.o: curses.c
+	$(CC) $(CFLAGS) $(DEFINES) $(CURSES_CFLAGS) -c -o $@ $<
+
 depend: $(SRCS)
 	$(CC) -MM $(CFLAGS) $(DEFINES) $^ 1>.depend
 
diff -Naur qemu/configure qemu-curses/configure
--- qemu/configure	2005-12-06 21:42:55.000000000 +0000
+++ qemu-curses/configure	2005-12-12 05:11:14.000000000 +0000
@@ -181,6 +181,8 @@
   ;;
   --enable-dsound) dsound="yes"
   ;;
+  --disable-curses) curses="no"
+  ;;
   --enable-fmod) fmod="yes"
   ;;
   --fmod-lib=*) fmod_lib=${opt#--fmod-lib=}
@@ -348,6 +350,24 @@
 fi # cross compilation
 fi # -z $sdl
 
+##########################################
+# curses probe
+
+if test -z "$curses" ; then
+
+curses=no
+
+cat > $TMPC << EOF
+#include <curses.h>
+int main( void ) { return curses_version (); }
+EOF
+
+if $cc -o $TMPE -lcurses $TMPC 2> /dev/null ; then
+curses=yes
+fi
+
+fi # -z $curses
+
 if test x"$1" = x"-h" -o x"$1" = x"--help" ; then
 cat << EOF
 
@@ -469,6 +489,7 @@
 if test "$sdl" != "no" ; then
     echo "SDL static link   $sdl_static"
 fi
+echo "curses support    $curses"
 echo "mingw32 support   $mingw32"
 echo "Adlib support     $adlib"
 echo "CoreAudio support $coreaudio"
@@ -680,7 +701,8 @@
         -a "$sdl" = "no" -a "$cocoa" = "no" ; then
     echo "ERROR: QEMU requires SDL or Cocoa for graphical output"
     echo "To build QEMU with graphical output configure with --disable-gfx-check"
-    echo "Note that this will disable all output from the virtual graphics card."
+    echo "Note that this will disable all output from the virtual graphics card"
+    echo "except for the curses interface."
     exit 1;
 fi
 
@@ -798,6 +820,15 @@
     echo "CONFIG_COCOA=yes" >> $config_mak
 fi
 
+if test "$curses" = "yes" ; then
+    if test "$target_cpu" != "sparc" -a "$target_cpu" != "arm" ; then
+        echo "#define CONFIG_CURSES 1" >> $config_h
+        echo "CONFIG_CURSES=yes" >> $config_mak
+        echo "CURSES_LIBS=-lcurses" >> $config_mak
+        echo "CURSES_CFLAGS=" >> $config_mak
+    fi
+fi
+
 done # for target in $targets
 
 # build tree in object directory if source path is different from current one
diff -Naur qemu/console.c qemu-curses/console.c
--- qemu/console.c	2004-10-09 17:32:58.000000000 +0000
+++ qemu-curses/console.c	2005-12-08 18:59:24.000000000 +0000
@@ -21,52 +21,11 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-#include "vl.h"
+#include "console.h"
 
 #define DEFAULT_BACKSCROLL 512
 #define MAX_CONSOLES 12
 
-#define RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
-#define RGB(r, g, b) RGBA(r, g, b, 0xff)
-
-typedef struct TextCell {
-    uint8_t ch;
-    uint8_t bgcol:4;
-    uint8_t fgcol:4;
-} TextCell;
-
-#define MAX_ESC_PARAMS 3
-
-enum TTYState {
-    TTY_STATE_NORM,
-    TTY_STATE_ESC,
-    TTY_STATE_CSI,
-};
-
-struct TextConsole {
-    int text_console; /* true if text console */
-    DisplayState *ds;
-    int g_width, g_height;
-    int width;
-    int height;
-    int total_height;
-    int backscroll_height;
-    int fgcol;
-    int bgcol;
-    int x, y;
-    int y_displayed;
-    int y_base;
-    TextCell *cells;
-
-    enum TTYState state;
-    int esc_params[MAX_ESC_PARAMS];
-    int nb_esc_params;
-
-    /* kbd read handler */
-    IOReadHandler *fd_read;
-    void *fd_opaque;
-};
-
 static TextConsole *active_console;
 static TextConsole *consoles[MAX_CONSOLES];
 static int nb_consoles = 0;
@@ -729,3 +688,8 @@
 
     return chr;
 }
+
+TextConsole *get_active_console()
+{
+    return active_console;
+}
diff -Naur qemu/console.h qemu-curses/console.h
--- qemu/console.h	1970-01-01 00:00:00.000000000 +0000
+++ qemu-curses/console.h	2005-12-08 18:59:24.000000000 +0000
@@ -0,0 +1,70 @@
+/*
+ * QEMU graphical console definitions
+ * 
+ * Copyright (c) 2004 Fabrice Bellard
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CONSOLE_H
+#define CONSOLE_H
+
+#include "vl.h"
+
+#define RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+#define RGB(r, g, b) RGBA(r, g, b, 0xff)
+
+typedef struct TextCell {
+    uint8_t ch;
+    uint8_t bgcol:4;
+    uint8_t fgcol:4;
+} TextCell;
+
+#define MAX_ESC_PARAMS 3
+
+enum TTYState {
+    TTY_STATE_NORM,
+    TTY_STATE_ESC,
+    TTY_STATE_CSI,
+};
+
+struct TextConsole {
+    int text_console; /* true if text console */
+    DisplayState *ds;
+    int g_width, g_height;
+    int width;
+    int height;
+    int total_height;
+    int backscroll_height;
+    int fgcol;
+    int bgcol;
+    int x, y;
+    int y_displayed;
+    int y_base;
+    TextCell *cells;
+
+    enum TTYState state;
+    int esc_params[MAX_ESC_PARAMS];
+    int nb_esc_params;
+
+    /* kbd read handler */
+    IOReadHandler *fd_read;
+    void *fd_opaque;
+};
+
+#endif /* CONSOLE_H */
diff -Naur qemu/curses.c qemu-curses/curses.c
--- qemu/curses.c	1970-01-01 00:00:00.000000000 +0000
+++ qemu-curses/curses.c	2005-12-12 04:45:50.000000000 +0000
@@ -0,0 +1,319 @@
+/*
+ * QEMU curses/ncurses display driver
+ * 
+ * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org>
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "vl.h"
+#include "console.h"
+
+#include <curses.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#endif
+
+#include "curses_keys.h"
+
+static char screen[160 * 100];
+static WINDOW *screenpad = NULL;
+static int width, height, invalidate;
+static int px, py, sminx, sminy, smaxx, smaxy;
+
+static void curses_update(DisplayState *ds, int x, int y, int w, int h)
+{
+    int i, j, mw, mh;
+    chtype *line;
+    TextConsole *s;
+
+    if (is_active_console(vga_console)) {
+        line = ((chtype *) screen) + y * width;
+        for (h += y; y < h; y ++, line += width)
+            mvwaddchnstr(screenpad, y, 0, line, width);
+
+        pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
+    } else {
+        s = get_active_console();
+        if (!s->text_console)
+            return;
+
+        mw = s->width;
+        if (mw > COLS)
+            mw = COLS;
+        mh = s->height;
+        if (mh > LINES)
+            mh = LINES;
+
+        for (i = 0; i < mh; i ++)
+            for (j = 0; j < mw; j ++)
+                mvaddch(i, j, s->cells[
+                        (i + s->y_base + s->height - mh) * s->width + j].ch);
+        move(s->y + mh - s->height, s->x);
+        curs_set(2);
+    }
+    refresh();
+}
+
+static void curses_calc_pad()
+{
+    if (width > COLS) {
+        px = (width - COLS) / 2;
+        sminx = 0;
+        smaxx = COLS;
+    } else {
+        px = 0;
+        sminx = (COLS - width) / 2;
+        smaxx = sminx + width;
+    }
+
+    if (height > LINES) {
+        py = (height - LINES) / 2;
+        sminy = 0;
+        smaxy = LINES;
+    } else {
+        py = 0;
+        sminy = (LINES - height) / 2;
+        smaxy = sminy + height;
+    }
+}
+
+static void curses_resize(DisplayState *ds, int w, int h)
+{
+    if (w == width && h == height)
+        return;
+
+    if (screenpad)
+        delwin(screenpad);
+
+    clear();
+    refresh();
+
+    screenpad = newpad(h, w);
+    width = w;
+    height = h;
+
+    curses_calc_pad();
+}
+
+#ifndef _WIN32
+#ifdef SIGWINCH
+static void curses_winch_handler(int signum)
+{
+    struct winsize {
+        unsigned short ws_row;
+        unsigned short ws_col;
+        unsigned short ws_xpixel;   /* unused */
+        unsigned short ws_ypixel;   /* unused */
+    } ws;
+
+    /* terminal size changed */
+    if (ioctl(1, TIOCGWINSZ, &ws) == -1)
+        return;
+
+    resize_term(ws.ws_row, ws.ws_col);
+    curses_calc_pad();
+    invalidate = 1;
+
+    /* some systems require this */
+    signal(SIGWINCH, curses_winch_handler);
+}
+#endif
+#endif
+
+static void curses_cursor_position(DisplayState *ds, int x, int y)
+{
+    if (x >= 0) {
+        x = sminx + x - px;
+        y = sminy + y - py;
+
+        if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
+            move(y, x);
+            curs_set(1);
+            return;
+        }
+    }
+
+    curs_set(0);
+}
+
+static void curses_refresh(DisplayState *ds)
+{
+    int chr, nextchr, keysym, keycode;
+
+    if (invalidate) {
+        clear();
+        refresh();
+        if (is_active_console(vga_console)) 
+            vga_invalidate_display();
+        else
+            curses_update(ds, 0, 0, width, height);
+
+        invalidate = 0;
+    }
+
+    if (is_active_console(vga_console)) 
+        vga_update_text(screen, curses_cursor_position);
+
+    nextchr = ERR;
+    while (1) {
+        /* while there are any pending key strokes to process */
+        if (nextchr == ERR)
+            chr = getch();
+        else {
+            chr = nextchr;
+            nextchr = ERR;
+        }
+
+        if (chr == ERR)
+            break;
+
+        /* this shouldn't occur when we use a custom SIGWINCH handler */
+        if (chr == KEY_RESIZE) {
+            clear();
+            refresh();
+            curses_calc_pad();
+            curses_update(ds, 0, 0, width, height);
+            continue;
+        }
+
+        keycode = curses2keycode[chr];
+        if (keycode == -1)
+            continue;
+
+        /* alt key */
+        if (keycode == 1) {
+            nextchr = getch();
+
+            if (nextchr != ERR) {
+                keycode = curses2keycode[nextchr];
+                nextchr = ERR;
+                if (keycode == -1)
+                    continue;
+
+                keycode |= ALT;
+
+                /* process keys reserved for qemu */
+                if (keycode >= QEMU_KEY_CONSOLE0 &&
+                        keycode < QEMU_KEY_CONSOLE0 + 9) {
+                    erase();
+                    wnoutrefresh(stdscr);
+                    console_select(keycode - QEMU_KEY_CONSOLE0);
+
+                    invalidate = 1;
+                    continue;
+                }
+            }
+        }
+
+        if (is_active_console(vga_console)) {
+            /* since terminals don't know about key press and release
+             * events, we need to emit both for each key received */
+            if (keycode & SHIFT)
+                kbd_put_keycode(SHIFT_CODE);
+            if (keycode & CNTRL)
+                kbd_put_keycode(CNTRL_CODE);
+            if (keycode & ALT)
+                kbd_put_keycode(ALT_CODE);
+            if (keycode & GREY)
+                kbd_put_keycode(GREY_CODE);
+            kbd_put_keycode(keycode & KEY_MASK);
+            if (keycode & GREY)
+                kbd_put_keycode(GREY_CODE);
+            kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE);
+            if (keycode & ALT)
+                kbd_put_keycode(ALT_CODE | KEY_RELEASE);
+            if (keycode & CNTRL)
+                kbd_put_keycode(CNTRL_CODE | KEY_RELEASE);
+            if (keycode & SHIFT)
+                kbd_put_keycode(SHIFT_CODE | KEY_RELEASE);
+        } else {
+            keysym = curses2keysym[chr];
+            if (keysym == -1)
+                keysym = chr;
+
+            kbd_put_keysym(keysym);
+        }
+    }
+}
+
+static void curses_cleanup(void) 
+{
+    endwin();
+}
+
+static void curses_setup(void)
+{
+    int i, colour_default[8] = {
+        COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
+        COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE,
+    };
+
+    /* input as raw as possible, let everything be interpreted
+     * by the guest system */
+    initscr(); noecho(); intrflush(stdscr, FALSE);
+    nonl(); keypad(stdscr, TRUE); start_color();
+    raw(); scrollok(stdscr, FALSE);
+
+    for (i = 0; i < 64; i ++)
+        init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
+}
+
+void curses_display_init(DisplayState *ds, int full_screen)
+{
+    /* the terminal deals with keyboard layouts so we don't need to */
+
+#ifndef _WIN32
+    if (!isatty(1)) {
+        fprintf(stderr, "We need a terminal output\n");
+        exit(1);
+    }
+#endif
+
+    curses_setup();
+
+#ifndef _WIN32
+    signal(SIGINT, SIG_DFL);
+    signal(SIGQUIT, SIG_DFL);
+#ifdef SIGWINCH
+    /* some curses implementations provide a handler, but we
+     * want to be sure this is handled regardless of the library */
+    signal(SIGWINCH, curses_winch_handler);
+#endif
+#endif
+
+    ds->data = screen;
+    ds->linesize = 0;
+    ds->depth = 0;
+    ds->width = 640;
+    ds->height = 400;
+    ds->dpy_update = curses_update;
+    ds->dpy_resize = curses_resize;
+    ds->dpy_refresh = curses_refresh;
+
+    invalidate = 0;
+
+    /* Standard VGA initial text mode dimensions */
+    curses_resize(ds, 80, 25);
+
+    atexit(curses_cleanup);
+}
+/* vim: set ai ts=4 sw=4 et: */
diff -Naur qemu/curses_keys.h qemu-curses/curses_keys.h
--- qemu/curses_keys.h	1970-01-01 00:00:00.000000000 +0000
+++ qemu-curses/curses_keys.h	2005-12-12 05:28:52.000000000 +0000
@@ -0,0 +1,241 @@
+/*
+ * Keycode and keysyms conversion tables for curses
+ * 
+ * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org>
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define KEY_RELEASE         128
+#define KEY_MASK            127
+#define SHIFT_CODE          42
+#define SHIFT               128
+#define GREY_CODE           224
+#define GREY                256
+#define CNTRL_CODE          29
+#define CNTRL               512
+#define ALT_CODE            56
+#define ALT                 1024
+
+/* curses won't detect a Control + Alt + 1, so use Alt + 1 */
+#define QEMU_KEY_CONSOLE0   (2 | ALT)   /* (curses2keycode['1'] | ALT) */
+
+#define CURSES_KEYS         KEY_MAX     /* KEY_MAX defined in <curses.h> */
+
+int curses2keycode[CURSES_KEYS] = {
+    [0 ... (CURSES_KEYS - 1)] = -1,
+
+    [27] = 1, /* Escape */
+    ['1'] = 2,
+    ['2'] = 3,
+    ['3'] = 4,
+    ['4'] = 5,
+    ['5'] = 6,
+    ['6'] = 7,
+    ['7'] = 8,
+    ['8'] = 9,
+    ['9'] = 10,
+    ['0'] = 11,
+    ['-'] = 12,
+    ['='] = 13,
+    [127] = 14, /* Backspace */
+    [263] = 14, /* Backspace */
+
+    ['\t'] = 15, /* Tab */
+    ['q'] = 16,
+    ['w'] = 17,
+    ['e'] = 18,
+    ['r'] = 19,
+    ['t'] = 20,
+    ['y'] = 21,
+    ['u'] = 22,
+    ['i'] = 23,
+    ['o'] = 24,
+    ['p'] = 25,
+    ['['] = 26,
+    [']'] = 27,
+    ['\n'] = 28, /* Return */
+    ['\r'] = 28, /* Return */
+    [343] = 28, /* Return */
+
+    ['a'] = 30,
+    ['s'] = 31,
+    ['d'] = 32,
+    ['f'] = 33,
+    ['g'] = 34,
+    ['h'] = 35,
+    ['j'] = 36,
+    ['k'] = 37,
+    ['l'] = 38,
+    [';'] = 39,
+    ['\''] = 40, /* Single quote */
+    ['`'] = 41,
+    ['\\'] = 43, /* Backslash */
+
+    ['z'] = 44,
+    ['x'] = 45,
+    ['c'] = 46,
+    ['v'] = 47,
+    ['b'] = 48,
+    ['n'] = 49,
+    ['m'] = 50,
+    [','] = 51,
+    ['.'] = 52,
+    ['/'] = 53,
+
+    [' '] = 57,
+
+    [265] = 59, /* Function Key 1 */
+    [266] = 60, /* Function Key 2 */
+    [267] = 61, /* Function Key 3 */
+    [268] = 62, /* Function Key 4 */
+    [269] = 63, /* Function Key 5 */
+    [270] = 64, /* Function Key 6 */
+    [271] = 65, /* Function Key 7 */
+    [272] = 66, /* Function Key 8 */
+    [273] = 67, /* Function Key 9 */
+    [274] = 68, /* Function Key 10 */
+    [275] = 87, /* Function Key 11 */
+    [276] = 88, /* Function Key 12 */
+
+    [262] = 71 | GREY, /* Home */
+    [259] = 72 | GREY, /* Up Arrow */
+    [339] = 73 | GREY, /* Page Up */
+    [260] = 75 | GREY, /* Left Arrow */
+    [261] = 77 | GREY, /* Right Arrow */
+    [360] = 79 | GREY, /* End */
+    [258] = 80 | GREY, /* Down Arrow */
+    [338] = 81 | GREY, /* Page Down */
+    [331] = 82 | GREY, /* Insert */
+    [330] = 83 | GREY, /* Delete */
+
+    ['!'] = 2 | SHIFT,
+    ['@'] = 3 | SHIFT,
+    ['#'] = 4 | SHIFT,
+    ['$'] = 5 | SHIFT,
+    ['%'] = 6 | SHIFT,
+    ['^'] = 7 | SHIFT,
+    ['&'] = 8 | SHIFT,
+    ['*'] = 9 | SHIFT,
+    ['('] = 10 | SHIFT,
+    [')'] = 11 | SHIFT,
+    ['_'] = 12 | SHIFT,
+    ['+'] = 13 | SHIFT,
+
+    [353] = 15 | SHIFT, /* Shift + Tab */
+    ['Q'] = 16 | SHIFT,
+    ['W'] = 17 | SHIFT,
+    ['E'] = 18 | SHIFT,
+    ['R'] = 19 | SHIFT,
+    ['T'] = 20 | SHIFT,
+    ['Y'] = 21 | SHIFT,
+    ['U'] = 22 | SHIFT,
+    ['I'] = 23 | SHIFT,
+    ['O'] = 24 | SHIFT,
+    ['P'] = 25 | SHIFT,
+    ['{'] = 26 | SHIFT,
+    ['}'] = 27 | SHIFT,
+
+    ['A'] = 30 | SHIFT,
+    ['S'] = 31 | SHIFT,
+    ['D'] = 32 | SHIFT,
+    ['F'] = 33 | SHIFT,
+    ['G'] = 34 | SHIFT,
+    ['H'] = 35 | SHIFT,
+    ['J'] = 36 | SHIFT,
+    ['K'] = 37 | SHIFT,
+    ['L'] = 38 | SHIFT,
+    [':'] = 39 | SHIFT,
+    ['"'] = 40 | SHIFT,
+    ['~'] = 41 | SHIFT,
+    ['|'] = 43 | SHIFT,
+
+    ['Z'] = 44 | SHIFT,
+    ['X'] = 45 | SHIFT,
+    ['C'] = 46 | SHIFT,
+    ['V'] = 47 | SHIFT,
+    ['B'] = 48 | SHIFT,
+    ['N'] = 49 | SHIFT,
+    ['M'] = 50 | SHIFT,
+    ['<'] = 51 | SHIFT,
+    ['>'] = 52 | SHIFT,
+    ['?'] = 53 | SHIFT,
+
+    [277] = 59 | SHIFT, /* Shift + Function Key 1 */
+    [278] = 60 | SHIFT, /* Shift + Function Key 2 */
+    [279] = 61 | SHIFT, /* Shift + Function Key 3 */
+    [280] = 62 | SHIFT, /* Shift + Function Key 4 */
+    [281] = 63 | SHIFT, /* Shift + Function Key 5 */
+    [282] = 64 | SHIFT, /* Shift + Function Key 6 */
+    [283] = 65 | SHIFT, /* Shift + Function Key 7 */
+    [284] = 66 | SHIFT, /* Shift + Function Key 8 */
+
+    [17] = 16 | CNTRL, /* Control + q */
+    [23] = 17 | CNTRL, /* Control + w */
+    [5] = 18 | CNTRL, /* Control + e */
+    [18] = 19 | CNTRL, /* Control + r */
+    [20] = 20 | CNTRL, /* Control + t */
+    [25] = 21 | CNTRL, /* Control + y */
+    [21] = 22 | CNTRL, /* Control + u */
+    [9] = 23 | CNTRL, /* Control + i */
+    [15] = 24 | CNTRL, /* Control + o */
+    [16] = 25 | CNTRL, /* Control + p */
+
+    [1] = 30 | CNTRL, /* Control + a */
+    [19] = 31 | CNTRL, /* Control + s */
+    [4] = 32 | CNTRL, /* Control + d */
+    [6] = 33 | CNTRL, /* Control + f */
+    [7] = 34 | CNTRL, /* Control + g */
+    [8] = 35 | CNTRL, /* Control + h */
+    [10] = 36 | CNTRL, /* Control + j */
+    [11] = 37 | CNTRL, /* Control + k */
+    [12] = 38 | CNTRL, /* Control + l */
+
+    [26] = 44 | CNTRL, /* Control + z */
+    [24] = 45 | CNTRL, /* Control + x */
+    [3] = 46 | CNTRL, /* Control + c */
+    [22] = 47 | CNTRL, /* Control + v */
+    [2] = 48 | CNTRL, /* Control + b */
+    [14] = 49 | CNTRL, /* Control + n */
+    /* Control + m collides with the keycode for Enter */
+
+};
+
+int curses2keysym[CURSES_KEYS] = {
+    [0 ... (CURSES_KEYS - 1)] = -1,
+
+    ['\n'] = '\n',
+    ['\r'] = '\n',
+
+    [127] = QEMU_KEY_BACKSPACE,
+
+    [258] = QEMU_KEY_DOWN,
+    [259] = QEMU_KEY_UP,
+    [260] = QEMU_KEY_LEFT,
+    [261] = QEMU_KEY_RIGHT,
+    [262] = QEMU_KEY_HOME,
+    [263] = QEMU_KEY_BACKSPACE,
+
+    [330] = QEMU_KEY_DELETE,
+    [338] = QEMU_KEY_PAGEDOWN,
+    [339] = QEMU_KEY_PAGEUP,
+    [343] = '\n',
+    [360] = QEMU_KEY_END,
+
+};
+/* vim: set ai ts=4 sw=4 et: */
diff -Naur qemu/hw/vga.c qemu-curses/hw/vga.c
--- qemu/hw/vga.c	2005-07-03 14:00:51.000000000 +0000
+++ qemu-curses/hw/vga.c	2005-12-09 03:59:45.000000000 +0000
@@ -1100,14 +1100,14 @@
         s->cursor_end = s->cr[0xb];
     }
     cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4;
-    
+
     depth_index = get_depth_index(s->ds->depth);
     if (cw == 16)
         vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
     else
         vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
     vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
-    
+
     dest = s->ds->data;
     linesize = s->ds->linesize;
     ch_attr_ptr = s->last_ch_attr;
@@ -1505,7 +1505,7 @@
             s->rgb_to_pixel = rgb_to_pixel32_dup;
             break;
         }
-        
+
         full_update = 0;
         if (!(s->ar_index & 0x20)) {
             graphic_mode = GMODE_BLANK;
@@ -1880,3 +1880,165 @@
     }
     s->ds = saved_ds;
 }
+
+#define TEXTMODE_X(x)	((x) % width)
+#define TEXTMODE_Y(x)	((x) / width)
+#define VMEM2CHTYPE(v)  ((v & 0xff0007ff) | \
+        ((v & 0x00000800) << 10) | ((v & 0x00007000) >> 1))
+/* relay text rendering to given functions (i.e curses driver)
+ * instead of doing full vga_update_display() */
+void vga_update_text(uint8_t *chardata, void (*dpy_cursor)(DisplayState *s,
+                      int x, int y))
+{
+    VGAState *s = vga_state;
+    int graphic_mode, i, cursor_offset, cursor_visible;
+    int cw, cheight, width, height, size, c_min, c_max;
+    uint32_t *src, *dst, val;
+    char msg_buffer[80];
+    int full_update;
+    full_update = 0;
+
+    if (!(s->ar_index & 0x20)) {
+        graphic_mode = GMODE_BLANK;
+    } else {
+        graphic_mode = s->gr[6] & 1;
+    }
+    if (graphic_mode != s->graphic_mode) {
+        s->graphic_mode = graphic_mode;
+        full_update = 1;
+    }
+    if (s->last_width == -1) {
+        s->last_width = 0;
+        full_update = 1;
+    }
+
+    switch(graphic_mode) {
+    case GMODE_TEXT:
+        /* TODO: update palette */
+        full_update |= update_basic_params(s);
+    
+        /* total width & height */
+        cheight = (s->cr[9] & 0x1f) + 1;
+        cw = 8;
+        if (!(s->sr[1] & 0x01))
+            cw = 9;
+        if (s->sr[1] & 0x08)
+            cw = 16; /* NOTE: no 18 pixel wide */
+        width = (s->cr[0x01] + 1);
+        if (s->cr[0x06] == 100) {
+            /* ugly hack for CGA 160x100x16 - explain me the logic */
+            height = 100;
+        } else {
+            height = s->cr[0x12] | 
+                ((s->cr[0x07] & 0x02) << 7) | 
+                ((s->cr[0x07] & 0x40) << 3);
+            height = (height + 1) / cheight;
+        }
+
+        size = (height * width);
+        if (size > CH_ATTR_SIZE) {
+            if (!full_update)
+                return;
+
+            sprintf(msg_buffer, "%i x %i Text mode", width, height);
+            break;
+        }
+    
+        if (width != s->last_width || height != s->last_height ||
+            cw != s->last_cw || cheight != s->last_ch) {
+            s->last_scr_width = width * cw;
+            s->last_scr_height = height * cheight;
+            dpy_resize(s->ds, width, height);
+            s->last_width = width;
+            s->last_height = height;
+            s->last_ch = cheight;
+            s->last_cw = cw;
+            full_update = 1;
+        }
+
+        /* Update "hardware" cursor */
+        cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr;
+        if (cursor_offset != s->cursor_offset ||
+            s->cr[0xa] != s->cursor_start ||
+            s->cr[0xb] != s->cursor_end || full_update) {
+            cursor_visible = !(s->cr[0xa] & 0x20);
+            if (cursor_visible && cursor_offset < size && cursor_offset >= 0)
+                dpy_cursor(s->ds,
+                           TEXTMODE_X(cursor_offset),
+                           TEXTMODE_Y(cursor_offset));
+            else
+                dpy_cursor(s->ds, -1, -1);
+            s->cursor_offset = cursor_offset;
+            s->cursor_start = s->cr[0xa];
+            s->cursor_end = s->cr[0xb];
+        }
+
+        src = (uint32_t *) s->vram_ptr + s->start_addr;
+        dst = (uint32_t *) chardata;
+
+        if (full_update) {
+            for (i = 0; i < size; src ++, dst ++, i ++) {
+                val = VMEM2CHTYPE(*src);
+                *dst = val;
+            }
+
+            dpy_update(s->ds, 0, 0, width, height);
+        } else {
+            c_max = 0;
+
+            for (i = 0; i < size; src ++, dst ++, i ++) {
+                val = VMEM2CHTYPE(*src);
+                if (*dst != val) {
+                    *dst = val;
+                    c_max = i;
+                    break;
+                }
+            }
+            c_min = i;
+            for (; i < size; src ++, dst ++, i ++) {
+                val = VMEM2CHTYPE(*src);
+                if (*dst != val) {
+                    *dst = val;
+                    c_max = i;
+                }
+            }
+
+            if (c_min <= c_max) {
+                i = TEXTMODE_Y(c_min);
+                dpy_update(s->ds, 0, i, width, TEXTMODE_Y(c_max) - i + 1);
+            }
+        }
+
+        return;
+    case GMODE_GRAPH:
+        if (!full_update)
+            return;
+
+        s->get_resolution(s, &width, &height);
+        sprintf(msg_buffer, "%i x %i Graphic mode", width, height);
+        break;
+    case GMODE_BLANK:
+    default:
+        if (!full_update)
+            return;
+
+        sprintf(msg_buffer, "VGA Blank mode");
+        break;
+    }
+
+    /* Display a message */
+    dpy_cursor(s->ds, -1, -1);
+    dpy_resize(s->ds, 60, 3);
+    memset(chardata, 0, 60 * 3 * 4);
+    size = strlen(msg_buffer);
+    width = (60 - size) / 2;
+    for (i = 0; i < 60 * 3 * 4; i += 4)
+        chardata[i] = ' ';
+    for (i = 0; i < size; i ++) {
+        chardata[(60 + width + i) * 4] = msg_buffer[i];
+        chardata[(60 + width + i) * 4 + 1] = 0x01; /* colour */
+        chardata[(60 + width + i) * 4 + 2] = 0x20; /* colour */
+    }
+    dpy_update(s->ds, 0, 0, 60, 3);
+}
+/* vim: set ai ts=4 sw=4 et: */
diff -Naur qemu/vl.c qemu-curses/vl.c
--- qemu/vl.c	2005-12-05 20:31:52.000000000 +0000
+++ qemu-curses/vl.c	2005-12-08 19:00:57.000000000 +0000
@@ -115,6 +115,7 @@
 int bios_size;
 static DisplayState display_state;
 int nographic;
+int curses;
 const char* keyboard_layout = NULL;
 int64_t ticks_per_sec;
 int boot_device = 'c';
@@ -3840,6 +3841,9 @@
            "-hdachs c,h,s[,t]  force hard disk 0 physical geometry and the optional BIOS\n"
            "                translation (t=none or lba) (usually qemu can guess them)\n"
            "-L path         set the directory for the BIOS and VGA BIOS\n"
+#ifdef CONFIG_CURSES
+           "-curses         use a curses/ncurses interface instead of SDL\n"
+#endif
 #ifdef USE_KQEMU
            "-no-kqemu       disable KQEMU kernel module usage\n"
 #endif
@@ -3934,6 +3938,7 @@
     QEMU_OPTION_usb,
     QEMU_OPTION_usbdevice,
     QEMU_OPTION_smp,
+    QEMU_OPTION_curses,
 };
 
 typedef struct QEMUOption {
@@ -3984,6 +3989,9 @@
     { "hdachs", HAS_ARG, QEMU_OPTION_hdachs },
     { "L", HAS_ARG, QEMU_OPTION_L },
     { "no-code-copy", 0, QEMU_OPTION_no_code_copy },
+#ifdef CONFIG_CURSES
+    { "curses", 0, QEMU_OPTION_curses },
+#endif
 #ifdef USE_KQEMU
     { "no-kqemu", 0, QEMU_OPTION_no_kqemu },
 #endif
@@ -4202,6 +4210,7 @@
 #endif
     snapshot = 0;
     nographic = 0;
+    curses = 0;
     kernel_filename = NULL;
     kernel_cmdline = "";
 #ifdef TARGET_PPC
@@ -4335,7 +4344,16 @@
                 pstrcpy(monitor_device, sizeof(monitor_device), "stdio");
                 pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "stdio");
                 nographic = 1;
+                curses = 0;
+                break;
+#ifdef CONFIG_CURSES
+            case QEMU_OPTION_curses:
+                pstrcpy(monitor_device, sizeof(monitor_device), "vc");
+                pstrcpy(serial_devices[0], sizeof(serial_devices[0]), "vc");
+                curses = 1;
+		nographic = 0;
                 break;
+#endif
             case QEMU_OPTION_kernel:
                 kernel_filename = optarg;
                 break;
@@ -4714,7 +4732,13 @@
     /* terminal init */
     if (nographic) {
         dumb_display_init(ds);
-    } else {
+    } else
+#if defined(CONFIG_CURSES)
+    if (curses) {
+        curses_display_init(ds, full_screen);
+    } else
+#endif
+    {
 #if defined(CONFIG_SDL)
         sdl_display_init(ds, full_screen);
 #elif defined(CONFIG_COCOA)
@@ -4725,7 +4749,7 @@
     }
 
     vga_console = graphic_console_init(ds);
-    
+
     monitor_hd = qemu_chr_open(monitor_device);
     if (!monitor_hd) {
         fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
diff -Naur qemu/vl.h qemu-curses/vl.h
--- qemu/vl.h	2005-12-05 20:31:52.000000000 +0000
+++ qemu-curses/vl.h	2005-12-08 18:59:24.000000000 +0000
@@ -265,6 +265,7 @@
 int is_active_console(TextConsole *s);
 CharDriverState *text_console_init(DisplayState *ds);
 void console_select(unsigned int index);
+TextConsole *get_active_console();
 
 /* serial ports */
 
@@ -652,6 +653,8 @@
 void vga_update_display(void);
 void vga_invalidate_display(void);
 void vga_screen_dump(const char *filename);
+void vga_update_text(uint8_t *chardata, void (*dpy_cursor)
+                      (DisplayState *s, int x, int y));
 
 /* cirrus_vga.c */
 void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, 
@@ -665,6 +668,9 @@
 /* cocoa.m */
 void cocoa_display_init(DisplayState *ds, int full_screen);
 
+/* curses.c */
+void curses_display_init(DisplayState *ds, int full_screen);
+
 /* ide.c */
 #define MAX_DISKS 4
 










