Initial revision
authorChris Hanson <org/chris-hanson/cph>
Fri, 2 Dec 1994 20:44:41 +0000 (20:44 +0000)
committerChris Hanson <org/chris-hanson/cph>
Fri, 2 Dec 1994 20:44:41 +0000 (20:44 +0000)
v7/src/microcode/os2pm.c [new file with mode: 0644]
v7/src/microcode/os2pm.h [new file with mode: 0644]
v7/src/microcode/os2pmcon.c [new file with mode: 0644]

diff --git a/v7/src/microcode/os2pm.c b/v7/src/microcode/os2pm.c
new file mode 100644 (file)
index 0000000..b27696b
--- /dev/null
@@ -0,0 +1,1362 @@
+/* -*-C-*-
+
+$Id: os2pm.c,v 1.1 1994/12/02 20:44:26 cph Exp $
+
+Copyright (c) 1994 Massachusetts Institute of Technology
+
+This material was developed by the Scheme project at the Massachusetts
+Institute of Technology, Department of Electrical Engineering and
+Computer Science.  Permission to copy this software, to redistribute
+it, and to use it for any purpose is granted, subject to the following
+restrictions and understandings.
+
+1. Any copy made of this software must include this copyright notice
+in full.
+
+2. Users of this software agree to make their best efforts (a) to
+return to the MIT Scheme project any improvements or extensions that
+they make, so that these may be included in future releases; and (b)
+to inform MIT of noteworthy uses of this software.
+
+3. All materials developed as a consequence of the use of this
+software shall duly acknowledge such use, in accordance with the usual
+standards of acknowledging credit in academic research.
+
+4. MIT has made no warrantee or representation that the operation of
+this software will be error-free, and MIT is under no obligation to
+provide any services, by way of maintenance, update, or otherwise.
+
+5. In conjunction with products arising from the use of this material,
+there shall be no use of the name of the Massachusetts Institute of
+Technology nor of any adaptation thereof in any advertising,
+promotional, or sales literature without prior written consent from
+MIT in each case. */
+
+#define INCL_WIN
+#define INCL_GPI
+#include "os2.h"
+\f
+typedef struct
+{
+  HWND frame;                  /* frame window handle */
+  HWND client;                 /* client window handle */
+  HPS hps;                     /* presentation space for client window */
+  unsigned short char_width;   /* char width (max increment) */
+  unsigned short char_ascender;        /* char ascender (max height above base) */
+  unsigned short char_descender; /* char descender (max height below base) */
+  unsigned short width;                /* width in characters */
+  unsigned short height;       /* height in characters */
+  char * charmap;              /* character array */
+  unsigned short cursor_x;     /* x coordinate of the cursor */
+  unsigned short cursor_y;     /* y coordinate of the cursor */
+  qid_t event_qid;             /* qid to send input events to */
+  twid_t twid;                 /* twid for this twindow */
+  unsigned int cursor_shownp : 1; /* nonzero if cursor is visible */
+  unsigned int minimizingp : 1; /* nonzero if window being minimized */
+  unsigned int minimizedp : 1; /* nonzero if window is minimized */
+} twindow_t;
+#define TWINDOW_FRAME(twindow) ((twindow) -> frame)
+#define TWINDOW_CLIENT(twindow) ((twindow) -> client)
+#define TWINDOW_HPS(twindow) ((twindow) -> hps)
+#define TWINDOW_CHAR_WIDTH(twindow) ((twindow) -> char_width)
+#define TWINDOW_CHAR_ASCENDER(twindow) ((twindow) -> char_ascender)
+#define TWINDOW_CHAR_DESCENDER(twindow) ((twindow) -> char_descender)
+#define TWINDOW_WIDTH(twindow) ((twindow) -> width)
+#define TWINDOW_HEIGHT(twindow) ((twindow) -> height)
+#define TWINDOW_CHARMAP(twindow) ((twindow) -> charmap)
+#define TWINDOW_CURSOR_X(twindow) ((twindow) -> cursor_x)
+#define TWINDOW_CURSOR_Y(twindow) ((twindow) -> cursor_y)
+#define TWINDOW_CURSOR_SHOWNP(twindow) ((twindow) -> cursor_shownp)
+#define TWINDOW_EVENT_QID(twindow) ((twindow) -> event_qid)
+#define TWINDOW_TWID(twindow) ((twindow) -> twid)
+#define TWINDOW_MINIMIZINGP(twindow) ((twindow) -> minimizingp)
+#define TWINDOW_MINIMIZEDP(twindow) ((twindow) -> minimizedp)
+
+#define TWINDOW_CHAR_HEIGHT(twindow)                                   \
+  ((TWINDOW_CHAR_ASCENDER (twindow)) + (TWINDOW_CHAR_DESCENDER (twindow)))
+
+#define TWINDOW_CHAR_LOC(twindow, x, y)                                        \
+  (& ((TWINDOW_CHARMAP (twindow)) [((y) * (TWINDOW_WIDTH (twindow))) + (x)]))
+
+typedef struct
+{
+  tqueue_type_t type;
+  HWND hwnd;
+} pm_tqueue_t;
+#define PM_TQUEUE_HWND(q) (((pm_tqueue_t *) (q)) -> hwnd)
+\f
+static void simple_transaction (qid_t, msg_t *);
+static void simple_reply (qid_t);
+
+static void pm_thread_procedure (void *);
+static tqueue_t * make_pm_tqueue (HWND);
+
+static unsigned short cx2x (twindow_t *, unsigned short);
+static unsigned short cy2y (twindow_t *, unsigned short, int);
+static unsigned short x2cx (twindow_t *, unsigned short, int);
+static unsigned short y2cy (twindow_t *, unsigned short, int);
+
+static void initialize_twid_table (void);
+static twid_t allocate_twid (twindow_t *);
+static void deallocate_twid (twid_t);
+static twindow_t * twid_to_twindow (twid_t);
+
+static MRESULT EXPENTRY object_window_procedure (HWND, ULONG, MPARAM, MPARAM);
+
+static msg_t * make_twindow_open_request (qid_t, const char *);
+static void handle_twindow_open_request (msg_t *);
+static msg_t * make_twindow_open_reply (twid_t);
+
+static msg_t * make_twindow_close_request (twid_t);
+static void handle_twindow_close_request (msg_t *);
+
+static msg_t * make_twindow_write_request
+  (twid_t, unsigned short, unsigned short, const char *, unsigned short);
+static void handle_twindow_write_request (msg_t *);
+
+static msg_t * make_twindow_move_cursor_request
+  (twid_t, unsigned short, unsigned short);
+static void handle_twindow_move_cursor_request (msg_t *);
+
+static void handle_twindow_clear_request (msg_t *);
+static msg_t * make_twindow_clear_request (twid_t);
+
+static msg_t * make_twindow_clear_eol_request
+  (twid_t, unsigned short, unsigned short);
+static void handle_twindow_clear_eol_request (msg_t *);
+
+static msg_t * make_twindow_scroll_request
+  (twid_t, unsigned short, unsigned short, unsigned short, unsigned short,
+   short, short);
+static void handle_twindow_scroll_request (msg_t *);
+
+static twindow_t * twindow_open (qid_t, const char *);
+static twindow_t * make_twindow (qid_t);
+static void twindow_close (twindow_t *);
+static msg_t * make_close_event (twid_t);
+static void twindow_write
+  (twindow_t *, unsigned short, unsigned short, const char *, unsigned short);
+static void twindow_clear (twindow_t *);
+static void twindow_clear_eol (twindow_t *, unsigned short, unsigned short);
+static void invalidate_partial_line
+  (twindow_t *, unsigned short, unsigned short, unsigned short);
+static void twindow_scroll
+  (twindow_t *, unsigned short, unsigned short, unsigned short, unsigned short,
+   short, short);
+static void twindow_move_cursor (twindow_t *, unsigned short, unsigned short);
+
+static MRESULT EXPENTRY frame_window_procedure (HWND, ULONG, MPARAM, MPARAM);
+
+static MRESULT EXPENTRY twindow_procedure (HWND, ULONG, MPARAM, MPARAM);
+static twindow_t * hwnd_to_twindow (HWND);
+
+static void twindow_initialize (twindow_t *);
+static void initialize_default_font (twindow_t *, PSZ, LONG, LONG);
+static void initialize_attributes (twindow_t *);
+static void set_twindow_char_dimensions (twindow_t *);
+static void initialize_charmap (twindow_t *);
+
+static int set_font_1 (HPS, PSZ, LONG, LONG);
+static int use_font (HPS, PFONTMETRICS, LONG);
+
+static void twindow_paint (twindow_t *);
+static void draw_partial_line
+  (twindow_t *, unsigned short, unsigned short, unsigned short);
+static void activate_cursor (twindow_t *);
+static void deactivate_cursor (twindow_t *);
+static void show_cursor (twindow_t *);
+static void hide_cursor (twindow_t *);
+static int twindow_focusp (twindow_t *);
+
+static void twindow_resize (twindow_t *, unsigned short, unsigned short);
+static msg_t * make_resize_event (twid_t, unsigned short, unsigned short);
+
+static int twindow_process_keychar
+  (twindow_t *, unsigned short, unsigned char, unsigned char, unsigned short,
+   unsigned short);
+static void send_key_event
+  (twindow_t *, unsigned short, unsigned short, unsigned short);
+static msg_t * make_key_event
+  (twid_t, unsigned short, unsigned short, unsigned short);
+\f
+#define ID_RESOURCES 1
+#define ID_FRAME 1
+
+#define UWM_ENCAPSULATION WM_USER
+
+#define QWP_TWINDOW QWL_USER
+
+/* These should have been defined by PM header file.  */
+#define MRVOID MRFROMP (0)
+#define MRTRUE MRFROMLONG (TRUE)
+#define MRFALSE MRFROMLONG (FALSE)
+
+static qid_t pm_qid_local;     /* PM thread side */
+static qid_t pm_qid_remote;    /* other thread side */
+static TID pm_tid;
+static HAB pm_hab;
+static HMQ pm_hmq;
+static HWND pm_object_window;
+static PFNWP original_frame_window_procedure;
+
+static const char object_class [] = "mit-scheme.object";
+static const char twindow_class [] = "mit-scheme.twindow";
+
+#define SEND_EVENT(twindow, message)                                   \
+{                                                                      \
+  if ((TWINDOW_EVENT_QID (twindow)) != QID_NONE)                       \
+    OS2_send_message ((TWINDOW_EVENT_QID (twindow)), (message));       \
+}
+
+#define FASTFILL(p, n, c)                                              \
+{                                                                      \
+  char * FASTFILL_scan = (p);                                          \
+  char * FASTFILL_end = (FASTFILL_scan + (n));                         \
+  while (FASTFILL_scan < FASTFILL_end)                                 \
+    (*FASTFILL_scan++) = (c);                                          \
+}
+
+#define window_error(name) OS2_logic_error (#name)
+
+void
+OS2_initialize_pm_thread (void)
+{
+  SET_MSG_TYPE_LENGTH (mt_twindow_open_request, sm_twindow_open_request_t);
+  SET_MSG_TYPE_LENGTH (mt_twindow_open_reply, sm_twindow_open_reply_t);
+  SET_MSG_TYPE_LENGTH (mt_twindow_close_request, sm_twindow_close_request_t);
+  SET_MSG_TYPE_LENGTH (mt_twindow_write_request, sm_twindow_write_request_t);
+  SET_MSG_TYPE_LENGTH (mt_twindow_move_cursor_request,
+                      sm_twindow_move_cursor_request_t);
+  SET_MSG_TYPE_LENGTH (mt_twindow_clear_request, sm_twindow_clear_request_t);
+  SET_MSG_TYPE_LENGTH (mt_twindow_clear_eol_request,
+                      sm_twindow_clear_eol_request_t);
+  SET_MSG_TYPE_LENGTH (mt_twindow_scroll_request, sm_twindow_scroll_request_t);
+  SET_MSG_TYPE_LENGTH (mt_key_event, sm_key_event_t);
+  SET_MSG_TYPE_LENGTH (mt_button_event, sm_button_event_t);
+  SET_MSG_TYPE_LENGTH (mt_close_event, sm_close_event_t);
+  SET_MSG_TYPE_LENGTH (mt_visibility_event, sm_visibility_event_t);
+  SET_MSG_TYPE_LENGTH (mt_resize_event, sm_resize_event_t);
+  initialize_twid_table ();
+  original_frame_window_procedure = 0;
+  OS2_make_qid_pair ((&pm_qid_local), (&pm_qid_remote));
+  OS2_open_qid (pm_qid_remote, OS2_scheme_tqueue);
+  pm_tid = (OS2_beginthread (pm_thread_procedure, 0, 0x4000));
+  /* Wait for init message from PM thread.  This message tells us that
+     the other end of the connection is established and that it is
+     safe to send messages on the connection.  */
+  OS2_destroy_message (OS2_wait_for_message (pm_qid_remote, mt_init));
+}
+
+static void
+simple_transaction (qid_t qid, msg_t * message)
+{
+  OS2_destroy_message
+    (OS2_message_transaction (qid, message, mt_generic_reply));
+}
+
+static void
+simple_reply (qid_t qid)
+{
+  OS2_send_message (qid, (OS2_create_message (mt_generic_reply)));
+}
+\f
+static void
+pm_thread_procedure (void * arg)
+{
+  QMSG qmsg;
+
+  if ((OS2_thread_initialize (pm_qid_local)) != 0)
+    OS2_logic_error ("Error signalled within PM thread.");
+  pm_hab = (WinInitialize (0));
+  if (pm_hab == NULLHANDLE)
+    window_error (WinInitialize);
+  pm_hmq = (WinCreateMsgQueue (pm_hab, 0));
+  if (pm_hmq == NULLHANDLE)
+    window_error (WinCreateMsgQueue);
+  if (!WinRegisterClass (pm_hab,
+                        ((PSZ) object_class),
+                        object_window_procedure,
+                        0,     /* class style */
+                        0))
+    window_error (WinRegisterClass);
+  if (!WinRegisterClass (pm_hab,
+                        ((PSZ) twindow_class),
+                        twindow_procedure,
+                        0,     /* class style */
+                        (sizeof (void *))))
+    window_error (WinRegisterClass);
+  pm_object_window
+    = (WinCreateWindow (HWND_OBJECT,
+                       ((PSZ) object_class),
+                       "",     /* text */
+                       0,      /* style */
+                       0, 0, 0, 0, /* size and position */
+                       NULLHANDLE, /* owner */
+                       HWND_BOTTOM,
+                       0,      /* ID */
+                       0,      /* control data */
+                       0       /* presentation parameters */
+                       ));
+  if (pm_object_window == NULLHANDLE)
+    window_error (WinCreateWindow);
+  OS2_open_qid (pm_qid_local, (make_pm_tqueue (pm_object_window)));
+  OS2_send_message (pm_qid_local, (OS2_create_message (mt_init)));
+  while (WinGetMsg (pm_hab, (&qmsg), 0, 0, 0))
+    WinDispatchMsg (pm_hab, (&qmsg));
+  if (!WinDestroyWindow (pm_object_window))
+    window_error (WinDestroyWindow);
+  WinDestroyMsgQueue (pm_hmq);
+  WinTerminate (pm_hab);
+}
+\f
+static tqueue_t *
+make_pm_tqueue (HWND hwnd)
+{
+  tqueue_t * tqueue = (OS_malloc (sizeof (pm_tqueue_t)));
+  (TQUEUE_TYPE (tqueue)) = tqt_pm;
+  (PM_TQUEUE_HWND (tqueue)) = hwnd;
+  return (tqueue);
+}
+
+int
+OS2_read_pm_tqueue (tqueue_t * tqueue, int blockp)
+{
+  OS2_logic_error ("Read from PM tqueue.");
+  return (0);
+}
+
+void
+OS2_write_pm_tqueue (tqueue_t * tqueue, msg_t * message)
+{
+  if (!WinPostMsg ((PM_TQUEUE_HWND (tqueue)),
+                  UWM_ENCAPSULATION,
+                  (MPFROMP (message)),
+                  MPVOID))
+    window_error (WinPostMsg);
+}
+
+static unsigned short
+cx2x (twindow_t * twindow, unsigned short x)
+{
+  return (x * (TWINDOW_CHAR_WIDTH (twindow)));
+}
+
+static unsigned short
+cy2y (twindow_t * twindow, unsigned short y, int lowerp)
+{
+  /* lowerp => result is bottommost pel of cell.  Otherwise result is
+     bottommost pel of cell above.  */
+  unsigned short height = (TWINDOW_HEIGHT (twindow));
+  unsigned short limit = (lowerp ? (height - 1) : height);
+  return (((y <= limit) ? (limit - y) : 0) * (TWINDOW_CHAR_HEIGHT (twindow)));
+}
+
+static unsigned short
+x2cx (twindow_t * twindow, unsigned short x, int lowerp)
+{
+  /* lowerp => `x' is inclusive lower bound, and result is cell it
+     falls in.  Otherwise, `x' is exclusive upper bound, and result is
+     cell to its right, unless it falls on leftmost edge of cell.  If
+     the argument is inclusive-lower, then the result is also;
+     likewise for exclusive-upper.  */
+  unsigned short cwidth = (TWINDOW_CHAR_WIDTH (twindow));
+  unsigned short cx = (x / cwidth);
+  return ((lowerp || ((x % cwidth) == 0)) ? cx : (cx + 1));
+}
+
+static unsigned short
+y2cy (twindow_t * twindow, unsigned short y, int lowerp)
+{
+  /* lowerp => `y' is inclusive lower bound, and result is cell below
+     the one it falls in.  Otherwise, `y' is exclusive upper bound,
+     and result is cell it falls in, unless it falls on bottommost
+     edge of cell, when result is cell below.  If the argument is
+     inclusive-lower, then the result is exclusive-upper, and
+     vice-versa.  */
+  unsigned short cheight = (TWINDOW_CHAR_HEIGHT (twindow));
+  short height = (TWINDOW_HEIGHT (twindow));
+  short cy = ((height - 1) - ((short) (y / cheight)));
+  if (lowerp || ((y % cheight) == 0))
+    cy += 1;
+  return ((cy < 0) ? 0 : cy);
+}
+\f
+static unsigned int twid_table_length;
+static twindow_t ** twid_table;
+
+static void
+initialize_twid_table (void)
+{
+  twid_table_length = 16;
+  twid_table = (OS_malloc ((sizeof (twindow_t *)) * twid_table_length));
+  {
+    twindow_t ** scan = twid_table;
+    twindow_t ** end = (scan + twid_table_length);
+    while (scan < end)
+      (*scan++) = 0;
+  }
+}
+
+static twid_t
+allocate_twid (twindow_t * twindow)
+{
+  twid_t twid = 0;
+  while (1)
+    {
+      if (twid == twid_table_length)
+       {
+         twid_table_length *= 2;
+         twid_table
+           = (OS_realloc (twid_table,
+                          ((sizeof (twindow_t *)) * twid_table_length)));
+         {
+           twindow_t ** scan = (twid_table + twid + 1);
+           twindow_t ** end = (twid_table + twid_table_length);
+           while (scan < end)
+             (*scan++) = 0;
+         }
+         break;
+       }
+      if ((twid_table [twid]) == 0)
+       break;
+      twid += 1;
+    }
+  (twid_table [twid]) = twindow;
+  (TWINDOW_TWID (twindow)) = twid;
+  return (twid);
+}
+
+static void
+deallocate_twid (twid_t twid)
+{
+  (twid_table [twid]) = 0;
+}
+
+static twindow_t *
+twid_to_twindow (twid_t twid)
+{
+  if ((twid_table [twid]) == 0)
+    OS2_logic_error ("Invalid terminal window ID.");
+  return (twid_table [twid]);
+}
+\f
+/* Implementation of the object window.  The object window handles
+   encapsulated messages sent from the Scheme thread.  This defines
+   the protocol used to communicate with the Scheme thread.  */
+
+static MRESULT EXPENTRY
+object_window_procedure (HWND window, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+  if (msg == UWM_ENCAPSULATION)
+    {
+      msg_t * message = (PVOIDFROMMP (mp1));
+      switch (MSG_TYPE (message))
+       {
+       case mt_twindow_open_request:
+         handle_twindow_open_request (message);
+         break;
+       case mt_twindow_close_request:
+         handle_twindow_close_request (message);
+         break;
+       case mt_twindow_write_request:
+         handle_twindow_write_request (message);
+         break;
+       case mt_twindow_move_cursor_request:
+         handle_twindow_move_cursor_request (message);
+         break;
+       case mt_twindow_clear_request:
+         handle_twindow_clear_request (message);
+         break;
+       case mt_twindow_clear_eol_request:
+         handle_twindow_clear_eol_request (message);
+         break;
+       case mt_twindow_scroll_request:
+         handle_twindow_scroll_request (message);
+         break;
+       default:
+         OS2_logic_error ("Unknown message type sent to PM thread.");
+         break;
+       }
+    }
+  return (MRVOID);
+}
+
+twid_t
+OS2_twindow_open (qid_t event_qid, const char * title)
+{
+  msg_t * reply
+    = (OS2_message_transaction (pm_qid_remote,
+                               (make_twindow_open_request (event_qid, title)),
+                               mt_twindow_open_reply));
+  twid_t twid = (SM_TWINDOW_OPEN_REPLY_TWID (reply));
+  OS2_destroy_message (reply);
+  return (twid);
+}
+
+static msg_t *
+make_twindow_open_request (qid_t event_qid, const char * title)
+{
+  msg_t * message = (OS2_create_message (mt_twindow_open_request));
+  (SM_TWINDOW_OPEN_REQUEST_EVENT_QID (message)) = event_qid;
+  (SM_TWINDOW_OPEN_REQUEST_TITLE (message)) = title;
+  return (message);
+}
+
+static void
+handle_twindow_open_request (msg_t * message)
+{
+  qid_t sender = (MSG_SENDER (message));
+  qid_t event_qid = (SM_TWINDOW_OPEN_REQUEST_EVENT_QID (message));
+  const char * title = (SM_TWINDOW_OPEN_REQUEST_TITLE (message));
+  OS2_destroy_message (message);
+  OS2_send_message
+    (sender,
+     (make_twindow_open_reply
+      (allocate_twid (twindow_open (event_qid, title)))));
+}
+
+static msg_t *
+make_twindow_open_reply (twid_t twid)
+{
+  msg_t * message = (OS2_create_message (mt_twindow_open_reply));
+  (SM_TWINDOW_OPEN_REPLY_TWID (message)) = twid;
+  return (message);
+}
+\f
+void
+OS2_twindow_close (twid_t twid)
+{
+  simple_transaction (pm_qid_remote, (make_twindow_close_request (twid)));
+}
+
+static msg_t *
+make_twindow_close_request (twid_t twid)
+{
+  msg_t * message = (OS2_create_message (mt_twindow_close_request));
+  (SM_TWINDOW_CLOSE_REQUEST_TWID (message)) = twid;
+  return (message);
+}
+
+static void
+handle_twindow_close_request (msg_t * message)
+{
+  qid_t qid = (MSG_SENDER (message));
+  twid_t twid = (SM_TWINDOW_CLOSE_REQUEST_TWID (message));
+  OS2_destroy_message (message);
+  twindow_close (twid_to_twindow (twid));
+  simple_reply (qid);
+}
+
+void
+OS2_twindow_write (twid_t twid, unsigned short x, unsigned short y,
+                  const char * data, unsigned short size)
+{
+  simple_transaction (pm_qid_remote,
+                     (make_twindow_write_request (twid, x, y, data, size)));
+}
+
+static msg_t *
+make_twindow_write_request (twid_t twid, unsigned short x, unsigned short y,
+                           const char * data, unsigned short size)
+{
+  msg_t * message = (OS2_create_message (mt_twindow_write_request));
+  (SM_TWINDOW_WRITE_REQUEST_TWID (message)) = twid;
+  (SM_TWINDOW_WRITE_REQUEST_X (message)) = x;
+  (SM_TWINDOW_WRITE_REQUEST_Y (message)) = y;
+  (SM_TWINDOW_WRITE_REQUEST_DATA (message)) = data;
+  (SM_TWINDOW_WRITE_REQUEST_SIZE (message)) = size;
+  return (message);
+}
+
+static void
+handle_twindow_write_request (msg_t * message)
+{
+  qid_t qid = (MSG_SENDER (message));
+  twid_t twid = (SM_TWINDOW_WRITE_REQUEST_TWID (message));
+  unsigned short x = (SM_TWINDOW_WRITE_REQUEST_X (message));
+  unsigned short y = (SM_TWINDOW_WRITE_REQUEST_Y (message));
+  const char * data = (SM_TWINDOW_WRITE_REQUEST_DATA (message));
+  unsigned short size = (SM_TWINDOW_WRITE_REQUEST_SIZE (message));
+  OS2_destroy_message (message);
+  twindow_write ((twid_to_twindow (twid)), x, y, data, size);
+  simple_reply (qid);
+}
+\f
+void
+OS2_twindow_move_cursor (twid_t twid, unsigned short x, unsigned short y)
+{
+  simple_transaction (pm_qid_remote,
+                     (make_twindow_move_cursor_request (twid, x, y)));
+}
+
+static msg_t *
+make_twindow_move_cursor_request (twid_t twid, unsigned short x,
+                                 unsigned short y)
+{
+  msg_t * message = (OS2_create_message (mt_twindow_move_cursor_request));
+  (SM_TWINDOW_MOVE_CURSOR_REQUEST_TWID (message)) = twid;
+  (SM_TWINDOW_MOVE_CURSOR_REQUEST_X (message)) = x;
+  (SM_TWINDOW_MOVE_CURSOR_REQUEST_Y (message)) = y;
+  return (message);
+}
+
+static void
+handle_twindow_move_cursor_request (msg_t * message)
+{
+  qid_t qid = (MSG_SENDER (message));
+  twid_t twid = (SM_TWINDOW_MOVE_CURSOR_REQUEST_TWID (message));
+  unsigned short x = (SM_TWINDOW_MOVE_CURSOR_REQUEST_X (message));
+  unsigned short y = (SM_TWINDOW_MOVE_CURSOR_REQUEST_Y (message));
+  OS2_destroy_message (message);
+  twindow_move_cursor ((twid_to_twindow (twid)), x, y);
+  simple_reply (qid);
+}
+\f
+void
+OS2_twindow_clear (twid_t twid)
+{
+  simple_transaction (pm_qid_remote, (make_twindow_clear_request (twid)));
+}
+
+static void
+handle_twindow_clear_request (msg_t * message)
+{
+  qid_t qid = (MSG_SENDER (message));
+  twid_t twid = (SM_TWINDOW_CLEAR_REQUEST_TWID (message));
+  OS2_destroy_message (message);
+  twindow_clear (twid_to_twindow (twid));
+  simple_reply (qid);
+}
+
+static msg_t *
+make_twindow_clear_request (twid_t twid)
+{
+  msg_t * message = (OS2_create_message (mt_twindow_clear_request));
+  (SM_TWINDOW_CLEAR_REQUEST_TWID (message)) = twid;
+  return (message);
+}
+
+void
+OS2_twindow_clear_eol (twid_t twid, unsigned short x, unsigned short y)
+{
+  simple_transaction (pm_qid_remote,
+                     (make_twindow_clear_eol_request (twid, x, y)));
+}
+
+static void
+handle_twindow_clear_eol_request (msg_t * message)
+{
+  qid_t qid = (MSG_SENDER (message));
+  twid_t twid = (SM_TWINDOW_CLEAR_EOL_REQUEST_TWID (message));
+  unsigned short x = (SM_TWINDOW_CLEAR_EOL_REQUEST_X (message));
+  unsigned short y = (SM_TWINDOW_CLEAR_EOL_REQUEST_Y (message));
+  OS2_destroy_message (message);
+  twindow_clear_eol ((twid_to_twindow (twid)), x, y);
+  simple_reply (qid);
+}
+
+static msg_t *
+make_twindow_clear_eol_request (twid_t twid, unsigned short x,
+                               unsigned short y)
+{
+  msg_t * message = (OS2_create_message (mt_twindow_clear_eol_request));
+  (SM_TWINDOW_CLEAR_EOL_REQUEST_TWID (message)) = twid;
+  (SM_TWINDOW_CLEAR_EOL_REQUEST_X (message)) = x;
+  (SM_TWINDOW_CLEAR_EOL_REQUEST_Y (message)) = y;
+  return (message);
+}
+\f
+void
+OS2_twindow_scroll (twid_t twid,
+                   unsigned short x_start, unsigned short x_end,
+                   unsigned short y_start, unsigned short y_end,
+                   short x_delta, short y_delta)
+{
+  simple_transaction
+    (pm_qid_remote,
+     (make_twindow_scroll_request
+      (twid, x_start, x_end, y_start, y_end, x_delta, y_delta)));
+}
+
+static msg_t *
+make_twindow_scroll_request (twid_t twid,
+                            unsigned short x_start, unsigned short x_end,
+                            unsigned short y_start, unsigned short y_end,
+                            short x_delta, short y_delta)
+{
+  msg_t * message = (OS2_create_message (mt_twindow_scroll_request));
+  (SM_TWINDOW_SCROLL_REQUEST_TWID (message)) = twid;
+  (SM_TWINDOW_SCROLL_REQUEST_X_START (message)) = x_start;
+  (SM_TWINDOW_SCROLL_REQUEST_X_END (message)) = x_end;
+  (SM_TWINDOW_SCROLL_REQUEST_Y_START (message)) = y_start;
+  (SM_TWINDOW_SCROLL_REQUEST_Y_END (message)) = y_end;
+  (SM_TWINDOW_SCROLL_REQUEST_X_DELTA (message)) = x_delta;
+  (SM_TWINDOW_SCROLL_REQUEST_Y_DELTA (message)) = y_delta;
+  return (message);
+}
+
+static void
+handle_twindow_scroll_request (msg_t * message)
+{
+  qid_t qid = (MSG_SENDER (message));
+  twid_t twid = (SM_TWINDOW_SCROLL_REQUEST_TWID (message));
+  unsigned short x_start = (SM_TWINDOW_SCROLL_REQUEST_X_START (message));
+  unsigned short x_end = (SM_TWINDOW_SCROLL_REQUEST_X_END (message));
+  unsigned short y_start = (SM_TWINDOW_SCROLL_REQUEST_Y_START (message));
+  unsigned short y_end = (SM_TWINDOW_SCROLL_REQUEST_Y_END (message));
+  short x_delta = (SM_TWINDOW_SCROLL_REQUEST_X_DELTA (message));
+  short y_delta = (SM_TWINDOW_SCROLL_REQUEST_Y_DELTA (message));
+  OS2_destroy_message (message);
+  twindow_scroll
+    ((twid_to_twindow (twid)),
+     x_start, x_end, y_start, y_end, x_delta, y_delta);
+  simple_reply (qid);
+}
+\f
+static twindow_t *
+twindow_open (qid_t event_qid, const char * title)
+{
+  twindow_t * twindow = (make_twindow (event_qid));
+  FRAMECDATA frame_data;
+  HWND frame_window;
+
+  (frame_data . cb) = (sizeof (frame_data));
+  (frame_data . flCreateFlags)
+     = (FCF_TITLEBAR | FCF_SYSMENU | FCF_SHELLPOSITION | FCF_SIZEBORDER
+       | FCF_MINMAX | FCF_TASKLIST);
+  (frame_data . hmodResources) = NULLHANDLE;
+  (frame_data . idResources) = ID_RESOURCES;
+  frame_window
+    = (WinCreateWindow (HWND_DESKTOP,
+                       WC_FRAME,
+                       ((PSZ) title), /* title string */
+                       0,      /* window style */
+                       0, 0, 0, 0, /* size and position */
+                       NULLHANDLE, /* owner window */
+                       HWND_TOP,
+                       ID_FRAME,       /* window ID */
+                       (& frame_data),
+                       0));
+  if (frame_window == NULLHANDLE)
+    window_error (WinCreateWindow);
+  (TWINDOW_FRAME (twindow)) = frame_window;
+  {
+    PFNWP procedure
+      = (WinSubclassWindow (frame_window, frame_window_procedure));
+    if (procedure == 0)
+      window_error (WinSubclassWindow);
+    if (original_frame_window_procedure == 0)
+      original_frame_window_procedure = procedure;
+    else if (original_frame_window_procedure != procedure)
+      OS2_logic_error ("WinSubclassWindow returned two different answers.");
+  }
+  if ((WinCreateWindow (frame_window,
+                       ((PSZ) twindow_class),
+                       0,      /* window text (class-specific) */
+                       0,      /* window style */
+                       0, 0, 0, 0, /* size and position */
+                       frame_window, /* owner window */
+                       HWND_BOTTOM,
+                       FID_CLIENT, /* window ID */
+                       twindow,
+                       0))
+      == NULLHANDLE)
+    window_error (WinCreateWindow);
+  if (!WinShowWindow ((TWINDOW_FRAME (twindow)), TRUE))
+    window_error (WinShowWindow);
+  if (!WinShowWindow ((TWINDOW_CLIENT (twindow)), TRUE))
+    window_error (WinShowWindow);
+  return (twindow);
+}
+
+static twindow_t *
+make_twindow (qid_t event_qid)
+{
+  twindow_t * twindow = (OS_malloc (sizeof (twindow_t)));
+  (TWINDOW_FRAME (twindow)) = NULLHANDLE;
+  (TWINDOW_CLIENT (twindow)) = NULLHANDLE;
+  (TWINDOW_CHARMAP (twindow)) = 0;
+  (TWINDOW_CURSOR_X (twindow)) = 0;
+  (TWINDOW_CURSOR_Y (twindow)) = 0;
+  (TWINDOW_CURSOR_SHOWNP (twindow)) = 1;
+  (TWINDOW_EVENT_QID (twindow)) = event_qid;
+  (TWINDOW_MINIMIZINGP (twindow)) = 0;
+  (TWINDOW_MINIMIZEDP (twindow)) = 0;
+  return (twindow);
+}
+\f
+static void
+twindow_close (twindow_t * twindow)
+{
+  OS_free (TWINDOW_CHARMAP (twindow));
+  if (!GpiDestroyPS (TWINDOW_HPS (twindow)))
+    window_error (GpiDestroyPS);
+  if (!WinDestroyWindow (TWINDOW_FRAME (twindow)))
+    window_error (WinDestroyWindow);
+  deallocate_twid (TWINDOW_TWID (twindow));
+}
+
+static msg_t *
+make_close_event (twid_t twid)
+{
+  msg_t * message = (OS2_create_message (mt_close_event));
+  (SM_CLOSE_EVENT_TWID (message)) = twid;
+  return (message);
+}
+
+static void
+twindow_write (twindow_t * twindow, unsigned short x, unsigned short y,
+              const char * data, unsigned short size)
+{
+  unsigned short width = (TWINDOW_WIDTH (twindow));
+  unsigned short height = (TWINDOW_HEIGHT (twindow));
+  if ((y < (TWINDOW_HEIGHT (twindow))) && (x < width))
+    {
+      char * target = (TWINDOW_CHAR_LOC (twindow, x, y));
+      if (size > (width - x))
+       size = (width - x);
+      FASTCOPY (data, target, size);
+      invalidate_partial_line (twindow, x, y, size);
+    }
+}
+
+static void
+twindow_clear (twindow_t * twindow)
+{
+  FASTFILL ((TWINDOW_CHARMAP (twindow)),
+           ((TWINDOW_WIDTH (twindow)) * (TWINDOW_HEIGHT (twindow))),
+           ' ');
+  if (!WinInvalidateRect ((TWINDOW_CLIENT (twindow)), 0, FALSE))
+    window_error (WinInvalidateRect);
+}
+
+static void
+twindow_clear_eol (twindow_t * twindow, unsigned short x, unsigned short y)
+{
+  unsigned short width = (TWINDOW_WIDTH (twindow));
+  if ((y < (TWINDOW_HEIGHT (twindow))) && (x < width))
+    {
+      unsigned short size = (width - x);
+      FASTFILL ((TWINDOW_CHAR_LOC (twindow, x, y)), size, ' ');
+      invalidate_partial_line (twindow, x, y, size);
+    }
+}
+
+static void
+invalidate_partial_line (twindow_t * twindow, unsigned short x,
+                        unsigned short y, unsigned short size)
+{
+  RECTL rectl;
+  (rectl . xLeft) = (cx2x (twindow, x));
+  (rectl . xRight) = (cx2x (twindow, (x + size)));
+  (rectl . yBottom) = (cy2y (twindow, y, 1));
+  (rectl . yTop) = (cy2y (twindow, y, 0));
+  if (!WinInvalidateRect ((TWINDOW_CLIENT (twindow)), (& rectl), FALSE))
+    window_error (WinInvalidateRect);
+}
+\f
+static void
+twindow_scroll (twindow_t * twindow,
+               unsigned short x_start, unsigned short x_end,
+               unsigned short y_start, unsigned short y_end,
+               short x_delta, short y_delta)
+{
+  RECTL rectl;
+  (rectl . xLeft) = (cx2x (twindow, x_start));
+  (rectl . xRight) = (cx2x (twindow, x_end));
+  (rectl . yBottom) = (cy2y (twindow, y_end, 1));
+  (rectl . yTop) = (cy2y (twindow, y_start, 0));
+  if ((WinScrollWindow ((TWINDOW_CLIENT (twindow)),
+                       (x_delta * (TWINDOW_CHAR_WIDTH (twindow))),
+                       (y_delta * (TWINDOW_CHAR_HEIGHT (twindow))),
+                       (& rectl),
+                       0,
+                       NULLHANDLE,
+                       0,
+                       0))
+      == RGN_ERROR)
+    window_error (WinScrollWindow);
+}
+
+static void
+twindow_move_cursor (twindow_t * twindow, unsigned short x, unsigned short y)
+{
+  if ((x < (TWINDOW_WIDTH (twindow))) && (y < (TWINDOW_HEIGHT (twindow))))
+    {
+      (TWINDOW_CURSOR_X (twindow)) = x;
+      (TWINDOW_CURSOR_Y (twindow)) = y;
+      if (twindow_focusp (twindow))
+       if (!WinCreateCursor ((TWINDOW_CLIENT (twindow)),
+                             (cx2x (twindow, x)),
+                             (cy2y (twindow, y, 1)),
+                             0,
+                             0,
+                             CURSOR_SETPOS,
+                             0))
+         window_error (WinCreateCursor);
+    }
+}
+\f
+static MRESULT EXPENTRY
+frame_window_procedure (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+  twindow_t * twindow = (hwnd_to_twindow (WinWindowFromID (hwnd, FID_CLIENT)));
+  switch (msg)
+    {
+    case WM_QUERYTRACKINFO:
+      /* Set the tracking grid for the resize operation.  */
+      {
+       MRESULT mr
+         = ((* original_frame_window_procedure) (hwnd, msg, mp1, mp2));
+       if (mr == MRTRUE)
+         {
+           PTRACKINFO pti = (PVOIDFROMMP (mp2));
+           if ((((pti -> fs) & TF_MOVE) != TF_MOVE)
+               && ((((pti -> fs) & TF_MOVE) != 0)
+                   || (((pti -> fs) & TF_SETPOINTERPOS) != 0)))
+             {
+               (pti -> fs) |= TF_GRID;
+               (pti -> cxGrid) = (TWINDOW_CHAR_WIDTH (twindow));
+               (pti -> cyGrid) = (TWINDOW_CHAR_HEIGHT (twindow));
+               (pti -> cxKeyboard) = (TWINDOW_CHAR_WIDTH (twindow));
+               (pti -> cyKeyboard) = (TWINDOW_CHAR_HEIGHT (twindow));
+             }
+         }
+       return (mr);
+      }
+    case WM_MINMAXFRAME:
+      /* If minimizing, mark the window to indicate this.  The client
+        will shortly receive a WM_SIZE which indicates that the
+        minimization has completed.  */
+      {
+       PSWP pswp = (PVOIDFROMMP (mp1));
+       if ((!TWINDOW_MINIMIZEDP (twindow))
+           && (((pswp -> fl) & SWP_MINIMIZE) != 0))
+         {
+           (TWINDOW_MINIMIZINGP (twindow)) = 1;
+           (TWINDOW_MINIMIZEDP (twindow)) = 1;
+         }
+       else if ((TWINDOW_MINIMIZEDP (twindow))
+                && (((pswp -> fl) & (SWP_RESTORE | SWP_MAXIMIZE)) != 0))
+         (TWINDOW_MINIMIZEDP (twindow)) = 0;
+      }
+      break;
+    }
+  return ((* original_frame_window_procedure) (hwnd, msg, mp1, mp2));
+}
+\f
+static MRESULT EXPENTRY
+twindow_procedure (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
+{
+  switch (msg)
+    {
+    case WM_CREATE:
+      {
+       twindow_t * twindow = (PVOIDFROMMP (mp1));
+       (TWINDOW_CLIENT (twindow)) = hwnd;
+       if (!WinSetWindowPtr (hwnd, QWP_TWINDOW, twindow))
+         window_error (WinSetWindowPtr);
+       twindow_initialize (twindow);
+       return (MRFALSE);
+      }
+    case WM_PAINT:
+      {
+       twindow_t * twindow = (hwnd_to_twindow (hwnd));
+       if (((WinQueryWindowULong ((TWINDOW_FRAME (twindow)), QWL_STYLE))
+            & WS_MINIMIZED)
+           != 0)
+         break;
+       twindow_paint (twindow);
+       return (MRVOID);
+      }
+    case WM_SETFOCUS:
+      {
+       twindow_t * twindow = (hwnd_to_twindow (hwnd));
+       if (SHORT1FROMMP (mp2))
+         activate_cursor (twindow);
+       else
+         deactivate_cursor (twindow);
+       return (MRVOID);
+      }
+    case WM_CHAR:
+      return
+       ((twindow_process_keychar ((hwnd_to_twindow (hwnd)),
+                                  (SHORT1FROMMP (mp1)),
+                                  (CHAR3FROMMP (mp1)),
+                                  (CHAR4FROMMP (mp1)),
+                                  (SHORT1FROMMP (mp2)),
+                                  (SHORT2FROMMP (mp2))))
+        ? MRTRUE
+        : MRFALSE);
+    case WM_CLOSE:
+      {
+       twindow_t * twindow = (hwnd_to_twindow (hwnd));
+       SEND_EVENT (twindow, (make_close_event (TWINDOW_TWID (twindow))));
+       return (MRVOID);
+      }
+    case WM_SIZE:
+      {
+       twindow_t * twindow = (hwnd_to_twindow (hwnd));
+       /* If this message is part of a minimization, ignore it.  */
+       if (TWINDOW_MINIMIZINGP (twindow))
+         {
+           (TWINDOW_MINIMIZINGP (twindow)) = 0;
+           (TWINDOW_MINIMIZEDP (twindow)) = 1;
+           break;
+         }
+       twindow_resize (twindow, (SHORT1FROMMP (mp2)), (SHORT2FROMMP (mp2)));
+       return (MRVOID);
+      }
+    case WM_SHOW:
+    case WM_BUTTON1CLICK:
+    case WM_BUTTON1DOWN:
+    case WM_BUTTON1UP:
+    case WM_BUTTON1DBLCLK:
+    case WM_BUTTON2CLICK:
+    case WM_BUTTON2DOWN:
+    case WM_BUTTON2UP:
+    case WM_BUTTON2DBLCLK:
+    case WM_BUTTON3CLICK:
+    case WM_BUTTON3DOWN:
+    case WM_BUTTON3UP:
+    case WM_BUTTON3DBLCLK:
+    case WM_MOUSEMOVE:
+    default:
+      break;
+    }
+  return (WinDefWindowProc (hwnd, msg, mp1, mp2));
+}
+
+static twindow_t *
+hwnd_to_twindow (HWND hwnd)
+{
+  twindow_t * twindow = (WinQueryWindowPtr (hwnd, QWP_TWINDOW));
+  if (twindow == 0)
+    window_error (WinQueryWindowPtr);
+  return (twindow);
+}
+\f
+static void
+twindow_initialize (twindow_t * twindow)
+{
+  SIZEL sizel;
+  (sizel . cx) = 0;
+  (sizel . cy) = 0;
+  (TWINDOW_HPS (twindow))
+    = (GpiCreatePS (pm_hab,
+                   (WinOpenWindowDC (TWINDOW_CLIENT (twindow))),
+                   (& sizel),
+                   (PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC)));
+  if ((TWINDOW_HPS (twindow)) == 0)
+    window_error (GpiCreatePS);
+  initialize_default_font (twindow, "System VIO", 40, 1);
+  initialize_attributes (twindow);
+  set_twindow_char_dimensions (twindow);
+  initialize_charmap (twindow);
+}
+
+static void
+initialize_default_font (twindow_t * twindow, PSZ font_name, LONG font_size,
+                        LONG font_id)
+{
+  HPS hps = (TWINDOW_HPS (twindow));
+  if ((!set_font_1 (hps, font_name, font_size, font_id))
+      && (!set_font_1 (hps, "Courier", 100, font_id)))
+    OS2_logic_error ("Unable to initialize default font.");
+  {
+    FONTMETRICS fm;
+    if (!GpiQueryFontMetrics (hps, (sizeof (fm)), (& fm)))
+      window_error (GpiQueryFontMetrics);
+    (TWINDOW_CHAR_WIDTH (twindow)) = (fm . lMaxCharInc);
+    (TWINDOW_CHAR_ASCENDER (twindow)) = (fm . lMaxBaselineExt);
+    (TWINDOW_CHAR_DESCENDER (twindow)) = (fm . lMaxDescender);
+  }
+}
+
+static void
+initialize_attributes (twindow_t * twindow)
+{
+  CHARBUNDLE attrs;
+#if 0
+  LONG default_mask
+    = (GpiQueryAttrs ((TWINDOW_HPS (twindow)),
+                     PRIM_CHAR,
+                     (CBB_COLOR | CBB_BACK_COLOR | CBB_MIX_MODE
+                      | CBB_BACK_MIX_MODE | CBB_BOX | CBB_SET | CBB_MODE
+                      | CBB_ANGLE | CBB_SHEAR | CBB_DIRECTION
+                      | CBB_TEXT_ALIGN | CBB_EXTRA | CBB_BREAK_EXTRA),
+                     (& attrs)));
+  if (default_mask == GPI_ALTERROR)
+    window_error (GpiQueryAttrs);
+#endif
+#if 0
+  (attrs . lColor) = CLR_NEUTRAL;
+  (attrs . lBackColor) = CLR_BACKGROUND;
+  (attrs . usMixMode) = FM_OVERPAINT;
+  (attrs . usBackMixMode) = BM_LEAVEALONE;
+  if (!GpiSetAttrs ((TWINDOW_HPS (twindow)),
+                   PRIM_CHAR,
+                   (CBB_COLOR | CBB_BACK_COLOR | CBB_MIX_MODE
+                    | CBB_BACK_MIX_MODE),
+                   0,
+                   (& attrs)))
+    window_error (GpiSetAttrs);
+#endif
+}
+
+static void
+set_twindow_char_dimensions (twindow_t * twindow)
+{
+  SWP swp;
+  if (!WinQueryWindowPos ((TWINDOW_CLIENT (twindow)), (& swp)))
+    window_error (WinQueryWindowPos);
+  (TWINDOW_WIDTH (twindow)) = ((swp . cx) / (TWINDOW_CHAR_WIDTH (twindow)));
+  (TWINDOW_HEIGHT (twindow)) = ((swp . cy) / (TWINDOW_CHAR_HEIGHT (twindow)));
+}
+
+static void
+initialize_charmap (twindow_t * twindow)
+{
+  unsigned short n = ((TWINDOW_WIDTH (twindow)) * (TWINDOW_HEIGHT (twindow)));
+  if ((TWINDOW_CHARMAP (twindow)) != 0)
+    OS_free (TWINDOW_CHARMAP (twindow));
+  if (n == 0)
+    (TWINDOW_CHARMAP (twindow)) = 0;
+  else
+    {
+      (TWINDOW_CHARMAP (twindow)) = (OS_malloc (n));
+      FASTFILL ((TWINDOW_CHARMAP (twindow)), n, ' ');
+    }
+}
+\f
+static int
+set_font_1 (HPS hps, PSZ font_name, LONG font_size, LONG font_id)
+{
+  LONG nfonts;
+  ULONG index;
+  PFONTMETRICS pfm;
+
+  nfonts = 0;
+  nfonts = (GpiQueryFonts (hps,
+                          (QF_PUBLIC | QF_PRIVATE),
+                          font_name,
+                          (& nfonts),
+                          (sizeof (FONTMETRICS)),
+                          0));
+  if (nfonts == GPI_ALTERROR)
+    window_error (GpiQueryFonts);
+  if (nfonts == 0)
+    return (0);
+  pfm = (OS_malloc (nfonts * (sizeof (FONTMETRICS))));
+  if ((GpiQueryFonts (hps,
+                     (QF_PUBLIC | QF_PRIVATE),
+                     font_name,
+                     (& nfonts),
+                     (sizeof (FONTMETRICS)),
+                     pfm))
+      == GPI_ALTERROR)
+    window_error (GpiQueryFonts);
+  for (index = 0; (index < nfonts); index += 1)
+    if (((((pfm [index]) . fsType) & FM_TYPE_FIXED) != 0)
+       && ((((pfm [index]) . fsDefn) & FM_DEFN_OUTLINE) == 0)
+       && (((pfm [index]) . sNominalPointSize) == font_size)
+       && (use_font (hps, (& (pfm [index])), font_id)))
+      {
+       OS_free (pfm);
+       return (1);
+      }
+  OS_free (pfm);
+  return (0);
+}
+
+static int
+use_font (HPS hps, PFONTMETRICS pfm, LONG font_id)
+{
+  FATTRS font_attrs;
+
+  (font_attrs . usRecordLength) = (sizeof (font_attrs));
+  (font_attrs . fsSelection) = 0;
+  (font_attrs . lMatch) = (pfm -> lMatch);
+  strcpy ((font_attrs . szFacename), (pfm -> szFacename));
+  (font_attrs . idRegistry) = 0;
+  (font_attrs . usCodePage) = (WinQueryCp (pm_hmq));
+  if ((font_attrs . usCodePage) == 0)
+    window_error (WinQueryCp);
+  (font_attrs . lMaxBaselineExt) = 0;
+  (font_attrs . lAveCharWidth) = 0;
+  (font_attrs . fsType) = 0;
+  (font_attrs . fsFontUse) = 0;
+  if ((GpiCreateLogFont (hps, 0, font_id, (& font_attrs))) != FONT_MATCH)
+    return (0);
+  GpiSetCharSet (hps, font_id);
+  return (1);
+}
+\f
+static void
+twindow_paint (twindow_t * twindow)
+{
+  RECTL rectl;
+  HPS hps =
+    (WinBeginPaint ((TWINDOW_CLIENT (twindow)),
+                   (TWINDOW_HPS (twindow)),
+                   (& rectl)));
+  if (hps == NULLHANDLE)
+    window_error (WinBeginPaint);
+  if (!WinFillRect ((TWINDOW_HPS (twindow)), (& rectl), CLR_BACKGROUND))
+    window_error (WinFillRect);
+  {
+    unsigned short xl = (x2cx (twindow, (rectl . xLeft), 1));
+    unsigned short xh = (x2cx (twindow, (rectl . xRight), 0));
+    unsigned short yl = (y2cy (twindow, (rectl . yTop), 0));
+    unsigned short yh = (y2cy (twindow, (rectl . yBottom), 1));
+    unsigned short size = (xh - xl);
+    while (yl < yh)
+      draw_partial_line (twindow, xl, (yl++), size);
+  }
+  if (!WinEndPaint (hps))
+    window_error (WinEndPaint);
+}
+
+static void
+draw_partial_line (twindow_t * twindow, unsigned short x, unsigned short y,
+                  unsigned short size)
+{
+  HPS hps = (TWINDOW_HPS (twindow));
+  char * target = (TWINDOW_CHAR_LOC (twindow, x, y));
+  POINTL ptl;
+  (ptl . x) = (cx2x (twindow, x));
+  (ptl . y) = ((cy2y (twindow, y, 1)) + (TWINDOW_CHAR_DESCENDER (twindow)));
+  if (size <= 512)
+    GpiCharStringAt (hps, (& ptl), size, target);
+  else
+    {
+      GpiMove (hps, (& ptl));
+      while (size > 0)
+       {
+         unsigned short n = ((size > 512) ? 512 : size);
+         GpiCharString (hps, n, target);
+         size -= n;
+         target += n;
+       }
+    }
+}
+\f
+static void
+activate_cursor (twindow_t * twindow)
+{
+  if (!WinCreateCursor ((TWINDOW_CLIENT (twindow)),
+                       (cx2x (twindow, (TWINDOW_CURSOR_X (twindow)))),
+                       (cy2y (twindow, (TWINDOW_CURSOR_Y (twindow)), 1)),
+                       (TWINDOW_CHAR_WIDTH (twindow)),
+                       (TWINDOW_CHAR_HEIGHT (twindow)),
+                       (CURSOR_SOLID | CURSOR_FLASH),
+                       0))
+    window_error (WinCreateCursor);
+  if (TWINDOW_CURSOR_SHOWNP (twindow))
+    if (!WinShowCursor ((TWINDOW_CLIENT (twindow)), TRUE))
+      window_error (WinShowCursor);
+}
+
+static void
+deactivate_cursor (twindow_t * twindow)
+{
+  if (!WinDestroyCursor (TWINDOW_CLIENT (twindow)))
+    window_error (WinDestroyCursor);
+}
+
+static void
+show_cursor (twindow_t * twindow)
+{
+  if (!TWINDOW_CURSOR_SHOWNP (twindow))
+    {
+      if (twindow_focusp (twindow))
+       if (!WinShowCursor ((TWINDOW_CLIENT (twindow)), TRUE))
+         window_error (WinShowCursor);
+      (TWINDOW_CURSOR_SHOWNP (twindow)) = 1;
+    }
+}
+
+static void
+hide_cursor (twindow_t * twindow)
+{
+  if (TWINDOW_CURSOR_SHOWNP (twindow))
+    {
+      if (twindow_focusp (twindow))
+       if (!WinShowCursor ((TWINDOW_CLIENT (twindow)), FALSE))
+         window_error (WinShowCursor);
+      (TWINDOW_CURSOR_SHOWNP (twindow)) = 0;
+    }
+}
+
+static int
+twindow_focusp (twindow_t * twindow)
+{
+  return ((TWINDOW_CLIENT (twindow)) == (WinQueryFocus (HWND_DESKTOP)));
+}
+
+static void
+twindow_resize (twindow_t * twindow, unsigned short width,
+               unsigned short height)
+{
+  unsigned short cx = (width / (TWINDOW_CHAR_WIDTH (twindow)));
+  unsigned short cy = (height / (TWINDOW_CHAR_HEIGHT (twindow)));
+  if ((cx == (TWINDOW_WIDTH (twindow))) && (cy == (TWINDOW_HEIGHT (twindow))))
+    return;
+  (TWINDOW_WIDTH (twindow)) = cx;
+  (TWINDOW_HEIGHT (twindow)) = cy;
+  initialize_charmap (twindow);
+  SEND_EVENT (twindow, (make_resize_event ((TWINDOW_TWID (twindow)), cx, cy)));
+}
+
+static msg_t *
+make_resize_event (twid_t twid, unsigned short width, unsigned short height)
+{
+  msg_t * message = (OS2_create_message (mt_resize_event));
+  (SM_RESIZE_EVENT_TWID (message)) = twid;
+  (SM_RESIZE_EVENT_WIDTH (message)) = width;
+  (SM_RESIZE_EVENT_HEIGHT (message)) = height;
+  return (message);
+}
+\f
+static int
+twindow_process_keychar (twindow_t * twindow, unsigned short flags,
+                        unsigned char repeat, unsigned char scan_code,
+                        unsigned short char_code, unsigned short virtual_key)
+{
+  /* Ignore compound keys for now.  */
+  if ((flags & (KC_DEADKEY | KC_COMPOSITE | KC_INVALIDCOMP | KC_KEYUP)) != 0)
+    return (0);
+  else if ((flags & KC_VIRTUALKEY) != 0)
+    {
+      send_key_event (twindow, virtual_key, flags, repeat);
+      return (1);
+    }
+  else if ((flags & (KC_CHAR | KC_CTRL | KC_ALT)) != 0)
+    {
+      send_key_event (twindow, char_code, flags, repeat);
+      return (1);
+    }
+  else
+    return (0);
+}
+
+static void
+send_key_event (twindow_t * twindow, unsigned short code,
+               unsigned short flags, unsigned short repeat)
+{
+  SEND_EVENT
+    (twindow,
+     (make_key_event ((TWINDOW_TWID (twindow)), code, flags, repeat)));
+}
+
+static msg_t *
+make_key_event (twid_t twid, unsigned short code,
+               unsigned short flags, unsigned short repeat)
+{
+  msg_t * message = (OS2_create_message (mt_key_event));
+  (SM_KEY_EVENT_TWID (message)) = twid;
+  (SM_KEY_EVENT_CODE (message)) = code;
+  (SM_KEY_EVENT_FLAGS (message)) = flags;
+  (SM_KEY_EVENT_REPEAT (message)) = repeat;
+  return (message);
+}
diff --git a/v7/src/microcode/os2pm.h b/v7/src/microcode/os2pm.h
new file mode 100644 (file)
index 0000000..c50bacf
--- /dev/null
@@ -0,0 +1,219 @@
+/* -*-C-*-
+
+$Id: os2pm.h,v 1.1 1994/12/02 20:44:35 cph Exp $
+
+Copyright (c) 1994 Massachusetts Institute of Technology
+
+This material was developed by the Scheme project at the Massachusetts
+Institute of Technology, Department of Electrical Engineering and
+Computer Science.  Permission to copy this software, to redistribute
+it, and to use it for any purpose is granted, subject to the following
+restrictions and understandings.
+
+1. Any copy made of this software must include this copyright notice
+in full.
+
+2. Users of this software agree to make their best efforts (a) to
+return to the MIT Scheme project any improvements or extensions that
+they make, so that these may be included in future releases; and (b)
+to inform MIT of noteworthy uses of this software.
+
+3. All materials developed as a consequence of the use of this
+software shall duly acknowledge such use, in accordance with the usual
+standards of acknowledging credit in academic research.
+
+4. MIT has made no warrantee or representation that the operation of
+this software will be error-free, and MIT is under no obligation to
+provide any services, by way of maintenance, update, or otherwise.
+
+5. In conjunction with products arising from the use of this material,
+there shall be no use of the name of the Massachusetts Institute of
+Technology nor of any adaptation thereof in any advertising,
+promotional, or sales literature without prior written consent from
+MIT in each case. */
+
+#ifndef SCM_OS2PM_H
+#define SCM_OS2PM_H
+\f
+typedef unsigned short twid_t;
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  qid_t event_qid;
+  const char * title;
+} sm_twindow_open_request_t;
+#define SM_TWINDOW_OPEN_REQUEST_EVENT_QID(m)                           \
+  (((sm_twindow_open_request_t *) (m)) -> event_qid)
+#define SM_TWINDOW_OPEN_REQUEST_TITLE(m)                               \
+  (((sm_twindow_open_request_t *) (m)) -> title)
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+} sm_twindow_open_reply_t;
+#define SM_TWINDOW_OPEN_REPLY_TWID(m)                                  \
+  (((sm_twindow_open_reply_t *) (m)) -> twid)
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+} sm_twindow_close_request_t;
+#define SM_TWINDOW_CLOSE_REQUEST_TWID(m)                               \
+  (((sm_twindow_close_request_t *) (m)) -> twid)
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+  unsigned short x;
+  unsigned short y;
+  unsigned short size;
+  const char * data;
+} sm_twindow_write_request_t;
+#define SM_TWINDOW_WRITE_REQUEST_TWID(m)                               \
+  (((sm_twindow_write_request_t *) (m)) -> twid)
+#define SM_TWINDOW_WRITE_REQUEST_X(m)                                  \
+  (((sm_twindow_write_request_t *) (m)) -> x)
+#define SM_TWINDOW_WRITE_REQUEST_Y(m)                                  \
+  (((sm_twindow_write_request_t *) (m)) -> y)
+#define SM_TWINDOW_WRITE_REQUEST_SIZE(m)                               \
+  (((sm_twindow_write_request_t *) (m)) -> size)
+#define SM_TWINDOW_WRITE_REQUEST_DATA(m)                               \
+  (((sm_twindow_write_request_t *) (m)) -> data)
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+  unsigned short x;
+  unsigned short y;
+} sm_twindow_move_cursor_request_t;
+#define SM_TWINDOW_MOVE_CURSOR_REQUEST_TWID(m)                         \
+  (((sm_twindow_move_cursor_request_t *) (m)) -> twid)
+#define SM_TWINDOW_MOVE_CURSOR_REQUEST_X(m)                            \
+  (((sm_twindow_move_cursor_request_t *) (m)) -> x)
+#define SM_TWINDOW_MOVE_CURSOR_REQUEST_Y(m)                            \
+  (((sm_twindow_move_cursor_request_t *) (m)) -> y)
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+} sm_twindow_clear_request_t;
+#define SM_TWINDOW_CLEAR_REQUEST_TWID(m)                               \
+  (((sm_twindow_clear_request_t *) (m)) -> twid)
+#define SM_TWINDOW_CLEAR_REQUEST_X(m)                                  \
+  (((sm_twindow_clear_request_t *) (m)) -> x)
+#define SM_TWINDOW_CLEAR_REQUEST_Y(m)                                  \
+  (((sm_twindow_clear_request_t *) (m)) -> y)
+\f
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+  unsigned short x;
+  unsigned short y;
+} sm_twindow_clear_eol_request_t;
+#define SM_TWINDOW_CLEAR_EOL_REQUEST_TWID(m)                           \
+  (((sm_twindow_clear_eol_request_t *) (m)) -> twid)
+#define SM_TWINDOW_CLEAR_EOL_REQUEST_X(m)                              \
+  (((sm_twindow_clear_eol_request_t *) (m)) -> x)
+#define SM_TWINDOW_CLEAR_EOL_REQUEST_Y(m)                              \
+  (((sm_twindow_clear_eol_request_t *) (m)) -> y)
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+  unsigned short x_start;
+  unsigned short x_end;
+  unsigned short y_start;
+  unsigned short y_end;
+  short x_delta;
+  short y_delta;
+} sm_twindow_scroll_request_t;
+#define SM_TWINDOW_SCROLL_REQUEST_TWID(m)                              \
+  (((sm_twindow_scroll_request_t *) (m)) -> twid)
+#define SM_TWINDOW_SCROLL_REQUEST_X_START(m)                           \
+  (((sm_twindow_scroll_request_t *) (m)) -> x_start)
+#define SM_TWINDOW_SCROLL_REQUEST_X_END(m)                             \
+  (((sm_twindow_scroll_request_t *) (m)) -> x_end)
+#define SM_TWINDOW_SCROLL_REQUEST_Y_START(m)                           \
+  (((sm_twindow_scroll_request_t *) (m)) -> y_start)
+#define SM_TWINDOW_SCROLL_REQUEST_Y_END(m)                             \
+  (((sm_twindow_scroll_request_t *) (m)) -> y_end)
+#define SM_TWINDOW_SCROLL_REQUEST_X_DELTA(m)                           \
+  (((sm_twindow_scroll_request_t *) (m)) -> x_delta)
+#define SM_TWINDOW_SCROLL_REQUEST_Y_DELTA(m)                           \
+  (((sm_twindow_scroll_request_t *) (m)) -> y_delta)
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+  unsigned short code;
+  unsigned short flags;
+  unsigned short repeat;
+} sm_key_event_t;
+#define SM_KEY_EVENT_TWID(m) (((sm_key_event_t *) (m)) -> twid)
+#define SM_KEY_EVENT_CODE(m) (((sm_key_event_t *) (m)) -> code)
+#define SM_KEY_EVENT_FLAGS(m) (((sm_key_event_t *) (m)) -> flags)
+#define SM_KEY_EVENT_REPEAT(m) (((sm_key_event_t *) (m)) -> repeat)
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+  
+} sm_button_event_t;
+#define SM_BUTTON_EVENT_TWID(m) (((sm_button_event_t *) (m)) -> twid)
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+} sm_close_event_t;
+#define SM_CLOSE_EVENT_TWID(m) (((sm_close_event_t *) (m)) -> twid)
+\f
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+  unsigned char state;
+} sm_visibility_event_t;
+#define SM_VISIBILITY_EVENT_TWID(m) (((sm_visibility_event_t *) (m)) -> twid)
+#define SM_VISIBILITY_EVENT_STATE(m) (((sm_visibility_event_t *) (m)) -> state)
+
+#define VISIBILITY_OBSCURED 0
+#define VISIBILITY_PARTIALLY_OBSCURED 1
+#define VISIBILITY_UNOBSCURED 2
+
+typedef struct
+{
+  DECLARE_MSG_HEADER_FIELDS;
+  twid_t twid;
+  unsigned short width;
+  unsigned short height;
+} sm_resize_event_t;
+#define SM_RESIZE_EVENT_TWID(m) (((sm_resize_event_t *) (m)) -> twid)
+#define SM_RESIZE_EVENT_WIDTH(m) (((sm_resize_event_t *) (m)) -> width)
+#define SM_RESIZE_EVENT_HEIGHT(m) (((sm_resize_event_t *) (m)) -> height)
+
+extern int OS2_read_pm_tqueue (tqueue_t *, int);
+extern void OS2_write_pm_tqueue (tqueue_t *, msg_t *);
+
+extern twid_t OS2_twindow_open (qid_t, const char *);
+extern void OS2_twindow_close (twid_t);
+extern void OS2_twindow_write
+  (twid_t, unsigned short, unsigned short, const char *, unsigned short);
+extern void OS2_twindow_move_cursor (twid_t, unsigned short, unsigned short);
+extern void OS2_twindow_clear (twid_t);
+extern void OS2_twindow_clear_eol (twid_t, unsigned short, unsigned short);
+extern void OS2_twindow_scroll
+  (twid_t, unsigned short, unsigned short, unsigned short, unsigned short,
+   short, short);
+
+#endif /* SCM_OS2PM_H */
diff --git a/v7/src/microcode/os2pmcon.c b/v7/src/microcode/os2pmcon.c
new file mode 100644 (file)
index 0000000..592e512
--- /dev/null
@@ -0,0 +1,303 @@
+/* -*-C-*-
+
+$Id: os2pmcon.c,v 1.1 1994/12/02 20:44:41 cph Exp $
+
+Copyright (c) 1994 Massachusetts Institute of Technology
+
+This material was developed by the Scheme project at the Massachusetts
+Institute of Technology, Department of Electrical Engineering and
+Computer Science.  Permission to copy this software, to redistribute
+it, and to use it for any purpose is granted, subject to the following
+restrictions and understandings.
+
+1. Any copy made of this software must include this copyright notice
+in full.
+
+2. Users of this software agree to make their best efforts (a) to
+return to the MIT Scheme project any improvements or extensions that
+they make, so that these may be included in future releases; and (b)
+to inform MIT of noteworthy uses of this software.
+
+3. All materials developed as a consequence of the use of this
+software shall duly acknowledge such use, in accordance with the usual
+standards of acknowledging credit in academic research.
+
+4. MIT has made no warrantee or representation that the operation of
+this software will be error-free, and MIT is under no obligation to
+provide any services, by way of maintenance, update, or otherwise.
+
+5. In conjunction with products arising from the use of this material,
+there shall be no use of the name of the Massachusetts Institute of
+Technology nor of any adaptation thereof in any advertising,
+promotional, or sales literature without prior written consent from
+MIT in each case. */
+
+#define INCL_WIN
+#include "os2.h"
+
+static void process_events (int);
+static int  translate_key_event (msg_t *);
+static const char * find_nonprint (const char *, const char *);
+static void do_carriage_return (void);
+static void do_linefeed (void);
+static void do_formfeed (void);
+static void do_backspace (void);
+static void do_alert (void);
+\f
+static unsigned short console_width;
+static unsigned short console_height;
+static unsigned short point_x;
+static unsigned short point_y;
+static tqueue_t * console_tqueue;
+static qid_t console_qid;
+static qid_t remote_qid;
+static twid_t console_twid;
+static int console_closedp;
+static unsigned short readahead_repeat;
+static char readahead_char;
+static msg_list_t * pending_events_head;
+static msg_list_t * pending_events_tail;
+
+void
+OS2_initialize_pm_console (void)
+{
+  point_x = 0;
+  point_y = 0;
+  console_closedp = 0;
+  readahead_repeat = 0;
+  pending_events_head = 0;
+  console_tqueue = (OS2_make_std_tqueue ());
+  OS2_make_qid_pair ((&console_qid), (&remote_qid));
+  OS2_open_qid (console_qid, console_tqueue);
+  console_twid = (OS2_twindow_open (remote_qid, "Scheme"));
+}
+
+static void
+process_events (int blockp)
+{
+  while (1)
+    {
+      msg_t * message = (OS2_receive_message (console_qid, blockp));
+      if (message == 0)
+       break;
+      switch (MSG_TYPE (message))
+       {
+       case mt_key_event:
+       case mt_close_event:
+         {
+           msg_list_t * element = (OS_malloc (sizeof (msg_list_t)));
+           (element -> message) = message;
+           (element -> next) = 0;
+           if (pending_events_head == 0)
+             pending_events_head = element;
+           else
+             (pending_events_tail -> next) = element;
+           pending_events_tail = element;
+           if (blockp)
+             return;
+           break;
+         }
+       case mt_resize_event:
+         console_width = (SM_RESIZE_EVENT_WIDTH (message));
+         console_height = (SM_RESIZE_EVENT_HEIGHT (message));
+         OS2_destroy_message (message);
+         break;
+       case mt_button_event:
+       case mt_visibility_event:
+         OS2_destroy_message (message);
+         break;
+       default:
+         OS2_logic_error ("Unknown message type received by PM console.");
+         break;
+       }
+    }
+}
+\f
+int
+OS2_pm_console_getch (void)
+{
+  if (readahead_repeat == 0)
+    while (1)
+      {
+       process_events (pending_events_head == 0);
+       {
+         msg_list_t * element = pending_events_head;
+         msg_t * message = (element -> message);
+         pending_events_head = (element -> next);
+         OS_free (element);
+         switch (MSG_TYPE (message))
+           {
+           case mt_key_event:
+             {
+               int translation = (translate_key_event (message));
+               unsigned short repeat = (SM_KEY_EVENT_REPEAT (message));
+               OS2_destroy_message (message);
+               if ((translation >= 0) && (repeat > 0))
+                 {
+                   readahead_char = translation;
+                   readahead_repeat = repeat;
+                   goto do_read;
+                 }
+               break;
+             }
+           case mt_close_event:
+             {
+               twid_t twid = (SM_CLOSE_EVENT_TWID (message));
+               OS2_destroy_message (message);
+               OS2_twindow_close (twid);
+             }
+             OS2_close_qid (remote_qid);
+             OS2_close_qid (console_qid);
+             OS2_close_std_tqueue (console_tqueue);
+             console_closedp = 1;
+             goto do_read;
+           default:
+             OS2_logic_error ("Unknown message type received by PM console.");
+             break;
+           }
+       }
+      }
+ do_read:
+  if ((readahead_repeat == 0) && console_closedp)
+    return (-1);
+  readahead_repeat -= 1;
+  return (readahead_char);
+}
+\f
+static int
+translate_key_event (msg_t * message)
+{
+  unsigned short code = (SM_KEY_EVENT_CODE (message));
+  unsigned short flags = (SM_KEY_EVENT_FLAGS (message));
+  if ((flags & KC_VIRTUALKEY) != 0)
+    switch (code)
+      {
+      case VK_BACKSPACE:
+       code = '\177';
+       break;
+      case VK_TAB:
+       code = '\t';
+       break;
+      case VK_ESC:
+       code = '\033';
+       break;
+      case VK_SPACE:
+       code = ' ';
+       break;
+      case VK_NEWLINE:
+      case VK_ENTER:
+       code = '\r';
+       break;
+      default:
+       return (-1);
+      }
+  if ((code >= 0200) || ((flags & KC_ALT) != 0))
+    return (-1);
+  if ((flags & KC_CTRL) != 0)
+    if (code >= 040)
+      code &= 037;
+    else
+      return (-1);
+  if (code == 0)
+    return (-1);
+  return (code);
+}
+\f
+void
+OS2_pm_console_write (const char * data, size_t size)
+{
+  const char * end = (data + size);
+  while (data < end)
+    {
+      process_events (0);
+      {
+       const char * nonprint = (find_nonprint (data, end));
+       if (data < nonprint)
+         {
+           unsigned short size = (nonprint - data);
+           if (size > (console_width - point_x))
+             size = (console_width - point_x);
+           OS2_twindow_write (console_twid, point_x, point_y, data, size);
+           data += size;
+           point_x += size;
+           if (point_x == console_width)
+             {
+               do_carriage_return ();
+               do_linefeed ();
+             }
+           if (data == end)
+             break;
+         }
+      }
+      switch (*data++)
+       {
+       case '\r':
+         do_carriage_return ();
+         break;
+       case '\012':
+         do_linefeed ();
+         break;
+       case '\f':
+         do_formfeed ();
+         break;
+       case '\b':
+         do_backspace ();
+         break;
+       case '\a':
+         do_alert ();
+         break;
+       }
+    }
+  OS2_twindow_move_cursor (console_twid, point_x, point_y);
+}
+
+static const char *
+find_nonprint (const char * start, const char * end)
+{
+  while (start < end)
+    if (!isprint (*start++))
+      return (--start);
+  return (end);
+}
+\f
+static void
+do_carriage_return (void)
+{
+  point_x = 0;
+}
+
+static void
+do_linefeed (void)
+{
+  if (point_y < (console_height - 1))
+    point_y += 1;
+  else
+    {
+      point_y = (console_height - 1);
+      OS2_twindow_scroll (console_twid, 0, console_width, 0, point_y, 0, 1);
+    }
+  OS2_twindow_clear_eol (console_twid, 0, point_y);
+}
+
+static void
+do_formfeed (void)
+{
+  point_x = 0;
+  point_y = 0;
+  OS2_twindow_clear (console_twid);
+}
+
+static void
+do_backspace (void)
+{
+  if (point_x > 0)
+    {
+      point_x -= 1;
+    }
+}
+
+static void
+do_alert (void)
+{
+  DosBeep (880, 50);
+}