diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/w32console.c | 605 | ||||
| -rw-r--r-- | src/w32inevt.c | 459 | ||||
| -rw-r--r-- | src/w32proc.c | 780 |
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 */ | ||
| 40 | extern Lisp_Object Frecenter (); | ||
| 41 | |||
| 42 | /* from keyboard.c */ | ||
| 43 | extern int detect_input_pending (); | ||
| 44 | |||
| 45 | /* from sysdep.c */ | ||
| 46 | extern int read_input_pending (); | ||
| 47 | |||
| 48 | extern FRAME_PTR updating_frame; | ||
| 49 | extern int meta_key; | ||
| 50 | |||
| 51 | static void move_cursor (int row, int col); | ||
| 52 | static void clear_to_end (void); | ||
| 53 | static void clear_frame (void); | ||
| 54 | static void clear_end_of_line (int); | ||
| 55 | static void ins_del_lines (int vpos, int n); | ||
| 56 | static void change_line_highlight (int, int, int); | ||
| 57 | static void reassert_line_highlight (int, int); | ||
| 58 | static void insert_glyphs (GLYPH *start, int len); | ||
| 59 | static void write_glyphs (GLYPH *string, int len); | ||
| 60 | static void delete_glyphs (int n); | ||
| 61 | static void ring_bell (void); | ||
| 62 | static void reset_terminal_modes (void); | ||
| 63 | static void set_terminal_modes (void); | ||
| 64 | static void set_terminal_window (int size); | ||
| 65 | static void update_begin (FRAME_PTR f); | ||
| 66 | static void update_end (FRAME_PTR f); | ||
| 67 | static void reset_kbd (void); | ||
| 68 | static void unset_kbd (void); | ||
| 69 | static int hl_mode (int new_highlight); | ||
| 70 | |||
| 71 | void | ||
| 72 | DebPrint () | ||
| 73 | { | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Init hook called in init_keyboard. */ | ||
| 77 | void (*keyboard_init_hook)(void) = reset_kbd; | ||
| 78 | |||
| 79 | COORD cursor_coords; | ||
| 80 | HANDLE prev_screen, cur_screen; | ||
| 81 | UCHAR char_attr, char_attr_normal, char_attr_reverse; | ||
| 82 | HANDLE 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). */ | ||
| 87 | BOOL | ||
| 88 | ctrl_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). */ | ||
| 98 | void | ||
| 99 | move_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. */ | ||
| 111 | void | ||
| 112 | clear_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. */ | ||
| 121 | void | ||
| 122 | clear_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 | |||
| 147 | static GLYPH glyph_base[256]; | ||
| 148 | static BOOL ceol_initialized = FALSE; | ||
| 149 | |||
| 150 | /* Clear from Cursor to end (what's "standout marker"?). */ | ||
| 151 | void | ||
| 152 | clear_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. */ | ||
| 167 | void | ||
| 168 | ins_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. */ | ||
| 239 | static int | ||
| 240 | hl_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. */ | ||
| 260 | void | ||
| 261 | change_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. */ | ||
| 270 | void | ||
| 271 | reassert_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 | |||
| 282 | void | ||
| 283 | scroll_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 ?. */ | ||
| 317 | void | ||
| 318 | insert_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 | |||
| 336 | void | ||
| 337 | write_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 | |||
| 406 | void | ||
| 407 | delete_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 | |||
| 416 | void | ||
| 417 | ring_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. */ | ||
| 424 | void | ||
| 425 | restore_console (void) | ||
| 426 | { | ||
| 427 | unset_kbd (); | ||
| 428 | SetConsoleActiveScreenBuffer (prev_screen); | ||
| 429 | } | ||
| 430 | |||
| 431 | /* Put our console back up, for ending a suspended session. */ | ||
| 432 | void | ||
| 433 | take_console (void) | ||
| 434 | { | ||
| 435 | reset_kbd (); | ||
| 436 | SetConsoleActiveScreenBuffer (cur_screen); | ||
| 437 | } | ||
| 438 | |||
| 439 | void | ||
| 440 | reset_terminal_modes (void) | ||
| 441 | { | ||
| 442 | unset_kbd (); | ||
| 443 | SetConsoleActiveScreenBuffer (prev_screen); | ||
| 444 | CloseHandle (cur_screen); | ||
| 445 | cur_screen = NULL; | ||
| 446 | } | ||
| 447 | |||
| 448 | void | ||
| 449 | set_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. */ | ||
| 482 | void | ||
| 483 | update_begin (FRAME_PTR f) | ||
| 484 | { | ||
| 485 | } | ||
| 486 | |||
| 487 | void | ||
| 488 | update_end (FRAME_PTR f) | ||
| 489 | { | ||
| 490 | SetConsoleCursorPosition (cur_screen, cursor_coords); | ||
| 491 | } | ||
| 492 | |||
| 493 | void | ||
| 494 | set_terminal_window (int size) | ||
| 495 | { | ||
| 496 | } | ||
| 497 | |||
| 498 | void | ||
| 499 | unset_kbd (void) | ||
| 500 | { | ||
| 501 | SetConsoleMode (keyboard_handle, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | | ||
| 502 | ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT); | ||
| 503 | } | ||
| 504 | |||
| 505 | void | ||
| 506 | reset_kbd (void) | ||
| 507 | { | ||
| 508 | keyboard_handle = GetStdHandle (STD_INPUT_HANDLE); | ||
| 509 | SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT); | ||
| 510 | } | ||
| 511 | |||
| 512 | typedef int (*term_hook) (); | ||
| 513 | |||
| 514 | void | ||
| 515 | initialize_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 | |||
| 559 | DEFUN ("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 | |||
| 572 | DEFUN ("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 | |||
| 585 | void | ||
| 586 | pixel_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 | |||
| 593 | void | ||
| 594 | glyph_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_ | ||
| 601 | syms_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 */ | ||
| 37 | extern HANDLE keyboard_handle; | ||
| 38 | |||
| 39 | /* Indicate mouse motion, from keyboard.c */ | ||
| 40 | extern int mouse_moved; | ||
| 41 | |||
| 42 | /* Info for last mouse motion */ | ||
| 43 | static COORD movement_pos; | ||
| 44 | static DWORD movement_time; | ||
| 45 | |||
| 46 | /* from keyboard.c */ | ||
| 47 | extern void reinvoke_input_signal (void); | ||
| 48 | |||
| 49 | /* from dispnew.c */ | ||
| 50 | extern int change_frame_size (FRAME_PTR, int, int, int, int); | ||
| 51 | |||
| 52 | /* Event queue */ | ||
| 53 | #define EVENT_QUEUE_SIZE 50 | ||
| 54 | static INPUT_RECORD event_queue[EVENT_QUEUE_SIZE]; | ||
| 55 | static INPUT_RECORD *queue_ptr = event_queue, *queue_end = event_queue; | ||
| 56 | |||
| 57 | static int | ||
| 58 | fill_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. */ | ||
| 89 | static FRAME_PTR | ||
| 90 | get_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. */ | ||
| 102 | static int | ||
| 103 | nt_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 | |||
| 122 | static 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 | |||
| 230 | static int | ||
| 231 | key_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. */ | ||
| 281 | void | ||
| 282 | win32_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. */ | ||
| 304 | static void | ||
| 305 | mouse_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 | |||
| 329 | static int emacs_button_translation[NUM_MOUSE_BUTTONS] = | ||
| 330 | { | ||
| 331 | 0, 2, 1, 3, 4, | ||
| 332 | }; | ||
| 333 | |||
| 334 | static int | ||
| 335 | do_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 | |||
| 384 | static void | ||
| 385 | resize_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 | |||
| 393 | int | ||
| 394 | win32_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 | |||
| 40 | typedef void (_CALLBACK_ *signal_handler)(int); | ||
| 41 | |||
| 42 | /* Defined in process.h which conflicts with the local copy */ | ||
| 43 | #define _P_NOWAIT 1 | ||
| 44 | |||
| 45 | typedef 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 | ||
| 60 | void _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. */ | ||
| 74 | static int child_proc_count = 0; | ||
| 75 | static child_process child_procs[MAX_CHILDREN]; | ||
| 76 | static 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. */ | ||
| 82 | static signal_handler sig_handlers[NSIG]; | ||
| 83 | |||
| 84 | /* Fake signal implementation to record the SIGCHLD handler. */ | ||
| 85 | signal_handler | ||
| 86 | win32_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. */ | ||
| 101 | static child_process * | ||
| 102 | new_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. */ | ||
| 116 | static child_process * | ||
| 117 | find_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. */ | ||
| 128 | static child_process * | ||
| 129 | find_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. */ | ||
| 145 | DWORD WINAPI | ||
| 146 | reader_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 | |||
| 198 | static BOOL | ||
| 199 | create_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. */ | ||
| 265 | void | ||
| 266 | register_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. */ | ||
| 296 | static void | ||
| 297 | remove_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. */ | ||
| 323 | int | ||
| 324 | win32_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. */ | ||
| 412 | char 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. */ | ||
| 416 | int | ||
| 417 | win32_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 */ | ||
| 520 | extern HANDLE keyboard_handle; | ||
| 521 | /* From process.c */ | ||
| 522 | extern int proc_buffered_char[]; | ||
| 523 | |||
| 524 | int | ||
| 525 | select (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 | */ | ||
| 692 | int | ||
| 693 | win32_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. */ | ||
| 743 | int | ||
| 744 | read_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 | } | ||