Mercurial > emacs
changeset 9907:2e5a14f7c44e
Initial revision
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Mon, 14 Nov 1994 01:32:24 +0000 |
parents | c062f5bc946a |
children | 32d20ec5ed51 |
files | src/w32console.c src/w32inevt.c src/w32proc.c |
diffstat | 3 files changed, 1844 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/w32console.c Mon Nov 14 01:32:24 1994 +0000 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/w32inevt.c Mon Nov 14 01:32:24 1994 +0000 @@ -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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/w32proc.c Mon Nov 14 01:32:24 1994 +0000 @@ -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; +}