Commits

Fredrik Lundh committed 41f4b09

console 1.1a1 code drop.

  • Participants

Comments (0)

Files changed (19)

+#
+# CONSOLE
+# $Id$
+#
+# Simple console interface
+#
+# This module provides a simple "console" interface for
+# a number of platforms.
+#
+# History:
+# 1999-01-05 fl  Created
+# 2000-11-18 fl  Added support for Python 2.0
+# 2001-01-03 fl  Minor fixes
+#
+# Written by Fredrik Lundh, January 1999.
+#
+# Copyright (c) 1999-2001 by Secret Labs AB.
+# Copyright (c) 1999-2001 by Fredrik Lundh.
+#
+# fredrik@pythonware.com
+# http://www.pythonware.com
+#
+
+import sys
+
+try:
+    if sys.platform == "win32":
+        import _wincon
+        Console = _wincon.Console
+        install_readline = _wincon.install_readline
+    else:
+        from _cursescon import Console
+except ImportError:
+    raise ImportError, "installation error: cannot find a console driver"
+
+#
+# console factory (if you don't want to redirect stdout/stderr,
+# simply instantiate the Console class yourself)
+
+def getconsole(buffer=1):
+    """Get a console handle.
+
+    If buffer is non-zero, a new console buffer is allocated and
+    installed.  Otherwise, this returns a handle to the current
+    console buffer"""
+
+    c = Console(buffer)
+
+    # try to redirect stdout and stderr
+    try:
+        if sys.stdout.isatty():
+            sys.stdout = c
+    except:
+        pass
+    try:
+        if sys.stderr.isatty():
+            sys.stderr = c
+    except:
+        pass
+
+    return c
+Metadata-Version: 1.0
+Name: console
+Version: 1.1a1-20011229
+Summary: Console -- a console driver for Windows 9X/NT/2K/XP
+Home-page: UNKNOWN
+Author: Fredrik Lundh
+Author-email: fredrik@pythonware.com
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
+$Id: //modules/console/README#5 $
+
+==================
+The console module
+==================
+
+This kit contains a simple console interface for Windows 9X/NT/2K/XP.
+It provides a cursor-addressable text display, plus support for
+keyboard and mouse input.
+
+The console interface also comes with a command editing and history
+implementation (readline) for Windows platforms.
+
+Enjoy /F
+
+fredrik@pythonware.com
+http://www.pythonware.com
+
+--------------------------------------------------------------------
+The Windows Console Interface is
+
+Copyright (c) 1999-2001 by Secret Labs AB
+Copyright (c) 1999-2001 by Fredrik Lundh
+
+By obtaining, using, and/or copying this software and/or its
+associated documentation, you agree that you have read, understood,
+and will comply with the following terms and conditions:
+
+Permission to use, copy, modify, and distribute this software and its
+associated documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies, and that both that copyright notice and this permission notice
+appear in supporting documentation, and that the name of Secret Labs
+AB or the author not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+--------------------------------------------------------------------
+
+release info
+------------
+
+This is the second public release.
+
+Comments, bug reports, and patches are welcome.  Send them to
+fredrik@pythonware.com.
+
+Note that this is free software, with limited support.  If you need
+commercial support on this module, please contact info@pythonware.com.
+
+contents
+--------
+
+README		  this file
+
+Console.htm	  documentation
+
+_wincon.c	  the low-level driver for Win32
+
+Console.py	  high-level interface
+
+readline.py	  (experimental) command history and editing for Windows.
+		  to enable this module, just put it on the Python path.
+
+setup.py	  build/installation script
+
+*.html            documentation
+
+demo*.py	  various demo and test scripts	
+tests/*.py
+/*
+ * CONSOLE
+ * $Id$
+ *
+ * Simple console interface
+ *
+ * This module provides a console driver for the console
+ * subsystem on Windows 9X/NT/2K.
+ *
+ * History:
+ * 1999-01-05 fl  Created
+ * 1999-01-06 fl  Added Event type and readline hooks
+ * 2000-11-15 fl  Added getchar method, default style (attr)
+ * 2000-11-22 fl  Added home method
+ * 2001-01-03 fl  Minor fixes
+ * 2001-05-08 fl  Use DL_EXPORT instead of declspec hack
+ *
+ * Written by Fredrik Lundh, January 1999.
+ *
+ * Copyright (c) 1999-2001 by Secret Labs AB.
+ * Copyright (c) 1999-2001 by Fredrik Lundh.
+ *
+ * fredrik@pythonware.com
+ * http://www.pythonware.com
+ *
+ * --------------------------------------------------------------------
+ * The Windows Console Driver is
+ * 
+ * Copyright (c) 1999-2001 by Secret Labs AB
+ * Copyright (c) 1999-2001 by Fredrik Lundh
+ * 
+ * By obtaining, using, and/or copying this software and/or its
+ * associated documentation, you agree that you have read, understood,
+ * and will comply with the following terms and conditions:
+ * 
+ * Permission to use, copy, modify, and distribute this software and its
+ * associated documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appears in all
+ * copies, and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Secret Labs
+ * AB or the author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.
+ * 
+ * SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ * -------------------------------------------------------------------- */
+
+#include "Python.h"
+#include "structmember.h"
+
+#undef UNICODE /* not yet */
+
+#define WITH_READLINE /* readline support */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+/* default attribute */
+#define WHITE 7
+#define BLACK 0
+
+/* console type definition */
+typedef struct {
+    PyObject_HEAD
+    HANDLE in;
+    HANDLE out;
+    DWORD inmode;
+    WORD attr;
+    /* file emulation stuff */
+    int softspace;
+    int serial;
+} ConsoleObject;
+
+staticforward PyTypeObject Console_Type;
+
+/* event type definition (from uiToolkit) */
+typedef struct {
+    PyObject_HEAD
+    char* type;
+    int serial;
+    int time;
+    int x, y;
+    int state;
+    int keycode;
+    char* keysym;
+    char* char_;
+    int width;
+    int height;
+    PyObject* widget;
+    char character[2];
+} EventObject;
+
+staticforward PyTypeObject Event_Type;
+
+/* ==================================================================== */
+/* console instances */
+
+static PyObject*
+console_new(PyObject* self_, PyObject* args)
+{
+    ConsoleObject* self;
+
+    int newbuffer = 1;
+    if (!PyArg_ParseTuple(args, "|i", &newbuffer))
+        return NULL;
+
+    self = PyObject_NEW(ConsoleObject, &Console_Type);
+    if (self == NULL)
+        return NULL;
+    
+    /* FIXME: add error checking! */
+
+    AllocConsole();
+
+    /* setup outbut buffers */
+    if (newbuffer) {
+        self->out = CreateConsoleScreenBuffer(
+            GENERIC_READ | GENERIC_WRITE, 0,
+            NULL, CONSOLE_TEXTMODE_BUFFER, NULL
+            );
+        SetConsoleActiveScreenBuffer(self->out);
+    } else
+        self->out = GetStdHandle(STD_OUTPUT_HANDLE);
+
+    /* setup input buffers */
+    self->in = GetStdHandle(STD_INPUT_HANDLE);
+    GetConsoleMode(self->in, &self->inmode);
+    SetConsoleMode(self->in, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
+
+    /* default style */
+    self->attr = WHITE;
+
+    self->softspace = 0;
+
+    self->serial = 0;
+
+    return (PyObject*) self;
+}
+
+static void
+console_dealloc(ConsoleObject* self)
+{
+    SetConsoleMode(self->in, self->inmode);
+    FreeConsole();
+
+    PyMem_DEL(self);
+}
+
+/* ==================================================================== */
+/* event instances */
+
+static char*
+keysym(int keycode)
+{
+    /* map virtual keys to X windows key symbols (from uiToolkit) */
+
+    switch (keycode) {
+
+    case VK_CANCEL: return "Cancel";
+
+    case VK_BACK: return "BackSpace";
+    case VK_TAB: return "Tab";
+
+    case VK_CLEAR: return "Clear";
+    case VK_RETURN: return "Return";
+
+    case VK_SHIFT: return "Shift_L";
+    case VK_CONTROL: return "Control_L";
+    case VK_MENU: return "Alt_L";
+    case VK_PAUSE: return "Pause";
+    case VK_CAPITAL: return "Caps_Lock";
+
+    case VK_ESCAPE: return "Escape";
+
+    case VK_SPACE: return "space";
+    case VK_PRIOR: return "Prior";
+    case VK_NEXT: return "Next";
+    case VK_END: return "End";
+    case VK_HOME: return "Home";
+    case VK_LEFT: return "Left";
+    case VK_UP: return "Up";
+    case VK_RIGHT: return "Right";
+    case VK_DOWN: return "Down";
+    case VK_SELECT: return "Select";
+    case VK_PRINT: return "Print";
+    case VK_EXECUTE: return "Execute";
+    case VK_SNAPSHOT: return "Snapshot";
+    case VK_INSERT: return "Insert";
+    case VK_DELETE: return "Delete";
+    case VK_HELP: return "Help";
+
+   /* FIXME: should the numerical keyboard keys be included in this
+    * list?  if not, there's no way to distinguish them from the
+    * corresponding ASCII characters */
+
+    case VK_F1: return "F1";
+    case VK_F2: return "F2";
+    case VK_F3: return "F3";
+    case VK_F4: return "F4";
+    case VK_F5: return "F5";
+    case VK_F6: return "F6";
+    case VK_F7: return "F7";
+    case VK_F8: return "F8";
+    case VK_F9: return "F9";
+    case VK_F10: return "F10";
+    case VK_F11: return "F11";
+    case VK_F12: return "F12";
+    case VK_F13: return "F13";
+    case VK_F14: return "F14";
+    case VK_F15: return "F15";
+    case VK_F16: return "F16";
+    case VK_F17: return "F17";
+    case VK_F18: return "F18";
+    case VK_F19: return "F19";
+    case VK_F20: return "F20";
+    case VK_F21: return "F21";
+    case VK_F22: return "F22";
+    case VK_F23: return "F23";
+    case VK_F24: return "F24";
+
+    case VK_NUMLOCK: return "Num_Lock,";
+    case VK_SCROLL: return "Scroll_Lock";
+
+    /* FIXME: remove the following? */
+
+#if defined(VK_APPS)
+    case VK_APPS: return "VK_APPS";
+#endif
+#if defined(VK_PROCESSKEY)
+    case VK_PROCESSKEY: return "VK_PROCESSKEY";
+#endif
+#if defined(VK_ATTN)
+    case VK_ATTN: return "VK_ATTN";
+#endif
+#if defined(VK_CRSEL)
+    case VK_CRSEL: return "VK_CRSEL";
+#endif
+#if defined(VK_EXSEL)
+    case VK_EXSEL: return "VK_EXSEL";
+#endif
+#if defined(VK_EREOF)
+    case VK_EREOF: return "VK_EREOF";
+#endif
+#if defined(VK_PLAY)
+    case VK_PLAY: return "VK_PLAY";
+#endif
+#if defined(VK_ZOOM)
+    case VK_ZOOM: return "VK_ZOOM";
+#endif
+#if defined(VK_NONAME)
+    case VK_NONAME: return "VK_NONAME";
+#endif
+#if defined(VK_PA1)
+    case VK_PA1: return "VK_PA1";
+#endif
+#if defined(VK_OEM_CLEAR)
+    case VK_OEM_CLEAR: return "VK_OEM_CLEAR";
+#endif
+    }
+
+    return "";
+};
+
+static PyObject*
+event_new(ConsoleObject* console, INPUT_RECORD* input)
+{
+    EventObject* self;
+
+    self = PyObject_NEW(EventObject, &Event_Type);
+    if (self == NULL)
+        return NULL;
+
+    self->type = "??";
+
+    self->serial = ++console->serial;
+
+    self->width = 0;
+    self->height = 0;
+
+    self->x = self->y = 0;
+
+    self->character[0] = 0;
+
+    self->keycode = 0;
+    self->keysym = "??";
+
+    self->char_ = self->character;
+    
+    Py_INCREF(Py_None);
+    self->widget = Py_None;
+
+    switch (input->EventType) {
+    case KEY_EVENT:
+        if (input->Event.KeyEvent.bKeyDown)
+            self->type = "KeyPress";
+        else
+            self->type = "KeyRelease";
+        self->character[0] = input->Event.KeyEvent.uChar.AsciiChar;
+        self->character[1] = 0;
+        self->keysym = keysym(input->Event.KeyEvent.wVirtualKeyCode);
+        self->keycode = input->Event.KeyEvent.wVirtualKeyCode;
+        self->state = input->Event.KeyEvent.dwControlKeyState;
+        /* FIXME: map state to Tkinter compatible state */
+        break;
+    case MOUSE_EVENT:
+        if (input->Event.MouseEvent.dwEventFlags & MOUSE_MOVED)
+            self->type = "Motion";
+        else
+            self->type = "Button";
+        /* FIXME: map state change to Tkinter button event */
+        self->x = input->Event.MouseEvent.dwMousePosition.X;
+        self->y = input->Event.MouseEvent.dwMousePosition.Y;
+        self->state = input->Event.MouseEvent.dwButtonState;
+        /* FIXME: map state to Tkinter compatible state */
+        break;
+    case WINDOW_BUFFER_SIZE_EVENT:
+        self->type = "Configure";
+        self->width = input->Event.WindowBufferSizeEvent.dwSize.X;
+        self->height = input->Event.WindowBufferSizeEvent.dwSize.Y;
+        break;
+    case FOCUS_EVENT:
+        if (input->Event.FocusEvent.bSetFocus)
+            self->type = "FocusIn";
+        else
+            self->type = "FocusOut";
+        break;
+    case MENU_EVENT:
+        self->type = "Menu";
+        self->state = input->Event.MenuEvent.dwCommandId;
+        break;
+    default:
+        self->type = "??";
+    }
+
+    return (PyObject*) self;
+}
+
+static void
+event_dealloc(EventObject* self)
+{
+    PyMem_DEL(self);
+}
+
+/* utility functions */
+
+static __inline PyObject*
+fixstatus(int flag)
+{
+    if (flag) {
+        Py_INCREF(Py_True);
+        return Py_True;
+    } else {
+        Py_INCREF(Py_False);
+        return Py_False;
+    }
+}
+
+static __inline COORD
+fixcoord(ConsoleObject* self, int x, int y)
+{
+    COORD coord;
+    CONSOLE_SCREEN_BUFFER_INFO info;
+
+    if (x < 0 || y < 0) {
+        /* wrap coordinates */
+        GetConsoleScreenBufferInfo(self->out, &info);
+        if (x < 0)
+            x = info.srWindow.Right - x;
+        if (y < 0)
+            y = info.srWindow.Bottom + y;
+    }
+
+    coord.X = x;
+    coord.Y = y;
+
+    return coord;
+}
+
+/* ==================================================================== */
+/* console methods */
+
+/* file interface (this also includes the softspace attribute) */ 
+
+static PyObject*
+console_pos(ConsoleObject* self, PyObject* args)
+{
+    /* move or query the window cursor */
+
+    int x, y;
+
+    if (!PyTuple_Size(args)) {
+        /* get current position */
+        CONSOLE_SCREEN_BUFFER_INFO info;
+        GetConsoleScreenBufferInfo(self->out, &info);
+        return Py_BuildValue("ii", info.dwCursorPosition.X,
+                             info.dwCursorPosition.Y);
+    }
+
+    if (!PyArg_ParseTuple(args, "ii", &x, &y))
+        return NULL;
+
+    return fixstatus(SetConsoleCursorPosition(
+        self->out, fixcoord(self, x, y)
+        ));
+}
+
+static PyObject*
+console_home(ConsoleObject* self, PyObject* args)
+{
+    /* convenience: same as pos(0, 0) */
+
+    if (!PyArg_NoArgs(args))
+        return NULL;
+
+    return fixstatus(SetConsoleCursorPosition(
+        self->out, fixcoord(self, 0, 0)
+        ));
+}
+
+static PyObject*
+console_write(ConsoleObject* self, PyObject* args)
+{
+    /* write text at current cursor position */
+
+    DWORD n;
+
+    char* text;
+    int length;
+    int attr = self->attr;
+    if (!PyArg_ParseTuple(args, "s#|i", &text, &length, &attr))
+        return NULL;
+
+    SetConsoleTextAttribute(self->out, (WORD) attr);
+
+    WriteConsole(self->out, text, length, &n, NULL);
+
+    return fixstatus(0);
+}
+
+/* graphics api */
+
+static PyObject*
+console_page(ConsoleObject* self, PyObject* args)
+{
+    /* fill the entire screen */
+
+    CONSOLE_SCREEN_BUFFER_INFO info;
+    COORD pos;
+    DWORD n;
+    int w;
+
+    int attr = self->attr;
+    char fill = ' ';
+    if (!PyArg_ParseTuple(args, "|ic", &attr, &fill))
+        return NULL;
+
+    GetConsoleScreenBufferInfo(self->out, &info);
+
+    pos.X = pos.Y = 0;
+
+    if (info.dwCursorPosition.X != 0 || info.dwCursorPosition.Y != 0)
+        /* setting the cursor is many times slower than clearing
+           the entire console... */
+        SetConsoleCursorPosition(self->out, pos);
+
+    w = info.dwSize.X;
+
+    for (; pos.Y < info.dwSize.Y; pos.Y++) {
+        FillConsoleOutputAttribute(self->out, (WORD) attr, w, pos, &n);
+        FillConsoleOutputCharacter(self->out, fill, w, pos, &n);
+    }
+
+    self->attr = attr;
+
+    return fixstatus(0);
+}
+
+static PyObject*
+console_text(ConsoleObject* self, PyObject* args)
+{
+    /* write text at given position */
+
+    COORD pos;
+    DWORD n;
+
+    int x, y;
+    char* text;
+    int length;
+    int attr = self->attr;
+    if (!PyArg_ParseTuple(args, "iis#|i", &x, &y, &text, &length, &attr))
+        return NULL;
+
+    pos = fixcoord(self, x, y);
+
+    WriteConsoleOutputCharacter(self->out, text, length, pos, &n);
+    FillConsoleOutputAttribute(self->out, (WORD) attr, n, pos, &n);
+
+    return fixstatus(0);
+}
+
+static PyObject*
+console_rectangle(ConsoleObject* self, PyObject* args)
+{
+    /* fill rectangle */
+
+    COORD pos;
+    DWORD n;
+
+    int x0, y0, x1, y1;
+    int attr = self->attr;
+    char fill = ' ';
+    if (!PyArg_ParseTuple(args, "(iiii)|ic", &x0, &y0, &x1, &y1, &attr, &fill))
+        return NULL;
+
+    pos.X = x0; 
+    pos.Y = y0;
+
+    while (pos.Y < y1) {
+        FillConsoleOutputAttribute(self->out, (WORD) attr, x1-x0, pos, &n);
+        FillConsoleOutputCharacter(self->out, fill, x1-x0, pos, &n);
+        pos.Y++;
+    }
+
+    return fixstatus(0);
+}
+
+static PyObject*
+console_scroll(ConsoleObject* self, PyObject* args)
+{
+    /* scroll a region */
+
+    SMALL_RECT source;
+    CHAR_INFO style;
+    COORD dest;
+
+    int x0, y0, x1, y1;
+    int dx, dy;
+    int attr = self->attr;
+    char fill = ' ';
+    if (!PyArg_ParseTuple(args, "(iiii)ii|ic", &x0, &y0, &x1, &y1,
+                          &dx, &dy, &attr, &fill))
+        return NULL;
+
+    source.Left = x0;
+    source.Top = y0;
+    source.Right = x1 - 1;
+    source.Bottom = y1 - 1;
+
+    dest.X = x0 + dx;
+    dest.Y = y0 + dy;
+
+    style.Attributes = attr;
+    style.Char.AsciiChar = fill;
+
+    return fixstatus(ScrollConsoleScreenBuffer(
+        self->out,
+        &source,&source,
+        dest, &style
+        ));
+}
+
+/* event management */
+
+static PyObject*
+console_get(ConsoleObject* self, PyObject* args)
+{
+    /* get next event from queue */
+
+    INPUT_RECORD event;
+    DWORD count = 0;
+    int status;
+
+    if (!PyArg_NoArgs(args))
+        return NULL;
+
+    status = ReadConsoleInput(self->in, &event, 1, &count);
+    if (status && count == 1)
+        return event_new(self, &event);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject*
+console_getchar(ConsoleObject* self, PyObject* args)
+{
+    /* get next character from queue */
+
+    INPUT_RECORD event;
+    DWORD count = 0;
+    int status;
+
+    if (!PyArg_NoArgs(args))
+        return NULL;
+
+    for (;;) {
+        status = ReadConsoleInput(self->in, &event, 1, &count);
+        if (status && count == 1 && event.EventType == KEY_EVENT &&
+            event.Event.KeyEvent.bKeyDown) {
+            char* sym = keysym(event.Event.KeyEvent.wVirtualKeyCode);
+            if (sym && sym[0])
+                return Py_BuildValue("s", sym);
+            else
+                return Py_BuildValue(
+                    "c", event.Event.KeyEvent.uChar.AsciiChar
+                    );
+        }
+    }
+
+    return NULL;
+}
+
+static PyObject*
+console_peek(ConsoleObject* self, PyObject* args)
+{
+    /* check event queue */
+
+    INPUT_RECORD event;
+    DWORD count = 0;
+    int status;
+
+    if (!PyArg_NoArgs(args))
+        return NULL;
+
+    status = PeekConsoleInput(self->in, &event, 1, &count);
+    if (status && count == 1)
+        return event_new(self, &event);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+/* configuration */
+
+static PyObject*
+console_cursor(ConsoleObject* self, PyObject* args)
+{
+    /* set cursor on or off */
+
+    CONSOLE_CURSOR_INFO info;
+
+    int visible;
+    if (!PyArg_ParseTuple(args, "i", &visible))
+        return NULL;
+
+    if (GetConsoleCursorInfo(self->out, &info)) {
+        info.bVisible = (visible) ? TRUE : FALSE;
+        SetConsoleCursorInfo(self->out, &info);
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject*
+console_size(ConsoleObject* self, PyObject* args)
+{
+    /* set/get window size */
+
+    CONSOLE_SCREEN_BUFFER_INFO info;
+
+    int width = -1;
+    int height = -1;
+    if (!PyArg_ParseTuple(args, "|ii", &width, &height))
+        return NULL;
+
+    if (width >= 0 && height >= 0)
+        /* FIXME */;
+    else {
+        if (GetConsoleScreenBufferInfo(self->out, &info))
+            return Py_BuildValue("ii", info.dwSize.X, info.dwSize.Y);
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyObject*
+console_title(ConsoleObject* self, PyObject* args)
+{
+    /* set/get title */
+
+    char* title = NULL;
+    if (!PyArg_ParseTuple(args, "|s", &title))
+        return NULL;
+
+    if (title)
+        SetConsoleTitle(title);
+    else {
+        char buffer[200];
+        if (GetConsoleTitle(buffer, sizeof buffer))
+            return Py_BuildValue("s", buffer);
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef
+console_methods[] = {
+
+    /* graphics */
+    {"text", (PyCFunction) console_text, 1},
+    {"rectangle", (PyCFunction) console_rectangle, 1},
+
+    {"scroll", (PyCFunction) console_scroll, 1},
+
+    {"page", (PyCFunction) console_page, 1},
+
+/*    {"save", (PyCFunction) console_save, 1}, */
+/*    {"restore", (PyCFunction) console_restore, 1},*/
+
+    /* stream output */
+    {"write", (PyCFunction) console_write, 1},
+    {"pos", (PyCFunction) console_pos, 1},
+    {"home", (PyCFunction) console_home, 0},
+
+    /* events */
+    {"get", (PyCFunction) console_get, 0},
+    {"getchar", (PyCFunction) console_getchar, 0},
+    {"peek", (PyCFunction) console_peek, 0},
+
+    /* misc. settings */
+    {"cursor", (PyCFunction) console_cursor, 1},
+    {"size", (PyCFunction) console_size, 1},
+    {"title", (PyCFunction) console_title, 1},
+
+    {NULL, NULL}
+};
+
+static PyObject*  
+console_getattr(ConsoleObject* self, char* name)
+{
+    if (name[0] == 's' && strcmp(name, "softspace") == 0)
+	  return PyInt_FromLong(self->softspace);
+
+    return Py_FindMethod(console_methods, (PyObject*) self, name);
+}
+
+static int
+console_setattr(ConsoleObject* self, char* name, PyObject* value)
+{
+    if (!value) {
+        PyErr_SetString(
+            PyExc_AttributeError,
+            "can't delete Console attributes"
+            );
+        return -1;
+    }
+
+    if (!strcmp(name, "softspace")) {
+	long v;
+	v = PyInt_AsLong(value);
+	if (v == -1 && PyErr_Occurred())
+            return -1;
+	self->softspace = v;
+	return 0;
+    }
+
+    PyErr_SetString(PyExc_AttributeError, name);
+    return -1;
+}
+
+statichere PyTypeObject Console_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0, /* ob_size */
+    "Console", /* tp_name */
+    sizeof(ConsoleObject), /* tp_size */
+    0, /* tp_itemsize */
+    /* methods */
+    (destructor)console_dealloc, /* tp_dealloc */
+    NULL, /* tp_print */
+    (getattrfunc)console_getattr, /* tp_getattr */
+    (setattrfunc)console_setattr, /* tp_setattr */
+};
+
+/* ==================================================================== */
+/* event attributes */
+
+#define EVENT_OFF(x) offsetof(EventObject, x)
+
+static struct memberlist
+event_members[] = {
+    {"type", T_STRING, EVENT_OFF(type), RO},
+    {"keycode", T_INT, EVENT_OFF(keycode), RO},
+    {"keysym", T_STRING, EVENT_OFF(keysym), RO},
+    {"char", T_STRING, EVENT_OFF(char_), RO},
+    {"state", T_INT, EVENT_OFF(state), RO},
+    {"x", T_INT, EVENT_OFF(x), RO},
+    {"y", T_INT, EVENT_OFF(y), RO},
+    {"width", T_INT, EVENT_OFF(width), RO},
+    {"height", T_INT, EVENT_OFF(height), RO},
+    {"widget", T_OBJECT, EVENT_OFF(widget)},
+    {"serial", T_INT, EVENT_OFF(serial), RO},
+    {"time", T_INT, EVENT_OFF(time), RO},
+    {NULL}
+};
+
+static PyObject*  
+event_getattr(ConsoleObject* self, char* name)
+{
+    return PyMember_Get((char*)self, event_members, name);
+}
+
+static int
+event_setattr(ConsoleObject* self, char* name, PyObject* value)
+{
+    return PyMember_Set((char*)self, event_members, name, value);
+}
+
+static PyObject *
+event_repr(EventObject* self)
+{
+    char buffer[300];
+
+    sprintf(
+        buffer, "<%s Event at %lx>",
+        self->type,
+        (long) self
+        );
+
+    return PyString_FromString(buffer);
+}
+
+statichere PyTypeObject Event_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0, /* ob_size */
+    "Event", /* tp_name */
+    sizeof(EventObject), /* tp_size */
+    0, /* tp_itemsize */
+    /* methods */
+    (destructor)event_dealloc, /* tp_dealloc */
+    NULL, /* tp_print */
+    (getattrfunc)event_getattr, /* tp_getattr */
+    (setattrfunc)event_setattr, /* tp_setattr */
+    NULL, /* tp_compare */
+    (reprfunc)event_repr /* tp_repr */
+};
+
+/* ==================================================================== */
+/* readline support */
+
+#ifdef WITH_READLINE
+
+static PyObject* rl_write;
+static PyObject* rl_readline;
+static PyThreadState *rl_threadstate;
+
+static char*
+rl_function(char* prompt)
+{
+    PyObject* res;
+    char* p;
+
+    /* switch to original thread state */
+    PyThreadState *threadstate;
+    threadstate = PyThreadState_Swap(NULL);
+    PyEval_RestoreThread(rl_threadstate);
+
+    res = PyObject_CallFunction(rl_write, "s", prompt);
+    if (res)
+        Py_DECREF(res);
+    else {
+        /* better than nothing... */
+        PyErr_Print();
+        PyErr_Clear();
+    }
+
+    res = PyObject_CallFunction(rl_readline, NULL);
+    if (!res) {
+        PyErr_Print();
+        goto done;
+    }
+
+    if (res == Py_None) {
+        p = NULL; /* genereate KeyboardInterrupt */
+        goto done;
+    }
+
+    if (!PyString_Check(res)) {
+        PyErr_SetString(PyExc_ValueError, "readline must return string");
+        PyErr_Print();
+        goto done;
+    }
+
+    p = strdup(PyString_AsString(res));
+    if (!p) {
+        PyErr_NoMemory();
+        PyErr_Print();
+        goto done;
+    }
+
+    Py_DECREF(res);
+
+done:
+    PyEval_SaveThread();
+    PyThreadState_Swap(threadstate);
+
+    return p;
+}
+
+static PyObject*
+install_readline(ConsoleObject* self, PyObject* args)
+{
+    /* install readline hook */
+
+    PyObject* handler;
+    if (!PyArg_ParseTuple(args, "O", &handler))
+        return NULL;
+
+    rl_write = PyObject_GetAttrString(handler, "write");
+    if (!rl_write)
+        return NULL;
+
+    rl_readline = PyObject_GetAttrString(handler, "readline");
+    if (!rl_readline) {
+        Py_DECREF(rl_write);
+        return NULL;
+    }
+
+    rl_threadstate = PyThreadState_Get();
+
+    PyOS_ReadlineFunctionPointer = rl_function;
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+#endif
+
+/* ==================================================================== */
+/* module stuff */
+
+static PyMethodDef _functions[] = {
+    {"Console", (PyCFunction) console_new, 1},
+#ifdef WITH_READLINE
+    {"install_readline", (PyCFunction) install_readline, 1},
+#endif
+    {NULL, NULL}
+};
+
+DL_EXPORT(void)
+init_wincon()
+{
+    /* Patch object type */
+    Console_Type.ob_type = &PyType_Type;
+    Event_Type.ob_type = &PyType_Type;
+
+    Py_InitModule("_wincon", _functions);
+}
+/* style sheet for the 2001 edition of www.pythonware.com */
+
+BODY {
+    font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+    color: #000000; background: #ffffff;
+    /* background-image: url(images/snow2.gif) */
+}
+
+.normal {
+    font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+}
+
+H1, H2, H3 {
+    font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+    color: #044484; font-weight: bold; font-style: italic;
+}
+
+.title {
+    font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+    font-size: 18pt; font-style: normal; font-weight: normal;
+    background: #044484; color: white;
+}
+
+.footer {
+    font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+    background: #044484; color: white;
+}
+
+.titlebox {
+    font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+    font-size: 18pt; font-style: normal; font-weight: normal;
+    background: #044484; color: white;
+    padding: 5px;
+}
+
+IMG.title { border: 1px; margin: 5px; padding: 5px; }
+
+.browser {
+    font-family: Verdana, Helvetica, Arial, sans-serif;
+    font-size: 8pt; border-bottom: 1px solid #add8e6;
+}
+
+.news {
+    font-family: Verdana, Helvetica, Arial, sans-serif;
+    font-size: 8pt;
+}
+
+.newsdate {
+    font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+    font-weight: bold; color: #044484;
+}
+
+.newsitem {
+    font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+}
+
+.search {
+   font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+   background: #c0c0c0; padding: 2px;
+}
+
+.pad {
+   font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+   padding: 20px;
+}
+
+.news {
+   font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+   padding: 5px;
+}
+
+PRE { margin-left: 20px; color: #044484; }
+
+HR { height: 2px; color: #044484; }
+
+A:link { color: #044484; }
+A:active { color: #044484; }
+A:visited { color: #444444; }
+
+/* make sure title anchors stay the same */
+H1 A:visited { color: #044484; }
+H2 A:visited { color: #044484; }
+H3 A:visited { color: #044484; }
+
+/* assorted style classes */
+.timestamp { color: #044484; }
+.bluebox { background: #add8e6; } /* light blue */
+.compact { margin: 0; }
+.mark { color: #044484; }
+.small { font-size: xx-small; }
+.highlight { background: #ffffcc; }
+.note { background: #ffffcc; }
+.mark { color: #044484; }
+.grid { background: #000000; }
+.hidden { background: #c0c0c0; color: #c0c0c0; }

File demoAttributes.py

+#
+# CONSOLE
+# $Id$
+#
+# show available colors
+#
+
+import Console
+
+c = Console.getconsole(0)
+
+INK = 0x1f
+
+c.title("Console Attributes")
+
+c.page(INK)
+
+c.text(0, 0, chr(0xfe) + " Console Attributes " + chr(0xfe), INK)
+c.text(0, 1, chr(0xcd) * 80, INK)
+
+#
+# attribute masks:
+#
+# 0x01 = blue foreground
+# 0x02 = green foreground
+# 0x04 = red foreground
+# 0x08 = highlight foreground
+# 0x10 = blue background
+# 0x20 = green background
+# 0x40 = red background
+# 0x80 = highlight background
+
+for x in range(16):
+    for y in range(16):
+        attribute = x*16+y
+        c.text(x*5, y+2, " %02x " % attribute, attribute)
+
+c.pos(0, -2)

File demoEvents.py

+#
+# CONSOLE
+# $Id$
+#
+# test event handling
+#
+
+import Console
+
+c = Console.getconsole()
+
+c.title("event codes")
+c.text(0, 0, "CONSOLE -- event codes (click or type into window)")
+c.pos(0, 5)
+
+c.cursor(0)
+
+while 1:
+    e = c.get()
+    d = {}
+    for m in e.__members__:
+	d[m] = getattr(e, m)
+    print d

File demoQueens.py

+#
+# taken from Demo/scripts
+#
+# adapted for console display by Fredrik Lundh, November 2000
+
+"""N queens problem.
+
+The (well-known) problem is due to Niklaus Wirth.
+
+This solution is inspired by Dijkstra (Structured Programming).  It is
+a classic recursive backtracking approach.
+
+"""
+
+N = 8                                   # Default; command line overrides
+
+import Console
+console = Console.getconsole(0)
+
+class Queens:
+
+    def __init__(self, n=N):
+        self.n = n
+        self.reset()
+
+    def reset(self):
+        n = self.n
+        self.y = [None]*n               # Where is the queen in column x
+        self.row = [0]*n                # Is row[y] safe?
+        self.up = [0] * (2*n-1)         # Is upward diagonal[x-y] safe?
+        self.down = [0] * (2*n-1)       # Is downward diagonal[x+y] safe?
+        self.nfound = 0                 # Instrumentation
+
+    def solve(self, x=0):               # Recursive solver
+        for y in range(self.n):
+            if self.safe(x, y):
+                self.place(x, y)
+                if x+1 == self.n:
+                    self.display()
+                else:
+                    self.solve(x+1)
+                self.remove(x, y)
+
+    def safe(self, x, y):
+        return not self.row[y] and not self.up[x-y] and not self.down[x+y]
+
+    def place(self, x, y):
+        self.y[x] = y
+        self.row[y] = 1
+        self.up[x-y] = 1
+        self.down[x+y] = 1
+
+    def remove(self, x, y):
+        self.y[x] = None
+        self.row[y] = 0
+        self.up[x-y] = 0
+        self.down[x+y] = 0
+
+    def display(self):
+        self.nfound = self.nfound + 1
+        if self.nfound == 1:
+            console.page(0x20)
+        else:
+            console.home()
+        print '+-' + '--'*self.n + '+'
+        for y in range(self.n-1, -1, -1):
+            print '|',
+            for x in range(self.n):
+                if self.y[x] == y:
+                    print "Q",
+                else:
+                    print ".",
+            print '|'
+        print '+-' + '--'*self.n + '+'
+
+def main():
+    import sys
+    n = N
+    if sys.argv[1:]:
+        n = int(sys.argv[1])
+    q = Queens(n)
+    q.solve()
+    print "Found", q.nfound, "solutions."
+
+if __name__ == "__main__":
+    main()

File module-console.html

+<html>
+<head>
+<title>The Console Module</title>
+<link rel='stylesheet' href='console.css' type='text/css' />
+</head>
+<body>
+<p>[<a href='index.html'>index</a>]</p>
+<div class='bluebox'>
+<b>Author:</b> Fredrik Lundh<br />
+<b>Copyright &copy; 1999-2000 by Fredrik Lundh</b><br />
+</div>
+<h1>The Console Module</h1>
+<p><i>[This is work in progress. Last updated December 2,
+2000]</i></p>
+
+<p>The <i>Console</i> module provides a simple console interface,
+which provides cursor-addressable text output, plus support for
+keyboard and mouse input.</p>
+
+<p>The <i>Console</i> module is currently only available for Windows
+95, 98, NT, and 2000.  It probably works under Windows XP, but it
+hasn't been tested on that platform.</p>
+
+<p>Software (including precompiled binaries) and documentation can be
+downloaded from <a href="http://effbot.org/efflib/console">
+http://effbot.org/efflib/console</a></p>
+
+<h1>Concepts</h1>
+
+<h2>Screen</h2>
+
+<p>The console screen consists of a 2-dimensional grid containing
+character cells. All characters cells have the same size.</p>
+
+<p>Each character cell has a unique coordinates. The coordinate
+origin (0, 0) is in the upper left corner, as usual.</p>
+
+<p>You can use negative coordinates as well. They work pretty much
+like negative sequence indexes in Python; for example, column -1 is
+the rightmost column on the console.</p>
+
+<p>The <b><a href="#console-rectangle-method">rectangle</a></b>
+and <b><a href="#console-save-method">save</a></b> methods
+require you to specify character rectangles. A rectangle is a tuple
+containing two coordinate pairs. The second pair of coordinates
+specify the cell just to the right and below the last cell in the
+rectangle. In other words, the rectangle (0, 0, 20, 10) is twenty
+characters wide and ten characters high.</p>
+
+<p>Note that the second coordinate must be equal to or larger than
+the first coordinate. Also, the current implementation doesn't
+support negative coordinates in rectangles.</p>
+
+<h2>Cursor</h2>
+
+<p>The console driver keeps track of the current cursor position.
+However, only three methods actually use this position: <b><a href="#console-write-method">write</a></b>, <b><a href="#console-page-method">page</a></b>, and of course <b><a href="#console-pos-method">pos</a></b>.</p>
+
+<p>The <b><a href="#console-cursor-method">cursor</a></b> method
+allows you to switch the cursor on and off.</p>
+
+<h2>Styles</h2>
+
+<p>Several methods take <b>style</b> arguments. The style is an
+integer value that combines a foreground and a background color.
+The console driver supports 16 colors in total:</p>
+
+<pre>
+0 = black (#000000)
+1 = blue (#0000a8)
+2 = dark green (#00a800)
+3 = n/a (#00a8a8)
+4 = red (#a80000)
+5 = magenta (#a800a8)
+6 = brown (#a8a800)
+7 = light grey (#a8a8a8)
+8 = dark grey (#545454)
+9 = n/a (#5454fc)
+10 = green (#54fc54)
+11 = cyan (#54fcfc)
+12 = n/a (#fc5454)
+13 = n/a (#fc54fc)
+14 = yellow (#fcfc54)
+15 = white (#fcfcfc)
+</pre>
+
+<p>To calculate the style, combine the colour for the foreground
+and background as follows:</p>
+
+<pre>
+style = foreground + background*16
+</pre>
+
+<p>The default style is light grey on black background.</p>
+
+<p><b>TBD: add names for remaining colors (see the HTML 3.2
+specification?)</b></p>
+
+<h1>The Console Class</h1>
+
+<p>To create a <b>Console</b> instance, import the <b>
+Console</b> module and call the <b><a href="#console-getconsole-method">getconsole</a></b> factory
+function.</p>
+
+<div class="example">
+<p><b>Example: Example: Using the Console module</b></p><pre>
+import Console
+
+c = Console.getconsole()
+
+c.title(&quot;Console Example&quot;)
+
+c.text(0, 0, &quot;here's some white text on white background&quot;, 0x1f)
+c.text(10, 5, &quot;line five, column ten&quot;)
+</pre>
+</div>
+
+<h2>Console Methods</h2>
+
+<p>Instances returned by <b><a href="#console-getconsole-method">
+getconsole</a></b> have the following methods:</p>
+
+<h3>Basic Graphic Methods</h3>
+
+<div class="method" name="text">
+<h3>text</h3><p><b>text(column, line, string, style)</b>
+
+</p><blockquote><p>Write <b>string</b> to the screen at the given position, using
+the given <b>style</b>. This method does not move the cursor. If
+the style is omitted, it defaults to white text on black
+background.</p>
+</blockquote></div>
+
+<div class="method" name="rectangle">
+<h3>rectangle</h3><p><b>rectangle(rect, style, character)</b>
+
+</p><blockquote><p>Blanks the given rectangle. The <b>rect</b> argument should be
+a 4-tuple. If the <b>character</b> argument is omitted, it
+defaults to space. If the <b>style</b> argument is omitted, it
+defaults to black.</p>
+</blockquote></div>
+
+<div class="method" name="scroll">
+<h3>scroll</h3><p><b>scroll(rect, dx, dy, style, character)</b>
+
+</p><blockquote><p>Moves the given rectangle <b>dx</b> cells to the right (or
+left, if dx is negative), and <b>dy</b> cells down (or up). The
+<b>style</b> and <b>character</b> attributes are used to fill
+empty regions. If the <b>character</b> argument is omitted, it
+defaults to space. If the <b>style</b> argument is omitted, it
+defaults to black.</p>
+</blockquote></div>
+
+<div class="method" name="page">
+<h3>page</h3><p><b>page()</b>
+
+</p><blockquote><p>Blanks the screen, and moves the cursor to (0, 0).</p>
+</blockquote></div>
+
+<div class="method" name="page">
+<h3>page</h3><p><b>page(style, character)</b>
+
+</p><blockquote><p>Clears the screen, using the given style and fill character. The
+style will also become the new default style. It moves the cursor
+to (0, 0).</p>
+</blockquote></div>
+
+<h3>File-Like Output</h3>
+
+<div class="method" name="write">
+<h3>write</h3><p><b>write(string)</b>
+
+</p><blockquote><p>Writes <b>string</b> at the current position, using a default
+style. This method treats the console as a conventional text
+terminal, which means that tabs, backspace, newline, and the bell
+character works as expected. The cursor is moved to the position
+just after the last written character.</p>
+</blockquote></div>
+
+<div class="method" name="pos">
+<h3>pos</h3><p><b>pos(column, line)</b>
+
+</p><blockquote><p>Moves the cursor to the given column and line. If the
+coordinates are omitted, this method returns the current cursor
+position [TBD: better name?]</p>
+</blockquote></div>
+
+<div class="attribute" name="softspace">
+<h3>softspace</h3><p><b>softspace</b>
+<i> (integer def)</i>
+
+</p><blockquote><p>This attribute isn't used by the console driver itself, but is
+there to make sure Python's <b>print</b> statement works as
+expected when printing to a console device.</p>
+
+</blockquote></div>
+
+<h3>Input</h3>
+
+<div class="method" name="get">
+<h3>get</h3><p><b>get()</b>
+<i>=&gt; event</i>
+
+</p><blockquote><p>Get the first event from the input queue. The return value is an
+instance of the <a href="#console-event-class"><b>Event</b></a>
+class. If the input queue is empty, this method blocks.</p>
+</blockquote></div>
+
+<div class="method" name="getchar">
+<h3>getchar</h3><p><b>getchar()</b>
+<i>=&gt; event</i>
+
+</p><blockquote><p>Get the first character event from the input queue, ignoring any
+other kind of event. The return value is an instance of the <a href="#console-keypress-event"><b>KeyPress</b></a> class. If the
+input queue is empty, this method blocks.</p>
+</blockquote></div>
+
+<div class="method" name="peek">
+<h3>peek</h3><p><b>peek()</b>
+<i>=&gt; event</i>
+
+</p><blockquote><p>Fetch the first event from the input queue, without removing it.
+If the input queue is empty, this method returns None.</p>
+</blockquote></div>
+
+<h3>Console Properties</h3>
+
+<div class="method" name="cursor">
+<h3>cursor</h3><p><b>cursor(flag)</b>
+
+</p><blockquote><p><b>cursor(0)</b> makes the text cursor invisible. <b> cursor(1)</b>
+makes it visible. [TBD: better name?]</p>
+
+</blockquote></div>
+
+<div class="method" name="size">
+<h3>size</h3><p><b>size()</b>
+<i>=&gt; (width, height)</i>
+
+</p><blockquote><p>Get the current size of the console window, as a 2-tuple (width,
+height).</p>
+</blockquote></div>
+
+<div class="method" name="title">
+<h3>title</h3><p><b>title(string)</b>
+
+</p><blockquote><p>Set the window title to <b>string</b>. If the string is omitted,
+this method returns the current title.</p>
+
+</blockquote></div>
+
+<div class="method" name="save">
+<h3>save</h3><p><b>save()</b>
+
+</p><blockquote><p>TBD: document save and restore.</p>
+</blockquote></div>
+
+<h2>The Event Class</h2>
+
+<p>The <b><a href="#console-get-method">get</a></b> and <tt><a href="#console-peek-method">peek</a></tt> methods return event
+descriptors. These are modelled after the Tkinter <b>Event</b>
+type, with a few minor differences.</p>
+
+<h3>Event Subtypes</h3>
+
+<p>Note: In the current release, all event objects are implemented
+using the same Python type. To check what kind of event you have,
+use the <a href="#console-event-type-attribute">type</a>
+attribute.</p>
+
+<h4>KeyPress</h4>
+
+<h4>KeyRelease</h4>
+
+<h4>ButtonPress</h4>
+
+<h4>ButtonRelease</h4>
+
+<h4>Motion</h4>
+
+<h4>Configure</h4>
+
+<h3>Event Attributes</h3>
+
+<div class="attribute" name="type">
+<h3>type</h3><p><b>type</b>
+<i> (string)</i>
+
+</p><blockquote><p>The event type, given as a string. This attribute contains one
+of <a href="#console-keypress-event"><b>KeyPress</b></a>, <a href="#console-keyrelease-event"><b>KeyRelease</b></a>, <a href="#console-buttonpress-event"><b>ButtonPress</b></a>, <a href="#console-buttonrelease-event"><b>ButtonRelease</b></a>, <a href="#console-motion-event"><b>Motion</b></a>, or <a href="#console-configure-event"><b>Configure</b></a>. Other event
+types may be returned, but such events should always be ignored by
+the application.</p>
+</blockquote></div>
+
+<div class="attribute" name="char">
+<h3>char</h3><p><b>char</b>
+<i> (string)</i>
+
+</p><blockquote><p>The character code for <a href="#console-keypress-event"><b>
+KeyPress</b></a> or <a href="#console-keyrelease-event"><b>
+KeyRelease</b></a> events. If this is empty, the event doesn't
+have an ASCII representation.</p>
+</blockquote></div>
+
+<div class="attribute" name="keycode">
+<h3>keycode</h3><p><b>keycode</b>
+<i> (integer)</i>
+
+</p><blockquote><p>The numerical keycode for <a href="#console-keypress-event"><b>
+KeyPress</b></a> or <a href="#console-keyrelease-event"><b>
+KeyRelease</b></a> events.</p>
+</blockquote></div>
+
+<div class="attribute" name="keysym">
+<h3>keysym</h3><p><b>keysym</b>
+<i> (string)</i>
+
+</p><blockquote><p>The symbolic name for <a href="#console-keypress-event"><b>
+KeyPress</b></a> or <a href="#console-keyrelease-event"><b>
+KeyRelease</b></a> events. If this is an empty string, this event
+doesn't have a symbolic name.</p>
+</blockquote></div>
+
+<div class="attribute" name="state">
+<h3>state</h3><p><b>state</b>
+<i> (integer)</i>
+
+</p><blockquote><p>The button and control key state. This is valid for <a href="#console-keypress-event"><b>KeyPress</b></a>, <a href="#console-keyrelease-event"><b>KeyRelease</b></a>, <a href="#console-buttonpress-event"><b>ButtonPress</b></a>, <a href="#console-buttonrelease-event"><b>ButtonRelease</b></a>, and
+<a href="#console-motion-event"><b>Motion</b></a> events.</p>
+</blockquote></div>
+
+<div class="attribute" name="x, y">
+<h3>x, y</h3><p><b>x</b>
+<i> (integer)</i>
+</p><p><b>y</b>
+<i> (integer)</i>
+
+</p><blockquote><p>The current mouse coordinate. This is valid for <a href="#console-buttonpress-event"><b>ButtonPress</b></a>, <a href="#console-buttonrelease-event"><b>ButtonRelease</b></a>, and
+<a href="#console-motion-event"><b>Motion</b></a> events.</p>
+</blockquote></div>
+
+<div class="attribute" name="width, height">
+<h3>width, height</h3><p><b>width</b>
+<i> (integer)</i>
+</p><p><b>height</b>
+<i> (integer)</i>
+
+</p><blockquote><p>The new window size. This is only valid for <a href="#console-configure-event"><b>Configure</b></a> events.</p>
+</blockquote></div>
+
+<div class="attribute" name="serial">
+<h3>serial</h3><p><b>serial</b>
+<i> (integer)</i>
+
+</p><blockquote><p><b>serial</b> is the serial number for this event.</p>
+</blockquote></div>
+
+<div class="attribute" name="time">
+<h3>time</h3><p><b>time</b>
+<i> (integer)</i>
+
+</p><blockquote><p>The relative time in milliseconds when this event was generated.
+In the current implementation, this is always 0.</p>
+</blockquote></div>
+
+<div class="attribute" name="widget">
+<h3>widget</h3><p><b>widget</b>
+<i> (widget or None)</i>
+
+</p><blockquote><p>The widget that generated this event. Unless you're using <b>
+uiToolkit/Text</b> or another console widget toolkit built on top
+of this module, this is always <b>None</b>.</p>
+</blockquote></div>
+
+<h1>License</h1>
+
+<p>The <i>Console</i> module was written by Fredrik Lundh at Secret
+Labs AB, in January 1999.</p>
+
+<blockquote>
+Copyright &copy; 1999-2000 by Secret Labs AB<br />
+Copyright &copy; 1999-2000 by Fredrik Lundh
+</blockquote>
+
+<p>By obtaining, using, and/or copying this software and/or its
+associated documentation, you agree that you have read, understood,
+and will comply with the following terms and conditions:</p>
+
+<p>Permission to use, copy, modify, and distribute this software
+and its associated documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice appears in
+all copies, and that both that copyright notice and this permission
+notice appear in supporting documentation, and that the name of
+Secret Labs AB or the author not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.</p>
+
+<p>SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR
+THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
+OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.</p>
+
+</body>
+</html>
+# *** THIS IS WORK IN PROGRESS ***
+#
+# CONSOLE
+# $Id$
+#
+# Readline for Windows 9X/NT
+#
+# This module provides a simple command history and editing
+# implementation based on the windows console driver.
+#
+# History:
+# 99-01-06 fl  Created
+#
+# Written by Fredrik Lundh, January 1999.
+#
+# Copyright (c) 1999 by Secret Labs AB.
+# Copyright (c) 1999 by Fredrik Lundh.
+#
+# fredrik@pythonware.com
+# http://www.pythonware.com
+#
+
+# FIXME: the completer is not finished
+# FIXME: the screen update breaks down if you exceed one line
+
+import Console, sys
+
+_completer = None
+
+def set_completer(completer):
+    global _completer
+    _completer = completer
+
+class Readline:
+
+    def __init__(self):
+        self.console = Console.getconsole(0)
+        sys.stdout = sys.stderr = self.console
+        self.console.cursor(0)
+        self.history = []
+
+    def write(self, string):
+        self.console.write(string, 0x0E)
+
+    def readline(self):
+
+        s = ""
+        t = ""
+
+        i = 0
+
+        c = self.console
+
+        x, y = c.pos()
+        w, h = c.size()
+
+        while 1:
+
+            # sanity
+            i = min(i, len(s))
+
+            # redraw the edit field
+            c.pos(x, y)
+            c.write(s, 0x0F)
+            c.rectangle((x+len(s), y, x+w, y+1))
+            c.pos(x+i, y)
+
+            # FIXME: should handle line wrap here!
+
+            # get event
+            c.cursor(1)
+            e = c.get()
+            c.cursor(0)
+
+            if e.type != "KeyPress":
+                continue
+
+            # interpret keyboard event
+            if e.keysym == "Return":
+                break
+            elif e.keysym == "Escape":
+                s = ""
+            elif e.keysym == "BackSpace":
+                if s:
+                    s = s[:i-1] + s[i:]
+                    i = i - 1
+            elif e.keysym == "Delete":
+                if s:
+                    s = s[:i] + s[i+1:]
+            elif e.keysym == "Home" or e.char == "\001":
+                i = 0
+            elif e.keysym == "End" or e.char == "\005":
+                i = len(s)
+            elif e.keysym == "Right" or e.char == "\006":
+                i = min(i+1, len(s))
+            elif e.keysym == "Left" or e.char == "\002":
+                i = max(i-1, 0)
+            elif e.keysym == "Tab":
+                pass # FIXME: implement completion
+            elif e.char == "\013":
+                s = s[:i]
+            elif e.char == "\003":
+                return None # force caller to generate KeyboardInterrupt
+            elif e.char == "\f":
+                c.page()
+                return "\n"
+            elif e.char and e.char >= " ":
+                s = s[:i] + e.char + s[i:]
+                i = i + len(e.char)
+
+        c.write("\r\n")
+
+        if s != "\n":
+            self.history.append(s)
+
+        return s + "\n"
+
+#
+# install readline utility
+
+handler = Readline()
+
+Console.install_readline(handler)
+
+try:
+    import rlcompleter
+except:
+    pass
+#!/usr/bin/env python
+#
+# Setup script
+# $Id: //modules/console/setup.py#4 $
+#
+# Usage: python setup.py install
+#
+
+from distutils.core import setup, Extension
+from glob import glob
+
+setup(
+    name="console",
+    version="1.1a1-20011229",
+    author="Fredrik Lundh",
+    author_email="fredrik@pythonware.com",
+    description="Console -- a console driver for Windows 9X/NT/2K/XP",
+    py_modules = ["Console"],
+    scripts = glob("demo*.py"),
+    ext_modules = [
+        Extension("_wincon", ["_wincon.c"])
+        ]
+    )

File tests/testattrs.py

+#
+# CONSOLE
+# $Id$
+#
+# show available colors
+#
+
+import Console
+
+c = Console.getconsole()
+
+INK = 0x1f
+
+c.title("Console Attributes")
+
+c.page(INK)
+
+c.text(0, 0, chr(0xfe) + " Console Attributes " + chr(0xfe), INK)
+c.text(0, 1, chr(0xcd) * 80, INK)
+
+#
+# attribute masks:
+#
+# 0x01 = blue foreground
+# 0x02 = green foreground
+# 0x04 = red foreground
+# 0x08 = highlight foreground
+# 0x10 = blue background
+# 0x20 = green background
+# 0x40 = red background
+# 0x80 = highlight background
+
+for x in range(16):
+    for y in range(16):
+	attribute = x*16+y
+	c.text(x*5, y+2, " %02x " % attribute, attribute)
+
+c.pos(0, -2)
+

File tests/testchar.py

+#
+# CONSOLE
+# $Id$
+#
+# test character input handling
+#
+
+import Console
+
+c = Console.getconsole()
+
+c.title("character input")
+c.text(0, 0, "CONSOLE -- character codes")
+c.pos(0, 5)
+
+c.cursor(0)
+
+while 1:
+    print repr(c.getchar()),

File tests/testchars.py

+#
+# CONSOLE
+# $Id$
+#
+# show character set
+#
+
+import Console
+
+c = Console.getconsole()
+
+c.title("Console Character Set")
+
+INK = 0x1f
+
+c.page(INK)
+
+c.text(0, 0, chr(0xfe) + " Console Character Set " + chr(0xfe), INK)
+c.text(0, 1, chr(0xcd) * 80, INK)
+
+for x in range(16):
+    for y in range(16):
+	char = x*16+y
+	c.text(x*5, y+2, "%02x=%c" % (char, char), INK)
+
+c.pos(0, -2)

File tests/testfull.py

+#
+# CONSOLE
+# $Id$
+#
+# various tests
+#
+
+import Console
+
+c = Console.getconsole()
+
+INK = 0x1f
+
+w, h = c.size()
+
+c.title("testing")
+
+c.rectangle((0, 0, w, h), INK)
+
+c.text(0, 0, chr(0xfe) + " Windows Console Driver", INK)
+c.text(0, 1, chr(0xcd) * 80, INK)
+
+i = 0
+for text in ("File", "Edit", "Image", "Help"):
+    c.text(4, 4+i, "%d. "%i + text, INK)
+    i = i + 1
+
+c.pos(0, 20)
+print c.pos()
+
+# print "hello, world", w, h

File tests/testgetch.py

+import msvcrt
+
+while 1:
+    ch = msvcrt.getch()
+    print repr(ch),

File tests/testpage.py

+#
+# CONSOLE
+# $Id$
+#
+# blank the screen over and over again
+#
+
+import Console
+import time
+
+c = Console.getconsole(0)
+
+t0 = time.time()
+
+ink = 0
+
+for char in range(256):
+    c.title("ink = %d, char = %d" % (ink, char))
+    c.page(ink, chr(char))
+    ink = ink + 1
+    if ink >= 31:
+        ink = 0
+
+print round((time.time() - t0) / (ink*char) * 1000, 3), "milliseconds per page"

File tests/testscroll.py

+#
+# CONSOLE
+# $Id$
+#
+# test scrolling
+#
+
+import Console
+import time
+
+c = Console.getconsole()
+
+t0 = time.time()
+
+w, h = c.size()
+
+for i in range(h):
+    c.text(0, i, chr(97+i)*w)
+
+for ink in range(h-10):
+    c.title("ink %d" % ink)
+    c.scroll((5, 5, w-5, h-5), 0, -1, ink, "*")
+    time.sleep(0.1)

File tests/tkevents.py

+from Tkinter import *
+
+c = Canvas()
+c.pack()
+
+def echo(event):
+    i = vars(event).items()
+    i.sort()
+    print i
+
+c.focus_set()
+
+c.bind("<KeyPress>", echo)
+c.bind("<KeyRelease>", echo)
+c.bind("<ButtonPress>", echo)
+c.bind("<ButtonRelease>", echo)
+c.bind("<Motion>", echo)
+
+mainloop()