diff src/event-msw.c @ 282:c42ec1d1cded r21-0b39

Import from CVS: tag r21-0b39
author cvs
date Mon, 13 Aug 2007 10:33:18 +0200
parents 7df0dd720c89
children 558f606b08ae
line wrap: on
line diff
--- a/src/event-msw.c	Mon Aug 13 10:32:23 2007 +0200
+++ b/src/event-msw.c	Mon Aug 13 10:33:18 2007 +0200
@@ -44,6 +44,7 @@
 #endif
 
 #include "device.h"
+#include "dragdrop.h"
 #include "events.h"
 #include "frame.h"
 #include "lstream.h"
@@ -74,11 +75,6 @@
 /* Timer ID used for button2 emulation */
 #define BUTTON_2_TIMER_ID 1
 
-/* Drag and drop event data types (subset of types in offix-types.h) */
-#define DndFile		2
-#define	DndFiles	3
-#define	DndText		4
-
 extern Lisp_Object 
 mswindows_get_toolbar_button_text (struct frame* f, int command_id);
 extern Lisp_Object
@@ -445,8 +441,6 @@
 /*                Pipe outstream - writes process input                 */
 /************************************************************************/
 
-#define MAX_FLUSH_TIME 500
-
 #define NTPIPE_SHOVE_STREAM_DATA(stream) \
   LSTREAM_TYPE_DATA (stream, ntpipe_shove)
 
@@ -606,6 +600,232 @@
 }
 
 /************************************************************************/
+/*                         Winsock I/O stream                           */
+/************************************************************************/
+#if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
+
+#define WINSOCK_READ_BUFFER_SIZE 1024
+
+struct winsock_stream
+{
+  LPARAM user_data;		/* Any user data stored in the stream object */
+  SOCKET s;			/* Socket handle (which is a Win32 handle)   */
+  OVERLAPPED ov;		/* Overlapped I/O structure		     */
+  void* buffer;			/* Buffer. Allocated for input stream only   */
+  unsigned int bufsize;		/* Number of bytes last read		     */
+  unsigned int bufpos;		/* Psition in buffer for next fetch	     */
+  unsigned int error_p :1;	/* I/O Error seen			     */
+  unsigned int eof_p :1;	/* EOF Error seen			     */
+  unsigned int pending_p :1;	/* There is a pending I/O operation	     */
+  unsigned int blocking_p :1;	/* Last write attempt would block	     */
+};
+
+#define WINSOCK_STREAM_DATA(stream) LSTREAM_TYPE_DATA (stream, winsock)
+
+DEFINE_LSTREAM_IMPLEMENTATION ("winsock", lstream_winsock,
+			       sizeof (struct winsock_stream));
+
+static void
+winsock_initiate_read (struct winsock_stream *str)
+{
+  ResetEvent (str->ov.hEvent);
+  str->bufpos = 0;
+
+  if (!ReadFile ((HANDLE)str->s, str->buffer, WINSOCK_READ_BUFFER_SIZE,
+		 &str->bufsize, &str->ov))
+    {
+      if (GetLastError () == ERROR_IO_PENDING)
+	str->pending_p = 1;
+      else if (GetLastError () == ERROR_HANDLE_EOF)
+	str->eof_p = 1;
+      else
+	str->error_p = 1;
+    }
+  else if (str->bufsize == 0)
+    str->eof_p = 1;
+}
+
+static int
+winsock_reader (Lstream *stream, unsigned char *data, size_t size)
+{
+  struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
+
+  /* If the current operation is not yet complete, there's nothing to
+     give back */
+  if (str->pending_p)
+    {
+      if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT)
+	{
+	  errno = EAGAIN;
+	  return -1;
+	}
+      else
+	{
+	  if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &str->bufsize, TRUE))
+	    {
+	      if (GetLastError() == ERROR_HANDLE_EOF)
+		str->bufsize = 0;
+	      else
+		str->error_p = 1;
+	    }
+	  if (str->bufsize == 0)
+	    str->eof_p = 1;
+	  str->pending_p = 0;
+	}
+    }
+
+  if (str->eof_p)
+    return 0;
+  if (str->error_p)
+    return -1;
+  
+  /* Return as much of buffer as we have */
+  size = min (size, (size_t) (str->bufsize - str->bufpos));
+  memcpy (data, (void*)((BYTE*)str->buffer + str->bufpos), size);
+  str->bufpos += size;
+
+  /* Read more if buffer is exhausted */
+  if (str->bufsize == str->bufpos)
+    winsock_initiate_read (str);
+
+  return size;
+}
+
+static int
+winsock_writer (Lstream *stream, CONST unsigned char *data, size_t size)
+{
+  struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
+
+  if (str->pending_p)
+    {
+      if (WaitForSingleObject (str->ov.hEvent, 0) == WAIT_TIMEOUT)
+	{
+	  str->blocking_p = 1;
+	  return -1;
+	}
+      else
+	{
+	  DWORD dw_unused;
+	  if (!GetOverlappedResult ((HANDLE)str->s, &str->ov, &dw_unused, TRUE))
+	    str->error_p = 1;
+	  str->pending_p = 0;
+	}
+    }
+
+  str->blocking_p = 0;
+
+  if (str->error_p)
+    return -1;
+
+  if (size == 0)
+    return 0;
+  
+  {
+    ResetEvent (str->ov.hEvent);
+    
+    if (WriteFile ((HANDLE)str->s, data, size, NULL, &str->ov)
+	|| GetLastError() == ERROR_IO_PENDING)
+      str->pending_p = 1;
+    else
+      str->error_p = 1;
+  }
+
+  return str->error_p ? -1 : size;
+}
+
+static int
+winsock_closer (Lstream *lstr)
+{
+  struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
+
+  if (lstr->flags & LSTREAM_FL_READ)
+    shutdown (str->s, 0);
+  else
+    shutdown (str->s, 1);
+
+  CloseHandle ((HANDLE)str->s);
+  if (str->pending_p)
+    WaitForSingleObject (str->ov.hEvent, INFINITE);
+
+  if (lstr->flags & LSTREAM_FL_READ)
+    xfree (str->buffer);
+
+  CloseHandle (str->ov.hEvent);
+  return 0;
+}
+
+static int
+winsock_was_blocked_p (Lstream *stream)
+{
+  struct winsock_stream *str = WINSOCK_STREAM_DATA (stream);
+  return str->blocking_p;
+}
+
+static Lisp_Object
+make_winsock_stream_1 (SOCKET s, LPARAM param, CONST char *mode)
+{
+  Lisp_Object obj;
+  Lstream *lstr = Lstream_new (lstream_winsock, mode);
+  struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
+
+  str->s = s;
+  str->blocking_p = 0;
+  str->error_p = 0;
+  str->eof_p = 0;
+  str->pending_p = 0;
+  str->user_data = param;
+
+  xzero (str->ov);
+  str->ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
+
+  if (lstr->flags & LSTREAM_FL_READ)
+    {
+      str->buffer = xmalloc (WINSOCK_READ_BUFFER_SIZE);
+      winsock_initiate_read (str);
+    }
+
+  lstr->flags |= LSTREAM_FL_CLOSE_AT_DISKSAVE;
+  XSETLSTREAM (obj, lstr);
+  return obj;
+}
+
+static Lisp_Object
+make_winsock_input_stream (SOCKET s, LPARAM param)
+{
+  return make_winsock_stream_1 (s, param, "r");
+}
+
+static Lisp_Object
+make_winsock_output_stream (SOCKET s, LPARAM param)
+{
+  return make_winsock_stream_1 (s, param, "w");
+}
+
+static HANDLE
+get_winsock_stream_waitable (Lstream *lstr)
+{
+  struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
+  return str->ov.hEvent;
+}
+
+static LPARAM
+get_winsock_stream_param (Lstream *lstr)
+{
+  struct winsock_stream *str = WINSOCK_STREAM_DATA (lstr);
+  return str->user_data;
+}
+
+static void
+init_winsock_stream (void)
+{
+  LSTREAM_HAS_METHOD (winsock, reader);
+  LSTREAM_HAS_METHOD (winsock, writer);
+  LSTREAM_HAS_METHOD (winsock, closer);
+  LSTREAM_HAS_METHOD (winsock, was_blocked_p);
+}
+#endif /* defined (HAVE_SOCKETS) */
+
+/************************************************************************/
 /*                     Dispatch queue management                        */
 /************************************************************************/
 
@@ -614,8 +834,7 @@
 {
   return (sevt->event_type == key_press_event
 	  || sevt->event_type == button_press_event
-	  || sevt->event_type == button_release_event
-	  || sevt->event_type == dnd_drop_event);
+	  || sevt->event_type == button_release_event);
 }
 
 /* 
@@ -1222,11 +1441,10 @@
 	{
 	  DWORD len = DdeGetData (hdata, NULL, 0, 0);
 	  char *cmd = alloca (len+1);
-#ifdef __CYGWIN32__
-	  char *cmd_1;
-#endif
 	  char *end;
-          Lisp_Object l_dndlist;
+	  char *filename;
+	  struct gcpro gcpro1, gcpro2;
+          Lisp_Object l_dndlist = Qnil;
 	  Lisp_Object emacs_event = Fmake_event (Qnil, Qnil);
 	  struct Lisp_Event *event = XEVENT (emacs_event);
 
@@ -1234,7 +1452,8 @@
 	  cmd[len] = '\0';
 	  DdeFreeDataHandle (hdata);
 
-	  /* Check syntax & that it's an [Open("foo")] command */
+	  /* Check syntax & that it's an [Open("foo")] command, which we
+	   * treat like a file drop */
 	  /* #### Ought to be generalised and accept some other commands */
 	  if (*cmd == '[')
 	    cmd++;
@@ -1258,27 +1477,37 @@
 	    end++;
 	  if (*end)
 	    return DDE_FNOTPROCESSED;
+
 #ifdef __CYGWIN32__
-	  CYGWIN_CONV_PATH(cmd,cmd_1);
-	  cmd = cmd_1;
+	  filename = alloca (cygwin32_win32_to_posix_path_list_buf_size (cmd) + 5);
+	  strcpy (filename, "file:");
+	  cygwin32_win32_to_posix_path_list (cmd, filename+5);
+#else
+	  dostounix_filename (cmd);
+	  filename = alloca (strlen (cmd)+6);
+	  strcpy (filename, "file:");
+	  strcat (filename, cmd);
 #endif
-	  l_dndlist = make_ext_string (cmd, strlen(cmd), FORMAT_FILENAME);
+	  GCPRO2 (emacs_event, l_dndlist);
+	  l_dndlist = make_string (filename, strlen (filename));
 
 	  event->channel = Qnil;
 	  event->timestamp = GetTickCount();
-	  event->event_type = dnd_drop_event;
-	  event->event.dnd_drop.button = 0;
-	  event->event.dnd_drop.modifiers = 0;
-	  event->event.dnd_drop.x = -1;
-	  event->event.dnd_drop.y = -1;
-	  event->event.dnd_drop.data = Fcons (make_int (DndFile),
-					      Fcons (l_dndlist, Qnil));
+	  event->event_type = misc_user_event;
+	  event->event.misc.button = 1;
+	  event->event.misc.modifiers = 0;
+	  event->event.misc.x = -1;
+	  event->event.misc.y = -1;
+	  event->event.misc.function = Qdragdrop_drop_dispatch;
+	  event->event.misc.object = Fcons (Qdragdrop_URL,
+					    Fcons (l_dndlist, Qnil));
 	  mswindows_enqueue_dispatch_event (emacs_event);
-
+	  UNGCPRO;
 	  return (HDDEDATA) DDE_FACK;
 	}
       DdeFreeDataHandle (hdata); 
       return (HDDEDATA) DDE_FNOTPROCESSED;
+
     default: 
       return (HDDEDATA) NULL; 
     } 
@@ -1834,62 +2063,63 @@
     {
       UINT filecount, i, len;
       POINT point;
-      char filename[MAX_PATH];
+      char* filename;
 #ifdef __CYGWIN32__
       char* fname;
 #endif
-      Lisp_Object l_type, l_dndlist = Qnil, l_item;
+      Lisp_Object l_dndlist = Qnil, l_item = Qnil;
+      struct gcpro gcpro1, gcpro2, gcpro3;
 
       emacs_event = Fmake_event (Qnil, Qnil);
       event = XEVENT(emacs_event);
 
+      GCPRO3 (emacs_event, l_dndlist, l_item);
+
       if (!DragQueryPoint ((HANDLE) wParam, &point))
 	point.x = point.y = -1;		/* outside client area */
 
-      filecount = DragQueryFile ((HANDLE) wParam, -1, NULL, 0);
-      if (filecount == 1)
+      event->event_type = misc_user_event;
+      event->channel = mswindows_find_frame(hwnd);
+      event->timestamp = GetMessageTime();
+      event->event.misc.button = 1;		/* #### Should try harder */
+      event->event.misc.modifiers = mswindows_modifier_state (NULL, 0);
+      event->event.misc.x = point.x;
+      event->event.misc.y = point.y;
+      event->event.misc.function = Qdragdrop_drop_dispatch;
+
+      filecount = DragQueryFile ((HANDLE) wParam, 0xffffffff, NULL, 0);
+      for (i=0; i<filecount; i++)
 	{
-      	  l_type = make_int (DndFile);
-	  len = DragQueryFile ((HANDLE) wParam, 0, filename, MAX_PATH);
+	  len = DragQueryFile ((HANDLE) wParam, i, NULL, 0);
+	  /* The URLs that we make here aren't correct according to section
+	   * 3.10 of rfc1738 because they're missing the //<host>/ part and
+	   * because they may contain reserved characters. But that's OK. */
 #ifdef __CYGWIN32__
-	  CYGWIN_CONV_PATH(filename, fname);
-	  len=strlen(fname);
-	  l_dndlist = make_ext_string (fname, len, FORMAT_FILENAME);
+	  fname = (char *)xmalloc (len+1);
+	  DragQueryFile ((HANDLE) wParam, i, fname, len+1);
+	  filename = xmalloc (cygwin32_win32_to_posix_path_list_buf_size (fname) + 5);
+	  strcpy (filename, "file:");
+	  cygwin32_win32_to_posix_path_list (fname, filename+5);
+	  xfree (fname);
 #else
-	  l_dndlist = make_ext_string (filename, len, FORMAT_FILENAME);
+	  filename = (char *)xmalloc (len+6);
+	  strcpy (filename, "file:");
+	  DragQueryFile ((HANDLE) wParam, i, filename+5, len+1);
+	  dostounix_filename (filename+5);
 #endif
-	}
-      else
-	{
-	  l_type = make_int (DndFiles);	  
-	  for (i=0; i<filecount; i++)
-	    {
-  	      len = DragQueryFile ((HANDLE) wParam, i, filename, MAX_PATH);
-#ifdef __CYGWIN32__
-	      CYGWIN_CONV_PATH(filename, fname);
-	      len=strlen(fname);
-	      l_item = make_ext_string (fname, len, FORMAT_FILENAME);
-#else
-	      l_item = make_ext_string (filename, len, FORMAT_FILENAME);
-#endif
-	      l_dndlist = Fcons (l_item, l_dndlist);	/* reverse order */
-	    }
+	  l_item = make_string (filename, strlen (filename));
+	  l_dndlist = Fcons (l_item, l_dndlist);
+	  xfree (filename);
 	}
       DragFinish ((HANDLE) wParam);
-      
-      event->channel = mswindows_find_frame(hwnd);
-      event->timestamp = GetMessageTime();
-      event->event_type = dnd_drop_event;
-      event->event.dnd_drop.button = 1;		/* #### Should try harder */
-      event->event.dnd_drop.modifiers = mswindows_modifier_state (NULL, 0);
-      event->event.dnd_drop.x = point.x;
-      event->event.dnd_drop.y = point.y;
-      event->event.dnd_drop.data = Fcons (l_type, Fcons (l_dndlist, Qnil));
 
+      event->event.misc.object = Fcons (Qdragdrop_URL, l_dndlist);
       mswindows_enqueue_dispatch_event (emacs_event);
+      UNGCPRO;
     }
   break;
 
+
   defproc:
   default:
     return DefWindowProc (hwnd, message, wParam, lParam);
@@ -2213,10 +2443,17 @@
 static HANDLE
 get_process_input_waitable (struct Lisp_Process *process)
 {
-  Lisp_Object instr, outstr;
+  Lisp_Object instr, outstr, p;
+  XSETPROCESS (p, process);
   get_process_streams (process, &instr, &outstr);
   assert (!NILP (instr));
-  return get_ntpipe_input_stream_waitable (XLSTREAM (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 void
@@ -2229,11 +2466,16 @@
 
 #ifdef HAVE_WIN32_PROCESSES
   {
-    HANDLE hprocess = get_nt_process_handle (process);
-    if (!add_waitable_handle (hprocess))
+    Lisp_Object p;
+    XSETPROCESS (p, process);
+    if (!network_connection_p (p))
       {
-	remove_waitable_handle (hev);
-	error ("Too many active processes");
+	HANDLE hprocess = get_nt_process_handle (process);
+	if (!add_waitable_handle (hprocess))
+	  {
+	    remove_waitable_handle (hev);
+	    error ("Too many active processes");
+	  }
       }
   }
 #endif
@@ -2321,14 +2563,22 @@
 #error "So, WHICH kind of processes do you want?"
 #endif
 
-  *instream = (hin != INVALID_HANDLE_VALUE
-	       ? make_ntpipe_input_stream (hin, fdi)
-	       : Qnil);
+  *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_ntpipe_input_stream (hin, fdi));
 
 #ifdef HAVE_WIN32_PROCESSES
-  *outstream = (hout != INVALID_HANDLE_VALUE
-		? make_ntpipe_output_stream (hout, fdo)
-		: Qnil);
+  *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)
@@ -2336,7 +2586,7 @@
 
 #if defined(HAVE_UNIX_PROCESSES) && defined(HAVE_PTYS)
   /* FLAGS is process->pty_flag for UNIX_PROCESSES */
-  if (flags && fdo >= 0)
+  if ((flags & STREAM_PTY_FLUSHING) && fdo >= 0)
     {
       Bufbyte eof_char = get_eof_char (fdo);
       int pty_max_bytes = get_pty_max_bytes (fdo);
@@ -2345,7 +2595,12 @@
 #endif
 #endif
 
-  return (NILP (*instream) ? USID_ERROR
+  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))));
 }
 
@@ -2355,7 +2610,12 @@
 {
   /* Oh nothing special here for Win32 at all */
 #if defined (HAVE_UNIX_PROCESSES)
-  int in = (NILP(instream) ? -1
+  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)));
@@ -2366,11 +2626,15 @@
     close (out);
 #endif
 
-  return (NILP (instream) ? USID_DONTHASH
+  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))));
 }
 
-
 #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.
@@ -2491,6 +2755,9 @@
 {
   init_slurp_stream ();
   init_shove_stream ();
+#if defined (HAVE_SOCKETS) && !defined(HAVE_MSG_SELECT)
+  init_winsock_stream ();
+#endif
 }
 
 void