From e5ef75596ecbc63891853d4cb10aa17be9b8c0ab Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Sun, 7 Jan 2007 03:02:28 +0000 Subject: [PATCH] Handle GIOChannels for file descriptors connected to the console 2007-01-07 Tor Lillqvist * 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 | 8 +++ glib/giowin32.c | 170 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 143 insertions(+), 35 deletions(-) diff --git a/ChangeLog b/ChangeLog index 103e08add..5fbcd3c67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2007-01-07 Tor Lillqvist + + * 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 * tests/bit-test.c (builtin_bit_nth_lsf1), (builtin_bit_nth_lsf2), diff --git a/glib/giowin32.c b/glib/giowin32.c index 53549687c..8632209dc 100644 --- a/glib/giowin32.c +++ b/glib/giowin32.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -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; @@ -1328,6 +1355,44 @@ g_io_win32_fd_create_watch (GIOChannel *channel, return source; } +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, @@ -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 = @@ -1729,6 +1777,33 @@ g_io_win32_fd_get_flags (GIOChannel *channel) return 0; } +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) { @@ -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;