diff src/event-msw.c @ 853:2b6fa2618f76

[xemacs-hg @ 2002-05-28 08:44:22 by ben] merge my stderr-proc ws make-docfile.c: Fix places where we forget to check for EOF. code-init.el: Don't use CRLF conversion by default on process output. CMD.EXE and friends work both ways but Cygwin programs don't like the CRs. code-process.el, multicast.el, process.el: Removed. Improvements to call-process-internal: -- allows a buffer to be specified for input and stderr output -- use it on all systems -- implement C-g as documented -- clean up and comment call-process-region uses new call-process facilities; no temp file. remove duplicate funs in process.el. comment exactly how coding systems work and fix various problems. open-multicast-group now does similar coding-system frobbing to open-network-stream. dumped-lisp.el, faces.el, msw-faces.el: Fix some hidden errors due to code not being defined at the right time. xemacs.mak: Add -DSTRICT. ================================================================ ALLOW SEPARATION OF STDOUT AND STDERR IN PROCESSES ================================================================ Standard output and standard error can be processed separately in a process. Each can have its own buffer, its own mark in that buffer, and its filter function. You can specify a separate buffer for stderr in `start-process' to get things started, or use the new primitives: set-process-stderr-buffer process-stderr-buffer process-stderr-mark set-process-stderr-filter process-stderr-filter Also, process-send-region takes a 4th optional arg, a buffer. Currently always uses a pipe() under Unix to read the error output. (#### Would a PTY be better?) sysdep.h, sysproc.h, unexfreebsd.c, unexsunos4.c, nt.c, emacs.c, callproc.c, symsinit.h, sysdep.c, Makefile.in.in, process-unix.c: Delete callproc.c. Move child_setup() to process-unix.c. wait_for_termination() now only needed on a few really old systems. console-msw.h, event-Xt.c, event-msw.c, event-stream.c, event-tty.c, event-unixoid.c, events.h, process-nt.c, process-unix.c, process.c, process.h, procimpl.h: Rewrite the process methods to handle a separate channel for error input. Create Lstreams for reading in the error channel. Many process methods need change. In general the changes are fairly clear as they involve duplicating what's used for reading the normal stdout and changing for stderr -- although tedious, as such changes are required throughout the entire process code. Rewrote the code that reads process output to do two loops, one for stdout and one for stderr. gpmevent.c, tooltalk.c: set_process_filter takes an argument for stderr. ================================================================ NEW ERROR-TRAPPING MECHANISM ================================================================ Totally rewrite error trapping code to be unified and support more features. Basic function is call_trapping_problems(), which lets you specify, by means of flags, what sorts of problems you want trapped. these can include -- quit -- errors -- throws past the function -- creation of "display objects" (e.g. buffers) -- deletion of already-existing "display objects" (e.g. buffers) -- modification of already-existing buffers -- entering the debugger -- gc -- errors->warnings (ala suspended errors) etc. All other error funs rewritten in terms of this one. Various older mechanisms removed or rewritten. window.c, insdel.c, console.c, buffer.c, device.c, frame.c: When creating a display object, added call to note_object_created(), for use with trapping_problems mechanism. When deleting, call check_allowed_operation() and note_object deleted(). The trapping-problems code records the objects created since the call-trapping-problems began. Those objects can be deleted, but none others (i.e. previously existing ones). bytecode.c, cmdloop.c: internal_catch takes another arg. eval.c: Add long comments describing the "five lists" used to maintain state (backtrace, gcpro, specbind, etc.) in the Lisp engine. backtrace.h, eval.c: Implement trapping-problems mechanism, eliminate old mechanisms or redo in terms of new one. frame.c, gutter.c: Flush out the concept of "critical display section", defined by the in_display() var. Use an internal_bind() to get it reset, rather than just doing it at end, because there may be a non-local exit. event-msw.c, event-stream.c, console-msw.h, device.c, dialog-msw.c, frame.c, frame.h, intl.c, toolbar.c, menubar-msw.c, redisplay.c, alloc.c, menubar-x.c: Make use of new trapping-errors stuff and rewrite code based on old mechanisms. glyphs-widget.c, redisplay.h: Protect calling Lisp in redisplay. insdel.c: Protect hooks against deleting existing buffers. frame-msw.c: Use EQ, not EQUAL in hash tables whose keys are just numbers. Otherwise we run into stickiness in redisplay because internal_equal() can QUIT. ================================================================ SIGNAL, C-G CHANGES ================================================================ Here we change the way that C-g interacts with event reading. The idea is that a C-g occurring while we're reading a user event should be read as C-g, but elsewhere should be a QUIT. The former code did all sorts of bizarreness -- requiring that no QUIT occurs anywhere in event-reading code (impossible to enforce given the stuff called or Lisp code invoked), and having some weird system involving enqueue/dequeue of a C-g and interaction with Vquit_flag -- and it didn't work. Now, we simply enclose all code where we want C-g read as an event with {begin/end}_dont_check_for_quit(). This completely turns off the mechanism that checks (and may remove or alter) C-g in the read-ahead queues, so we just get the C-g normal. Signal.c documents this very carefully. cmdloop.c: Correct use of dont_check_for_quit to new scheme, remove old out-of-date comments. event-stream.c: Fix C-g handling to actually work. device-x.c: Disable quit checking when err out. signal.c: Cleanup. Add large descriptive comment. process-unix.c, process-nt.c, sysdep.c: Use QUIT instead of REALLY_QUIT. It's not necessary to use REALLY_QUIT and just confuses the issue. lisp.h: Comment quit handlers. ================================================================ CONS CHANGES ================================================================ free_cons() now takes a Lisp_Object not the result of XCONS(). car and cdr have been renamed so that they don't get used directly; go through XCAR(), XCDR() instead. alloc.c, dired.c, editfns.c, emodules.c, fns.c, glyphs-msw.c, glyphs-x.c, glyphs.c, keymap.c, minibuf.c, search.c, eval.c, lread.c, lisp.h: Correct free_cons calling convention: now takes Lisp_Object, not Lisp_Cons chartab.c: Eliminate direct use of ->car, ->cdr, should be black box. callint.c: Rewrote using EXTERNAL_LIST_LOOP to avoid use of Lisp_Cons. ================================================================ USE INTERNAL-BIND-* ================================================================ eval.c: Cleanups of these funs. alloc.c, fileio.c, undo.c, specifier.c, text.c, profile.c, lread.c, redisplay.c, menubar-x.c, macros.c: Rewrote to use internal_bind_int() and internal_bind_lisp_object() in place of whatever varied and cumbersome mechanisms were formerly there. ================================================================ SPECBIND SANITY ================================================================ backtrace.h: - Improved comments backtrace.h, bytecode.c, eval.c: Add new mechanism check_specbind_stack_sanity() for sanity checking code each time the catchlist or specbind stack change. Removed older prototype of same mechanism. ================================================================ MISC ================================================================ lisp.h, insdel.c, window.c, device.c, console.c, buffer.c: Fleshed out authorship. device-msw.c: Correct bad Unicode-ization. print.c: Be more careful when not initialized or in fatal error handling. search.c: Eliminate running_asynch_code, an FSF holdover. alloc.c: Added comments about gc-cons-threshold. dialog-x.c: Use begin_gc_forbidden() around code to build up a widget value tree, like in menubar-x.c. gui.c: Use Qunbound not Qnil as the default for gethash. lisp-disunion.h, lisp-union.h: Added warnings on use of VOID_TO_LISP(). lisp.h: Use ERROR_CHECK_STRUCTURES to turn on ERROR_CHECK_TRAPPING_PROBLEMS and ERROR_CHECK_TYPECHECK lisp.h: Add assert_with_message. lisp.h: Add macros for gcproing entire arrays. (You could do this before but it required manual twiddling the gcpro structure.) lisp.h: Add prototypes for new functions defined elsewhere.
author ben
date Tue, 28 May 2002 08:45:36 +0000
parents e7ee5f8bde58
children b27b70c1252a
line wrap: on
line diff
--- a/src/event-msw.c	Sat May 25 01:55:30 2002 +0000
+++ b/src/event-msw.c	Tue May 28 08:45:36 2002 +0000
@@ -39,6 +39,28 @@
 #include <config.h>
 #include "lisp.h"
 
+#if defined (CYGWIN) && !defined (HAVE_MSG_SELECT)
+#error We do not support non-select() versions (i.e. very old) of Cygwin.
+#endif
+
+/* Acceptable are:
+
+   WIN32_NATIVE and HAVE_WIN32_PROCESSES and nothing else
+
+   CYGWIN and HAVE_MSG_SELECT and HAVE_UNIX_PROCESSES and nothing else
+*/
+#ifdef WIN32_NATIVE
+# if !(defined (HAVE_WIN32_PROCESSES && !defined (HAVE_UNIX_PROCESSES) && !defined (HAVE_MSG_SELECT) && !defined (CYGWIN)))
+#  error Something is wrong with your process definitions for Windows native.
+# endif
+#elif defined (CYGWIN)
+# if !(defined (HAVE_UNIX_PROCESSES) && defined (HAVE_MSG_SELECT) && !defined (HAVE_WIN32_PROCESSES) && !defined (WIN32_NATIVE))
+#  error Something is wrong with your process definitions for Cygwin.
+# endif
+#else
+# error Something is wrong -- you are neither Windows native (possibly MinGW) nor Cygwin.
+#endif
+
 #include "buffer.h"
 #include "device.h"
 #include "events.h"
@@ -72,10 +94,8 @@
 #include "systime.h"
 #include "syswait.h"
 
-#ifdef HAVE_MSG_SELECT
+#ifdef CYGWIN
 #include "console-tty.h"
-#elif defined (CYGWIN)
-typedef unsigned int SOCKET;
 #endif
 
 #ifdef HAVE_MENUBARS
@@ -105,12 +125,22 @@
 
 static struct event_stream *mswindows_event_stream;
 
-#ifdef HAVE_MSG_SELECT
+#ifdef CYGWIN
+
 extern SELECT_TYPE input_wait_mask, non_fake_input_wait_mask;
 extern SELECT_TYPE process_only_mask, tty_only_mask;
 SELECT_TYPE zero_mask;
 extern int signal_event_pipe_initialized;
 int windows_fd;
+
+#else
+
+/* List of mswindows waitable handles. */
+static HANDLE mswindows_waitable_handles[MAX_WAITABLE];
+
+/* Number of wait handles */
+static int mswindows_waitable_count = 0;
+
 #endif
 
 /*
@@ -125,14 +155,6 @@
 /* The number of things we can wait on */
 #define MAX_WAITABLE (MAXIMUM_WAIT_OBJECTS - 1)
 
-#ifndef HAVE_MSG_SELECT
-/* List of mswindows waitable handles. */
-static HANDLE mswindows_waitable_handles[MAX_WAITABLE];
-
-/* Number of wait handles */
-static int mswindows_waitable_count=0;
-#endif /* HAVE_MSG_SELECT */
-
 /* Brush for painting widgets */
 static HBRUSH widget_brush = 0;
 static LONG	last_widget_brushed = 0;
@@ -159,14 +181,17 @@
 
 /* This is the event signaled by the event pump.
    See mswindows_pump_outstanding_events for comments */
-static Lisp_Object mswindows_error_caught_in_modal_loop;
+static int mswindows_error_caught_in_modal_loop;
 static int mswindows_in_modal_loop;
 
 /* Count of wound timers */
 static int mswindows_pending_timers_count;
 
 static DWORD mswindows_last_mouse_button_state;
+
 
+#ifndef CYGWIN /* Skips past slurp, shove, or winsock streams */
+
 /************************************************************************/
 /*                Pipe instream - reads process output                  */
 /************************************************************************/
@@ -192,11 +217,11 @@
 struct ntpipe_slurp_stream_shared_data
 {
   HANDLE hev_thread;	/* Our thread blocks on this, signaled by caller */
-  /* This is a manual-reset object. 		 */
+			/* This is a manual-reset object. 		 */
   HANDLE hev_caller;	/* Caller blocks on this, and we signal it	 */
-  /* This is a manual-reset object. 		 */
+			/* This is a manual-reset object. 		 */
   HANDLE hev_unsleep;	/* Pipe read delay is canceled if this is set	 */
-  /* This is a manual-reset object. 		 */
+			/* This is a manual-reset object. 		 */
   HANDLE hpipe;		/* Pipe read end handle.			 */
   LONG   die_p;		/* Thread must exit ASAP if non-zero		 */
   BOOL   eof_p   : 1;	/* Set when thread saw EOF			 */
@@ -467,6 +492,7 @@
   LSTREAM_HAS_METHOD (ntpipe_slurp, reader);
   LSTREAM_HAS_METHOD (ntpipe_slurp, closer);
 }
+
 
 /************************************************************************/
 /*                Pipe outstream - writes process input                 */
@@ -481,7 +507,7 @@
 {
   LPARAM user_data;	/* Any user data stored in the stream object	 */
   HANDLE hev_thread;	/* Our thread blocks on this, signaled by caller */
-  /* This is an auto-reset object. 		 */
+			/* This is an auto-reset object. 		 */
   HANDLE hpipe;		/* Pipe write end handle.			 */
   HANDLE hthread;	/* Reader thread handle.			 */
   char	 buffer[MAX_SHOVE_BUFFER_SIZE];	/* Buffer being written		 */
@@ -494,7 +520,6 @@
 
 DEFINE_LSTREAM_IMPLEMENTATION ("ntpipe-output", ntpipe_shove);
 
-#ifndef HAVE_MSG_SELECT
 static DWORD WINAPI
 shove_thread (LPVOID vparam)
 {
@@ -579,7 +604,6 @@
   struct ntpipe_shove_stream *s = NTPIPE_SHOVE_STREAM_DATA(stream);
   return s->user_data;
 }
-#endif
 
 static Bytecount
 ntpipe_shove_writer (Lstream *stream, const unsigned char *data,
@@ -656,7 +680,6 @@
 /************************************************************************/
 /*                         Winsock I/O stream                           */
 /************************************************************************/
-#if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
 
 #define WINSOCK_READ_BUFFER_SIZE 1024
 
@@ -883,7 +906,7 @@
   LSTREAM_HAS_METHOD (winsock, closer);
   LSTREAM_HAS_METHOD (winsock, was_blocked_p);
 }
-#endif /* defined (HAVE_SOCKETS) */
+#endif /* ! CYGWIN */
 
 /************************************************************************/
 /*                     Dispatch queue management                        */
@@ -1104,7 +1127,7 @@
   return Qnil;
 }
 
-#ifndef HAVE_MSG_SELECT
+#ifndef CYGWIN
 /************************************************************************/
 /*                     Waitable handles manipulation                    */
 /************************************************************************/
@@ -1140,7 +1163,8 @@
   mswindows_waitable_handles [ix] =
     mswindows_waitable_handles [--mswindows_waitable_count];
 }
-#endif /* HAVE_MSG_SELECT */
+
+#endif /* CYGWIN */
 
 /*
  * Given a lisp process pointer remove the corresponding process handle
@@ -1152,9 +1176,9 @@
 void
 mswindows_unwait_process (Lisp_Process *p)
 {
-#ifndef HAVE_MSG_SELECT
+#ifndef CYGWIN
   remove_waitable_handle (get_nt_process_handle (p));
-#endif /* HAVE_MSG_SELECT */
+#endif /* CYGWIN */
 }
 
 
@@ -1177,24 +1201,38 @@
   return !ascii_strcasecmp (class_name_buf, XEMACS_CLASS);
 }
 
+struct mswindows_protect_modal_loop
+{
+  Lisp_Object (*bfun) (Lisp_Object barg);
+  Lisp_Object barg;
+};
+
 static Lisp_Object
-mswindows_modal_loop_error_handler (Lisp_Object cons_sig_data,
-				    Lisp_Object u_n_u_s_e_d)
+mswindows_protect_modal_loop_1 (void *gack)
 {
-  mswindows_error_caught_in_modal_loop = cons_sig_data;
-  return Qunbound;
+  struct mswindows_protect_modal_loop *gata =
+    (struct mswindows_protect_modal_loop *) gack;
+
+  return (gata->bfun) (gata->barg);
 }
 
 Lisp_Object
-mswindows_protect_modal_loop (Lisp_Object (*bfun) (Lisp_Object barg),
-			      Lisp_Object barg)
+mswindows_protect_modal_loop (const char *error_string,
+			      Lisp_Object (*bfun) (Lisp_Object barg),
+			      Lisp_Object barg, int flags)
 {
   Lisp_Object tmp;
+  struct mswindows_protect_modal_loop bluh;
+
+  bluh.bfun = bfun;
+  bluh.barg = barg;
 
   ++mswindows_in_modal_loop;
-  tmp = condition_case_1 (Qt,
-			  bfun, barg,
-			  mswindows_modal_loop_error_handler, Qnil);
+  tmp = call_trapping_problems (Qevent, error_string,
+				flags, 0,
+				mswindows_protect_modal_loop_1, &bluh);
+  if (UNBOUNDP (tmp))
+    mswindows_error_caught_in_modal_loop = 1;
   --mswindows_in_modal_loop;
 
   return tmp;
@@ -1203,15 +1241,7 @@
 void
 mswindows_unmodalize_signal_maybe (void)
 {
-  if (!NILP (mswindows_error_caught_in_modal_loop))
-    {
-      /* Got an error while messages were pumped while
-	 in window procedure - have to resignal */
-      Lisp_Object sym = XCAR (mswindows_error_caught_in_modal_loop);
-      Lisp_Object data = XCDR (mswindows_error_caught_in_modal_loop);
-      mswindows_error_caught_in_modal_loop = Qnil;
-      Fsignal (sym, data);
-    }
+  mswindows_error_caught_in_modal_loop = 0;
 }
 
 /*
@@ -1259,26 +1289,23 @@
  * Return value is Qt if no errors was trapped, or Qunbound if
  * there was an error.
  *
- * In case of error, a cons representing the error, in the
- * form (SIGNAL . DATA), is stored in the module local variable
- * mswindows_error_caught_in_modal_loop. This error is signaled
- * again when DispatchMessage returns. Thus, Windows internal
- * modal loops are protected against throws, which are proven
- * to corrupt internal Windows structures.
+ * In case of error, a warning is issued and the module local variable
+ * mswindows_error_caught_in_modal_loop is set to non-zero.  Thus,
+ * Windows internal modal loops are protected against throws, which
+ * are proven to corrupt internal Windows structures.
  *
  * In case of success, mswindows_error_caught_in_modal_loop is
- * assigned Qnil.
+ * assigned 0.
  *
  * If the value of mswindows_error_caught_in_modal_loop is not
- * nil already upon entry, the function just returns non-nil.
+ * zero already upon entry, the function just returns non-nil.
  * This situation means that a new event has been queued while
  * in cancel mode. The event will be dequeued on the next regular
  * call of next-event; the pump is off since error is caught.
  * The caller must *unconditionally* cancel modal loop if the
  * value returned by this function is nil. Otherwise, everything
  * will become frozen until the modal loop exits under normal
- * condition (scrollbar drag is released, menu closed etc.)
- */
+ * condition (scrollbar drag is released, menu closed etc.)  */
 Lisp_Object
 mswindows_pump_outstanding_events (void)
 {
@@ -1288,8 +1315,9 @@
   struct gcpro gcpro1;
   GCPRO1 (result);
 
-  if (NILP(mswindows_error_caught_in_modal_loop))
-    result = mswindows_protect_modal_loop (mswindows_unsafe_pump_events, Qnil);
+  if (!mswindows_error_caught_in_modal_loop)
+    result = mswindows_protect_modal_loop
+      ("Error during event handling", mswindows_unsafe_pump_events, Qnil, 0);
   UNGCPRO;
   return result;
 }
@@ -1409,7 +1437,7 @@
   while (NILP (mswindows_u_dispatch_event_queue)
 	 && NILP (mswindows_s_dispatch_event_queue))
     {
-#ifdef HAVE_MSG_SELECT
+#ifdef CYGWIN
       int i;
       int active;
       SELECT_TYPE temp_mask = input_wait_mask;
@@ -1506,7 +1534,7 @@
 	{
 	  assert(0);
 	}
-#else /* not HAVE_MSG_SELECT */
+#else /* not CYGWIN */
       /* Now try getting a message or process event */
       DWORD active;
       DWORD what_events;
@@ -1592,18 +1620,18 @@
 	 (hyphen, VK_SUBTRACT) key on an 82-key keyboard.
 	 */
 
-  __try
-    {
-      active = MsgWaitForMultipleObjects (mswindows_waitable_count,
-					  mswindows_waitable_handles,
-					  FALSE, badly_p ? INFINITE : 0,
-					  what_events);
-    }
-  __except (GetExceptionCode () == EXCEPTION_BREAKPOINT ?
-	    EXCEPTION_CONTINUE_EXECUTION :
-	    EXCEPTION_CONTINUE_SEARCH)
-    {
-    }
+      __try
+	{
+	  active = MsgWaitForMultipleObjects (mswindows_waitable_count,
+					      mswindows_waitable_handles,
+					      FALSE, badly_p ? INFINITE : 0,
+					      what_events);
+	}
+      __except (GetExceptionCode () == EXCEPTION_BREAKPOINT ?
+		EXCEPTION_CONTINUE_EXECUTION :
+		EXCEPTION_CONTINUE_SEARCH)
+	{
+	}
 
       /* This will assert if handle being waited for becomes abandoned.
 	 Not the case currently tho */
@@ -1629,7 +1657,8 @@
 	  int ix = active - WAIT_OBJECT_0;
 	  /* First, try to find which process' output has signaled */
 	  Lisp_Process *p =
-	    get_process_from_usid (HANDLE_TO_USID (mswindows_waitable_handles[ix]));
+	    get_process_from_usid (HANDLE_TO_USID
+				   (mswindows_waitable_handles[ix]));
 	  if (p != NULL)
 	    {
 	      /* Found a signaled process input handle */
@@ -1652,22 +1681,20 @@
 		 (1) accept-process-output will return when called on this
 		 process, and (2) status notifications will happen in
 		 accept-process-output, sleep-for, and sit-for. */
-	      /* #### horrible kludge till my real process fixes go in.
-		 #### Replaced with a slightly less horrible kluge that
-		      at least finds the right process instead of axing the
-		      first one on the list.
-	       */
 	      if (!NILP (vproctail))
+		  mswindows_enqueue_process_event (XPROCESS (vaffanculo));
+	      else
 		{
-		  mswindows_enqueue_process_event (XPROCESS (vaffanculo));
+		  /* abort (); */
+		  /* #### FUCKME!  When can this happen?  I hit this abort()
+		     when I tried enabling it. */
+		  /* Have to return something: there may be no accompanying
+		     process event */
+		  mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
 		}
-	      else /* trash me soon. */
-		/* Have to return something: there may be no accompanying
-		   process event */
-		mswindows_enqueue_magic_event (NULL, XM_BUMPQUEUE);
 	    }
 	}
-#endif /* not HAVE_MSG_SELECT */
+#endif /* not CYGWIN */
     } /* while */
 }
 
@@ -1715,8 +1742,8 @@
     {
     case XTYP_CONNECT:
       if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
-	return (HDDEDATA)TRUE;
-      return (HDDEDATA)FALSE;
+	return (HDDEDATA) TRUE;
+      return (HDDEDATA) FALSE;
 
     case XTYP_WILDCONNECT:
       {
@@ -1729,10 +1756,10 @@
 	      || DdeCmpStringHandles (hszItem, mswindows_dde_service)) &&
 	    !(hszTopic
 	      || DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system)))
-	  return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE)pairs,
+	  return (DdeCreateDataHandle (mswindows_dde_mlid, (LPBYTE) pairs,
 				       sizeof (pairs), 0L, 0, uFmt, 0));
       }
-      return (HDDEDATA)NULL;
+      return (HDDEDATA) NULL;
 
     case XTYP_EXECUTE:
       if (!mswindows_dde_enable)
@@ -1741,7 +1768,7 @@
       if (!DdeCmpStringHandles (hszTopic, mswindows_dde_topic_system))
 	{
 	  DWORD len = DdeGetData (hdata, NULL, 0, 0);
-	  LPBYTE extcmd = (LPBYTE) ALLOCA (len+1);
+	  LPBYTE extcmd = (LPBYTE) ALLOCA (len + 1);
 	  Intbyte *cmd;
 	  Intbyte *end;
 	  struct gcpro gcpro1, gcpro2;
@@ -2197,7 +2224,8 @@
 
     case WM_CLOSE:
       fobj = mswindows_find_frame (hwnd);
-      mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj, Qt));
+      mswindows_enqueue_misc_user_event (fobj, Qeval, list3 (Qdelete_frame, fobj,
+							     Qt));
       break;
 
     case WM_KEYUP:
@@ -3163,7 +3191,7 @@
 
 	mswindows_handle_scrollbar_event (hwndScrollBar, code,  pos);
 	GCPRO2 (emacs_event, fobj);
-	if (UNBOUNDP (mswindows_pump_outstanding_events()))	/* Can GC */
+	if (UNBOUNDP (mswindows_pump_outstanding_events ())) /* Can GC */
 	  {
 	    /* Error during event pumping - cancel scroll */
 	    qxeSendMessage (hwndScrollBar, WM_CANCELMODE, 0, 0);
@@ -3297,7 +3325,7 @@
       goto defproc;
 
 #ifdef HAVE_DRAGNDROP
-    case WM_DROPFILES:	/* implementation ripped-off from event-Xt.c */
+    case WM_DROPFILES:		/* implementation ripped-off from event-Xt.c */
       {
 	UINT filecount, i;
 	POINT point;
@@ -3311,7 +3339,7 @@
 	GCPRO3 (emacs_event, l_dndlist, l_item);
 
 	if (!DragQueryPoint ((HDROP) wParam, &point))
-	  point.x = point.y = -1;		/* outside client area */
+	  point.x = point.y = -1; /* outside client area */
 
 	event->event_type = misc_user_event;
 	event->channel = mswindows_find_frame (hwnd);
@@ -4095,62 +4123,121 @@
     }
 }
 
-#ifndef HAVE_MSG_SELECT
+#ifndef CYGWIN
+
 static HANDLE
 get_process_input_waitable (Lisp_Process *process)
 {
-  Lisp_Object instr, outstr, p;
+  Lisp_Object instr, outstr, errstr, p;
   p = wrap_process (process);
-  get_process_streams (process, &instr, &outstr);
+  get_process_streams (process, &instr, &outstr, &errstr);
   assert (!NILP (instr));
-#if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
   return (network_connection_p (p)
 	  ? get_winsock_stream_waitable (XLSTREAM (instr))
 	  : get_ntpipe_input_stream_waitable (XLSTREAM (instr)));
-#else
-  return get_ntpipe_input_stream_waitable (XLSTREAM (instr));
-#endif
 }
 
+static HANDLE
+get_process_stderr_waitable (Lisp_Process *process)
+{
+  Lisp_Object instr, outstr, errstr;
+  get_process_streams (process, &instr, &outstr, &errstr);
+  if (NILP (errstr))
+    return INVALID_HANDLE_VALUE;
+  return get_ntpipe_input_stream_waitable (XLSTREAM (errstr));
+}
+
+#endif /* not CYGWIN */
+
 static void
-emacs_mswindows_select_process (Lisp_Process *process)
+emacs_mswindows_select_process (Lisp_Process *process, int doin, int doerr)
 {
-  HANDLE hev = get_process_input_waitable (process);
-
-  if (!add_waitable_handle (hev))
-    invalid_operation ("Too many active processes", Qunbound);
-
-#ifdef HAVE_WIN32_PROCESSES
+#ifdef CYGWIN
+  int infd, errfd;
+
+  event_stream_unixoid_select_process (process, doin, doerr, &infd, &errfd);
+#else
+  HANDLE hev = INVALID_HANDLE_VALUE;
+  HANDLE herr = INVALID_HANDLE_VALUE;
+
+  if (doin)
+    {
+      hev = get_process_input_waitable (process);
+      if (!add_waitable_handle (hev))
+	{
+	  hev = INVALID_HANDLE_VALUE;
+	  goto err;
+	}
+    }
+
+  if (doerr)
+    {
+      herr = get_process_stderr_waitable (process);
+      if (herr != INVALID_HANDLE_VALUE && !add_waitable_handle (herr))
+	{
+	  herr = INVALID_HANDLE_VALUE;
+	  goto err;
+	}
+    }
+
   {
+    /* Also select on the process handle itself, so we can receive
+       exit notifications.  Only do this once, not each time this
+       function is called (which can happen many times, e.g. if
+       (set-process-filter proc t) is called and then a process filter
+       is set again).  It will be unselected in mswindows_need_event(). */
     Lisp_Object p = wrap_process (process);
 
     if (!network_connection_p (p))
       {
-	HANDLE hprocess = get_nt_process_handle (process);
-	if (!add_waitable_handle (hprocess))
-	  {
-	    remove_waitable_handle (hev);
-	    invalid_operation ("Too many active processes", Qunbound);
-	  }
+	HANDLE hprocess = get_nt_process_handle_only_first_time (process);
+	if (hprocess != INVALID_HANDLE_VALUE
+	    && !add_waitable_handle (hprocess))
+	  goto err;
       }
   }
-#endif
+
+  return;
+
+ err:
+  if (hev != INVALID_HANDLE_VALUE)
+    remove_waitable_handle (hev);
+  if (herr != INVALID_HANDLE_VALUE)
+    remove_waitable_handle (herr);
+  invalid_operation ("Too many active processes", wrap_process (process));
+#endif /* CYGWIN */
 }
 
 static void
-emacs_mswindows_unselect_process (Lisp_Process *process)
+emacs_mswindows_unselect_process (Lisp_Process *process, int doin, int doerr)
 {
-  /* Process handle is removed in the event loop as soon
-     as it is signaled, so don't bother here about it */
-  HANDLE hev = get_process_input_waitable (process);
-  remove_waitable_handle (hev);
+#ifdef CYGWIN
+  int infd, errfd;
+
+  event_stream_unixoid_unselect_process (process, doin, doerr, &infd, &errfd);
+#else
+  if (doin)
+    {
+      /* Process handle is removed in the event loop as soon
+	 as it is signaled, so don't bother here about it */
+      HANDLE hev = get_process_input_waitable (process);
+      remove_waitable_handle (hev);
+    }
+  if (doerr)
+    {
+      /* Process handle is removed in the event loop as soon
+	 as it is signaled, so don't bother here about it */
+      HANDLE herr = get_process_stderr_waitable (process);
+      if (herr != INVALID_HANDLE_VALUE)
+        remove_waitable_handle (herr);
+    }
+#endif /* CYGWIN */
 }
-#endif /* HAVE_MSG_SELECT */
 
 static void
 emacs_mswindows_select_console (struct console *con)
 {
-#ifdef HAVE_MSG_SELECT
+#ifdef CYGWIN
   if (CONSOLE_MSWINDOWS_P (con))
     return; /* mswindows consoles are automatically selected */
 
@@ -4161,7 +4248,7 @@
 static void
 emacs_mswindows_unselect_console (struct console *con)
 {
-#ifdef HAVE_MSG_SELECT
+#ifdef CYGWIN
   if (CONSOLE_MSWINDOWS_P (con))
     return; /* mswindows consoles are automatically selected */
 
@@ -4209,107 +4296,95 @@
     }
 }
 
-USID
-emacs_mswindows_create_stream_pair (void *inhandle, void *outhandle,
-				    Lisp_Object *instream,
-				    Lisp_Object *outstream,
-				    int flags)
+static void
+emacs_mswindows_create_io_streams (void *inhandle, void *outhandle,
+				   void *errhandle, Lisp_Object *instream,
+				   Lisp_Object *outstream,
+				   Lisp_Object *errstream,
+				   USID *in_usid,
+				   USID *err_usid,
+				   int flags)
 {
+#ifdef CYGWIN
+  event_stream_unixoid_create_io_streams (inhandle, outhandle,
+					  errhandle, instream,
+					  outstream, errstream,
+					  in_usid, err_usid, flags);
+#else
   /* Handles for streams */
-  HANDLE hin, hout;
+  HANDLE hin, hout, herr;
   /* fds. These just stored along with the streams, and are closed in
      delete stream pair method, because we need to handle fake unices
      here. */
-  int fdi, fdo;
-
-  /* Decode inhandle and outhandle. Their meaning depends on
+  int fdi, fdo, fde;
+
+  /* Decode inhandle, outhandle, errhandle. Their meaning depends on
      the process implementation being used. */
-#if defined (HAVE_WIN32_PROCESSES)
-  /* We're passed in Windows handles. That's what we like most... */
   hin = (HANDLE) inhandle;
   hout = (HANDLE) outhandle;
-  fdi = fdo = -1;
-#elif defined (HAVE_UNIX_PROCESSES)
-  /* We are passed UNIX fds. This must be Cygwin.
-     Fetch os handles */
-  hin = inhandle >= 0 ? (HANDLE)get_osfhandle ((int)inhandle) : INVALID_HANDLE_VALUE;
-  hout = outhandle >= 0 ? (HANDLE)get_osfhandle ((int)outhandle) : INVALID_HANDLE_VALUE;
-  fdi=(int)inhandle;
-  fdo=(int)outhandle;
-#else
-#error "So, WHICH kind of processes do you want?"
-#endif
+  if (errhandle == (void *) -1)
+    herr = INVALID_HANDLE_VALUE;
+  else
+    herr = (HANDLE) errhandle;
+  fdi = fdo = fde = -1;
 
   *instream = (hin == INVALID_HANDLE_VALUE
 	       ? Qnil
-#if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
 	       : flags & STREAM_NETWORK_CONNECTION
-	       ? make_winsock_input_stream ((SOCKET)hin, fdi)
-#endif
+	       ? make_winsock_input_stream ((SOCKET) hin, fdi)
 	       : make_ntpipe_input_stream (hin, fdi));
 
-#ifdef HAVE_WIN32_PROCESSES
+  *errstream = (herr == INVALID_HANDLE_VALUE
+		? Qnil
+		: make_ntpipe_input_stream (herr, fde));
+
   *outstream = (hout == INVALID_HANDLE_VALUE
 		? Qnil
-#if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
 		: flags & STREAM_NETWORK_CONNECTION
 		? make_winsock_output_stream ((SOCKET)hout, fdo)
-#endif
 		: make_ntpipe_output_stream (hout, fdo));
-#elif defined (HAVE_UNIX_PROCESSES)
-  *outstream = (fdo >= 0
-		? make_filedesc_output_stream (fdo, 0, -1, LSTR_BLOCKED_OK)
-		: Qnil);
-
-#if defined(HAVE_UNIX_PROCESSES)
-  /* FLAGS is process->pty_flag for UNIX_PROCESSES */
-  if ((flags & STREAM_PTY_FLUSHING) && fdo >= 0)
-    {
-      Intbyte eof_char = get_eof_char (fdo);
-      int pty_max_bytes = get_pty_max_bytes (fdo);
-      filedesc_stream_set_pty_flushing (XLSTREAM(*outstream), pty_max_bytes, eof_char);
-    }
-#endif
-#endif
-
-  return (NILP (*instream)
-	  ? USID_ERROR
-#if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
-	  : flags & STREAM_NETWORK_CONNECTION
-	  ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream)))
-#endif
-	  : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (*instream))));
+
+  *in_usid =
+    (NILP (*instream)
+     ? USID_ERROR
+     : flags & STREAM_NETWORK_CONNECTION
+     ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (*instream)))
+     : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM
+							 (*instream))));
+
+  *err_usid =
+    (NILP (*errstream)
+     ? USID_DONTHASH
+     : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM
+							 (*errstream))));
+#endif /* CYGWIN */
 }
 
-USID
-emacs_mswindows_delete_stream_pair (Lisp_Object instream,
-				    Lisp_Object outstream)
+static void
+emacs_mswindows_delete_io_streams (Lisp_Object instream,
+				   Lisp_Object outstream,
+				   Lisp_Object errstream,
+				   USID *in_usid,
+				   USID *err_usid)
 {
-  /* Oh nothing special here for Win32 at all */
-#if defined (HAVE_UNIX_PROCESSES)
-  int in = (NILP(instream)
-	    ? -1
-#if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
-	    : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
-	    ? get_winsock_stream_param (XLSTREAM (instream))
-#endif
-	    : get_ntpipe_input_stream_param (XLSTREAM (instream)));
-  int out = (NILP(outstream) ? -1
-	     : filedesc_stream_fd (XLSTREAM (outstream)));
-
-  if (in >= 0)
-    retry_close (in);
-  if (out != in && out >= 0)
-    retry_close (out);
-#endif
-
-  return (NILP (instream)
-	  ? USID_DONTHASH
-#if defined(HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
-	  : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
-	  ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream)))
-#endif
-	  : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM (instream))));
+#ifdef CYGWIN
+  event_stream_unixoid_delete_io_streams (instream, outstream, errstream,
+					  in_usid, err_usid);
+#else
+  *in_usid =
+    (NILP (instream)
+     ? USID_DONTHASH
+     : LSTREAM_TYPE_P (XLSTREAM (instream), winsock)
+     ? HANDLE_TO_USID (get_winsock_stream_waitable (XLSTREAM (instream)))
+     : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM
+							 (instream))));
+
+  *err_usid =
+    (NILP (errstream)
+     ? USID_DONTHASH
+     : HANDLE_TO_USID (get_ntpipe_input_stream_waitable (XLSTREAM
+							 (errstream))));
+#endif /* CYGWIN */
 }
 
 static int
@@ -4321,17 +4396,19 @@
 #ifndef HAVE_X_WINDOWS
 /* This is called from GC when a process object is about to be freed.
    If we've still got pointers to it in this file, we're gonna lose hard.
- */
+*/
+void debug_process_finalization (Lisp_Process *p);
 void
 debug_process_finalization (Lisp_Process *p)
 {
 #if 0 /* #### */
-  Lisp_Object instr, outstr;
-
-  get_process_streams (p, &instr, &outstr);
+  Lisp_Object instr, outstr, errstr;
+
+  get_process_streams (p, &instr, &outstr, &errstr);
   /* if it still has fds, then it hasn't been killed yet. */
   assert (NILP (instr));
   assert (NILP (outstr));
+  assert (NILP (errstr));
 
   /* #### More checks here */
 #endif
@@ -4679,19 +4756,10 @@
   mswindows_event_stream->quit_p_cb		= emacs_mswindows_quit_p;
   mswindows_event_stream->select_console_cb 	= emacs_mswindows_select_console;
   mswindows_event_stream->unselect_console_cb	= emacs_mswindows_unselect_console;
-#ifdef HAVE_MSG_SELECT
-  mswindows_event_stream->select_process_cb 	=
-    (void (*)(Lisp_Process *)) event_stream_unixoid_select_process;
-  mswindows_event_stream->unselect_process_cb	=
-    (void (*)(Lisp_Process *)) event_stream_unixoid_unselect_process;
-  mswindows_event_stream->create_stream_pair_cb = event_stream_unixoid_create_stream_pair;
-  mswindows_event_stream->delete_stream_pair_cb = event_stream_unixoid_delete_stream_pair;
-#else
   mswindows_event_stream->select_process_cb 	= emacs_mswindows_select_process;
   mswindows_event_stream->unselect_process_cb	= emacs_mswindows_unselect_process;
-  mswindows_event_stream->create_stream_pair_cb = emacs_mswindows_create_stream_pair;
-  mswindows_event_stream->delete_stream_pair_cb = emacs_mswindows_delete_stream_pair;
-#endif
+  mswindows_event_stream->create_io_streams_cb = emacs_mswindows_create_io_streams;
+  mswindows_event_stream->delete_io_streams_cb = emacs_mswindows_delete_io_streams;
   mswindows_event_stream->current_event_timestamp_cb =
     emacs_mswindows_current_event_timestamp;
 }
@@ -4711,9 +4779,7 @@
   mswindows_s_dispatch_event_queue_tail = Qnil;
   dump_add_root_object (&mswindows_s_dispatch_event_queue_tail);
 
-  mswindows_error_caught_in_modal_loop = Qnil;
-  staticpro (&mswindows_error_caught_in_modal_loop);
-
+  mswindows_error_caught_in_modal_loop = 0;
 
 #ifdef DEBUG_XEMACS
   DEFVAR_INT ("debug-mswindows-events", &debug_mswindows_events /*
@@ -4792,9 +4858,9 @@
 void
 lstream_type_create_mswindows_selectable (void)
 {
+#ifndef CYGWIN
   init_slurp_stream ();
   init_shove_stream ();
-#if defined (HAVE_SOCKETS) && !defined (HAVE_MSG_SELECT)
   init_winsock_stream ();
 #endif
 }
@@ -4802,7 +4868,7 @@
 void
 init_event_mswindows_late (void)
 {
-#ifdef HAVE_MSG_SELECT
+#ifdef CYGWIN
   windows_fd = retry_open ("/dev/windows", O_RDONLY | O_NONBLOCK, 0);
   assert (windows_fd >= 0);
   FD_SET (windows_fd, &input_wait_mask);