Initial revision
This commit is contained in:
parent
dd3240b7be
commit
6cdfb6e60d
3 changed files with 1844 additions and 0 deletions
605
src/w32console.c
Normal file
605
src/w32console.c
Normal 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
459
src/w32inevt.c
Normal 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
780
src/w32proc.c
Normal 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;
|
||||
}
|
Loading…
Add table
Reference in a new issue