Handle GIOChannels for file descriptors connected to the console

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
This commit is contained in:
Tor Lillqvist 2007-01-07 03:02:28 +00:00 committed by Tor Lillqvist
parent 0def1ee468
commit e5ef75596e
2 changed files with 143 additions and 35 deletions

View File

@ -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),

View 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;
@ -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;