aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRichard M. Stallman1994-11-14 01:32:24 +0000
committerRichard M. Stallman1994-11-14 01:32:24 +0000
commit6cdfb6e60ddaa1486ae70374e13bb5deeb40c2f0 (patch)
treeef2f9081dde6068a5eaa0fbb284c5d85a1364d0a /src
parentdd3240b7beea10d052584206e477594351fde704 (diff)
downloademacs-6cdfb6e60ddaa1486ae70374e13bb5deeb40c2f0.tar.gz
emacs-6cdfb6e60ddaa1486ae70374e13bb5deeb40c2f0.zip
Initial revision
Diffstat (limited to 'src')
-rw-r--r--src/w32console.c605
-rw-r--r--src/w32inevt.c459
-rw-r--r--src/w32proc.c780
3 files changed, 1844 insertions, 0 deletions
diff --git a/src/w32console.c b/src/w32console.c
new file mode 100644
index 00000000000..53e8e2cbcda
--- /dev/null
+++ b/src/w32console.c
@@ -0,0 +1,605 @@
1/* Terminal hooks for Windows NT port of GNU Emacs.
2 Copyright (C) 1992 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU General Public License along
17 with GNU Emacs; see the file COPYING. If not, write to the Free Software
18 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 Tim Fleehart (apollo@online.com) 1-17-92
21 Geoff Voelker (voelker@cs.washington.edu) 9-12-93
22*/
23
24
25#include <stdlib.h>
26#include <stdio.h>
27
28#include "config.h"
29
30#include <windows.h>
31
32#include "lisp.h"
33#include "frame.h"
34#include "disptab.h"
35#include "termhooks.h"
36
37#include "ntinevt.h"
38
39/* frrom window.c */
40extern Lisp_Object Frecenter ();
41
42/* from keyboard.c */
43extern int detect_input_pending ();
44
45/* from sysdep.c */
46extern int read_input_pending ();
47
48extern FRAME_PTR updating_frame;
49extern int meta_key;
50
51static void move_cursor (int row, int col);
52static void clear_to_end (void);
53static void clear_frame (void);
54static void clear_end_of_line (int);
55static void ins_del_lines (int vpos, int n);
56static void change_line_highlight (int, int, int);
57static void reassert_line_highlight (int, int);
58static void insert_glyphs (GLYPH *start, int len);
59static void write_glyphs (GLYPH *string, int len);
60static void delete_glyphs (int n);
61static void ring_bell (void);
62static void reset_terminal_modes (void);
63static void set_terminal_modes (void);
64static void set_terminal_window (int size);
65static void update_begin (FRAME_PTR f);
66static void update_end (FRAME_PTR f);
67static void reset_kbd (void);
68static void unset_kbd (void);
69static int hl_mode (int new_highlight);
70
71void
72DebPrint ()
73{
74}
75
76/* Init hook called in init_keyboard. */
77void (*keyboard_init_hook)(void) = reset_kbd;
78
79COORD cursor_coords;
80HANDLE prev_screen, cur_screen;
81UCHAR char_attr, char_attr_normal, char_attr_reverse;
82HANDLE keyboard_handle;
83
84
85/* Setting this as the ctrl handler prevents emacs from being killed when
86 * someone hits ^C in a 'suspended' session (child shell). */
87BOOL
88ctrl_c_handler (unsigned long type)
89{
90 return (type == CTRL_C_EVENT) ? TRUE : FALSE;
91}
92
93/* If we're updating a frame, use it as the current frame
94 Otherwise, use the selected frame. */
95#define PICK_FRAME() (updating_frame ? updating_frame : selected_frame)
96
97/* Move the cursor to (row, col). */
98void
99move_cursor (int row, int col)
100{
101 cursor_coords.X = col;
102 cursor_coords.Y = row;
103
104 if (updating_frame == NULL)
105 {
106 SetConsoleCursorPosition (cur_screen, cursor_coords);
107 }
108}
109
110/* Clear from cursor to end of screen. */
111void
112clear_to_end (void)
113{
114 FRAME_PTR f = PICK_FRAME ();
115
116 clear_end_of_line (FRAME_WIDTH (f) - 1);
117 ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
118}
119
120/* Clear the frame. */
121void
122clear_frame (void)
123{
124 SMALL_RECT scroll;
125 COORD dest;
126 CHAR_INFO fill;
127 FRAME_PTR f = PICK_FRAME ();
128
129 hl_mode (0);
130
131 scroll.Top = 0;
132 scroll.Bottom = FRAME_HEIGHT (f) - 1;
133 scroll.Left = 0;
134 scroll.Right = FRAME_WIDTH (f) - 1;
135
136 dest.Y = FRAME_HEIGHT (f);
137 dest.X = 0;
138
139 fill.Char.AsciiChar = 0x20;
140 fill.Attributes = char_attr;
141
142 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
143 move_cursor (0, 0);
144}
145
146
147static GLYPH glyph_base[256];
148static BOOL ceol_initialized = FALSE;
149
150/* Clear from Cursor to end (what's "standout marker"?). */
151void
152clear_end_of_line (int end)
153{
154 if (!ceol_initialized)
155 {
156 int i;
157 for (i = 0; i < 256; i++)
158 {
159 glyph_base[i] = SPACEGLYPH; /* empty space */
160 }
161 ceol_initialized = TRUE;
162 }
163 write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
164}
165
166/* Insert n lines at vpos. if n is negative delete -n lines. */
167void
168ins_del_lines (int vpos, int n)
169{
170 int i, nb, save_highlight;
171 SMALL_RECT scroll;
172 COORD dest;
173 CHAR_INFO fill;
174 FRAME_PTR f = PICK_FRAME ();
175
176 if (n < 0)
177 {
178 scroll.Top = vpos - n;
179 scroll.Bottom = FRAME_HEIGHT (f);
180 dest.Y = vpos;
181 }
182 else
183 {
184 scroll.Top = vpos;
185 scroll.Bottom = FRAME_HEIGHT (f) - n;
186 dest.Y = vpos + n;
187 }
188 scroll.Left = 0;
189 scroll.Right = FRAME_WIDTH (f);
190
191 dest.X = 0;
192
193 save_highlight = hl_mode (0);
194
195 fill.Char.AsciiChar = 0x20;
196 fill.Attributes = char_attr;
197
198 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
199
200 /* Here we have to deal with a win32 console flake: If the scroll
201 region looks like abc and we scroll c to a and fill with d we get
202 cbd... if we scroll block c one line at a time to a, we get cdd...
203 Emacs expects cdd consistently... So we have to deal with that
204 here... (this also occurs scrolling the same way in the other
205 direction. */
206
207 if (n > 0)
208 {
209 if (scroll.Bottom < dest.Y)
210 {
211 for (i = scroll.Bottom; i < dest.Y; i++)
212 {
213 move_cursor (i, 0);
214 clear_end_of_line (FRAME_WIDTH (f));
215 }
216 }
217 }
218 else
219 {
220 nb = dest.Y + (scroll.Bottom - scroll.Top) + 1;
221
222 if (nb < scroll.Top)
223 {
224 for (i = nb; i < scroll.Top; i++)
225 {
226 move_cursor (i, 0);
227 clear_end_of_line (FRAME_WIDTH (f));
228 }
229 }
230 }
231
232 cursor_coords.X = 0;
233 cursor_coords.Y = vpos;
234
235 hl_mode (save_highlight);
236}
237
238/* Changes attribute to use when drawing characters to control. */
239static int
240hl_mode (int new_highlight)
241{
242 static int highlight = 0;
243 int old_highlight;
244
245 old_highlight = highlight;
246 highlight = (new_highlight != 0);
247 if (highlight)
248 {
249 char_attr = char_attr_reverse;
250 }
251 else
252 {
253 char_attr = char_attr_normal;
254 }
255 return old_highlight;
256}
257
258/* Call this when about to modify line at position VPOS and change whether it
259 is highlighted. */
260void
261change_line_highlight (int new_highlight, int vpos, int first_unused_hpos)
262{
263 hl_mode (new_highlight);
264 move_cursor (vpos, 0);
265 clear_end_of_line (first_unused_hpos);
266}
267
268/* External interface to control of standout mode. Call this when about to
269 * modify line at position VPOS and not change whether it is highlighted. */
270void
271reassert_line_highlight (int highlight, int vpos)
272{
273 hl_mode (highlight);
274 vpos; /* pedantic compiler silencer */
275}
276
277#undef LEFT
278#undef RIGHT
279#define LEFT 1
280#define RIGHT 0
281
282void
283scroll_line (int dist, int direction)
284{
285 /* The idea here is to implement a horizontal scroll in one line to
286 implement delete and half of insert. */
287 SMALL_RECT scroll;
288 COORD dest;
289 CHAR_INFO fill;
290 FRAME_PTR f = PICK_FRAME ();
291
292 scroll.Top = cursor_coords.Y;
293 scroll.Bottom = cursor_coords.Y;
294
295 if (direction == LEFT)
296 {
297 scroll.Left = cursor_coords.X + dist;
298 scroll.Right = FRAME_WIDTH (f) - 1;
299 }
300 else
301 {
302 scroll.Left = cursor_coords.X;
303 scroll.Right = FRAME_WIDTH (f) - dist - 1;
304 }
305
306 dest.X = cursor_coords.X;
307 dest.Y = cursor_coords.Y;
308
309 fill.Char.AsciiChar = 0x20;
310 fill.Attributes = char_attr;
311
312 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
313}
314
315
316/* If start is zero insert blanks instead of a string at start ?. */
317void
318insert_glyphs (register GLYPH *start, register int len)
319{
320 scroll_line (len, RIGHT);
321
322 /* Move len chars to the right starting at cursor_coords, fill with blanks */
323 if (start)
324 {
325 /* Print the first len characters of start, cursor_coords.X adjusted
326 by write_glyphs. */
327
328 write_glyphs (start, len);
329 }
330 else
331 {
332 clear_end_of_line (cursor_coords.X + len);
333 }
334}
335
336void
337write_glyphs (register GLYPH *string, register int len)
338{
339 register unsigned int glyph_len = GLYPH_TABLE_LENGTH;
340 Lisp_Object *glyph_table = GLYPH_TABLE_BASE;
341 FRAME_PTR f = PICK_FRAME ();
342 register char *ptr;
343 GLYPH glyph;
344 WORD *attrs;
345 char *chars;
346 int i;
347
348 attrs = alloca (len * sizeof (*attrs));
349 chars = alloca (len * sizeof (*chars));
350 if (attrs == NULL || chars == NULL)
351 {
352 printf ("alloca failed in write_glyphs\n");
353 return;
354 }
355
356 /* We have to deal with the glyph indirection...go over the glyph
357 buffer and extract the characters. */
358 ptr = chars;
359 while (--len >= 0)
360 {
361 glyph = *string++;
362
363 if (glyph > glyph_len)
364 {
365 *ptr++ = glyph & 0xFF;
366 continue;
367 }
368 GLYPH_FOLLOW_ALIASES (glyph_table, glyph_len, glyph);
369 if (GLYPH_FACE (fixfix, glyph) != 0)
370 printf ("Glyph face is %d\n", GLYPH_FACE (fixfix, glyph));
371 if (GLYPH_SIMPLE_P (glyph_table, glyph_len, glyph))
372 {
373 *ptr++ = glyph & 0xFF;
374 continue;
375 }
376 for (i = 0; i < GLYPH_LENGTH (glyph_table, glyph); i++)
377 {
378 *ptr++ = (GLYPH_STRING (glyph_table, glyph))[i];
379 }
380 }
381
382 /* Number of characters we have in the buffer. */
383 len = ptr-chars;
384
385 /* Fill in the attributes for these characters. */
386 memset (attrs, char_attr, len*sizeof (*attrs));
387
388 /* Write the attributes. */
389 if (!WriteConsoleOutputAttribute (cur_screen, attrs, len, cursor_coords, &i))
390 {
391 printf ("Failed writing console attributes.\n");
392 fflush (stdout);
393 }
394
395 /* Write the characters. */
396 if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
397 {
398 printf ("Failed writing console characters.\n");
399 fflush (stdout);
400 }
401
402 cursor_coords.X += len;
403 move_cursor (cursor_coords.Y, cursor_coords.X);
404}
405
406void
407delete_glyphs (int n)
408{
409 /* delete chars means scroll chars from cursor_coords.X + n to
410 cursor_coords.X, anything beyond the edge of the screen should
411 come out empty... */
412
413 scroll_line (n, LEFT);
414}
415
416void
417ring_bell (void)
418{
419 Beep (666, 100);
420}
421
422/* Reset to the original console mode but don't get rid of our console
423 For suspending emacs. */
424void
425restore_console (void)
426{
427 unset_kbd ();
428 SetConsoleActiveScreenBuffer (prev_screen);
429}
430
431/* Put our console back up, for ending a suspended session. */
432void
433take_console (void)
434{
435 reset_kbd ();
436 SetConsoleActiveScreenBuffer (cur_screen);
437}
438
439void
440reset_terminal_modes (void)
441{
442 unset_kbd ();
443 SetConsoleActiveScreenBuffer (prev_screen);
444 CloseHandle (cur_screen);
445 cur_screen = NULL;
446}
447
448void
449set_terminal_modes (void)
450{
451 CONSOLE_CURSOR_INFO cci;
452
453 if (cur_screen == NULL)
454 {
455 reset_kbd ();
456 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
457 0, NULL,
458 CONSOLE_TEXTMODE_BUFFER,
459 NULL);
460
461 if (cur_screen == INVALID_HANDLE_VALUE)
462 {
463 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
464 printf ("LastError = 0x%lx\n", GetLastError ());
465 fflush (stdout);
466 exit (0);
467 }
468
469 SetConsoleActiveScreenBuffer (cur_screen);
470
471 /* make cursor big and visible */
472 cci.dwSize = 100;
473 cci.bVisible = TRUE;
474 (void) SetConsoleCursorInfo (cur_screen, &cci);
475 }
476}
477
478/* hmmm... perhaps these let us bracket screen changes so that we can flush
479 clumps rather than one-character-at-a-time...
480
481 we'll start with not moving the cursor while an update is in progress. */
482void
483update_begin (FRAME_PTR f)
484{
485}
486
487void
488update_end (FRAME_PTR f)
489{
490 SetConsoleCursorPosition (cur_screen, cursor_coords);
491}
492
493void
494set_terminal_window (int size)
495{
496}
497
498void
499unset_kbd (void)
500{
501 SetConsoleMode (keyboard_handle, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
502 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT);
503}
504
505void
506reset_kbd (void)
507{
508 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
509 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
510}
511
512typedef int (*term_hook) ();
513
514void
515initialize_win_nt_display (void)
516{
517 CONSOLE_SCREEN_BUFFER_INFO info;
518
519 cursor_to_hook = (term_hook) move_cursor;
520 raw_cursor_to_hook = (term_hook) move_cursor;
521 clear_to_end_hook = (term_hook) clear_to_end;
522 clear_frame_hook = (term_hook) clear_frame;
523 clear_end_of_line_hook = (term_hook) clear_end_of_line;
524 ins_del_lines_hook = (term_hook) ins_del_lines;
525 change_line_highlight_hook = (term_hook) change_line_highlight;
526 reassert_line_highlight_hook = (term_hook) reassert_line_highlight;
527 insert_glyphs_hook = (term_hook) insert_glyphs;
528 write_glyphs_hook = (term_hook) write_glyphs;
529 delete_glyphs_hook = (term_hook) delete_glyphs;
530 ring_bell_hook = (term_hook) ring_bell;
531 reset_terminal_modes_hook = (term_hook) reset_terminal_modes;
532 set_terminal_modes_hook = (term_hook) set_terminal_modes;
533 set_terminal_window_hook = (term_hook) set_terminal_window;
534 update_begin_hook = (term_hook) update_begin;
535 update_end_hook = (term_hook) update_end;
536
537 read_socket_hook = win32_read_socket;
538 mouse_position_hook = win32_mouse_position;
539
540 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
541
542 set_terminal_modes ();
543
544 GetConsoleScreenBufferInfo (cur_screen, &info);
545
546 meta_key = 1;
547 char_attr = info.wAttributes & 0xFF;
548 char_attr_normal = char_attr;
549 char_attr_reverse = ((char_attr & 0xf) << 4) + ((char_attr & 0xf0) >> 4);
550
551 FRAME_HEIGHT (selected_frame) = info.dwSize.Y; /* lines per page */
552 FRAME_WIDTH (selected_frame) = info.dwSize.X; /* characters per line */
553
554 move_cursor (0, 0);
555
556 clear_frame ();
557}
558
559DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
560 "Set screen colors.")
561 (foreground, background)
562 Lisp_Object foreground;
563 Lisp_Object background;
564{
565 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
566 char_attr_reverse = XFASTINT (background) + (XFASTINT (foreground) << 4);
567
568 Frecenter (Qnil);
569 return Qt;
570}
571
572DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
573 "Set cursor size.")
574 (size)
575 Lisp_Object size;
576{
577 CONSOLE_CURSOR_INFO cci;
578 cci.dwSize = XFASTINT (size);
579 cci.bVisible = TRUE;
580 (void) SetConsoleCursorInfo (cur_screen, &cci);
581
582 return Qt;
583}
584
585void
586pixel_to_glyph_coords (FRAME_PTR f, int pix_x, int pix_y, int *x, int *y,
587 void *bounds, int noclip)
588{
589 *x = pix_x;
590 *y = pix_y;
591}
592
593void
594glyph_to_pixel_coords (FRAME_PTR f, int x, int y, int *pix_x, int *pix_y)
595{
596 *pix_x = x;
597 *pix_y = y;
598}
599
600_VOID_
601syms_of_ntterm ()
602{
603 defsubr (&Sset_screen_color);
604 defsubr (&Sset_cursor_size);
605}
diff --git a/src/w32inevt.c b/src/w32inevt.c
new file mode 100644
index 00000000000..e41c57e5fe5
--- /dev/null
+++ b/src/w32inevt.c
@@ -0,0 +1,459 @@
1/* Input event support for Windows NT port of GNU Emacs.
2 Copyright (C) 1992, 1993 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU General Public License along
17 with GNU Emacs; see the file COPYING. If not, write to the Free Software
18 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 Drew Bliss 01-Oct-93
21 Adapted from ntkbd.c by Tim Fleehart
22*/
23
24
25#include "config.h"
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <windows.h>
30
31#include "lisp.h"
32#include "frame.h"
33#include "blockinput.h"
34#include "termhooks.h"
35
36/* stdin, from ntterm */
37extern HANDLE keyboard_handle;
38
39/* Indicate mouse motion, from keyboard.c */
40extern int mouse_moved;
41
42/* Info for last mouse motion */
43static COORD movement_pos;
44static DWORD movement_time;
45
46/* from keyboard.c */
47extern void reinvoke_input_signal (void);
48
49/* from dispnew.c */
50extern int change_frame_size (FRAME_PTR, int, int, int, int);
51
52/* Event queue */
53#define EVENT_QUEUE_SIZE 50
54static INPUT_RECORD event_queue[EVENT_QUEUE_SIZE];
55static INPUT_RECORD *queue_ptr = event_queue, *queue_end = event_queue;
56
57static int
58fill_queue (BOOL block)
59{
60 BOOL rc;
61 DWORD events_waiting;
62
63 if (queue_ptr < queue_end)
64 return queue_end-queue_ptr;
65
66 if (!block)
67 {
68 /* Check to see if there are some events to read before we try
69 because we can't block. */
70 if (!GetNumberOfConsoleInputEvents (keyboard_handle, &events_waiting))
71 return -1;
72 if (events_waiting == 0)
73 return 0;
74 }
75
76 rc = ReadConsoleInput (keyboard_handle, event_queue, EVENT_QUEUE_SIZE,
77 &events_waiting);
78 if (!rc)
79 return -1;
80 queue_ptr = event_queue;
81 queue_end = event_queue + events_waiting;
82 return (int) events_waiting;
83}
84
85/* In a generic, multi-frame world this should take a console handle
86 and return the frame for it
87
88 Right now, there's only one frame so return it. */
89static FRAME_PTR
90get_frame (void)
91{
92 return selected_frame;
93}
94
95#ifdef MULTI_FRAME
96#define SET_FRAME(o, f) XSET (o, Lisp_Frame, f)
97#else
98#define SET_FRAME(o, f) ((o) = Qnil)
99#endif
100
101/* Translate console modifiers to emacs modifiers. */
102static int
103nt_kbd_mods_to_emacs (DWORD mods)
104{
105 return ((mods & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) ?
106 meta_modifier : 0) |
107 ((mods & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) ?
108 ctrl_modifier : 0) |
109 ((mods & (SHIFT_PRESSED | CAPSLOCK_ON)) ?
110 shift_modifier : 0);
111}
112
113/* Map virtual key codes into:
114 -1 - Ignore this key
115 -2 - ASCII char
116 Other - Map non-ASCII keys into X keysyms so that they are looked up
117 correctly in keyboard.c
118
119 Return, escape and tab are mapped to ASCII rather than coming back
120 as non-ASCII to be more compatible with old-style keyboard support. */
121
122static int map_virt_key[256] =
123{
124 -1,
125 -1, /* VK_LBUTTON */
126 -1, /* VK_RBUTTON */
127 0x69, /* VK_CANCEL */
128 -1, /* VK_MBUTTON */
129 -1, -1, -1,
130 8, /* VK_BACK */
131 -2, /* VK_TAB */
132 -1, -1,
133 11, /* VK_CLEAR */
134 -2, /* VK_RETURN */
135 -1, -1,
136 -1, /* VK_SHIFT */
137 -1, /* VK_CONTROL */
138 -1, /* VK_MENU */
139 0x13, /* VK_PAUSE */
140 -1, /* VK_CAPITAL */
141 -1, -1, -1, -1, -1, -1,
142 -2, /* VK_ESCAPE */
143 -1, -1, -1, -1,
144 -2, /* VK_SPACE */
145 0x55, /* VK_PRIOR */
146 0x56, /* VK_NEXT */
147 0x57, /* VK_END */
148 0x50, /* VK_HOME */
149 0x51, /* VK_LEFT */
150 0x52, /* VK_UP */
151 0x53, /* VK_RIGHT */
152 0x54, /* VK_DOWN */
153 0x60, /* VK_SELECT */
154 0x61, /* VK_PRINT */
155 0x62, /* VK_EXECUTE */
156 -1, /* VK_SNAPSHOT */
157 0x63, /* VK_INSERT */
158 0xff, /* VK_DELETE */
159 0x6a, /* VK_HELP */
160 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0 - 9 */
161 -1, -1, -1, -1, -1, -1, -1,
162 -2, -2, -2, -2, -2, -2, -2, -2, /* A - Z */
163 -2, -2, -2, -2, -2, -2, -2, -2,
164 -2, -2, -2, -2, -2, -2, -2, -2,
165 -2, -2,
166 -1, -1, -1, -1, -1,
167 0xb0, /* VK_NUMPAD0 */
168 0xb1, /* VK_NUMPAD1 */
169 0xb2, /* VK_NUMPAD2 */
170 0xb3, /* VK_NUMPAD3 */
171 0xb4, /* VK_NUMPAD4 */
172 0xb5, /* VK_NUMPAD5 */
173 0xb6, /* VK_NUMPAD6 */
174 0xb7, /* VK_NUMPAD7 */
175 0xb8, /* VK_NUMPAD8 */
176 0xb9, /* VK_NUMPAD9 */
177 0xaa, /* VK_MULTIPLY */
178 0xab, /* VK_ADD */
179 0xac, /* VK_SEPARATOR */
180 0xad, /* VK_SUBTRACT */
181 0xae, /* VK_DECIMAL */
182 0xaf, /* VK_DIVIDE */
183 0xbe, /* VK_F1 */
184 0xbf, /* VK_F2 */
185 0xc0, /* VK_F3 */
186 0xc1, /* VK_F4 */
187 0xc2, /* VK_F5 */
188 0xc3, /* VK_F6 */
189 0xc4, /* VK_F7 */
190 0xc5, /* VK_F8 */
191 0xc6, /* VK_F9 */
192 0xc7, /* VK_F10 */
193 0xc8, /* VK_F11 */
194 0xc9, /* VK_F12 */
195 0xca, /* VK_F13 */
196 0xcb, /* VK_F14 */
197 0xcc, /* VK_F15 */
198 0xcd, /* VK_F16 */
199 0xce, /* VK_F17 */
200 0xcf, /* VK_F18 */
201 0xd0, /* VK_F19 */
202 0xd1, /* VK_F20 */
203 0xd2, /* VK_F21 */
204 0xd3, /* VK_F22 */
205 0xd4, /* VK_F23 */
206 0xd5, /* VK_F24 */
207 -1, -1, -1, -1, -1, -1, -1, -1,
208 0x7f, /* VK_NUMLOCK */
209 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x9f */
210 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xaf */
211 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xb9 */
212 -2, /* ; */
213 -2, /* = */
214 -2, /* , */
215 -2, /* \ */
216 -2, /* . */
217 -2, /* / */
218 -2, /* ` */
219 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xcf */
220 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xda */
221 -2, /* [ */
222 -2, /* - */
223 -2, /* ] */
224 -2, /* ' */
225 -1, /* 0xdf */
226 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xef */
227 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 0xff */
228};
229
230static int
231key_event (KEY_EVENT_RECORD *event, struct input_event *emacs_ev)
232{
233 int map;
234
235 /* Skip key-up events. */
236 if (event->bKeyDown == FALSE)
237 return 0;
238
239 if (event->wVirtualKeyCode > 0xff)
240 {
241 printf ("Unknown key code %d\n", event->wVirtualKeyCode);
242 return 0;
243 }
244
245 /* BUGBUG - Ignores the repeat count
246 It's questionable whether we want to obey the repeat count anyway
247 since keys usually aren't repeated unless key events back up in
248 the queue. If they're backing up then we don't generally want
249 to honor them later since that leads to significant slop in
250 cursor motion when the system is under heavy load. */
251
252 map = map_virt_key[event->wVirtualKeyCode];
253 if (map == -1)
254 {
255 return 0;
256 }
257 else if (map == -2)
258 {
259 /* ASCII */
260 emacs_ev->kind = ascii_keystroke;
261 XSET (emacs_ev->code, Lisp_Int, event->uChar.AsciiChar);
262 }
263 else
264 {
265 /* non-ASCII */
266 emacs_ev->kind = non_ascii_keystroke;
267 /*
268 * make_lispy_event () now requires non-ascii codes to have
269 * the full X keysym values (2nd byte is 0xff). add it on.
270 */
271 map |= 0xff00;
272 XSET (emacs_ev->code, Lisp_Int, map);
273 }
274 SET_FRAME (emacs_ev->frame_or_window, get_frame ());
275 emacs_ev->modifiers = nt_kbd_mods_to_emacs (event->dwControlKeyState);
276 emacs_ev->timestamp = GetTickCount ();
277 return 1;
278}
279
280/* Mouse position hook. */
281void
282win32_mouse_position (FRAME_PTR *f,
283 Lisp_Object *bar_window,
284 enum scroll_bar_part *part,
285 Lisp_Object *x,
286 Lisp_Object *y,
287 unsigned long *time)
288{
289 BLOCK_INPUT;
290
291 *f = get_frame ();
292 *bar_window = Qnil;
293 *part = 0;
294 mouse_moved = 0;
295
296 *x = movement_pos.X;
297 *y = movement_pos.Y;
298 *time = movement_time;
299
300 UNBLOCK_INPUT;
301}
302
303/* Remember mouse motion and notify emacs. */
304static void
305mouse_moved_to (int x, int y)
306{
307 /* If we're in the same place, ignore it */
308 if (x != movement_pos.X || y != movement_pos.Y)
309 {
310 mouse_moved = 1;
311 movement_pos.X = x;
312 movement_pos.Y = y;
313 movement_time = GetTickCount ();
314 }
315}
316
317/* Consoles return button bits in a strange order:
318 least significant - Leftmost button
319 next - Rightmost button
320 next - Leftmost+1
321 next - Leftmost+2...
322
323 Assume emacs likes three button mice, so
324 Left == 0
325 Middle == 1
326 Right == 2
327 Others increase from there. */
328
329static int emacs_button_translation[NUM_MOUSE_BUTTONS] =
330{
331 0, 2, 1, 3, 4,
332};
333
334static int
335do_mouse_event (MOUSE_EVENT_RECORD *event,
336 struct input_event *emacs_ev)
337{
338 static DWORD button_state = 0;
339 DWORD but_change, mask;
340 int i;
341
342 if (event->dwEventFlags == MOUSE_MOVED)
343 {
344 /* For movement events we just note that the mouse has moved
345 so that emacs will generate drag events. */
346 mouse_moved_to (event->dwMousePosition.X, event->dwMousePosition.Y);
347 return 0;
348 }
349
350 /* It looks like the console code sends us a mouse event with
351 dwButtonState == 0 when a window is activated. Ignore this case. */
352 if (event->dwButtonState == button_state)
353 return 0;
354
355 emacs_ev->kind = mouse_click;
356
357 /* Find out what button has changed state since the last button event. */
358 but_change = button_state ^ event->dwButtonState;
359 mask = 1;
360 for (i = 0; i < NUM_MOUSE_BUTTONS; i++, mask <<= 1)
361 if (but_change & mask)
362 {
363 XSET (emacs_ev->code, Lisp_Int, emacs_button_translation[i]);
364 break;
365 }
366
367 /* If the changed button is out of emacs' range (highly unlikely)
368 ignore this event. */
369 if (i == NUM_MOUSE_BUTTONS)
370 return 0;
371
372 button_state = event->dwButtonState;
373 emacs_ev->timestamp = GetTickCount ();
374 emacs_ev->modifiers = nt_kbd_mods_to_emacs (event->dwControlKeyState) |
375 ((event->dwButtonState & mask) ? down_modifier : up_modifier);
376
377 XFASTINT (emacs_ev->x) = event->dwMousePosition.X;
378 XFASTINT (emacs_ev->y) = event->dwMousePosition.Y;
379 SET_FRAME (emacs_ev->frame_or_window, get_frame ());
380
381 return 1;
382}
383
384static void
385resize_event (WINDOW_BUFFER_SIZE_RECORD *event)
386{
387 FRAME_PTR f = get_frame ();
388
389 change_frame_size (f, event->dwSize.Y, event->dwSize.X, 0, 1);
390 SET_FRAME_GARBAGED (f);
391}
392
393int
394win32_read_socket (int sd, struct input_event *bufp, int numchars,
395 int waitp, int expected)
396{
397 BOOL no_events = TRUE;
398 int nev, ret = 0, add;
399
400 if (interrupt_input_blocked)
401 {
402 interrupt_input_pending = 1;
403 return -1;
404 }
405
406 interrupt_input_pending = 0;
407 BLOCK_INPUT;
408
409 for (;;)
410 {
411 nev = fill_queue (waitp != 0);
412 if (nev <= 0)
413 {
414 /* If nev == -1, there was some kind of error
415 If nev == 0 then waitp must be zero and no events were available
416 so return. */
417 UNBLOCK_INPUT;
418 return nev;
419 }
420
421 while (nev > 0 && numchars > 0)
422 {
423 switch (queue_ptr->EventType)
424 {
425 case KEY_EVENT:
426 add = key_event (&queue_ptr->Event.KeyEvent, bufp);
427 bufp += add;
428 ret += add;
429 numchars -= add;
430 break;
431
432 case MOUSE_EVENT:
433 add = do_mouse_event (&queue_ptr->Event.MouseEvent, bufp);
434 bufp += add;
435 ret += add;
436 numchars -= add;
437 break;
438
439 case WINDOW_BUFFER_SIZE_EVENT:
440 resize_event (&queue_ptr->Event.WindowBufferSizeEvent);
441 break;
442
443 case MENU_EVENT:
444 case FOCUS_EVENT:
445 /* Internal event types, ignored. */
446 break;
447 }
448
449 queue_ptr++;
450 nev--;
451 }
452
453 if (ret > 0 || expected == 0)
454 break;
455 }
456
457 UNBLOCK_INPUT;
458 return ret;
459}
diff --git a/src/w32proc.c b/src/w32proc.c
new file mode 100644
index 00000000000..cfed163a656
--- /dev/null
+++ b/src/w32proc.c
@@ -0,0 +1,780 @@
1/* Process support for Windows NT port of GNU EMACS.
2 Copyright (C) 1992 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU General Public License along
17 with GNU Emacs; see the file COPYING. If not, write to the Free Software
18 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 Drew Bliss Oct 14, 1993
21 Adapted from alarm.c by Tim Fleehart
22*/
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <errno.h>
27#include <io.h>
28#include <signal.h>
29
30#include "config.h"
31
32#include <windows.h>
33
34#include "lisp.h"
35#include "nt.h"
36#include "systime.h"
37
38/* #define FULL_DEBUG */
39
40typedef void (_CALLBACK_ *signal_handler)(int);
41
42/* Defined in process.h which conflicts with the local copy */
43#define _P_NOWAIT 1
44
45typedef struct _child_process
46{
47 int fd;
48 HANDLE char_avail;
49 HANDLE char_consumed;
50 char chr;
51 BOOL status;
52 HANDLE process;
53 DWORD pid;
54 HANDLE thrd;
55} child_process;
56
57#define MAX_CHILDREN MAXDESC
58
59#ifdef EMACSDEBUG
60void _CRTAPI1
61_DebPrint (char *fmt, ...)
62{
63 char buf[256];
64 va_list args;
65
66 va_start (args, fmt);
67 vsprintf (buf, fmt, args);
68 va_end (args);
69 OutputDebugString (buf);
70}
71#endif
72
73/* Child process management list. */
74static int child_proc_count = 0;
75static child_process child_procs[MAX_CHILDREN];
76static child_process *dead_child = NULL;
77
78#define CHILD_ACTIVE(cp) ((cp)->process != NULL)
79#define DEACTIVATE_CHILD(cp) ((cp)->process = NULL)
80
81/* Signal handlers...SIG_DFL == 0 so this is initialized correctly. */
82static signal_handler sig_handlers[NSIG];
83
84/* Fake signal implementation to record the SIGCHLD handler. */
85signal_handler
86win32_signal (int sig, signal_handler handler)
87{
88 signal_handler old;
89
90 if (sig != SIGCHLD)
91 {
92 errno = EINVAL;
93 return SIG_ERR;
94 }
95 old = sig_handlers[sig];
96 sig_handlers[sig] = handler;
97 return old;
98}
99
100/* Find an unused process slot. */
101static child_process *
102new_child (void)
103{
104 child_process *cp;
105
106 if (child_proc_count == MAX_CHILDREN)
107 return NULL;
108
109 for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
110 if (!CHILD_ACTIVE (cp))
111 return cp;
112 return &child_procs[child_proc_count++];
113}
114
115/* Find a child by pid. */
116static child_process *
117find_child_pid (DWORD pid)
118{
119 child_process *cp;
120
121 for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
122 if (CHILD_ACTIVE (cp) && pid == cp->pid)
123 return cp;
124 return NULL;
125}
126
127/* Find a child by fd. */
128static child_process *
129find_child_fd (int fd)
130{
131 child_process *cp;
132
133 for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
134 if (CHILD_ACTIVE (cp) && fd == cp->fd)
135 return cp;
136 return NULL;
137}
138
139/* Thread proc for child process reader threads
140 The threads just sit in a loop waiting for input
141 When they detect input, they signal the char_avail input to
142 wake up the select emulator
143 When the select emulator processes their input, it pulses
144 char_consumed so that the reader thread goes back to reading. */
145DWORD WINAPI
146reader_thread (void *arg)
147{
148 child_process *cp;
149
150 /* Our identity */
151 cp = (child_process *)arg;
152
153 /* We have to wait for the go-ahead before we can start */
154 if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
155 return 0;
156 /* If something went wrong, quit */
157 if (!cp->status)
158 return 0;
159
160 for (;;)
161 {
162 /* Use read to get CRLF translation */
163 if (read (cp->fd, &cp->chr, sizeof (char)) == sizeof (char))
164 {
165 cp->status = TRUE;
166 }
167 else
168 {
169#ifdef FULL_DEBUG
170 DebPrint (("reader_thread.read failed with %lu for fd %ld\n",
171 GetLastError (), cp->fd));
172#endif
173 cp->status = FALSE;
174 }
175
176 if (!SetEvent (cp->char_avail))
177 {
178 DebPrint (("reader_thread.SetEvent failed with %lu for fd %ld\n",
179 GetLastError (), cp->fd));
180 break;
181 }
182
183 /* If the read died, the child has died so let the thread die */
184 if (!cp->status)
185 break;
186
187 /* Wait until our input is acknowledged before reading again */
188 if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
189 {
190 DebPrint (("reader_thread.WaitForSingleObject failed with "
191 "%lu for fd %ld\n", GetLastError (), cp->fd));
192 break;
193 }
194 }
195 return 0;
196}
197
198static BOOL
199create_child (char *exe, char *cmdline, char *env,
200 PROCESS_INFORMATION *info)
201{
202 child_process *cp;
203 DWORD id;
204 STARTUPINFO start;
205 SECURITY_ATTRIBUTES sec_attrs;
206 SECURITY_DESCRIPTOR sec_desc;
207
208 cp = new_child ();
209 if (cp == NULL)
210 goto EH_Fail;
211
212 cp->fd = -1;
213
214 cp->char_avail = CreateEvent (NULL, FALSE, FALSE, NULL);
215 if (cp->char_avail == NULL)
216 goto EH_Fail;
217
218 cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL);
219 if (cp->char_consumed == NULL)
220 goto EH_char_avail;
221
222 cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id);
223 if (cp->thrd == NULL)
224 goto EH_char_consumed;
225
226 memset (&start, 0, sizeof (start));
227 start.cb = sizeof (start);
228
229 /* Explicitly specify no security */
230 if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
231 goto EH_thrd;
232 if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
233 goto EH_thrd;
234 sec_attrs.nLength = sizeof (sec_attrs);
235 sec_attrs.lpSecurityDescriptor = &sec_desc;
236 sec_attrs.bInheritHandle = FALSE;
237
238 if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE,
239 CREATE_NEW_PROCESS_GROUP, env, NULL,
240 &start, info))
241 goto EH_thrd;
242 cp->process = info->hProcess;
243 cp->pid = info->dwProcessId;
244
245 return TRUE;
246
247 EH_thrd:
248 id = GetLastError ();
249
250 cp->status = FALSE;
251 SetEvent (cp->char_consumed);
252 EH_char_consumed:
253 CloseHandle (cp->char_consumed);
254 EH_char_avail:
255 CloseHandle (cp->char_avail);
256 EH_Fail:
257 return FALSE;
258}
259
260/* create_child doesn't know what emacs' file handle will be for waiting
261 on output from the child, so we need to make this additional call
262 to register the handle with the process
263 This way the select emulator knows how to match file handles with
264 entries in child_procs. */
265void
266register_child (int pid, int fd)
267{
268 child_process *cp;
269
270 cp = find_child_pid (pid);
271 if (cp == NULL)
272 {
273 DebPrint (("register_child unable to find pid %lu\n", pid));
274 return;
275 }
276
277#ifdef FULL_DEBUG
278 DebPrint (("register_child registered fd %d with pid %lu\n", fd, pid));
279#endif
280
281 cp->fd = fd;
282 cp->status = TRUE;
283
284 /* Tell the reader thread to start */
285 if (!SetEvent (cp->char_consumed))
286 {
287 DebPrint (("register_child.SetEvent failed with %lu for fd %ld\n",
288 GetLastError (), cp->fd));
289 }
290}
291
292/* When a process dies its pipe will break so the reader thread will
293 signal failure to the select emulator.
294 The select emulator then calls this routine to clean up.
295 Since the thread signaled failure we can assume it is exiting. */
296static void
297remove_child (child_process *cp)
298{
299 /* Reap the thread */
300 if (WaitForSingleObject (cp->thrd, INFINITE) != WAIT_OBJECT_0)
301 {
302 DebPrint (("remove_child.WaitForSingleObject (thread) failed "
303 "with %lu for fd %ld\n", GetLastError (), cp->fd));
304 }
305 CloseHandle (cp->thrd);
306 CloseHandle (cp->char_consumed);
307 CloseHandle (cp->char_avail);
308
309 /* Reap the process */
310 if (WaitForSingleObject (cp->process, INFINITE) != WAIT_OBJECT_0)
311 {
312 DebPrint (("remove_child.WaitForSingleObject (process) failed "
313 "with %lu for fd %ld\n", GetLastError (), cp->fd));
314 }
315 CloseHandle (cp->process);
316
317 DEACTIVATE_CHILD (cp);
318}
319
320/* Wait for any of our existing child processes to die
321 When it does, close its handle
322 Return the pid and fill in the status if non-NULL. */
323int
324win32_wait (int *status)
325{
326 DWORD active, retval;
327 int nh;
328 child_process *cp, *cps[MAX_CHILDREN];
329 HANDLE wait_hnd[MAX_CHILDREN];
330
331 nh = 0;
332 if (dead_child != NULL)
333 {
334 /* We want to wait for a specific child */
335 wait_hnd[nh] = dead_child->process;
336 cps[nh] = dead_child;
337 nh++;
338 }
339 else
340 {
341 for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
342 if (CHILD_ACTIVE (cp))
343 {
344 wait_hnd[nh] = cp->process;
345 cps[nh] = cp;
346 nh++;
347 }
348 }
349
350 if (nh == 0)
351 {
352 /* Nothing to wait on, so fail */
353 errno = ECHILD;
354 return -1;
355 }
356
357 active = WaitForMultipleObjects (nh, wait_hnd, FALSE, INFINITE);
358 if (active == WAIT_FAILED)
359 {
360 errno = EBADF;
361 return -1;
362 }
363 else if (active == WAIT_TIMEOUT)
364 {
365 /* Should never happen */
366 errno = EINVAL;
367 return -1;
368 }
369 else if (active >= WAIT_OBJECT_0 &&
370 active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
371 {
372 active -= WAIT_OBJECT_0;
373 }
374 else if (active >= WAIT_ABANDONED_0 &&
375 active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
376 {
377 active -= WAIT_ABANDONED_0;
378 }
379
380 if (!GetExitCodeProcess (wait_hnd[active], &retval))
381 {
382 DebPrint (("Wait.GetExitCodeProcess failed with %lu\n",
383 GetLastError ()));
384 retval = 1;
385 }
386 if (retval == STILL_ACTIVE)
387 {
388 /* Should never happen */
389 DebPrint (("Wait.WaitForMultipleObjects returned an active process\n"));
390 errno = EINVAL;
391 return -1;
392 }
393
394 cp = cps[active];
395#ifdef FULL_DEBUG
396 DebPrint (("Wait signaled with process pid %d\n", cp->pid));
397#endif
398
399 if (status)
400 {
401 /* In process.c the default WAITTYPE is defined.
402 Since we can't determine anything about why a process died
403 we can only return a code that looks like WIFEXITED */
404 *status = (retval & 0x7fffff) << 8;
405 }
406
407 return cp->pid;
408}
409
410/* We pass our process ID to our children by setting up an environment
411 variable in their environment. */
412char ppid_env_var_buffer[64];
413
414/* When a new child process is created we need to register it in our list,
415 so intercept spawn requests. */
416int
417win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
418{
419 char *cmdline, *env, *parg, **targ;
420 int arglen;
421 PROCESS_INFORMATION pi;
422
423 if (child_proc_count == MAX_CHILDREN)
424 {
425 errno = EAGAIN;
426 return -1;
427 }
428
429 /* We don't care about the other modes */
430 if (mode != _P_NOWAIT)
431 {
432 errno = EINVAL;
433 return -1;
434 }
435
436 /* we have to do some conjuring here to put argv and envp into the
437 form CreateProcess wants... argv needs to be a space separated/null
438 terminated list of parameters, and envp is a null
439 separated/double-null terminated list of parameters.
440
441 Since I have no idea how large argv and envp are likely to be
442 we figure out list lengths on the fly and allocate them. */
443
444 /* do argv... */
445 arglen = 0;
446 targ = argv;
447 while (*targ)
448 {
449 arglen += strlen (*targ++) + 1;
450 }
451 cmdline = malloc (arglen);
452 if (cmdline == NULL)
453 {
454 errno = ENOMEM;
455 goto EH_Fail;
456 }
457 targ = argv;
458 parg = cmdline;
459 while (*targ)
460 {
461 strcpy (parg, *targ);
462 parg += strlen (*targ++);
463 *parg++ = ' ';
464 }
465 *--parg = '\0';
466
467 /* and envp... */
468 arglen = 1;
469 targ = envp;
470 while (*targ)
471 {
472 arglen += strlen (*targ++) + 1;
473 }
474 sprintf (ppid_env_var_buffer, "__PARENT_PROCESS_ID=%d",
475 GetCurrentProcessId ());
476 arglen += strlen (ppid_env_var_buffer) + 1;
477
478 env = malloc (arglen);
479 if (env == NULL)
480 {
481 errno = ENOMEM;
482 goto EH_cmdline;
483 }
484 targ = envp;
485 parg = env;
486 while (*targ)
487 {
488 strcpy (parg, *targ);
489 parg += strlen (*targ++);
490 *parg++ = '\0';
491 }
492 strcpy (parg, ppid_env_var_buffer);
493 parg += strlen (ppid_env_var_buffer);
494 *parg++ = '\0';
495 *parg = '\0';
496
497 /* Now create the process. */
498 if (!create_child (cmdname, cmdline, env, &pi))
499 {
500 errno = ENOEXEC;
501 goto EH_env;
502 }
503
504 return pi.dwProcessId;
505
506 EH_env:
507 free (env);
508 EH_cmdline:
509 free (cmdline);
510 EH_Fail:
511 return -1;
512}
513
514/* Emulate the select call
515 Wait for available input on any of the given rfds, or timeout if
516 a timeout is given and no input is detected
517 wfds and efds are not supported and must be NULL. */
518
519/* From ntterm.c */
520extern HANDLE keyboard_handle;
521/* From process.c */
522extern int proc_buffered_char[];
523
524int
525select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
526 EMACS_TIME *timeout)
527{
528 SELECT_TYPE orfds;
529 DWORD timeout_ms;
530 int i, nh, nr;
531 DWORD active;
532 child_process *cp, *cps[MAX_CHILDREN];
533 HANDLE wait_hnd[MAX_CHILDREN];
534
535 /* If the descriptor sets are NULL but timeout isn't, then just Sleep. */
536 if (rfds == NULL && wfds == NULL && efds == NULL && timeout != NULL)
537 {
538 Sleep ((*timeout) * 1000);
539 return 0;
540 }
541
542 /* Otherwise, we only handle rfds, so fail otherwise. */
543 if (rfds == NULL || wfds != NULL || efds != NULL)
544 {
545 errno = EINVAL;
546 return -1;
547 }
548
549 orfds = *rfds;
550 FD_ZERO (rfds);
551 nr = 0;
552
553 /* Build a list of handles to wait on. */
554 nh = 0;
555 for (i = 0; i < nfds; i++)
556 if (FD_ISSET (i, &orfds))
557 {
558 if (i == 0)
559 {
560 /* Handle stdin specially */
561 wait_hnd[nh] = keyboard_handle;
562 cps[nh] = NULL;
563 nh++;
564
565 /* Check for any emacs-generated input in the queue since
566 it won't be detected in the wait */
567 if (detect_input_pending ())
568 {
569 FD_SET (i, rfds);
570 nr++;
571 }
572 }
573 else
574 {
575 /* Child process input */
576 cp = find_child_fd (i);
577 if (cp)
578 {
579#ifdef FULL_DEBUG
580 DebPrint (("select waiting on child %d fd %d\n",
581 cp-child_procs, i));
582#endif
583 wait_hnd[nh] = cp->char_avail;
584 cps[nh] = cp;
585 nh++;
586 }
587 else
588 {
589 /* Unable to find something to wait on for this fd, fail */
590 DebPrint (("select unable to find child process "
591 "for fd %ld\n", i));
592 nh = 0;
593 break;
594 }
595 }
596 }
597
598 /* Nothing to look for, so we didn't find anything */
599 if (nh == 0)
600 {
601 Sleep ((*timeout) * 1000);
602 return 0;
603 }
604
605 /* Check for immediate return without waiting */
606 if (nr > 0)
607 return nr;
608
609 /*
610 Wait for input
611 If a child process dies while this is waiting, its pipe will break
612 so the reader thread will signal an error condition, thus, the wait
613 will wake up
614 */
615 timeout_ms = timeout ? *timeout*1000 : INFINITE;
616 active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms);
617 if (active == WAIT_FAILED)
618 {
619 DebPrint (("select.WaitForMultipleObjects (%d, %lu) failed with %lu\n",
620 nh, timeout_ms, GetLastError ()));
621 /* Is there a better error? */
622 errno = EBADF;
623 return -1;
624 }
625 else if (active == WAIT_TIMEOUT)
626 {
627 return 0;
628 }
629 else if (active >= WAIT_OBJECT_0 &&
630 active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
631 {
632 active -= WAIT_OBJECT_0;
633 }
634 else if (active >= WAIT_ABANDONED_0 &&
635 active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
636 {
637 active -= WAIT_ABANDONED_0;
638 }
639
640 if (cps[active] == NULL)
641 {
642 /* Keyboard input available */
643 FD_SET (0, rfds);
644 nr++;
645
646 /* This shouldn't be necessary, but apparently just setting the input
647 fd is not good enough for emacs */
648 read_input_waiting ();
649 }
650 else
651 {
652 /* Child process */
653 cp = cps[active];
654
655 /* If status is FALSE the read failed so don't report input */
656 if (cp->status)
657 {
658 FD_SET (cp->fd, rfds);
659 proc_buffered_char[cp->fd] = cp->chr;
660 nr++;
661 }
662 else
663 {
664 /* The SIGCHLD handler will do a Wait so we know it won't
665 return until the process is dead
666 We force Wait to only wait for this process to avoid it
667 picking up other children that happen to be dead but that
668 we haven't noticed yet
669 SIG_DFL for SIGCHLD is ignore? */
670 if (sig_handlers[SIGCHLD] != SIG_DFL &&
671 sig_handlers[SIGCHLD] != SIG_IGN)
672 {
673#ifdef FULL_DEBUG
674 DebPrint (("select calling SIGCHLD handler for pid %d\n",
675 cp->pid));
676#endif
677 dead_child = cp;
678 sig_handlers[SIGCHLD](SIGCHLD);
679 dead_child = NULL;
680 }
681
682 /* Clean up the child process entry in the table */
683 remove_child (cp);
684 }
685 }
686 return nr;
687}
688
689/*
690 Substitute for certain kill () operations
691 */
692int
693win32_kill_process (int pid, int sig)
694{
695 child_process *cp;
696
697 /* Only handle signals that will result in the process dying */
698 if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP)
699 {
700 errno = EINVAL;
701 return -1;
702 }
703
704 cp = find_child_pid (pid);
705 if (cp == NULL)
706 {
707 DebPrint (("win32_kill_process didn't find a child with pid %lu\n", pid));
708 errno = ECHILD;
709 return -1;
710 }
711
712 if (sig == SIGINT)
713 {
714 /* Fake Ctrl-Break. */
715 if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid))
716 {
717 DebPrint (("win32_kill_process.GenerateConsoleCtrlEvent return %d "
718 "for pid %lu\n", GetLastError (), pid));
719 errno = EINVAL;
720 return -1;
721 }
722 }
723 else
724 {
725 /* Kill the process. On Win32 this doesn't kill child processes
726 so it doesn't work very well for shells which is why it's
727 not used in every case. */
728 if (!TerminateProcess (cp->process, 0xff))
729 {
730 DebPrint (("win32_kill_process.TerminateProcess returned %d "
731 "for pid %lu\n", GetLastError (), pid));
732 errno = EINVAL;
733 return -1;
734 }
735 }
736 return 0;
737}
738
739/* If the channel is a pipe this read might block since we don't
740 know how many characters are available, so check and read only
741 what's there
742 We also need to wake up the reader thread once we've read our data. */
743int
744read_child_output (int fd, char *buf, int max)
745{
746 HANDLE h;
747 int to_read, nchars;
748 DWORD waiting;
749 child_process *cp;
750
751 h = (HANDLE)_get_osfhandle (fd);
752 if (GetFileType (h) == FILE_TYPE_PIPE)
753 {
754 PeekNamedPipe (h, NULL, 0, NULL, &waiting, NULL);
755 to_read = min (waiting, (DWORD)max);
756 }
757 else
758 to_read = max;
759
760 /* Use read to get CRLF translation */
761 nchars = read (fd, buf, to_read);
762
763 if (GetFileType (h) == FILE_TYPE_PIPE)
764 {
765 /* Wake up the reader thread
766 for this process */
767 cp = find_child_fd (fd);
768 if (cp)
769 {
770 if (!SetEvent (cp->char_consumed))
771 DebPrint (("read_child_output.SetEvent failed with "
772 "%lu for fd %ld\n", GetLastError (), fd));
773 }
774 else
775 DebPrint (("read_child_output couldn't find a child with fd %d\n",
776 fd));
777 }
778
779 return nchars;
780}