Handle GIOChannels for file descriptors connected to the console
authorTor Lillqvist <tml@novell.com>
Sun, 7 Jan 2007 03:02:28 +0000 (03:02 +0000)
committerTor Lillqvist <tml@src.gnome.org>
Sun, 7 Jan 2007 03:02:28 +0000 (03:02 +0000)
2007-01-07  Tor Lillqvist  <tml@novell.com>

* glib/giowin32.c: Handle GIOChannels for file descriptors
connected to the console separately. This would typically be the
fd 0, 1, or 2 (if not redirected) in a console application. For
such fds we don't need a separate thread, as console HANDLEs are
waitable objects. (#359202, Michiel de Hoon)

svn path=/trunk/; revision=5222

ChangeLog
glib/giowin32.c

index 103e08a..5fbcd3c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2007-01-07  Tor Lillqvist  <tml@novell.com>
+
+       * glib/giowin32.c: Handle GIOChannels for file descriptors
+       connected to the console separately. This would typically be the
+       fd 0, 1, or 2 (if not redirected) in a console application. For
+       such fds we don't need a separate thread, as console HANDLEs are
+       waitable objects. (#359202, Michiel de Hoon)
+
 2007-01-04  Behdad Esfahbod  <behdad@gnome.org>
 
        * tests/bit-test.c (builtin_bit_nth_lsf1), (builtin_bit_nth_lsf2),
index 5354968..8632209 100644 (file)
@@ -36,6 +36,7 @@
 #include <stdlib.h>
 #include <winsock2.h>
 #include <windows.h>
+#include <conio.h>
 #include <fcntl.h>
 #include <io.h>
 #include <process.h>
@@ -55,9 +56,10 @@ typedef struct _GIOWin32Watch GIOWin32Watch;
 typedef enum {
   G_IO_WIN32_WINDOWS_MESSAGES, /* Windows messages */
   G_IO_WIN32_FILE_DESC,                /* Unix-like file descriptors from
-                                * _open() or _pipe(). Read with read().
+                                * _open() or _pipe(), except for console IO.
                                 * Have to create separate thread to read.
                                 */
+  G_IO_WIN32_CONSOLE,          /* Console IO (usually stdin, stdout, stderr) */
   G_IO_WIN32_SOCKET            /* Sockets. No separate thread */
 } GIOWin32ChannelType;
 
@@ -777,6 +779,7 @@ g_io_win32_prepare (GSource *source,
   switch (channel->type)
     {
     case G_IO_WIN32_WINDOWS_MESSAGES:
+    case G_IO_WIN32_CONSOLE:
       break;
 
     case G_IO_WIN32_FILE_DESC:
@@ -873,6 +876,29 @@ g_io_win32_check (GSource *source)
 
       return ((watch->pollfd.revents | buffer_condition) & watch->condition);
 
+    case G_IO_WIN32_CONSOLE:
+      if (watch->channel->is_writeable)
+       return TRUE;
+      else if (watch->channel->is_readable)
+        {
+         INPUT_RECORD buffer;
+         DWORD n;
+         if (PeekConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n) &&
+             n == 1)
+           {
+             /* _kbhit() does quite complex processing to find out
+              * whether at least one of the key events pending corresponds
+              * to a "real" character that can be read.
+              */
+             if (_kbhit ())
+               return TRUE;
+             
+             /* Discard all other kinds of events */
+             ReadConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n);
+           }
+        }
+      return FALSE;
+
     case G_IO_WIN32_SOCKET:
       if (channel->last_events & FD_WRITE)
        {
@@ -963,6 +989,7 @@ g_io_win32_finalize (GSource *source)
   switch (channel->type)
     {
     case G_IO_WIN32_WINDOWS_MESSAGES:
+    case G_IO_WIN32_CONSOLE:
       break;
 
     case G_IO_WIN32_FILE_DESC:
@@ -1111,11 +1138,11 @@ g_io_win32_msg_create_watch (GIOChannel   *channel,
 }
 
 static GIOStatus
-g_io_win32_fd_read (GIOChannel *channel,
-                   gchar      *buf,
-                   gsize       count,
-                   gsize      *bytes_read,
-                   GError    **err)
+g_io_win32_fd_and_console_read (GIOChannel *channel,
+                               gchar      *buf,
+                               gsize       count,
+                               gsize      *bytes_read,
+                               GError    **err)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
   gint result;
@@ -1158,11 +1185,11 @@ g_io_win32_fd_read (GIOChannel *channel,
 }
 
 static GIOStatus
-g_io_win32_fd_write (GIOChannel  *channel,
-                    const gchar *buf,
-                    gsize        count,
-                    gsize       *bytes_written,
-                    GError     **err)
+g_io_win32_fd_and_console_write (GIOChannel  *channel,
+                                const gchar *buf,
+                                gsize        count,
+                                gsize       *bytes_written,
+                                GError     **err)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
   gint result;
@@ -1329,6 +1356,44 @@ g_io_win32_fd_create_watch (GIOChannel    *channel,
 }
 
 static GIOStatus
+g_io_win32_console_close (GIOChannel *channel,
+                         GError    **err)
+{
+  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+  
+  if (close (win32_channel->fd) < 0)
+    {
+      g_set_error (err, G_IO_CHANNEL_ERROR,
+                  g_io_channel_error_from_errno (errno),
+                  g_strerror (errno));
+      return G_IO_STATUS_ERROR;
+    }
+
+  return G_IO_STATUS_NORMAL;
+}
+
+static GSource *
+g_io_win32_console_create_watch (GIOChannel    *channel,
+                                GIOCondition   condition)
+{
+  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+  GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
+  GIOWin32Watch *watch = (GIOWin32Watch *)source;
+
+  watch->channel = channel;
+  g_io_channel_ref (channel);
+  
+  watch->condition = condition;
+  
+  watch->pollfd.fd = (gint) _get_osfhandle (win32_channel->fd);
+  watch->pollfd.events = condition;
+  
+  g_source_add_poll (source, &watch->pollfd);
+
+  return source;
+}
+
+static GIOStatus
 g_io_win32_sock_read (GIOChannel *channel,
                      gchar      *buf,
                      gsize       count,
@@ -1682,23 +1747,6 @@ g_io_win32_fd_get_flags_internal (GIOChannel  *channel,
        (WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
       channel->is_seekable  = FALSE;
     }
-  else if (st->st_mode & _S_IFCHR)
-    {
-      /* XXX Seems there is no way to find out the readability of file
-       * handles to device files (consoles, mostly) without doing a
-       * blocking read. So punt, say it's readable.
-       */
-      channel->is_readable = TRUE;
-
-      channel->is_writeable =
-       (WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
-
-      /* XXX What about devices that actually *are* seekable? But
-       * those would probably not be handled using the C runtime
-       * anyway, but using Windows-specific code.
-       */
-      channel->is_seekable = FALSE;
-    }
   else
     {
       channel->is_readable =
@@ -1730,6 +1778,33 @@ g_io_win32_fd_get_flags (GIOChannel *channel)
 }
 
 static GIOFlags
+g_io_win32_console_get_flags_internal (GIOChannel  *channel)
+{
+  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
+  HANDLE handle = (HANDLE) _get_osfhandle (win32_channel->fd);
+  gchar c;
+  DWORD count;
+  INPUT_RECORD record;
+
+  channel->is_readable = PeekConsoleInput (handle, &record, 1, &count);
+  channel->is_writeable = WriteFile (handle, &c, 0, &count, NULL);
+  channel->is_seekable = FALSE;
+
+  return 0;
+}
+
+static GIOFlags
+g_io_win32_console_get_flags (GIOChannel *channel)
+{
+  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
+
+  g_return_val_if_fail (win32_channel != NULL, 0);
+  g_return_val_if_fail (win32_channel->type == G_IO_WIN32_CONSOLE, 0);
+
+  return g_io_win32_console_get_flags_internal (channel);
+}
+
+static GIOFlags
 g_io_win32_msg_get_flags (GIOChannel *channel)
 {
   return 0;
@@ -1795,8 +1870,8 @@ static GIOFuncs win32_channel_msg_funcs = {
 };
 
 static GIOFuncs win32_channel_fd_funcs = {
-  g_io_win32_fd_read,
-  g_io_win32_fd_write,
+  g_io_win32_fd_and_console_read,
+  g_io_win32_fd_and_console_write,
   g_io_win32_fd_seek,
   g_io_win32_fd_close,
   g_io_win32_fd_create_watch,
@@ -1805,6 +1880,17 @@ static GIOFuncs win32_channel_fd_funcs = {
   g_io_win32_fd_get_flags,
 };
 
+static GIOFuncs win32_channel_console_funcs = {
+  g_io_win32_fd_and_console_read,
+  g_io_win32_fd_and_console_write,
+  NULL,
+  g_io_win32_console_close,
+  g_io_win32_console_create_watch,
+  g_io_win32_free,
+  g_io_win32_unimpl_set_flags,
+  g_io_win32_console_get_flags,
+};
+
 static GIOFuncs win32_channel_sock_funcs = {
   g_io_win32_sock_read,
   g_io_win32_sock_write,
@@ -1851,13 +1937,23 @@ g_io_channel_win32_new_fd_internal (gint         fd,
 
   g_io_channel_init (channel);
   g_io_channel_win32_init (win32_channel);
-  if (win32_channel->debug)
-    g_print ("g_io_channel_win32_new_fd: %u\n", fd);
-  channel->funcs = &win32_channel_fd_funcs;
-  win32_channel->type = G_IO_WIN32_FILE_DESC;
+
   win32_channel->fd = fd;
 
-  g_io_win32_fd_get_flags_internal (channel, st);
+  if (win32_channel->debug)
+    g_print ("g_io_channel_win32_new_fd: %u\n", fd);
+  if (st->st_mode & _S_IFCHR) /* console */
+    {
+      channel->funcs = &win32_channel_console_funcs;
+      win32_channel->type = G_IO_WIN32_CONSOLE;
+      g_io_win32_console_get_flags_internal (channel);
+    }
+  else
+    {
+      channel->funcs = &win32_channel_fd_funcs;
+      win32_channel->type = G_IO_WIN32_FILE_DESC;
+      g_io_win32_fd_get_flags_internal (channel, st);
+    }
   
   return channel;
 }
@@ -1984,6 +2080,10 @@ g_io_channel_win32_make_pollfd (GIOChannel   *channel,
        }
       break;
 
+    case G_IO_WIN32_CONSOLE:
+      fd->fd = (gint) _get_osfhandle (win32_channel->fd);
+      break;
+
     case G_IO_WIN32_SOCKET:
       fd->fd = (int) WSACreateEvent ();
       break;