changeset 9907:2e5a14f7c44e

Initial revision
author Richard M. Stallman <rms@gnu.org>
date Mon, 14 Nov 1994 01:32:24 +0000
parents c062f5bc946a
children 32d20ec5ed51
files src/w32console.c src/w32inevt.c src/w32proc.c
diffstat 3 files changed, 1844 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/w32console.c	Mon Nov 14 01:32:24 1994 +0000
@@ -0,0 +1,605 @@
+/* Terminal hooks for Windows NT port of GNU Emacs.
+   Copyright (C) 1992 Free Software Foundation, Inc.
+
+   This file is part of GNU Emacs.
+
+   GNU Emacs is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any later
+   version.
+
+   GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+   more details.
+
+   You should have received a copy of the GNU General Public License along
+   with GNU Emacs; see the file COPYING.  If not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Tim Fleehart (apollo@online.com)		1-17-92
+   Geoff Voelker (voelker@cs.washington.edu)	9-12-93
+*/
+
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "config.h"
+
+#include <windows.h>
+
+#include "lisp.h"
+#include "frame.h"
+#include "disptab.h"
+#include "termhooks.h"
+
+#include "ntinevt.h"
+
+/* frrom window.c */
+extern Lisp_Object Frecenter ();
+
+/* from keyboard.c */
+extern int detect_input_pending ();
+
+/* from sysdep.c */
+extern int read_input_pending ();
+
+extern FRAME_PTR updating_frame;
+extern int meta_key;
+
+static void move_cursor (int row, int col);
+static void clear_to_end (void);
+static void clear_frame (void);
+static void clear_end_of_line (int);
+static void ins_del_lines (int vpos, int n);
+static void change_line_highlight (int, int, int);
+static void reassert_line_highlight (int, int);
+static void insert_glyphs (GLYPH *start, int len);
+static void write_glyphs (GLYPH *string, int len);
+static void delete_glyphs (int n);
+static void ring_bell (void);
+static void reset_terminal_modes (void);
+static void set_terminal_modes (void);
+static void set_terminal_window (int size);
+static void update_begin (FRAME_PTR f);
+static void update_end (FRAME_PTR f);
+static void reset_kbd (void);
+static void unset_kbd (void);
+static int  hl_mode (int new_highlight);
+
+void
+DebPrint ()
+{
+}
+
+/* Init hook called in init_keyboard.  */
+void (*keyboard_init_hook)(void) = reset_kbd;
+    
+COORD	cursor_coords;
+HANDLE	prev_screen, cur_screen;
+UCHAR	char_attr, char_attr_normal, char_attr_reverse;
+HANDLE  keyboard_handle;
+
+
+/* Setting this as the ctrl handler prevents emacs from being killed when
+ * someone hits ^C in a 'suspended' session (child shell).  */
+BOOL
+ctrl_c_handler (unsigned long type)
+{
+  return (type == CTRL_C_EVENT) ? TRUE : FALSE;
+}
+
+/* If we're updating a frame, use it as the current frame
+   Otherwise, use the selected frame.  */
+#define PICK_FRAME() (updating_frame ? updating_frame : selected_frame)
+
+/* Move the cursor to (row, col).  */
+void
+move_cursor (int row, int col)
+{
+  cursor_coords.X = col;
+  cursor_coords.Y = row;
+  
+  if (updating_frame == NULL)
+    {
+      SetConsoleCursorPosition (cur_screen, cursor_coords);
+    }
+}
+
+/* Clear from cursor to end of screen.  */
+void
+clear_to_end (void)
+{
+  FRAME_PTR f = PICK_FRAME ();
+  
+  clear_end_of_line (FRAME_WIDTH (f) - 1);
+  ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
+}
+
+/* Clear the frame.  */
+void
+clear_frame (void)
+{
+  SMALL_RECT scroll;
+  COORD	     dest;
+  CHAR_INFO  fill;
+  FRAME_PTR  f = PICK_FRAME ();
+  
+  hl_mode (0);
+  
+  scroll.Top = 0;
+  scroll.Bottom = FRAME_HEIGHT (f) - 1;
+  scroll.Left = 0;
+  scroll.Right = FRAME_WIDTH (f) - 1;
+  
+  dest.Y = FRAME_HEIGHT (f);
+  dest.X = 0;
+  
+  fill.Char.AsciiChar = 0x20;
+  fill.Attributes = char_attr;
+  
+  ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
+  move_cursor (0, 0);
+}
+
+
+static GLYPH glyph_base[256];
+static BOOL  ceol_initialized = FALSE;
+
+/* Clear from Cursor to end (what's "standout marker"?).  */
+void
+clear_end_of_line (int end)
+{
+  if (!ceol_initialized)
+    {
+      int i;
+      for (i = 0; i < 256; i++)
+        {
+	  glyph_base[i] = SPACEGLYPH;	/* empty space	*/
+        }
+      ceol_initialized = TRUE;
+    }
+  write_glyphs (glyph_base, end - cursor_coords.X);	/* fencepost ?	*/
+}
+
+/* Insert n lines at vpos. if n is negative delete -n lines.  */
+void
+ins_del_lines (int vpos, int n)
+{
+  int	     i, nb, save_highlight;
+  SMALL_RECT scroll;
+  COORD	     dest;
+  CHAR_INFO  fill;
+  FRAME_PTR  f = PICK_FRAME ();
+
+  if (n < 0)
+    {
+      scroll.Top = vpos - n;
+      scroll.Bottom = FRAME_HEIGHT (f);
+      dest.Y = vpos;
+    }
+  else
+    {
+      scroll.Top = vpos;
+      scroll.Bottom = FRAME_HEIGHT (f) - n;
+      dest.Y = vpos + n;
+    }
+  scroll.Left = 0;
+  scroll.Right = FRAME_WIDTH (f);
+  
+  dest.X = 0;
+  
+  save_highlight = hl_mode (0);
+  
+  fill.Char.AsciiChar = 0x20;
+  fill.Attributes = char_attr;
+  
+  ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
+
+  /* Here we have to deal with a win32 console flake: If the scroll
+     region looks like abc and we scroll c to a and fill with d we get
+     cbd... if we scroll block c one line at a time to a, we get cdd...
+     Emacs expects cdd consistently... So we have to deal with that
+     here... (this also occurs scrolling the same way in the other
+     direction.  */
+
+  if (n > 0)
+    {
+      if (scroll.Bottom < dest.Y)
+        {
+	  for (i = scroll.Bottom; i < dest.Y; i++)
+            {
+	      move_cursor (i, 0);
+	      clear_end_of_line (FRAME_WIDTH (f));
+            }
+        }
+    }
+  else
+    {
+      nb = dest.Y + (scroll.Bottom - scroll.Top) + 1;
+
+      if (nb < scroll.Top)
+        { 
+	  for (i = nb; i < scroll.Top; i++)
+            {
+	      move_cursor (i, 0);
+	      clear_end_of_line (FRAME_WIDTH (f));
+            }
+        }
+    }
+  
+  cursor_coords.X = 0;
+  cursor_coords.Y = vpos;
+  
+  hl_mode (save_highlight);
+}
+
+/* Changes attribute to use when drawing characters to control.  */
+static int
+hl_mode (int new_highlight)
+{
+  static int highlight = 0;
+  int old_highlight;
+  
+  old_highlight = highlight;
+  highlight = (new_highlight != 0);
+  if (highlight)
+    {
+      char_attr = char_attr_reverse;
+    }
+  else
+    {
+      char_attr = char_attr_normal;
+    }
+  return old_highlight;
+}
+
+/* Call this when about to modify line at position VPOS and change whether it
+   is highlighted.  */
+void
+change_line_highlight (int new_highlight, int vpos, int first_unused_hpos)
+{
+  hl_mode (new_highlight);
+  move_cursor (vpos, 0);
+  clear_end_of_line (first_unused_hpos);
+}
+
+/* External interface to control of standout mode. Call this when about to
+ * modify line at position VPOS and not change whether it is highlighted.  */
+void
+reassert_line_highlight (int highlight, int vpos)
+{
+  hl_mode (highlight);
+  vpos;				/* pedantic compiler silencer */
+}
+
+#undef	LEFT
+#undef	RIGHT
+#define	LEFT	1
+#define	RIGHT	0
+
+void
+scroll_line (int dist, int direction)
+{
+  /* The idea here is to implement a horizontal scroll in one line to
+     implement delete and half of insert.  */
+  SMALL_RECT scroll;
+  COORD	     dest;
+  CHAR_INFO  fill;
+  FRAME_PTR  f = PICK_FRAME ();
+  
+  scroll.Top = cursor_coords.Y;
+  scroll.Bottom = cursor_coords.Y;
+  
+  if (direction == LEFT)
+    {
+      scroll.Left = cursor_coords.X + dist;
+      scroll.Right = FRAME_WIDTH (f) - 1;
+    }
+  else
+    {
+      scroll.Left = cursor_coords.X;
+      scroll.Right = FRAME_WIDTH (f) - dist - 1;
+    }
+  
+  dest.X = cursor_coords.X;
+  dest.Y = cursor_coords.Y;
+  
+  fill.Char.AsciiChar = 0x20;
+  fill.Attributes = char_attr;
+  
+  ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
+}
+
+
+/* If start is zero insert blanks instead of a string at start ?. */
+void
+insert_glyphs (register GLYPH *start, register int len)
+{
+  scroll_line (len, RIGHT);
+
+  /* Move len chars to the right starting at cursor_coords, fill with blanks */
+  if (start)
+    {
+      /* Print the first len characters of start, cursor_coords.X adjusted
+	 by write_glyphs.  */
+	
+      write_glyphs (start, len);
+    }
+  else
+    {
+      clear_end_of_line (cursor_coords.X + len);
+    }
+}
+
+void
+write_glyphs (register GLYPH *string, register int len)
+{
+  register unsigned int glyph_len = GLYPH_TABLE_LENGTH;
+  Lisp_Object *glyph_table = GLYPH_TABLE_BASE;
+  FRAME_PTR f = PICK_FRAME ();
+  register char *ptr;
+  GLYPH glyph;
+  WORD *attrs;
+  char *chars;
+  int i;
+  
+  attrs = alloca (len * sizeof (*attrs));
+  chars = alloca (len * sizeof (*chars));
+  if (attrs == NULL || chars == NULL)
+    {
+      printf ("alloca failed in write_glyphs\n");
+      return;
+    }
+  
+  /* We have to deal with the glyph indirection...go over the glyph
+     buffer and extract the characters.  */
+  ptr = chars;
+  while (--len >= 0)
+    {
+      glyph = *string++;
+
+      if (glyph > glyph_len)
+        {
+	  *ptr++ = glyph & 0xFF;
+	  continue;
+	}
+      GLYPH_FOLLOW_ALIASES (glyph_table, glyph_len, glyph);
+      if (GLYPH_FACE (fixfix, glyph) != 0)
+	printf ("Glyph face is %d\n", GLYPH_FACE (fixfix, glyph));
+      if (GLYPH_SIMPLE_P (glyph_table, glyph_len, glyph))
+        {
+	  *ptr++ = glyph & 0xFF;
+	  continue;
+	}
+      for (i = 0; i < GLYPH_LENGTH (glyph_table, glyph); i++)
+        {
+	  *ptr++ = (GLYPH_STRING (glyph_table, glyph))[i];
+	}
+    }
+  
+  /* Number of characters we have in the buffer.  */
+  len = ptr-chars;
+  
+  /* Fill in the attributes for these characters.  */
+  memset (attrs, char_attr, len*sizeof (*attrs));
+  
+  /* Write the attributes.  */
+  if (!WriteConsoleOutputAttribute (cur_screen, attrs, len, cursor_coords, &i))
+    {
+      printf ("Failed writing console attributes.\n");
+      fflush (stdout);
+    }
+
+  /* Write the characters.  */
+  if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
+    {
+      printf ("Failed writing console characters.\n");
+      fflush (stdout);
+    }
+  
+  cursor_coords.X += len;
+  move_cursor (cursor_coords.Y, cursor_coords.X);
+}
+
+void
+delete_glyphs (int n)
+{
+  /* delete chars means scroll chars from cursor_coords.X + n to 
+     cursor_coords.X, anything beyond the edge of the screen should 
+     come out empty...  */
+
+  scroll_line (n, LEFT);
+}
+
+void
+ring_bell (void)
+{
+  Beep (666, 100);
+}
+
+/* Reset to the original console mode but don't get rid of our console
+   For suspending emacs.  */
+void
+restore_console (void)
+{
+  unset_kbd ();
+  SetConsoleActiveScreenBuffer (prev_screen);
+}
+
+/* Put our console back up, for ending a suspended session.  */
+void
+take_console (void)
+{
+  reset_kbd ();
+  SetConsoleActiveScreenBuffer (cur_screen);
+}
+   
+void
+reset_terminal_modes (void)
+{
+  unset_kbd ();
+  SetConsoleActiveScreenBuffer (prev_screen);
+  CloseHandle (cur_screen);
+  cur_screen = NULL;
+}
+
+void
+set_terminal_modes (void)
+{
+  CONSOLE_CURSOR_INFO cci;
+
+  if (cur_screen == NULL)
+    {
+      reset_kbd ();
+      cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
+                                              0, NULL,
+                                              CONSOLE_TEXTMODE_BUFFER,
+                                              NULL);
+
+      if (cur_screen == INVALID_HANDLE_VALUE)
+        {
+	  printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
+	  printf ("LastError = 0x%lx\n", GetLastError ());
+	  fflush (stdout);
+	  exit (0);
+	}
+
+      SetConsoleActiveScreenBuffer (cur_screen);
+
+      /* make cursor big and visible */
+      cci.dwSize = 100;
+      cci.bVisible = TRUE;
+      (void) SetConsoleCursorInfo (cur_screen, &cci);
+    }
+}
+
+/* hmmm... perhaps these let us bracket screen changes so that we can flush
+   clumps rather than one-character-at-a-time...
+   
+   we'll start with not moving the cursor while an update is in progress.  */
+void
+update_begin (FRAME_PTR f)
+{
+}
+
+void
+update_end (FRAME_PTR f)
+{
+  SetConsoleCursorPosition (cur_screen, cursor_coords);
+}
+
+void
+set_terminal_window (int size)
+{
+}
+
+void
+unset_kbd (void)
+{
+  SetConsoleMode (keyboard_handle, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
+		  ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT);
+}
+
+void
+reset_kbd (void)
+{
+  keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
+  SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
+}
+
+typedef int (*term_hook) ();
+
+void
+initialize_win_nt_display (void)
+{
+  CONSOLE_SCREEN_BUFFER_INFO	info;
+  
+  cursor_to_hook		= (term_hook) move_cursor;
+  raw_cursor_to_hook		= (term_hook) move_cursor;
+  clear_to_end_hook		= (term_hook) clear_to_end;
+  clear_frame_hook		= (term_hook) clear_frame;
+  clear_end_of_line_hook	= (term_hook) clear_end_of_line;
+  ins_del_lines_hook		= (term_hook) ins_del_lines;
+  change_line_highlight_hook	= (term_hook) change_line_highlight;
+  reassert_line_highlight_hook  = (term_hook) reassert_line_highlight;
+  insert_glyphs_hook		= (term_hook) insert_glyphs;
+  write_glyphs_hook		= (term_hook) write_glyphs;
+  delete_glyphs_hook		= (term_hook) delete_glyphs;
+  ring_bell_hook		= (term_hook) ring_bell;
+  reset_terminal_modes_hook	= (term_hook) reset_terminal_modes;
+  set_terminal_modes_hook	= (term_hook) set_terminal_modes;
+  set_terminal_window_hook	= (term_hook) set_terminal_window;
+  update_begin_hook		= (term_hook) update_begin;
+  update_end_hook		= (term_hook) update_end;
+  
+  read_socket_hook = win32_read_socket;
+  mouse_position_hook = win32_mouse_position;
+  
+  prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
+  
+  set_terminal_modes ();
+  
+  GetConsoleScreenBufferInfo (cur_screen, &info);
+  
+  meta_key = 1;
+  char_attr = info.wAttributes & 0xFF;
+  char_attr_normal = char_attr;
+  char_attr_reverse = ((char_attr & 0xf) << 4) + ((char_attr & 0xf0) >> 4);
+  
+  FRAME_HEIGHT (selected_frame) = info.dwSize.Y;	/* lines per page */
+  FRAME_WIDTH (selected_frame) = info.dwSize.X;  /* characters per line */
+  
+  move_cursor (0, 0);
+  
+  clear_frame ();
+}
+
+DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
+       "Set screen colors.")
+    (foreground, background)
+    Lisp_Object foreground;
+    Lisp_Object background;
+{
+  char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
+  char_attr_reverse = XFASTINT (background) + (XFASTINT (foreground) << 4);
+
+  Frecenter (Qnil);
+  return Qt;
+}
+
+DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
+       "Set cursor size.")
+    (size)
+    Lisp_Object size;
+{
+  CONSOLE_CURSOR_INFO cci;
+  cci.dwSize = XFASTINT (size);
+  cci.bVisible = TRUE;
+  (void) SetConsoleCursorInfo (cur_screen, &cci);
+  
+  return Qt;
+}
+
+void
+pixel_to_glyph_coords (FRAME_PTR f, int pix_x, int pix_y, int *x, int *y,
+		      void *bounds, int noclip)
+{
+  *x = pix_x;
+  *y = pix_y;
+}
+
+void
+glyph_to_pixel_coords (FRAME_PTR f, int x, int y, int *pix_x, int *pix_y)
+{
+  *pix_x = x;
+  *pix_y = y;
+}
+
+_VOID_
+syms_of_ntterm ()
+{
+  defsubr (&Sset_screen_color);
+  defsubr (&Sset_cursor_size);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/w32inevt.c	Mon Nov 14 01:32:24 1994 +0000
@@ -0,0 +1,459 @@
+/* Input event support for Windows NT port of GNU Emacs.
+   Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+
+   This file is part of GNU Emacs.
+
+   GNU Emacs is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any later
+   version.
+
+   GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+   more details.
+
+   You should have received a copy of the GNU General Public License along
+   with GNU Emacs; see the file COPYING.  If not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Drew Bliss                   01-Oct-93
+     Adapted from ntkbd.c by Tim Fleehart
+*/
+
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <windows.h>
+
+#include "lisp.h"
+#include "frame.h"
+#include "blockinput.h"
+#include "termhooks.h"
+
+/* stdin, from ntterm */
+extern HANDLE keyboard_handle;
+
+/* Indicate mouse motion, from keyboard.c */
+extern int mouse_moved;
+
+/* Info for last mouse motion */
+static COORD movement_pos;
+static DWORD movement_time;
+
+/* from keyboard.c */
+extern void reinvoke_input_signal (void);
+
+/* from dispnew.c */
+extern int change_frame_size (FRAME_PTR, int, int, int, int);
+
+/* Event queue */
+#define EVENT_QUEUE_SIZE 50
+static INPUT_RECORD event_queue[EVENT_QUEUE_SIZE];
+static INPUT_RECORD *queue_ptr = event_queue, *queue_end = event_queue;
+
+static int 
+fill_queue (BOOL block)
+{
+  BOOL rc;
+  DWORD events_waiting;
+  
+  if (queue_ptr < queue_end)
+    return queue_end-queue_ptr;
+  
+  if (!block)
+    {
+      /* Check to see if there are some events to read before we try
+	 because we can't block.  */
+      if (!GetNumberOfConsoleInputEvents (keyboard_handle, &events_waiting))
+	return -1;
+      if (events_waiting == 0)
+	return 0;
+    }
+  
+  rc = ReadConsoleInput (keyboard_handle, event_queue, EVENT_QUEUE_SIZE,
+			 &events_waiting);
+  if (!rc)
+    return -1;
+  queue_ptr = event_queue;
+  queue_end = event_queue + events_waiting;
+  return (int) events_waiting;
+}
+
+/* In a generic, multi-frame world this should take a console handle
+   and return the frame for it
+
+   Right now, there's only one frame so return it.  */
+static FRAME_PTR 
+get_frame (void)
+{
+  return selected_frame;
+}
+
+#ifdef MULTI_FRAME
+#define SET_FRAME(o, f) XSET (o, Lisp_Frame, f)
+#else
+#define SET_FRAME(o, f) ((o) = Qnil)
+#endif
+
+/* Translate console modifiers to emacs modifiers.  */
+static int 
+nt_kbd_mods_to_emacs (DWORD mods)
+{
+  return ((mods & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) ?
+	  meta_modifier : 0) |
+	    ((mods & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) ?
+	     ctrl_modifier : 0) |
+	       ((mods & (SHIFT_PRESSED | CAPSLOCK_ON)) ?
+		shift_modifier : 0);
+}
+
+/* Map virtual key codes into:
+   -1 - Ignore this key
+   -2 - ASCII char
+   Other - Map non-ASCII keys into X keysyms so that they are looked up
+   correctly in keyboard.c
+
+   Return, escape and tab are mapped to ASCII rather than coming back
+   as non-ASCII to be more compatible with old-style keyboard support.  */
+
+static int map_virt_key[256] =
+{
+  -1,
+  -1,                 /* VK_LBUTTON */
+  -1,                 /* VK_RBUTTON */
+  0x69,               /* VK_CANCEL */
+  -1,                 /* VK_MBUTTON */
+  -1, -1, -1,
+  8,                  /* VK_BACK */
+  -2,                 /* VK_TAB */
+  -1, -1,
+  11,                 /* VK_CLEAR */
+  -2,                 /* VK_RETURN */
+  -1, -1,
+  -1,                 /* VK_SHIFT */
+  -1,                 /* VK_CONTROL */
+  -1,                 /* VK_MENU */
+  0x13,               /* VK_PAUSE */
+  -1,                 /* VK_CAPITAL */
+  -1, -1, -1, -1, -1, -1,
+  -2,                 /* VK_ESCAPE */
+  -1, -1, -1, -1,
+  -2,                 /* VK_SPACE */
+  0x55,               /* VK_PRIOR */
+  0x56,               /* VK_NEXT */
+  0x57,               /* VK_END */
+  0x50,               /* VK_HOME */
+  0x51,               /* VK_LEFT */
+  0x52,               /* VK_UP */
+  0x53,               /* VK_RIGHT */
+  0x54,               /* VK_DOWN */
+  0x60,               /* VK_SELECT */
+  0x61,               /* VK_PRINT */
+  0x62,               /* VK_EXECUTE */
+  -1,                 /* VK_SNAPSHOT */
+  0x63,               /* VK_INSERT */
+  0xff,               /* VK_DELETE */
+  0x6a,               /* VK_HELP */
+  -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,     /* 0 - 9 */
+  -1, -1, -1, -1, -1, -1, -1,
+  -2, -2, -2, -2, -2, -2, -2, -2,             /* A - Z */
+  -2, -2, -2, -2, -2, -2, -2, -2,
+  -2, -2, -2, -2, -2, -2, -2, -2,
+  -2, -2,
+  -1, -1, -1, -1, -1,
+  0xb0,               /* VK_NUMPAD0 */
+  0xb1,               /* VK_NUMPAD1 */
+  0xb2,               /* VK_NUMPAD2 */
+  0xb3,               /* VK_NUMPAD3 */
+  0xb4,               /* VK_NUMPAD4 */
+  0xb5,               /* VK_NUMPAD5 */
+  0xb6,               /* VK_NUMPAD6 */
+  0xb7,               /* VK_NUMPAD7 */
+  0xb8,               /* VK_NUMPAD8 */
+  0xb9,               /* VK_NUMPAD9 */
+  0xaa,               /* VK_MULTIPLY */
+  0xab,               /* VK_ADD */
+  0xac,               /* VK_SEPARATOR */
+  0xad,               /* VK_SUBTRACT */
+  0xae,               /* VK_DECIMAL */
+  0xaf,               /* VK_DIVIDE */
+  0xbe,               /* VK_F1 */
+  0xbf,               /* VK_F2 */
+  0xc0,               /* VK_F3 */
+  0xc1,               /* VK_F4 */
+  0xc2,               /* VK_F5 */
+  0xc3,               /* VK_F6 */
+  0xc4,               /* VK_F7 */
+  0xc5,               /* VK_F8 */
+  0xc6,               /* VK_F9 */
+  0xc7,               /* VK_F10 */
+  0xc8,               /* VK_F11 */
+  0xc9,               /* VK_F12 */
+  0xca,               /* VK_F13 */
+  0xcb,               /* VK_F14 */
+  0xcc,               /* VK_F15 */
+  0xcd,               /* VK_F16 */
+  0xce,               /* VK_F17 */
+  0xcf,               /* VK_F18 */
+  0xd0,               /* VK_F19 */
+  0xd1,               /* VK_F20 */
+  0xd2,               /* VK_F21 */
+  0xd3,               /* VK_F22 */
+  0xd4,               /* VK_F23 */
+  0xd5,               /* VK_F24 */
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  0x7f,               /* VK_NUMLOCK */
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x9f */
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xaf */
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xb9 */
+  -2,                 /* ; */
+  -2,                 /* = */
+  -2,                 /* , */
+  -2,                 /* \ */
+  -2,                 /* . */
+  -2,                 /* / */
+  -2,                 /* ` */
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xcf */
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xda */
+  -2,                 /* [ */
+  -2,                 /* - */
+  -2,                 /* ] */
+  -2,                 /* ' */
+  -1, /* 0xdf */
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xef */
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 0xff */
+};
+
+static int 
+key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev)
+{
+  int map;
+  
+  /* Skip key-up events.  */
+  if (event->bKeyDown == FALSE)
+    return 0;
+  
+  if (event->wVirtualKeyCode > 0xff)
+    {
+      printf ("Unknown key code %d\n", event->wVirtualKeyCode);
+      return 0;
+    }
+  
+  /* BUGBUG - Ignores the repeat count
+     It's questionable whether we want to obey the repeat count anyway
+     since keys usually aren't repeated unless key events back up in
+     the queue.  If they're backing up then we don't generally want
+     to honor them later since that leads to significant slop in
+     cursor motion when the system is under heavy load.  */
+  
+  map = map_virt_key[event->wVirtualKeyCode];
+  if (map == -1)
+    {
+      return 0;
+    }
+  else if (map == -2)
+    {
+      /* ASCII */
+      emacs_ev->kind = ascii_keystroke;
+      XSET (emacs_ev->code, Lisp_Int, event->uChar.AsciiChar);
+    }
+  else
+    {
+      /* non-ASCII */
+      emacs_ev->kind = non_ascii_keystroke;
+      /*
+       * make_lispy_event () now requires non-ascii codes to have
+       * the full X keysym values (2nd byte is 0xff).  add it on.
+       */
+      map |= 0xff00;
+      XSET (emacs_ev->code, Lisp_Int, map);
+    }
+  SET_FRAME (emacs_ev->frame_or_window, get_frame ());
+  emacs_ev->modifiers = nt_kbd_mods_to_emacs (event->dwControlKeyState);
+  emacs_ev->timestamp = GetTickCount ();
+  return 1;
+}
+
+/* Mouse position hook.  */
+void 
+win32_mouse_position (FRAME_PTR *f,
+		      Lisp_Object *bar_window,
+		      enum scroll_bar_part *part,
+		      Lisp_Object *x,
+		      Lisp_Object *y,
+		      unsigned long *time)
+{
+  BLOCK_INPUT;
+  
+  *f = get_frame ();
+  *bar_window = Qnil;
+  *part = 0;
+  mouse_moved = 0;
+  
+  *x = movement_pos.X;
+  *y = movement_pos.Y;
+  *time = movement_time;
+  
+  UNBLOCK_INPUT;
+}
+
+/* Remember mouse motion and notify emacs.  */
+static void 
+mouse_moved_to (int x, int y)
+{
+  /* If we're in the same place, ignore it */
+  if (x != movement_pos.X || y != movement_pos.Y)
+    {
+      mouse_moved = 1;
+      movement_pos.X = x;
+      movement_pos.Y = y;
+      movement_time = GetTickCount ();
+    }
+}
+
+/* Consoles return button bits in a strange order:
+     least significant - Leftmost button
+     next - Rightmost button
+     next - Leftmost+1
+     next - Leftmost+2...
+
+   Assume emacs likes three button mice, so
+     Left == 0
+     Middle == 1
+     Right == 2
+   Others increase from there.  */
+
+static int emacs_button_translation[NUM_MOUSE_BUTTONS] =
+{
+  0, 2, 1, 3, 4,
+};
+
+static int 
+do_mouse_event (MOUSE_EVENT_RECORD *event,
+		struct input_event *emacs_ev)
+{
+  static DWORD button_state = 0;
+  DWORD but_change, mask;
+  int i;
+  
+  if (event->dwEventFlags == MOUSE_MOVED)
+    {
+      /* For movement events we just note that the mouse has moved
+	 so that emacs will generate drag events.  */
+      mouse_moved_to (event->dwMousePosition.X, event->dwMousePosition.Y);
+      return 0;
+    }
+  
+  /* It looks like the console code sends us a mouse event with
+     dwButtonState == 0 when a window is activated.  Ignore this case.  */
+  if (event->dwButtonState == button_state)
+    return 0;
+  
+  emacs_ev->kind = mouse_click;
+  
+  /* Find out what button has changed state since the last button event.  */
+  but_change = button_state ^ event->dwButtonState;
+  mask = 1;
+  for (i = 0; i < NUM_MOUSE_BUTTONS; i++, mask <<= 1)
+    if (but_change & mask)
+      {
+	XSET (emacs_ev->code, Lisp_Int, emacs_button_translation[i]);
+	break;
+      }
+
+  /* If the changed button is out of emacs' range (highly unlikely)
+     ignore this event.  */
+  if (i == NUM_MOUSE_BUTTONS)
+    return 0;
+  
+  button_state = event->dwButtonState;
+  emacs_ev->timestamp = GetTickCount ();
+  emacs_ev->modifiers = nt_kbd_mods_to_emacs (event->dwControlKeyState) |
+    ((event->dwButtonState & mask) ? down_modifier : up_modifier);
+  
+  XFASTINT (emacs_ev->x) = event->dwMousePosition.X;
+  XFASTINT (emacs_ev->y) = event->dwMousePosition.Y;
+  SET_FRAME (emacs_ev->frame_or_window, get_frame ());
+  
+  return 1;
+}
+
+static void 
+resize_event (WINDOW_BUFFER_SIZE_RECORD *event)
+{
+  FRAME_PTR f = get_frame ();
+  
+  change_frame_size (f, event->dwSize.Y, event->dwSize.X, 0, 1);
+  SET_FRAME_GARBAGED (f);
+}
+
+int 
+win32_read_socket (int sd, struct input_event *bufp, int numchars,
+		   int waitp, int expected)
+{
+  BOOL no_events = TRUE;
+  int nev, ret = 0, add;
+  
+  if (interrupt_input_blocked)
+    {
+      interrupt_input_pending = 1;
+      return -1;
+    }
+  
+  interrupt_input_pending = 0;
+  BLOCK_INPUT;
+  
+  for (;;)
+    {
+      nev = fill_queue (waitp != 0);
+      if (nev <= 0)
+        {
+	  /* If nev == -1, there was some kind of error
+	     If nev == 0 then waitp must be zero and no events were available
+	     so return.  */
+	  UNBLOCK_INPUT;
+	  return nev;
+        }
+
+      while (nev > 0 && numchars > 0)
+        {
+	  switch (queue_ptr->EventType)
+            {
+            case KEY_EVENT:
+	      add = key_event (&queue_ptr->Event.KeyEvent, bufp);
+	      bufp += add;
+	      ret += add;
+	      numchars -= add;
+	      break;
+
+            case MOUSE_EVENT:
+	      add = do_mouse_event (&queue_ptr->Event.MouseEvent, bufp);
+	      bufp += add;
+	      ret += add;
+	      numchars -= add;
+	      break;
+
+            case WINDOW_BUFFER_SIZE_EVENT:
+	      resize_event (&queue_ptr->Event.WindowBufferSizeEvent);
+	      break;
+            
+            case MENU_EVENT:
+            case FOCUS_EVENT:
+	      /* Internal event types, ignored. */
+	      break;
+            }
+            
+	  queue_ptr++;
+	  nev--;
+        }
+
+      if (ret > 0 || expected == 0)
+	break;
+    }
+  
+  UNBLOCK_INPUT;
+  return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/w32proc.c	Mon Nov 14 01:32:24 1994 +0000
@@ -0,0 +1,780 @@
+/* Process support for Windows NT port of GNU EMACS.
+   Copyright (C) 1992 Free Software Foundation, Inc.
+
+   This file is part of GNU Emacs.
+
+   GNU Emacs is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any later
+   version.
+
+   GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+   more details.
+
+   You should have received a copy of the GNU General Public License along
+   with GNU Emacs; see the file COPYING.  If not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   Drew Bliss                   Oct 14, 1993
+     Adapted from alarm.c by Tim Fleehart
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <io.h>
+#include <signal.h>
+
+#include "config.h"
+
+#include <windows.h>
+
+#include "lisp.h"
+#include "nt.h"
+#include "systime.h"
+
+/* #define FULL_DEBUG */
+
+typedef void (_CALLBACK_ *signal_handler)(int);
+
+/* Defined in process.h which conflicts with the local copy */
+#define	_P_NOWAIT 1
+
+typedef struct _child_process
+{
+  int fd;
+  HANDLE char_avail;
+  HANDLE char_consumed;
+  char chr;
+  BOOL status;
+  HANDLE process;
+  DWORD pid;
+  HANDLE thrd;
+} child_process;
+
+#define MAX_CHILDREN MAXDESC
+
+#ifdef EMACSDEBUG
+void _CRTAPI1
+_DebPrint (char *fmt, ...)
+{
+  char buf[256];
+  va_list args;
+
+  va_start (args, fmt);
+  vsprintf (buf, fmt, args);
+  va_end (args);
+  OutputDebugString (buf);
+}
+#endif
+
+/* Child process management list.  */
+static int child_proc_count = 0;
+static child_process child_procs[MAX_CHILDREN];
+static child_process *dead_child = NULL;
+
+#define CHILD_ACTIVE(cp) ((cp)->process != NULL)
+#define DEACTIVATE_CHILD(cp) ((cp)->process = NULL)
+
+/* Signal handlers...SIG_DFL == 0 so this is initialized correctly.  */
+static signal_handler sig_handlers[NSIG];
+
+/* Fake signal implementation to record the SIGCHLD handler.  */
+signal_handler 
+win32_signal (int sig, signal_handler handler)
+{
+  signal_handler old;
+  
+  if (sig != SIGCHLD)
+    {
+      errno = EINVAL;
+      return SIG_ERR;
+    }
+  old = sig_handlers[sig];
+  sig_handlers[sig] = handler;
+  return old;
+}
+
+/* Find an unused process slot.  */
+static child_process *
+new_child (void)
+{
+  child_process *cp;
+  
+  if (child_proc_count == MAX_CHILDREN)
+    return NULL;
+  
+  for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
+    if (!CHILD_ACTIVE (cp))
+      return cp;
+  return &child_procs[child_proc_count++];
+}
+
+/* Find a child by pid.  */
+static child_process *
+find_child_pid (DWORD pid)
+{
+  child_process *cp;
+  
+  for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
+    if (CHILD_ACTIVE (cp) && pid == cp->pid)
+      return cp;
+  return NULL;
+}
+
+/* Find a child by fd.  */
+static child_process *
+find_child_fd (int fd)
+{
+  child_process *cp;
+  
+  for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
+    if (CHILD_ACTIVE (cp) && fd == cp->fd)
+      return cp;
+  return NULL;
+}
+
+/* Thread proc for child process reader threads
+   The threads just sit in a loop waiting for input
+   When they detect input, they signal the char_avail input to
+   wake up the select emulator
+   When the select emulator processes their input, it pulses
+   char_consumed so that the reader thread goes back to reading.  */
+DWORD WINAPI 
+reader_thread (void *arg)
+{
+  child_process *cp;
+  
+  /* Our identity */
+  cp = (child_process *)arg;
+  
+  /* We have to wait for the go-ahead before we can start */
+  if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
+    return 0;
+  /* If something went wrong, quit */
+  if (!cp->status)
+    return 0;
+  
+  for (;;)
+    {
+      /* Use read to get CRLF translation */
+      if (read (cp->fd, &cp->chr, sizeof (char)) == sizeof (char))
+        {
+	  cp->status = TRUE;
+        }
+      else
+        {
+#ifdef FULL_DEBUG
+	  DebPrint (("reader_thread.read failed with %lu for fd %ld\n",
+		     GetLastError (), cp->fd));
+#endif
+	  cp->status = FALSE;
+        }
+        
+      if (!SetEvent (cp->char_avail))
+        {
+	  DebPrint (("reader_thread.SetEvent failed with %lu for fd %ld\n",
+		     GetLastError (), cp->fd));
+	  break;
+        }
+        
+      /* If the read died, the child has died so let the thread die */
+      if (!cp->status)
+	break;
+        
+      /* Wait until our input is acknowledged before reading again */
+      if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
+        {
+	  DebPrint (("reader_thread.WaitForSingleObject failed with "
+		     "%lu for fd %ld\n", GetLastError (), cp->fd));
+	  break;
+        }
+    }
+  return 0;
+}
+
+static BOOL 
+create_child (char *exe, char *cmdline, char *env,
+	     PROCESS_INFORMATION *info)
+{
+  child_process *cp;
+  DWORD id;
+  STARTUPINFO start;
+  SECURITY_ATTRIBUTES sec_attrs;
+  SECURITY_DESCRIPTOR sec_desc;
+  
+  cp = new_child ();
+  if (cp == NULL)
+    goto EH_Fail;
+  
+  cp->fd = -1;
+  
+  cp->char_avail = CreateEvent (NULL, FALSE, FALSE, NULL);
+  if (cp->char_avail == NULL)
+    goto EH_Fail;
+  
+  cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL);
+  if (cp->char_consumed == NULL)
+    goto EH_char_avail;
+  
+  cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id);
+  if (cp->thrd == NULL)
+    goto EH_char_consumed;
+  
+  memset (&start, 0, sizeof (start));
+  start.cb = sizeof (start);
+  
+  /* Explicitly specify no security */
+  if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
+    goto EH_thrd;
+  if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
+    goto EH_thrd;
+  sec_attrs.nLength = sizeof (sec_attrs);
+  sec_attrs.lpSecurityDescriptor = &sec_desc;
+  sec_attrs.bInheritHandle = FALSE;
+  
+  if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE,
+		      CREATE_NEW_PROCESS_GROUP, env, NULL,
+		      &start, info))
+    goto EH_thrd;
+  cp->process = info->hProcess;
+  cp->pid = info->dwProcessId;
+  
+  return TRUE;
+  
+ EH_thrd:
+  id = GetLastError ();
+  
+  cp->status = FALSE;
+  SetEvent (cp->char_consumed);
+ EH_char_consumed:
+  CloseHandle (cp->char_consumed);
+ EH_char_avail:
+  CloseHandle (cp->char_avail);
+ EH_Fail:
+  return FALSE;
+}
+
+/* create_child doesn't know what emacs' file handle will be for waiting
+   on output from the child, so we need to make this additional call
+   to register the handle with the process
+   This way the select emulator knows how to match file handles with
+   entries in child_procs.  */
+void 
+register_child (int pid, int fd)
+{
+  child_process *cp;
+  
+  cp = find_child_pid (pid);
+  if (cp == NULL)
+    {
+      DebPrint (("register_child unable to find pid %lu\n", pid));
+      return;
+    }
+  
+#ifdef FULL_DEBUG
+  DebPrint (("register_child registered fd %d with pid %lu\n", fd, pid));
+#endif
+  
+  cp->fd = fd;
+  cp->status = TRUE;
+
+  /* Tell the reader thread to start */
+  if (!SetEvent (cp->char_consumed))
+    {
+      DebPrint (("register_child.SetEvent failed with %lu for fd %ld\n",
+		 GetLastError (), cp->fd));
+    }
+}
+
+/* When a process dies its pipe will break so the reader thread will
+   signal failure to the select emulator.
+   The select emulator then calls this routine to clean up.
+   Since the thread signaled failure we can assume it is exiting.  */
+static void 
+remove_child (child_process *cp)
+{
+  /* Reap the thread */
+  if (WaitForSingleObject (cp->thrd, INFINITE) != WAIT_OBJECT_0)
+    {
+      DebPrint (("remove_child.WaitForSingleObject (thread) failed "
+		 "with %lu for fd %ld\n", GetLastError (), cp->fd));
+    }
+  CloseHandle (cp->thrd);
+  CloseHandle (cp->char_consumed);
+  CloseHandle (cp->char_avail);
+  
+  /* Reap the process */
+  if (WaitForSingleObject (cp->process, INFINITE) != WAIT_OBJECT_0)
+    {
+      DebPrint (("remove_child.WaitForSingleObject (process) failed "
+		 "with %lu for fd %ld\n", GetLastError (), cp->fd));
+    }
+  CloseHandle (cp->process);
+  
+  DEACTIVATE_CHILD (cp);
+}
+
+/* Wait for any of our existing child processes to die
+   When it does, close its handle
+   Return the pid and fill in the status if non-NULL.  */
+int 
+win32_wait (int *status)
+{
+  DWORD active, retval;
+  int nh;
+  child_process *cp, *cps[MAX_CHILDREN];
+  HANDLE wait_hnd[MAX_CHILDREN];
+  
+  nh = 0;
+  if (dead_child != NULL)
+    {
+      /* We want to wait for a specific child */
+      wait_hnd[nh] = dead_child->process;
+      cps[nh] = dead_child;
+      nh++;
+    }
+  else
+    {
+      for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
+	if (CHILD_ACTIVE (cp))
+	  {
+	    wait_hnd[nh] = cp->process;
+	    cps[nh] = cp;
+	    nh++;
+	  }
+    }
+  
+  if (nh == 0)
+    {
+      /* Nothing to wait on, so fail */
+      errno = ECHILD;
+      return -1;
+    }
+  
+  active = WaitForMultipleObjects (nh, wait_hnd, FALSE, INFINITE);
+  if (active == WAIT_FAILED)
+    {
+      errno = EBADF;
+      return -1;
+    }
+  else if (active == WAIT_TIMEOUT)
+    {
+      /* Should never happen */
+      errno = EINVAL;
+      return -1;
+    }
+  else if (active >= WAIT_OBJECT_0 &&
+	   active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
+    {
+      active -= WAIT_OBJECT_0;
+    }
+  else if (active >= WAIT_ABANDONED_0 &&
+	   active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
+    {
+      active -= WAIT_ABANDONED_0;
+    }
+  
+  if (!GetExitCodeProcess (wait_hnd[active], &retval))
+    {
+      DebPrint (("Wait.GetExitCodeProcess failed with %lu\n",
+		 GetLastError ()));
+      retval = 1;
+    }
+  if (retval == STILL_ACTIVE)
+    {
+      /* Should never happen */
+      DebPrint (("Wait.WaitForMultipleObjects returned an active process\n"));
+      errno = EINVAL;
+      return -1;
+    }
+  
+  cp = cps[active];
+#ifdef FULL_DEBUG
+  DebPrint (("Wait signaled with process pid %d\n", cp->pid));
+#endif
+  
+  if (status)
+    {
+      /* In process.c the default WAITTYPE is defined.
+	 Since we can't determine anything about why a process died
+	 we can only return a code that looks like WIFEXITED */
+      *status = (retval & 0x7fffff) << 8;
+    }
+  
+  return cp->pid;
+}
+
+/* We pass our process ID to our children by setting up an environment
+   variable in their environment.  */
+char ppid_env_var_buffer[64];
+
+/* When a new child process is created we need to register it in our list,
+   so intercept spawn requests.  */
+int 
+win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
+{
+  char *cmdline, *env, *parg, **targ;
+  int arglen;
+  PROCESS_INFORMATION pi;
+  
+  if (child_proc_count == MAX_CHILDREN)
+    {
+      errno = EAGAIN;
+      return -1;
+    }
+  
+  /* We don't care about the other modes */
+  if (mode != _P_NOWAIT)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  
+  /* we have to do some conjuring here to put argv and envp into the
+     form CreateProcess wants...  argv needs to be a space separated/null
+     terminated list of parameters, and envp is a null
+     separated/double-null terminated list of parameters.
+   
+     Since I have no idea how large argv and envp are likely to be
+     we figure out list lengths on the fly and allocate them.  */
+  
+  /* do argv...  */
+  arglen = 0;
+  targ = argv;
+  while (*targ)
+    {
+      arglen += strlen (*targ++) + 1;
+    }
+  cmdline = malloc (arglen);
+  if (cmdline == NULL)
+    {
+      errno = ENOMEM;
+      goto EH_Fail;
+    }
+  targ = argv;
+  parg = cmdline;
+  while (*targ)
+    {
+      strcpy (parg, *targ);
+      parg += strlen (*targ++);
+      *parg++ = ' ';
+    }
+  *--parg = '\0';
+  
+  /* and envp...  */
+  arglen = 1;
+  targ = envp;
+  while (*targ)
+    {
+      arglen += strlen (*targ++) + 1;
+    }
+  sprintf (ppid_env_var_buffer, "__PARENT_PROCESS_ID=%d", 
+	   GetCurrentProcessId ());
+  arglen += strlen (ppid_env_var_buffer) + 1;
+
+  env = malloc (arglen);
+  if (env == NULL)
+    {
+      errno = ENOMEM;
+      goto EH_cmdline;
+    }
+  targ = envp;
+  parg = env;
+  while (*targ)
+    {
+      strcpy (parg, *targ);
+      parg += strlen (*targ++);
+      *parg++ = '\0';
+    }
+  strcpy (parg, ppid_env_var_buffer);
+  parg += strlen (ppid_env_var_buffer);
+  *parg++ = '\0';
+  *parg = '\0';
+  
+  /* Now create the process.  */
+  if (!create_child (cmdname, cmdline, env, &pi))
+    {
+      errno = ENOEXEC;
+      goto EH_env;
+    }
+  
+  return pi.dwProcessId;
+  
+ EH_env:
+  free (env);
+ EH_cmdline:
+  free (cmdline);
+ EH_Fail:
+  return -1;
+}
+
+/* Emulate the select call
+   Wait for available input on any of the given rfds, or timeout if
+   a timeout is given and no input is detected
+   wfds and efds are not supported and must be NULL.  */
+
+/* From ntterm.c */
+extern HANDLE keyboard_handle;
+/* From process.c */
+extern int proc_buffered_char[];
+
+int 
+select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
+	EMACS_TIME *timeout)
+{
+  SELECT_TYPE orfds;
+  DWORD timeout_ms;
+  int i, nh, nr;
+  DWORD active;
+  child_process *cp, *cps[MAX_CHILDREN];
+  HANDLE wait_hnd[MAX_CHILDREN];
+  
+  /* If the descriptor sets are NULL but timeout isn't, then just Sleep.  */
+  if (rfds == NULL && wfds == NULL && efds == NULL && timeout != NULL) 
+    {
+      Sleep ((*timeout) * 1000);
+      return 0;
+    }
+
+  /* Otherwise, we only handle rfds, so fail otherwise.  */
+  if (rfds == NULL || wfds != NULL || efds != NULL)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  
+  orfds = *rfds;
+  FD_ZERO (rfds);
+  nr = 0;
+  
+  /* Build a list of handles to wait on.  */
+  nh = 0;
+  for (i = 0; i < nfds; i++)
+    if (FD_ISSET (i, &orfds))
+      {
+	if (i == 0)
+	  {
+	    /* Handle stdin specially */
+	    wait_hnd[nh] = keyboard_handle;
+	    cps[nh] = NULL;
+	    nh++;
+
+	    /* Check for any emacs-generated input in the queue since
+	       it won't be detected in the wait */
+	    if (detect_input_pending ())
+	      {
+		FD_SET (i, rfds);
+		nr++;
+	      }
+	  }
+	else
+	  {
+	    /* Child process input */
+	    cp = find_child_fd (i);
+	    if (cp)
+	      {
+#ifdef FULL_DEBUG
+		DebPrint (("select waiting on child %d fd %d\n",
+			   cp-child_procs, i));
+#endif
+		wait_hnd[nh] = cp->char_avail;
+		cps[nh] = cp;
+		nh++;
+	      }
+	    else
+	      {
+		/* Unable to find something to wait on for this fd, fail */
+		DebPrint (("select unable to find child process "
+			   "for fd %ld\n", i));
+		nh = 0;
+		break;
+	      }
+	  }
+      }
+  
+  /* Nothing to look for, so we didn't find anything */
+  if (nh == 0) 
+    {
+      Sleep ((*timeout) * 1000);
+      return 0;
+    }
+  
+  /* Check for immediate return without waiting */
+  if (nr > 0)
+    return nr;
+  
+  /*
+     Wait for input
+     If a child process dies while this is waiting, its pipe will break
+     so the reader thread will signal an error condition, thus, the wait
+     will wake up
+     */
+  timeout_ms = timeout ? *timeout*1000 : INFINITE;
+  active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms);
+  if (active == WAIT_FAILED)
+    {
+      DebPrint (("select.WaitForMultipleObjects (%d, %lu) failed with %lu\n",
+		 nh, timeout_ms, GetLastError ()));
+      /* Is there a better error? */
+      errno = EBADF;
+      return -1;
+    }
+  else if (active == WAIT_TIMEOUT)
+    {
+      return 0;
+    }
+  else if (active >= WAIT_OBJECT_0 &&
+	   active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
+    {
+      active -= WAIT_OBJECT_0;
+    }
+  else if (active >= WAIT_ABANDONED_0 &&
+	   active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
+    {
+      active -= WAIT_ABANDONED_0;
+    }
+  
+  if (cps[active] == NULL)
+    {
+      /* Keyboard input available */
+      FD_SET (0, rfds);
+      nr++;
+
+      /* This shouldn't be necessary, but apparently just setting the input
+	 fd is not good enough for emacs */
+      read_input_waiting ();
+    }
+  else
+    {
+      /* Child process */
+      cp = cps[active];
+
+      /* If status is FALSE the read failed so don't report input */
+      if (cp->status)
+        {
+	  FD_SET (cp->fd, rfds);
+	  proc_buffered_char[cp->fd] = cp->chr;
+	  nr++;
+        }
+      else
+        {
+	  /* The SIGCHLD handler will do a Wait so we know it won't
+	     return until the process is dead
+	     We force Wait to only wait for this process to avoid it
+	     picking up other children that happen to be dead but that
+	     we haven't noticed yet
+	     SIG_DFL for SIGCHLD is ignore? */
+	  if (sig_handlers[SIGCHLD] != SIG_DFL &&
+	      sig_handlers[SIGCHLD] != SIG_IGN)
+            {
+#ifdef FULL_DEBUG
+	      DebPrint (("select calling SIGCHLD handler for pid %d\n",
+			 cp->pid));
+#endif
+	      dead_child = cp;
+	      sig_handlers[SIGCHLD](SIGCHLD);
+	      dead_child = NULL;
+            }
+            
+	  /* Clean up the child process entry in the table */
+	  remove_child (cp);
+        }
+    }
+  return nr;
+}
+
+/*
+   Substitute for certain kill () operations
+   */
+int 
+win32_kill_process (int pid, int sig)
+{
+  child_process *cp;
+  
+  /* Only handle signals that will result in the process dying */
+  if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  
+  cp = find_child_pid (pid);
+  if (cp == NULL)
+    {
+      DebPrint (("win32_kill_process didn't find a child with pid %lu\n", pid));
+      errno = ECHILD;
+      return -1;
+    }
+  
+  if (sig == SIGINT)
+    {
+      /* Fake Ctrl-Break.  */
+      if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid))
+        {
+	  DebPrint (("win32_kill_process.GenerateConsoleCtrlEvent return %d "
+		     "for pid %lu\n", GetLastError (), pid));
+	  errno = EINVAL;
+	  return -1;
+        }
+    }
+  else
+    {
+      /* Kill the process.  On Win32 this doesn't kill child processes
+	 so it doesn't work very well for shells which is why it's
+	 not used in every case.  */
+      if (!TerminateProcess (cp->process, 0xff))
+        {
+	  DebPrint (("win32_kill_process.TerminateProcess returned %d "
+		     "for pid %lu\n", GetLastError (), pid));
+	  errno = EINVAL;
+	  return -1;
+        }
+    }
+  return 0;
+}
+
+/* If the channel is a pipe this read might block since we don't
+   know how many characters are available, so check and read only
+   what's there
+   We also need to wake up the reader thread once we've read our data.  */
+int 
+read_child_output (int fd, char *buf, int max)
+{
+  HANDLE h;
+  int to_read, nchars;
+  DWORD waiting;
+  child_process *cp;
+  
+  h = (HANDLE)_get_osfhandle (fd);
+  if (GetFileType (h) == FILE_TYPE_PIPE)
+    {
+      PeekNamedPipe (h, NULL, 0, NULL, &waiting, NULL);
+      to_read = min (waiting, (DWORD)max);
+    }
+  else
+    to_read = max;
+  
+  /* Use read to get CRLF translation */
+  nchars = read (fd, buf, to_read);
+  
+  if (GetFileType (h) == FILE_TYPE_PIPE)
+    {
+      /* Wake up the reader thread
+	 for this process */
+      cp = find_child_fd (fd);
+      if (cp)
+        {
+	  if (!SetEvent (cp->char_consumed))
+	    DebPrint (("read_child_output.SetEvent failed with "
+		       "%lu for fd %ld\n", GetLastError (), fd));
+        }
+      else
+	DebPrint (("read_child_output couldn't find a child with fd %d\n",
+		   fd));
+    }
+  
+  return nchars;
+}