Initial revision

This commit is contained in:
Richard M. Stallman 1994-11-14 01:32:24 +00:00
parent dd3240b7be
commit 6cdfb6e60d
3 changed files with 1844 additions and 0 deletions

605
src/w32console.c Normal file
View file

@ -0,0 +1,605 @@
/* Terminal hooks for Windows NT port of GNU Emacs.
Copyright (C) 1992 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any later
version.
GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with GNU Emacs; see the file COPYING. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Tim Fleehart (apollo@online.com) 1-17-92
Geoff Voelker (voelker@cs.washington.edu) 9-12-93
*/
#include <stdlib.h>
#include <stdio.h>
#include "config.h"
#include <windows.h>
#include "lisp.h"
#include "frame.h"
#include "disptab.h"
#include "termhooks.h"
#include "ntinevt.h"
/* frrom window.c */
extern Lisp_Object Frecenter ();
/* from keyboard.c */
extern int detect_input_pending ();
/* from sysdep.c */
extern int read_input_pending ();
extern FRAME_PTR updating_frame;
extern int meta_key;
static void move_cursor (int row, int col);
static void clear_to_end (void);
static void clear_frame (void);
static void clear_end_of_line (int);
static void ins_del_lines (int vpos, int n);
static void change_line_highlight (int, int, int);
static void reassert_line_highlight (int, int);
static void insert_glyphs (GLYPH *start, int len);
static void write_glyphs (GLYPH *string, int len);
static void delete_glyphs (int n);
static void ring_bell (void);
static void reset_terminal_modes (void);
static void set_terminal_modes (void);
static void set_terminal_window (int size);
static void update_begin (FRAME_PTR f);
static void update_end (FRAME_PTR f);
static void reset_kbd (void);
static void unset_kbd (void);
static int hl_mode (int new_highlight);
void
DebPrint ()
{
}
/* Init hook called in init_keyboard. */
void (*keyboard_init_hook)(void) = reset_kbd;
COORD cursor_coords;
HANDLE prev_screen, cur_screen;
UCHAR char_attr, char_attr_normal, char_attr_reverse;
HANDLE keyboard_handle;
/* Setting this as the ctrl handler prevents emacs from being killed when
* someone hits ^C in a 'suspended' session (child shell). */
BOOL
ctrl_c_handler (unsigned long type)
{
return (type == CTRL_C_EVENT) ? TRUE : FALSE;
}
/* If we're updating a frame, use it as the current frame
Otherwise, use the selected frame. */
#define PICK_FRAME() (updating_frame ? updating_frame : selected_frame)
/* Move the cursor to (row, col). */
void
move_cursor (int row, int col)
{
cursor_coords.X = col;
cursor_coords.Y = row;
if (updating_frame == NULL)
{
SetConsoleCursorPosition (cur_screen, cursor_coords);
}
}
/* Clear from cursor to end of screen. */
void
clear_to_end (void)
{
FRAME_PTR f = PICK_FRAME ();
clear_end_of_line (FRAME_WIDTH (f) - 1);
ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
}
/* Clear the frame. */
void
clear_frame (void)
{
SMALL_RECT scroll;
COORD dest;
CHAR_INFO fill;
FRAME_PTR f = PICK_FRAME ();
hl_mode (0);
scroll.Top = 0;
scroll.Bottom = FRAME_HEIGHT (f) - 1;
scroll.Left = 0;
scroll.Right = FRAME_WIDTH (f) - 1;
dest.Y = FRAME_HEIGHT (f);
dest.X = 0;
fill.Char.AsciiChar = 0x20;
fill.Attributes = char_attr;
ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
move_cursor (0, 0);
}
static GLYPH glyph_base[256];
static BOOL ceol_initialized = FALSE;
/* Clear from Cursor to end (what's "standout marker"?). */
void
clear_end_of_line (int end)
{
if (!ceol_initialized)
{
int i;
for (i = 0; i < 256; i++)
{
glyph_base[i] = SPACEGLYPH; /* empty space */
}
ceol_initialized = TRUE;
}
write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
}
/* Insert n lines at vpos. if n is negative delete -n lines. */
void
ins_del_lines (int vpos, int n)
{
int i, nb, save_highlight;
SMALL_RECT scroll;
COORD dest;
CHAR_INFO fill;
FRAME_PTR f = PICK_FRAME ();
if (n < 0)
{
scroll.Top = vpos - n;
scroll.Bottom = FRAME_HEIGHT (f);
dest.Y = vpos;
}
else
{
scroll.Top = vpos;
scroll.Bottom = FRAME_HEIGHT (f) - n;
dest.Y = vpos + n;
}
scroll.Left = 0;
scroll.Right = FRAME_WIDTH (f);
dest.X = 0;
save_highlight = hl_mode (0);
fill.Char.AsciiChar = 0x20;
fill.Attributes = char_attr;
ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
/* Here we have to deal with a win32 console flake: If the scroll
region looks like abc and we scroll c to a and fill with d we get
cbd... if we scroll block c one line at a time to a, we get cdd...
Emacs expects cdd consistently... So we have to deal with that
here... (this also occurs scrolling the same way in the other
direction. */
if (n > 0)
{
if (scroll.Bottom < dest.Y)
{
for (i = scroll.Bottom; i < dest.Y; i++)
{
move_cursor (i, 0);
clear_end_of_line (FRAME_WIDTH (f));
}
}
}
else
{
nb = dest.Y + (scroll.Bottom - scroll.Top) + 1;
if (nb < scroll.Top)
{
for (i = nb; i < scroll.Top; i++)
{
move_cursor (i, 0);
clear_end_of_line (FRAME_WIDTH (f));
}
}
}
cursor_coords.X = 0;
cursor_coords.Y = vpos;
hl_mode (save_highlight);
}
/* Changes attribute to use when drawing characters to control. */
static int
hl_mode (int new_highlight)
{
static int highlight = 0;
int old_highlight;
old_highlight = highlight;
highlight = (new_highlight != 0);
if (highlight)
{
char_attr = char_attr_reverse;
}
else
{
char_attr = char_attr_normal;
}
return old_highlight;
}
/* Call this when about to modify line at position VPOS and change whether it
is highlighted. */
void
change_line_highlight (int new_highlight, int vpos, int first_unused_hpos)
{
hl_mode (new_highlight);
move_cursor (vpos, 0);
clear_end_of_line (first_unused_hpos);
}
/* External interface to control of standout mode. Call this when about to
* modify line at position VPOS and not change whether it is highlighted. */
void
reassert_line_highlight (int highlight, int vpos)
{
hl_mode (highlight);
vpos; /* pedantic compiler silencer */
}
#undef LEFT
#undef RIGHT
#define LEFT 1
#define RIGHT 0
void
scroll_line (int dist, int direction)
{
/* The idea here is to implement a horizontal scroll in one line to
implement delete and half of insert. */
SMALL_RECT scroll;
COORD dest;
CHAR_INFO fill;
FRAME_PTR f = PICK_FRAME ();
scroll.Top = cursor_coords.Y;
scroll.Bottom = cursor_coords.Y;
if (direction == LEFT)
{
scroll.Left = cursor_coords.X + dist;
scroll.Right = FRAME_WIDTH (f) - 1;
}
else
{
scroll.Left = cursor_coords.X;
scroll.Right = FRAME_WIDTH (f) - dist - 1;
}
dest.X = cursor_coords.X;
dest.Y = cursor_coords.Y;
fill.Char.AsciiChar = 0x20;
fill.Attributes = char_attr;
ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
}
/* If start is zero insert blanks instead of a string at start ?. */
void
insert_glyphs (register GLYPH *start, register int len)
{
scroll_line (len, RIGHT);
/* Move len chars to the right starting at cursor_coords, fill with blanks */
if (start)
{
/* Print the first len characters of start, cursor_coords.X adjusted
by write_glyphs. */
write_glyphs (start, len);
}
else
{
clear_end_of_line (cursor_coords.X + len);
}
}
void
write_glyphs (register GLYPH *string, register int len)
{
register unsigned int glyph_len = GLYPH_TABLE_LENGTH;
Lisp_Object *glyph_table = GLYPH_TABLE_BASE;
FRAME_PTR f = PICK_FRAME ();
register char *ptr;
GLYPH glyph;
WORD *attrs;
char *chars;
int i;
attrs = alloca (len * sizeof (*attrs));
chars = alloca (len * sizeof (*chars));
if (attrs == NULL || chars == NULL)
{
printf ("alloca failed in write_glyphs\n");
return;
}
/* We have to deal with the glyph indirection...go over the glyph
buffer and extract the characters. */
ptr = chars;
while (--len >= 0)
{
glyph = *string++;
if (glyph > glyph_len)
{
*ptr++ = glyph & 0xFF;
continue;
}
GLYPH_FOLLOW_ALIASES (glyph_table, glyph_len, glyph);
if (GLYPH_FACE (fixfix, glyph) != 0)
printf ("Glyph face is %d\n", GLYPH_FACE (fixfix, glyph));
if (GLYPH_SIMPLE_P (glyph_table, glyph_len, glyph))
{
*ptr++ = glyph & 0xFF;
continue;
}
for (i = 0; i < GLYPH_LENGTH (glyph_table, glyph); i++)
{
*ptr++ = (GLYPH_STRING (glyph_table, glyph))[i];
}
}
/* Number of characters we have in the buffer. */
len = ptr-chars;
/* Fill in the attributes for these characters. */
memset (attrs, char_attr, len*sizeof (*attrs));
/* Write the attributes. */
if (!WriteConsoleOutputAttribute (cur_screen, attrs, len, cursor_coords, &i))
{
printf ("Failed writing console attributes.\n");
fflush (stdout);
}
/* Write the characters. */
if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
{
printf ("Failed writing console characters.\n");
fflush (stdout);
}
cursor_coords.X += len;
move_cursor (cursor_coords.Y, cursor_coords.X);
}
void
delete_glyphs (int n)
{
/* delete chars means scroll chars from cursor_coords.X + n to
cursor_coords.X, anything beyond the edge of the screen should
come out empty... */
scroll_line (n, LEFT);
}
void
ring_bell (void)
{
Beep (666, 100);
}
/* Reset to the original console mode but don't get rid of our console
For suspending emacs. */
void
restore_console (void)
{
unset_kbd ();
SetConsoleActiveScreenBuffer (prev_screen);
}
/* Put our console back up, for ending a suspended session. */
void
take_console (void)
{
reset_kbd ();
SetConsoleActiveScreenBuffer (cur_screen);
}
void
reset_terminal_modes (void)
{
unset_kbd ();
SetConsoleActiveScreenBuffer (prev_screen);
CloseHandle (cur_screen);
cur_screen = NULL;
}
void
set_terminal_modes (void)
{
CONSOLE_CURSOR_INFO cci;
if (cur_screen == NULL)
{
reset_kbd ();
cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
0, NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL);
if (cur_screen == INVALID_HANDLE_VALUE)
{
printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
printf ("LastError = 0x%lx\n", GetLastError ());
fflush (stdout);
exit (0);
}
SetConsoleActiveScreenBuffer (cur_screen);
/* make cursor big and visible */
cci.dwSize = 100;
cci.bVisible = TRUE;
(void) SetConsoleCursorInfo (cur_screen, &cci);
}
}
/* hmmm... perhaps these let us bracket screen changes so that we can flush
clumps rather than one-character-at-a-time...
we'll start with not moving the cursor while an update is in progress. */
void
update_begin (FRAME_PTR f)
{
}
void
update_end (FRAME_PTR f)
{
SetConsoleCursorPosition (cur_screen, cursor_coords);
}
void
set_terminal_window (int size)
{
}
void
unset_kbd (void)
{
SetConsoleMode (keyboard_handle, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT);
}
void
reset_kbd (void)
{
keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
}
typedef int (*term_hook) ();
void
initialize_win_nt_display (void)
{
CONSOLE_SCREEN_BUFFER_INFO info;
cursor_to_hook = (term_hook) move_cursor;
raw_cursor_to_hook = (term_hook) move_cursor;
clear_to_end_hook = (term_hook) clear_to_end;
clear_frame_hook = (term_hook) clear_frame;
clear_end_of_line_hook = (term_hook) clear_end_of_line;
ins_del_lines_hook = (term_hook) ins_del_lines;
change_line_highlight_hook = (term_hook) change_line_highlight;
reassert_line_highlight_hook = (term_hook) reassert_line_highlight;
insert_glyphs_hook = (term_hook) insert_glyphs;
write_glyphs_hook = (term_hook) write_glyphs;
delete_glyphs_hook = (term_hook) delete_glyphs;
ring_bell_hook = (term_hook) ring_bell;
reset_terminal_modes_hook = (term_hook) reset_terminal_modes;
set_terminal_modes_hook = (term_hook) set_terminal_modes;
set_terminal_window_hook = (term_hook) set_terminal_window;
update_begin_hook = (term_hook) update_begin;
update_end_hook = (term_hook) update_end;
read_socket_hook = win32_read_socket;
mouse_position_hook = win32_mouse_position;
prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
set_terminal_modes ();
GetConsoleScreenBufferInfo (cur_screen, &info);
meta_key = 1;
char_attr = info.wAttributes & 0xFF;
char_attr_normal = char_attr;
char_attr_reverse = ((char_attr & 0xf) << 4) + ((char_attr & 0xf0) >> 4);
FRAME_HEIGHT (selected_frame) = info.dwSize.Y; /* lines per page */
FRAME_WIDTH (selected_frame) = info.dwSize.X; /* characters per line */
move_cursor (0, 0);
clear_frame ();
}
DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
"Set screen colors.")
(foreground, background)
Lisp_Object foreground;
Lisp_Object background;
{
char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
char_attr_reverse = XFASTINT (background) + (XFASTINT (foreground) << 4);
Frecenter (Qnil);
return Qt;
}
DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
"Set cursor size.")
(size)
Lisp_Object size;
{
CONSOLE_CURSOR_INFO cci;
cci.dwSize = XFASTINT (size);
cci.bVisible = TRUE;
(void) SetConsoleCursorInfo (cur_screen, &cci);
return Qt;
}
void
pixel_to_glyph_coords (FRAME_PTR f, int pix_x, int pix_y, int *x, int *y,
void *bounds, int noclip)
{
*x = pix_x;
*y = pix_y;
}
void
glyph_to_pixel_coords (FRAME_PTR f, int x, int y, int *pix_x, int *pix_y)
{
*pix_x = x;
*pix_y = y;
}
_VOID_
syms_of_ntterm ()
{
defsubr (&Sset_screen_color);
defsubr (&Sset_cursor_size);
}

459
src/w32inevt.c Normal file
View file

@ -0,0 +1,459 @@
/* Input event support for Windows NT port of GNU Emacs.
Copyright (C) 1992, 1993 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any later
version.
GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with GNU Emacs; see the file COPYING. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Drew Bliss 01-Oct-93
Adapted from ntkbd.c by Tim Fleehart
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include "lisp.h"
#include "frame.h"
#include "blockinput.h"
#include "termhooks.h"
/* stdin, from ntterm */
extern HANDLE keyboard_handle;
/* Indicate mouse motion, from keyboard.c */
extern int mouse_moved;
/* Info for last mouse motion */
static COORD movement_pos;
static DWORD movement_time;
/* from keyboard.c */
extern void reinvoke_input_signal (void);
/* from dispnew.c */
extern int change_frame_size (FRAME_PTR, int, int, int, int);
/* Event queue */
#define EVENT_QUEUE_SIZE 50
static INPUT_RECORD event_queue[EVENT_QUEUE_SIZE];
static INPUT_RECORD *queue_ptr = event_queue, *queue_end = event_queue;
static int
fill_queue (BOOL block)
{
BOOL rc;
DWORD events_waiting;
if (queue_ptr < queue_end)
return queue_end-queue_ptr;
if (!block)
{
/* Check to see if there are some events to read before we try
because we can't block. */
if (!GetNumberOfConsoleInputEvents (keyboard_handle, &events_waiting))
return -1;
if (events_waiting == 0)
return 0;
}
rc = ReadConsoleInput (keyboard_handle, event_queue, EVENT_QUEUE_SIZE,
&events_waiting);
if (!rc)
return -1;
queue_ptr = event_queue;
queue_end = event_queue + events_waiting;
return (int) events_waiting;
}
/* In a generic, multi-frame world this should take a console handle
and return the frame for it
Right now, there's only one frame so return it. */
static FRAME_PTR
get_frame (void)
{
return selected_frame;
}
#ifdef MULTI_FRAME
#define SET_FRAME(o, f) XSET (o, Lisp_Frame, f)
#else
#define SET_FRAME(o, f) ((o) = Qnil)
#endif
/* Translate console modifiers to emacs modifiers. */
static int
nt_kbd_mods_to_emacs (DWORD mods)
{
return ((mods & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) ?
meta_modifier : 0) |
((mods & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) ?
ctrl_modifier : 0) |
((mods & (SHIFT_PRESSED | CAPSLOCK_ON)) ?
shift_modifier : 0);
}
/* Map virtual key codes into:
-1 - Ignore this key
-2 - ASCII char
Other - Map non-ASCII keys into X keysyms so that they are looked up
correctly in keyboard.c
Return, escape and tab are mapped to ASCII rather than coming back
as non-ASCII to be more compatible with old-style keyboard support. */
static int map_virt_key[256] =
{
-1,
-1, /* VK_LBUTTON */
-1, /* VK_RBUTTON */
0x69, /* VK_CANCEL */
-1, /* VK_MBUTTON */
-1, -1, -1,
8, /* VK_BACK */
-2, /* VK_TAB */
-1, -1,
11, /* VK_CLEAR */
-2, /* VK_RETURN */
-1, -1,
-1, /* VK_SHIFT */
-1, /* VK_CONTROL */
-1, /* VK_MENU */
0x13, /* VK_PAUSE */
-1, /* VK_CAPITAL */
-1, -1, -1, -1, -1, -1,
-2, /* VK_ESCAPE */
-1, -1, -1, -1,
-2, /* VK_SPACE */
0x55, /* VK_PRIOR */
0x56, /* VK_NEXT */
0x57, /* VK_END */
0x50, /* VK_HOME */
0x51, /* VK_LEFT */
0x52, /* VK_UP */
0x53, /* VK_RIGHT */
0x54, /* VK_DOWN */
0x60, /* VK_SELECT */
0x61, /* VK_PRINT */
0x62, /* VK_EXECUTE */
-1, /* VK_SNAPSHOT */
0x63, /* VK_INSERT */
0xff, /* VK_DELETE */
0x6a, /* VK_HELP */
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0 - 9 */
-1, -1, -1, -1, -1, -1, -1,
-2, -2, -2, -2, -2, -2, -2, -2, /* A - Z */
-2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2,
-2, -2,
-1, -1, -1, -1, -1,
0xb0, /* VK_NUMPAD0 */
0xb1, /* VK_NUMPAD1 */
0xb2, /* VK_NUMPAD2 */
0xb3, /* VK_NUMPAD3 */
0xb4, /* VK_NUMPAD4 */
0xb5, /* VK_NUMPAD5 */
0xb6, /* VK_NUMPAD6 */
0xb7, /* VK_NUMPAD7 */
0xb8, /* VK_NUMPAD8 */
0xb9, /* VK_NUMPAD9 */
0xaa, /* VK_MULTIPLY */
0xab, /* VK_ADD */
0xac, /* VK_SEPARATOR */
0xad, /* VK_SUBTRACT */
0xae, /* VK_DECIMAL */
0xaf, /* VK_DIVIDE */
0xbe, /* VK_F1 */
0xbf, /* VK_F2 */
0xc0, /* VK_F3 */
0xc1, /* VK_F4 */
0xc2, /* VK_F5 */
0xc3, /* VK_F6 */
0xc4, /* VK_F7 */
0xc5, /* VK_F8 */
0xc6, /* VK_F9 */
0xc7, /* VK_F10 */
0xc8, /* VK_F11 */
0xc9, /* VK_F12 */
0xca, /* VK_F13 */
0xcb, /* VK_F14 */
0xcc, /* VK_F15 */
0xcd, /* VK_F16 */
0xce, /* VK_F17 */
0xcf, /* VK_F18 */
0xd0, /* VK_F19 */
0xd1, /* VK_F20 */
0xd2, /* VK_F21 */
0xd3, /* VK_F22 */
0xd4, /* VK_F23 */
0xd5, /* VK_F24 */
-1, -1, -1, -1, -1, -1, -1, -1,
0x7f, /* VK_NUMLOCK */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x9f */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xaf */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xb9 */
-2, /* ; */
-2, /* = */
-2, /* , */
-2, /* \ */
-2, /* . */
-2, /* / */
-2, /* ` */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xcf */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xda */
-2, /* [ */
-2, /* - */
-2, /* ] */
-2, /* ' */
-1, /* 0xdf */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xef */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 0xff */
};
static int
key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev)
{
int map;
/* Skip key-up events. */
if (event->bKeyDown == FALSE)
return 0;
if (event->wVirtualKeyCode > 0xff)
{
printf ("Unknown key code %d\n", event->wVirtualKeyCode);
return 0;
}
/* BUGBUG - Ignores the repeat count
It's questionable whether we want to obey the repeat count anyway
since keys usually aren't repeated unless key events back up in
the queue. If they're backing up then we don't generally want
to honor them later since that leads to significant slop in
cursor motion when the system is under heavy load. */
map = map_virt_key[event->wVirtualKeyCode];
if (map == -1)
{
return 0;
}
else if (map == -2)
{
/* ASCII */
emacs_ev->kind = ascii_keystroke;
XSET (emacs_ev->code, Lisp_Int, event->uChar.AsciiChar);
}
else
{
/* non-ASCII */
emacs_ev->kind = non_ascii_keystroke;
/*
* make_lispy_event () now requires non-ascii codes to have
* the full X keysym values (2nd byte is 0xff). add it on.
*/
map |= 0xff00;
XSET (emacs_ev->code, Lisp_Int, map);
}
SET_FRAME (emacs_ev->frame_or_window, get_frame ());
emacs_ev->modifiers = nt_kbd_mods_to_emacs (event->dwControlKeyState);
emacs_ev->timestamp = GetTickCount ();
return 1;
}
/* Mouse position hook. */
void
win32_mouse_position (FRAME_PTR *f,
Lisp_Object *bar_window,
enum scroll_bar_part *part,
Lisp_Object *x,
Lisp_Object *y,
unsigned long *time)
{
BLOCK_INPUT;
*f = get_frame ();
*bar_window = Qnil;
*part = 0;
mouse_moved = 0;
*x = movement_pos.X;
*y = movement_pos.Y;
*time = movement_time;
UNBLOCK_INPUT;
}
/* Remember mouse motion and notify emacs. */
static void
mouse_moved_to (int x, int y)
{
/* If we're in the same place, ignore it */
if (x != movement_pos.X || y != movement_pos.Y)
{
mouse_moved = 1;
movement_pos.X = x;
movement_pos.Y = y;
movement_time = GetTickCount ();
}
}
/* Consoles return button bits in a strange order:
least significant - Leftmost button
next - Rightmost button
next - Leftmost+1
next - Leftmost+2...
Assume emacs likes three button mice, so
Left == 0
Middle == 1
Right == 2
Others increase from there. */
static int emacs_button_translation[NUM_MOUSE_BUTTONS] =
{
0, 2, 1, 3, 4,
};
static int
do_mouse_event (MOUSE_EVENT_RECORD *event,
struct input_event *emacs_ev)
{
static DWORD button_state = 0;
DWORD but_change, mask;
int i;
if (event->dwEventFlags == MOUSE_MOVED)
{
/* For movement events we just note that the mouse has moved
so that emacs will generate drag events. */
mouse_moved_to (event->dwMousePosition.X, event->dwMousePosition.Y);
return 0;
}
/* It looks like the console code sends us a mouse event with
dwButtonState == 0 when a window is activated. Ignore this case. */
if (event->dwButtonState == button_state)
return 0;
emacs_ev->kind = mouse_click;
/* Find out what button has changed state since the last button event. */
but_change = button_state ^ event->dwButtonState;
mask = 1;
for (i = 0; i < NUM_MOUSE_BUTTONS; i++, mask <<= 1)
if (but_change & mask)
{
XSET (emacs_ev->code, Lisp_Int, emacs_button_translation[i]);
break;
}
/* If the changed button is out of emacs' range (highly unlikely)
ignore this event. */
if (i == NUM_MOUSE_BUTTONS)
return 0;
button_state = event->dwButtonState;
emacs_ev->timestamp = GetTickCount ();
emacs_ev->modifiers = nt_kbd_mods_to_emacs (event->dwControlKeyState) |
((event->dwButtonState & mask) ? down_modifier : up_modifier);
XFASTINT (emacs_ev->x) = event->dwMousePosition.X;
XFASTINT (emacs_ev->y) = event->dwMousePosition.Y;
SET_FRAME (emacs_ev->frame_or_window, get_frame ());
return 1;
}
static void
resize_event (WINDOW_BUFFER_SIZE_RECORD *event)
{
FRAME_PTR f = get_frame ();
change_frame_size (f, event->dwSize.Y, event->dwSize.X, 0, 1);
SET_FRAME_GARBAGED (f);
}
int
win32_read_socket (int sd, struct input_event *bufp, int numchars,
int waitp, int expected)
{
BOOL no_events = TRUE;
int nev, ret = 0, add;
if (interrupt_input_blocked)
{
interrupt_input_pending = 1;
return -1;
}
interrupt_input_pending = 0;
BLOCK_INPUT;
for (;;)
{
nev = fill_queue (waitp != 0);
if (nev <= 0)
{
/* If nev == -1, there was some kind of error
If nev == 0 then waitp must be zero and no events were available
so return. */
UNBLOCK_INPUT;
return nev;
}
while (nev > 0 && numchars > 0)
{
switch (queue_ptr->EventType)
{
case KEY_EVENT:
add = key_event (&queue_ptr->Event.KeyEvent, bufp);
bufp += add;
ret += add;
numchars -= add;
break;
case MOUSE_EVENT:
add = do_mouse_event (&queue_ptr->Event.MouseEvent, bufp);
bufp += add;
ret += add;
numchars -= add;
break;
case WINDOW_BUFFER_SIZE_EVENT:
resize_event (&queue_ptr->Event.WindowBufferSizeEvent);
break;
case MENU_EVENT:
case FOCUS_EVENT:
/* Internal event types, ignored. */
break;
}
queue_ptr++;
nev--;
}
if (ret > 0 || expected == 0)
break;
}
UNBLOCK_INPUT;
return ret;
}

780
src/w32proc.c Normal file
View file

@ -0,0 +1,780 @@
/* Process support for Windows NT port of GNU EMACS.
Copyright (C) 1992 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any later
version.
GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with GNU Emacs; see the file COPYING. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
Drew Bliss Oct 14, 1993
Adapted from alarm.c by Tim Fleehart
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <io.h>
#include <signal.h>
#include "config.h"
#include <windows.h>
#include "lisp.h"
#include "nt.h"
#include "systime.h"
/* #define FULL_DEBUG */
typedef void (_CALLBACK_ *signal_handler)(int);
/* Defined in process.h which conflicts with the local copy */
#define _P_NOWAIT 1
typedef struct _child_process
{
int fd;
HANDLE char_avail;
HANDLE char_consumed;
char chr;
BOOL status;
HANDLE process;
DWORD pid;
HANDLE thrd;
} child_process;
#define MAX_CHILDREN MAXDESC
#ifdef EMACSDEBUG
void _CRTAPI1
_DebPrint (char *fmt, ...)
{
char buf[256];
va_list args;
va_start (args, fmt);
vsprintf (buf, fmt, args);
va_end (args);
OutputDebugString (buf);
}
#endif
/* Child process management list. */
static int child_proc_count = 0;
static child_process child_procs[MAX_CHILDREN];
static child_process *dead_child = NULL;
#define CHILD_ACTIVE(cp) ((cp)->process != NULL)
#define DEACTIVATE_CHILD(cp) ((cp)->process = NULL)
/* Signal handlers...SIG_DFL == 0 so this is initialized correctly. */
static signal_handler sig_handlers[NSIG];
/* Fake signal implementation to record the SIGCHLD handler. */
signal_handler
win32_signal (int sig, signal_handler handler)
{
signal_handler old;
if (sig != SIGCHLD)
{
errno = EINVAL;
return SIG_ERR;
}
old = sig_handlers[sig];
sig_handlers[sig] = handler;
return old;
}
/* Find an unused process slot. */
static child_process *
new_child (void)
{
child_process *cp;
if (child_proc_count == MAX_CHILDREN)
return NULL;
for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
if (!CHILD_ACTIVE (cp))
return cp;
return &child_procs[child_proc_count++];
}
/* Find a child by pid. */
static child_process *
find_child_pid (DWORD pid)
{
child_process *cp;
for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
if (CHILD_ACTIVE (cp) && pid == cp->pid)
return cp;
return NULL;
}
/* Find a child by fd. */
static child_process *
find_child_fd (int fd)
{
child_process *cp;
for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
if (CHILD_ACTIVE (cp) && fd == cp->fd)
return cp;
return NULL;
}
/* Thread proc for child process reader threads
The threads just sit in a loop waiting for input
When they detect input, they signal the char_avail input to
wake up the select emulator
When the select emulator processes their input, it pulses
char_consumed so that the reader thread goes back to reading. */
DWORD WINAPI
reader_thread (void *arg)
{
child_process *cp;
/* Our identity */
cp = (child_process *)arg;
/* We have to wait for the go-ahead before we can start */
if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
return 0;
/* If something went wrong, quit */
if (!cp->status)
return 0;
for (;;)
{
/* Use read to get CRLF translation */
if (read (cp->fd, &cp->chr, sizeof (char)) == sizeof (char))
{
cp->status = TRUE;
}
else
{
#ifdef FULL_DEBUG
DebPrint (("reader_thread.read failed with %lu for fd %ld\n",
GetLastError (), cp->fd));
#endif
cp->status = FALSE;
}
if (!SetEvent (cp->char_avail))
{
DebPrint (("reader_thread.SetEvent failed with %lu for fd %ld\n",
GetLastError (), cp->fd));
break;
}
/* If the read died, the child has died so let the thread die */
if (!cp->status)
break;
/* Wait until our input is acknowledged before reading again */
if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
{
DebPrint (("reader_thread.WaitForSingleObject failed with "
"%lu for fd %ld\n", GetLastError (), cp->fd));
break;
}
}
return 0;
}
static BOOL
create_child (char *exe, char *cmdline, char *env,
PROCESS_INFORMATION *info)
{
child_process *cp;
DWORD id;
STARTUPINFO start;
SECURITY_ATTRIBUTES sec_attrs;
SECURITY_DESCRIPTOR sec_desc;
cp = new_child ();
if (cp == NULL)
goto EH_Fail;
cp->fd = -1;
cp->char_avail = CreateEvent (NULL, FALSE, FALSE, NULL);
if (cp->char_avail == NULL)
goto EH_Fail;
cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL);
if (cp->char_consumed == NULL)
goto EH_char_avail;
cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id);
if (cp->thrd == NULL)
goto EH_char_consumed;
memset (&start, 0, sizeof (start));
start.cb = sizeof (start);
/* Explicitly specify no security */
if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
goto EH_thrd;
if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
goto EH_thrd;
sec_attrs.nLength = sizeof (sec_attrs);
sec_attrs.lpSecurityDescriptor = &sec_desc;
sec_attrs.bInheritHandle = FALSE;
if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE,
CREATE_NEW_PROCESS_GROUP, env, NULL,
&start, info))
goto EH_thrd;
cp->process = info->hProcess;
cp->pid = info->dwProcessId;
return TRUE;
EH_thrd:
id = GetLastError ();
cp->status = FALSE;
SetEvent (cp->char_consumed);
EH_char_consumed:
CloseHandle (cp->char_consumed);
EH_char_avail:
CloseHandle (cp->char_avail);
EH_Fail:
return FALSE;
}
/* create_child doesn't know what emacs' file handle will be for waiting
on output from the child, so we need to make this additional call
to register the handle with the process
This way the select emulator knows how to match file handles with
entries in child_procs. */
void
register_child (int pid, int fd)
{
child_process *cp;
cp = find_child_pid (pid);
if (cp == NULL)
{
DebPrint (("register_child unable to find pid %lu\n", pid));
return;
}
#ifdef FULL_DEBUG
DebPrint (("register_child registered fd %d with pid %lu\n", fd, pid));
#endif
cp->fd = fd;
cp->status = TRUE;
/* Tell the reader thread to start */
if (!SetEvent (cp->char_consumed))
{
DebPrint (("register_child.SetEvent failed with %lu for fd %ld\n",
GetLastError (), cp->fd));
}
}
/* When a process dies its pipe will break so the reader thread will
signal failure to the select emulator.
The select emulator then calls this routine to clean up.
Since the thread signaled failure we can assume it is exiting. */
static void
remove_child (child_process *cp)
{
/* Reap the thread */
if (WaitForSingleObject (cp->thrd, INFINITE) != WAIT_OBJECT_0)
{
DebPrint (("remove_child.WaitForSingleObject (thread) failed "
"with %lu for fd %ld\n", GetLastError (), cp->fd));
}
CloseHandle (cp->thrd);
CloseHandle (cp->char_consumed);
CloseHandle (cp->char_avail);
/* Reap the process */
if (WaitForSingleObject (cp->process, INFINITE) != WAIT_OBJECT_0)
{
DebPrint (("remove_child.WaitForSingleObject (process) failed "
"with %lu for fd %ld\n", GetLastError (), cp->fd));
}
CloseHandle (cp->process);
DEACTIVATE_CHILD (cp);
}
/* Wait for any of our existing child processes to die
When it does, close its handle
Return the pid and fill in the status if non-NULL. */
int
win32_wait (int *status)
{
DWORD active, retval;
int nh;
child_process *cp, *cps[MAX_CHILDREN];
HANDLE wait_hnd[MAX_CHILDREN];
nh = 0;
if (dead_child != NULL)
{
/* We want to wait for a specific child */
wait_hnd[nh] = dead_child->process;
cps[nh] = dead_child;
nh++;
}
else
{
for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
if (CHILD_ACTIVE (cp))
{
wait_hnd[nh] = cp->process;
cps[nh] = cp;
nh++;
}
}
if (nh == 0)
{
/* Nothing to wait on, so fail */
errno = ECHILD;
return -1;
}
active = WaitForMultipleObjects (nh, wait_hnd, FALSE, INFINITE);
if (active == WAIT_FAILED)
{
errno = EBADF;
return -1;
}
else if (active == WAIT_TIMEOUT)
{
/* Should never happen */
errno = EINVAL;
return -1;
}
else if (active >= WAIT_OBJECT_0 &&
active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
{
active -= WAIT_OBJECT_0;
}
else if (active >= WAIT_ABANDONED_0 &&
active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
{
active -= WAIT_ABANDONED_0;
}
if (!GetExitCodeProcess (wait_hnd[active], &retval))
{
DebPrint (("Wait.GetExitCodeProcess failed with %lu\n",
GetLastError ()));
retval = 1;
}
if (retval == STILL_ACTIVE)
{
/* Should never happen */
DebPrint (("Wait.WaitForMultipleObjects returned an active process\n"));
errno = EINVAL;
return -1;
}
cp = cps[active];
#ifdef FULL_DEBUG
DebPrint (("Wait signaled with process pid %d\n", cp->pid));
#endif
if (status)
{
/* In process.c the default WAITTYPE is defined.
Since we can't determine anything about why a process died
we can only return a code that looks like WIFEXITED */
*status = (retval & 0x7fffff) << 8;
}
return cp->pid;
}
/* We pass our process ID to our children by setting up an environment
variable in their environment. */
char ppid_env_var_buffer[64];
/* When a new child process is created we need to register it in our list,
so intercept spawn requests. */
int
win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
{
char *cmdline, *env, *parg, **targ;
int arglen;
PROCESS_INFORMATION pi;
if (child_proc_count == MAX_CHILDREN)
{
errno = EAGAIN;
return -1;
}
/* We don't care about the other modes */
if (mode != _P_NOWAIT)
{
errno = EINVAL;
return -1;
}
/* we have to do some conjuring here to put argv and envp into the
form CreateProcess wants... argv needs to be a space separated/null
terminated list of parameters, and envp is a null
separated/double-null terminated list of parameters.
Since I have no idea how large argv and envp are likely to be
we figure out list lengths on the fly and allocate them. */
/* do argv... */
arglen = 0;
targ = argv;
while (*targ)
{
arglen += strlen (*targ++) + 1;
}
cmdline = malloc (arglen);
if (cmdline == NULL)
{
errno = ENOMEM;
goto EH_Fail;
}
targ = argv;
parg = cmdline;
while (*targ)
{
strcpy (parg, *targ);
parg += strlen (*targ++);
*parg++ = ' ';
}
*--parg = '\0';
/* and envp... */
arglen = 1;
targ = envp;
while (*targ)
{
arglen += strlen (*targ++) + 1;
}
sprintf (ppid_env_var_buffer, "__PARENT_PROCESS_ID=%d",
GetCurrentProcessId ());
arglen += strlen (ppid_env_var_buffer) + 1;
env = malloc (arglen);
if (env == NULL)
{
errno = ENOMEM;
goto EH_cmdline;
}
targ = envp;
parg = env;
while (*targ)
{
strcpy (parg, *targ);
parg += strlen (*targ++);
*parg++ = '\0';
}
strcpy (parg, ppid_env_var_buffer);
parg += strlen (ppid_env_var_buffer);
*parg++ = '\0';
*parg = '\0';
/* Now create the process. */
if (!create_child (cmdname, cmdline, env, &pi))
{
errno = ENOEXEC;
goto EH_env;
}
return pi.dwProcessId;
EH_env:
free (env);
EH_cmdline:
free (cmdline);
EH_Fail:
return -1;
}
/* Emulate the select call
Wait for available input on any of the given rfds, or timeout if
a timeout is given and no input is detected
wfds and efds are not supported and must be NULL. */
/* From ntterm.c */
extern HANDLE keyboard_handle;
/* From process.c */
extern int proc_buffered_char[];
int
select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
EMACS_TIME *timeout)
{
SELECT_TYPE orfds;
DWORD timeout_ms;
int i, nh, nr;
DWORD active;
child_process *cp, *cps[MAX_CHILDREN];
HANDLE wait_hnd[MAX_CHILDREN];
/* If the descriptor sets are NULL but timeout isn't, then just Sleep. */
if (rfds == NULL && wfds == NULL && efds == NULL && timeout != NULL)
{
Sleep ((*timeout) * 1000);
return 0;
}
/* Otherwise, we only handle rfds, so fail otherwise. */
if (rfds == NULL || wfds != NULL || efds != NULL)
{
errno = EINVAL;
return -1;
}
orfds = *rfds;
FD_ZERO (rfds);
nr = 0;
/* Build a list of handles to wait on. */
nh = 0;
for (i = 0; i < nfds; i++)
if (FD_ISSET (i, &orfds))
{
if (i == 0)
{
/* Handle stdin specially */
wait_hnd[nh] = keyboard_handle;
cps[nh] = NULL;
nh++;
/* Check for any emacs-generated input in the queue since
it won't be detected in the wait */
if (detect_input_pending ())
{
FD_SET (i, rfds);
nr++;
}
}
else
{
/* Child process input */
cp = find_child_fd (i);
if (cp)
{
#ifdef FULL_DEBUG
DebPrint (("select waiting on child %d fd %d\n",
cp-child_procs, i));
#endif
wait_hnd[nh] = cp->char_avail;
cps[nh] = cp;
nh++;
}
else
{
/* Unable to find something to wait on for this fd, fail */
DebPrint (("select unable to find child process "
"for fd %ld\n", i));
nh = 0;
break;
}
}
}
/* Nothing to look for, so we didn't find anything */
if (nh == 0)
{
Sleep ((*timeout) * 1000);
return 0;
}
/* Check for immediate return without waiting */
if (nr > 0)
return nr;
/*
Wait for input
If a child process dies while this is waiting, its pipe will break
so the reader thread will signal an error condition, thus, the wait
will wake up
*/
timeout_ms = timeout ? *timeout*1000 : INFINITE;
active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms);
if (active == WAIT_FAILED)
{
DebPrint (("select.WaitForMultipleObjects (%d, %lu) failed with %lu\n",
nh, timeout_ms, GetLastError ()));
/* Is there a better error? */
errno = EBADF;
return -1;
}
else if (active == WAIT_TIMEOUT)
{
return 0;
}
else if (active >= WAIT_OBJECT_0 &&
active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
{
active -= WAIT_OBJECT_0;
}
else if (active >= WAIT_ABANDONED_0 &&
active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
{
active -= WAIT_ABANDONED_0;
}
if (cps[active] == NULL)
{
/* Keyboard input available */
FD_SET (0, rfds);
nr++;
/* This shouldn't be necessary, but apparently just setting the input
fd is not good enough for emacs */
read_input_waiting ();
}
else
{
/* Child process */
cp = cps[active];
/* If status is FALSE the read failed so don't report input */
if (cp->status)
{
FD_SET (cp->fd, rfds);
proc_buffered_char[cp->fd] = cp->chr;
nr++;
}
else
{
/* The SIGCHLD handler will do a Wait so we know it won't
return until the process is dead
We force Wait to only wait for this process to avoid it
picking up other children that happen to be dead but that
we haven't noticed yet
SIG_DFL for SIGCHLD is ignore? */
if (sig_handlers[SIGCHLD] != SIG_DFL &&
sig_handlers[SIGCHLD] != SIG_IGN)
{
#ifdef FULL_DEBUG
DebPrint (("select calling SIGCHLD handler for pid %d\n",
cp->pid));
#endif
dead_child = cp;
sig_handlers[SIGCHLD](SIGCHLD);
dead_child = NULL;
}
/* Clean up the child process entry in the table */
remove_child (cp);
}
}
return nr;
}
/*
Substitute for certain kill () operations
*/
int
win32_kill_process (int pid, int sig)
{
child_process *cp;
/* Only handle signals that will result in the process dying */
if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP)
{
errno = EINVAL;
return -1;
}
cp = find_child_pid (pid);
if (cp == NULL)
{
DebPrint (("win32_kill_process didn't find a child with pid %lu\n", pid));
errno = ECHILD;
return -1;
}
if (sig == SIGINT)
{
/* Fake Ctrl-Break. */
if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid))
{
DebPrint (("win32_kill_process.GenerateConsoleCtrlEvent return %d "
"for pid %lu\n", GetLastError (), pid));
errno = EINVAL;
return -1;
}
}
else
{
/* Kill the process. On Win32 this doesn't kill child processes
so it doesn't work very well for shells which is why it's
not used in every case. */
if (!TerminateProcess (cp->process, 0xff))
{
DebPrint (("win32_kill_process.TerminateProcess returned %d "
"for pid %lu\n", GetLastError (), pid));
errno = EINVAL;
return -1;
}
}
return 0;
}
/* If the channel is a pipe this read might block since we don't
know how many characters are available, so check and read only
what's there
We also need to wake up the reader thread once we've read our data. */
int
read_child_output (int fd, char *buf, int max)
{
HANDLE h;
int to_read, nchars;
DWORD waiting;
child_process *cp;
h = (HANDLE)_get_osfhandle (fd);
if (GetFileType (h) == FILE_TYPE_PIPE)
{
PeekNamedPipe (h, NULL, 0, NULL, &waiting, NULL);
to_read = min (waiting, (DWORD)max);
}
else
to_read = max;
/* Use read to get CRLF translation */
nchars = read (fd, buf, to_read);
if (GetFileType (h) == FILE_TYPE_PIPE)
{
/* Wake up the reader thread
for this process */
cp = find_child_fd (fd);
if (cp)
{
if (!SetEvent (cp->char_consumed))
DebPrint (("read_child_output.SetEvent failed with "
"%lu for fd %ld\n", GetLastError (), fd));
}
else
DebPrint (("read_child_output couldn't find a child with fd %d\n",
fd));
}
return nchars;
}