(Fprimitive_undo): Check veracity of delta,start,end.
[bpt/emacs.git] / src / w32console.c
1 /* Terminal hooks for GNU Emacs on the Microsoft W32 API.
2 Copyright (C) 1992, 1999 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
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20
21 Tim Fleehart (apollo@online.com) 1-17-92
22 Geoff Voelker (voelker@cs.washington.edu) 9-12-93
23 */
24
25
26 #include <config.h>
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <windows.h>
31 #include <string.h>
32
33 #include "lisp.h"
34 #include "charset.h"
35 #include "coding.h"
36 #include "disptab.h"
37 #include "termhooks.h"
38 #include "dispextern.h"
39 /* Disable features in frame.h that require a Window System. */
40 #undef HAVE_WINDOW_SYSTEM
41 #include "frame.h"
42 #include "w32inevt.h"
43
44 /* from window.c */
45 extern Lisp_Object Frecenter ();
46
47 /* from keyboard.c */
48 extern int detect_input_pending ();
49
50 /* from sysdep.c */
51 extern int read_input_pending ();
52
53 extern struct frame * updating_frame;
54 extern int meta_key;
55
56 static void w32con_move_cursor (int row, int col);
57 static void w32con_clear_to_end (void);
58 static void w32con_clear_frame (void);
59 static void w32con_clear_end_of_line (int);
60 static void w32con_ins_del_lines (int vpos, int n);
61 static void w32con_insert_glyphs (struct glyph *start, int len);
62 static void w32con_write_glyphs (struct glyph *string, int len);
63 static void w32con_delete_glyphs (int n);
64 void w32_sys_ring_bell (void);
65 static void w32con_reset_terminal_modes (void);
66 static void w32con_set_terminal_modes (void);
67 static void w32con_set_terminal_window (int size);
68 static void w32con_update_begin (struct frame * f);
69 static void w32con_update_end (struct frame * f);
70 static WORD w32_face_attributes (struct frame *f, int face_id);
71
72 static COORD cursor_coords;
73 static HANDLE prev_screen, cur_screen;
74 static WORD char_attr_normal;
75 static DWORD prev_console_mode;
76
77 #ifndef USE_SEPARATE_SCREEN
78 static CONSOLE_CURSOR_INFO prev_console_cursor;
79 #endif
80
81 /* Determine whether to make frame dimensions match the screen buffer,
82 or the current window size. The former is desirable when running
83 over telnet, while the latter is more useful when working directly at
84 the console with a large scroll-back buffer. */
85 int w32_use_full_screen_buffer;
86 HANDLE keyboard_handle;
87
88
89 /* Setting this as the ctrl handler prevents emacs from being killed when
90 someone hits ^C in a 'suspended' session (child shell).
91 Also ignore Ctrl-Break signals. */
92
93 BOOL
94 ctrl_c_handler (unsigned long type)
95 {
96 /* Only ignore "interrupt" events when running interactively. */
97 return (!noninteractive
98 && (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT));
99 }
100
101 /* If we're updating a frame, use it as the current frame
102 Otherwise, use the selected frame. */
103 #define PICK_FRAME() (updating_frame ? updating_frame : SELECTED_FRAME ())
104
105 /* Move the cursor to (row, col). */
106 static void
107 w32con_move_cursor (int row, int col)
108 {
109 cursor_coords.X = col;
110 cursor_coords.Y = row;
111
112 if (updating_frame == (struct frame *) NULL)
113 {
114 SetConsoleCursorPosition (cur_screen, cursor_coords);
115 }
116 }
117
118 /* Clear from cursor to end of screen. */
119 static void
120 w32con_clear_to_end (void)
121 {
122 struct frame * f = PICK_FRAME ();
123
124 w32con_clear_end_of_line (FRAME_COLS (f) - 1);
125 w32con_ins_del_lines (cursor_coords.Y, FRAME_LINES (f) - cursor_coords.Y - 1);
126 }
127
128 /* Clear the frame. */
129 static void
130 w32con_clear_frame (void)
131 {
132 struct frame * f = PICK_FRAME ();
133 COORD dest;
134 int n;
135 DWORD r;
136 CONSOLE_SCREEN_BUFFER_INFO info;
137
138 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
139
140 /* Remember that the screen buffer might be wider than the window. */
141 n = FRAME_LINES (f) * info.dwSize.X;
142 dest.X = dest.Y = 0;
143
144 FillConsoleOutputAttribute (cur_screen, char_attr_normal, n, dest, &r);
145 FillConsoleOutputCharacter (cur_screen, ' ', n, dest, &r);
146
147 w32con_move_cursor (0, 0);
148 }
149
150
151 static struct glyph glyph_base[256];
152 static BOOL ceol_initialized = FALSE;
153
154 /* Clear from Cursor to end (what's "standout marker"?). */
155 static void
156 w32con_clear_end_of_line (int end)
157 {
158 if (!ceol_initialized)
159 {
160 int i;
161 for (i = 0; i < 256; i++)
162 {
163 memcpy (&glyph_base[i], &space_glyph, sizeof (struct glyph));
164 }
165 ceol_initialized = TRUE;
166 }
167 w32con_write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
168 }
169
170 /* Insert n lines at vpos. if n is negative delete -n lines. */
171 static void
172 w32con_ins_del_lines (int vpos, int n)
173 {
174 int i, nb;
175 SMALL_RECT scroll;
176 COORD dest;
177 CHAR_INFO fill;
178 struct frame * f = PICK_FRAME ();
179
180 if (n < 0)
181 {
182 scroll.Top = vpos - n;
183 scroll.Bottom = FRAME_LINES (f);
184 dest.Y = vpos;
185 }
186 else
187 {
188 scroll.Top = vpos;
189 scroll.Bottom = FRAME_LINES (f) - n;
190 dest.Y = vpos + n;
191 }
192 scroll.Left = 0;
193 scroll.Right = FRAME_COLS (f);
194
195 dest.X = 0;
196
197 fill.Char.AsciiChar = 0x20;
198 fill.Attributes = char_attr_normal;
199
200 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
201
202 /* Here we have to deal with a w32 console flake: If the scroll
203 region looks like abc and we scroll c to a and fill with d we get
204 cbd... if we scroll block c one line at a time to a, we get cdd...
205 Emacs expects cdd consistently... So we have to deal with that
206 here... (this also occurs scrolling the same way in the other
207 direction. */
208
209 if (n > 0)
210 {
211 if (scroll.Bottom < dest.Y)
212 {
213 for (i = scroll.Bottom; i < dest.Y; i++)
214 {
215 w32con_move_cursor (i, 0);
216 w32con_clear_end_of_line (FRAME_COLS (f));
217 }
218 }
219 }
220 else
221 {
222 nb = dest.Y + (scroll.Bottom - scroll.Top) + 1;
223
224 if (nb < scroll.Top)
225 {
226 for (i = nb; i < scroll.Top; i++)
227 {
228 w32con_move_cursor (i, 0);
229 w32con_clear_end_of_line (FRAME_COLS (f));
230 }
231 }
232 }
233
234 cursor_coords.X = 0;
235 cursor_coords.Y = vpos;
236 }
237
238 #undef LEFT
239 #undef RIGHT
240 #define LEFT 1
241 #define RIGHT 0
242
243 static void
244 scroll_line (int dist, int direction)
245 {
246 /* The idea here is to implement a horizontal scroll in one line to
247 implement delete and half of insert. */
248 SMALL_RECT scroll;
249 COORD dest;
250 CHAR_INFO fill;
251 struct frame * f = PICK_FRAME ();
252
253 scroll.Top = cursor_coords.Y;
254 scroll.Bottom = cursor_coords.Y;
255
256 if (direction == LEFT)
257 {
258 scroll.Left = cursor_coords.X + dist;
259 scroll.Right = FRAME_COLS (f) - 1;
260 }
261 else
262 {
263 scroll.Left = cursor_coords.X;
264 scroll.Right = FRAME_COLS (f) - dist - 1;
265 }
266
267 dest.X = cursor_coords.X;
268 dest.Y = cursor_coords.Y;
269
270 fill.Char.AsciiChar = 0x20;
271 fill.Attributes = char_attr_normal;
272
273 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
274 }
275
276
277 /* If start is zero insert blanks instead of a string at start ?. */
278 static void
279 w32con_insert_glyphs (register struct glyph *start, register int len)
280 {
281 scroll_line (len, RIGHT);
282
283 /* Move len chars to the right starting at cursor_coords, fill with blanks */
284 if (start)
285 {
286 /* Print the first len characters of start, cursor_coords.X adjusted
287 by write_glyphs. */
288
289 w32con_write_glyphs (start, len);
290 }
291 else
292 {
293 w32con_clear_end_of_line (cursor_coords.X + len);
294 }
295 }
296
297 extern unsigned char *encode_terminal_code P_ ((struct glyph *, int,
298 struct coding_system *));
299
300 static void
301 w32con_write_glyphs (register struct glyph *string, register int len)
302 {
303 int produced, consumed;
304 DWORD r;
305 struct frame * f = PICK_FRAME ();
306 WORD char_attr;
307 unsigned char *conversion_buffer;
308 struct coding_system *coding;
309
310 if (len <= 0)
311 return;
312
313 /* If terminal_coding does any conversion, use it, otherwise use
314 safe_terminal_coding. We can't use CODING_REQUIRE_ENCODING here
315 because it always return 1 if the member src_multibyte is 1. */
316 coding = (terminal_coding.common_flags & CODING_REQUIRE_ENCODING_MASK
317 ? &terminal_coding : &safe_terminal_coding);
318 /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
319 the tail. */
320 terminal_coding.mode &= ~CODING_MODE_LAST_BLOCK;
321
322 while (len > 0)
323 {
324 /* Identify a run of glyphs with the same face. */
325 int face_id = string->face_id;
326 int n;
327
328 for (n = 1; n < len; ++n)
329 if (string[n].face_id != face_id)
330 break;
331
332 /* Turn appearance modes of the face of the run on. */
333 char_attr = w32_face_attributes (f, face_id);
334
335 if (n == len)
336 /* This is the last run. */
337 coding->mode |= CODING_MODE_LAST_BLOCK;
338 conversion_buffer = encode_terminal_code (string, n, coding);
339 if (coding->produced > 0)
340 {
341 /* Set the attribute for these characters. */
342 if (!FillConsoleOutputAttribute (cur_screen, char_attr,
343 coding->produced, cursor_coords,
344 &r))
345 {
346 printf ("Failed writing console attributes: %d\n",
347 GetLastError ());
348 fflush (stdout);
349 }
350
351 /* Write the characters. */
352 if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer,
353 coding->produced, cursor_coords,
354 &r))
355 {
356 printf ("Failed writing console characters: %d\n",
357 GetLastError ());
358 fflush (stdout);
359 }
360
361 cursor_coords.X += coding->produced;
362 w32con_move_cursor (cursor_coords.Y, cursor_coords.X);
363 }
364 len -= n;
365 string += n;
366 }
367 }
368
369
370 static void
371 w32con_delete_glyphs (int n)
372 {
373 /* delete chars means scroll chars from cursor_coords.X + n to
374 cursor_coords.X, anything beyond the edge of the screen should
375 come out empty... */
376
377 scroll_line (n, LEFT);
378 }
379
380 static unsigned int sound_type = 0xFFFFFFFF;
381 #define MB_EMACS_SILENT (0xFFFFFFFF - 1)
382
383 void
384 w32_sys_ring_bell (void)
385 {
386 if (sound_type == 0xFFFFFFFF)
387 {
388 Beep (666, 100);
389 }
390 else if (sound_type == MB_EMACS_SILENT)
391 {
392 /* Do nothing. */
393 }
394 else
395 MessageBeep (sound_type);
396 }
397
398 DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
399 doc: /* Set the sound generated when the bell is rung.
400 SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent
401 to use the corresponding system sound for the bell. The 'silent sound
402 prevents Emacs from making any sound at all.
403 SOUND is nil to use the normal beep. */)
404 (sound)
405 Lisp_Object sound;
406 {
407 CHECK_SYMBOL (sound);
408
409 if (NILP (sound))
410 sound_type = 0xFFFFFFFF;
411 else if (EQ (sound, intern ("asterisk")))
412 sound_type = MB_ICONASTERISK;
413 else if (EQ (sound, intern ("exclamation")))
414 sound_type = MB_ICONEXCLAMATION;
415 else if (EQ (sound, intern ("hand")))
416 sound_type = MB_ICONHAND;
417 else if (EQ (sound, intern ("question")))
418 sound_type = MB_ICONQUESTION;
419 else if (EQ (sound, intern ("ok")))
420 sound_type = MB_OK;
421 else if (EQ (sound, intern ("silent")))
422 sound_type = MB_EMACS_SILENT;
423 else
424 sound_type = 0xFFFFFFFF;
425
426 return sound;
427 }
428
429 static void
430 w32con_reset_terminal_modes (void)
431 {
432 #ifdef USE_SEPARATE_SCREEN
433 SetConsoleActiveScreenBuffer (prev_screen);
434 #else
435 SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
436 #endif
437 SetConsoleMode (keyboard_handle, prev_console_mode);
438 }
439
440 static void
441 w32con_set_terminal_modes (void)
442 {
443 CONSOLE_CURSOR_INFO cci;
444
445 /* make cursor big and visible (100 on Win95 makes it disappear) */
446 cci.dwSize = 99;
447 cci.bVisible = TRUE;
448 (void) SetConsoleCursorInfo (cur_screen, &cci);
449
450 SetConsoleActiveScreenBuffer (cur_screen);
451
452 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
453
454 /* Initialize input mode: interrupt_input off, no flow control, allow
455 8 bit character input, standard quit char. */
456 Fset_input_mode (Qnil, Qnil, make_number (2), Qnil);
457 }
458
459 /* hmmm... perhaps these let us bracket screen changes so that we can flush
460 clumps rather than one-character-at-a-time...
461
462 we'll start with not moving the cursor while an update is in progress. */
463 static void
464 w32con_update_begin (struct frame * f)
465 {
466 }
467
468 static void
469 w32con_update_end (struct frame * f)
470 {
471 SetConsoleCursorPosition (cur_screen, cursor_coords);
472 }
473
474 static void
475 w32con_set_terminal_window (int size)
476 {
477 }
478
479 /***********************************************************************
480 Faces
481 ***********************************************************************/
482
483
484 /* Turn appearances of face FACE_ID on tty frame F on. */
485
486 static WORD
487 w32_face_attributes (f, face_id)
488 struct frame *f;
489 int face_id;
490 {
491 WORD char_attr;
492 struct face *face = FACE_FROM_ID (f, face_id);
493
494 xassert (face != NULL);
495
496 char_attr = char_attr_normal;
497
498 if (face->foreground != FACE_TTY_DEFAULT_FG_COLOR
499 && face->foreground != FACE_TTY_DEFAULT_COLOR)
500 char_attr = (char_attr & 0xfff0) + (face->foreground % 16);
501
502 if (face->background != FACE_TTY_DEFAULT_BG_COLOR
503 && face->background != FACE_TTY_DEFAULT_COLOR)
504 char_attr = (char_attr & 0xff0f) + ((face->background % 16) << 4);
505
506
507 /* NTEMACS_TODO: Faces defined during startup get both foreground
508 and background of 0. Need a better way around this - for now detect
509 the problem and invert one of the faces to make the text readable. */
510 if (((char_attr & 0x00f0) >> 4) == (char_attr & 0x000f))
511 char_attr ^= 0x0007;
512
513 if (face->tty_reverse_p)
514 char_attr = (char_attr & 0xff00) + ((char_attr & 0x000f) << 4)
515 + ((char_attr & 0x00f0) >> 4);
516
517 return char_attr;
518 }
519
520
521 /* Emulation of some X window features from xfns.c and xfaces.c. */
522
523 extern char unspecified_fg[], unspecified_bg[];
524
525
526 /* Given a color index, return its standard name. */
527 Lisp_Object
528 vga_stdcolor_name (int idx)
529 {
530 /* Standard VGA colors, in the order of their standard numbering
531 in the default VGA palette. */
532 static char *vga_colors[16] = {
533 "black", "blue", "green", "cyan", "red", "magenta", "brown",
534 "lightgray", "darkgray", "lightblue", "lightgreen", "lightcyan",
535 "lightred", "lightmagenta", "yellow", "white"
536 };
537
538 extern Lisp_Object Qunspecified;
539
540 if (idx >= 0 && idx < sizeof (vga_colors) / sizeof (vga_colors[0]))
541 return build_string (vga_colors[idx]);
542 else
543 return Qunspecified; /* meaning the default */
544 }
545
546 typedef int (*term_hook) ();
547
548 void
549 initialize_w32_display (void)
550 {
551 CONSOLE_SCREEN_BUFFER_INFO info;
552
553 cursor_to_hook = w32con_move_cursor;
554 raw_cursor_to_hook = w32con_move_cursor;
555 clear_to_end_hook = w32con_clear_to_end;
556 clear_frame_hook = w32con_clear_frame;
557 clear_end_of_line_hook = w32con_clear_end_of_line;
558 ins_del_lines_hook = w32con_ins_del_lines;
559 insert_glyphs_hook = w32con_insert_glyphs;
560 write_glyphs_hook = w32con_write_glyphs;
561 delete_glyphs_hook = w32con_delete_glyphs;
562 ring_bell_hook = w32_sys_ring_bell;
563 reset_terminal_modes_hook = w32con_reset_terminal_modes;
564 set_terminal_modes_hook = w32con_set_terminal_modes;
565 set_terminal_window_hook = w32con_set_terminal_window;
566 update_begin_hook = w32con_update_begin;
567 update_end_hook = w32con_update_end;
568
569 read_socket_hook = w32_console_read_socket;
570 mouse_position_hook = w32_console_mouse_position;
571
572 /* Initialize interrupt_handle. */
573 init_crit ();
574
575 /* Remember original console settings. */
576 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
577 GetConsoleMode (keyboard_handle, &prev_console_mode);
578
579 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
580
581 #ifdef USE_SEPARATE_SCREEN
582 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
583 0, NULL,
584 CONSOLE_TEXTMODE_BUFFER,
585 NULL);
586
587 if (cur_screen == INVALID_HANDLE_VALUE)
588 {
589 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
590 printf ("LastError = 0x%lx\n", GetLastError ());
591 fflush (stdout);
592 exit (0);
593 }
594 #else
595 cur_screen = prev_screen;
596 GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
597 #endif
598
599 /* Respect setting of LINES and COLUMNS environment variables. */
600 {
601 char * lines = getenv("LINES");
602 char * columns = getenv("COLUMNS");
603
604 if (lines != NULL && columns != NULL)
605 {
606 SMALL_RECT new_win_dims;
607 COORD new_size;
608
609 new_size.X = atoi (columns);
610 new_size.Y = atoi (lines);
611
612 GetConsoleScreenBufferInfo (cur_screen, &info);
613
614 /* Shrink the window first, so the buffer dimensions can be
615 reduced if necessary. */
616 new_win_dims.Top = 0;
617 new_win_dims.Left = 0;
618 new_win_dims.Bottom = min (new_size.Y, info.dwSize.Y) - 1;
619 new_win_dims.Right = min (new_size.X, info.dwSize.X) - 1;
620 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
621
622 SetConsoleScreenBufferSize (cur_screen, new_size);
623
624 /* Set the window size to match the buffer dimension. */
625 new_win_dims.Top = 0;
626 new_win_dims.Left = 0;
627 new_win_dims.Bottom = new_size.Y - 1;
628 new_win_dims.Right = new_size.X - 1;
629 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
630 }
631 }
632
633 GetConsoleScreenBufferInfo (cur_screen, &info);
634
635 meta_key = 1;
636 char_attr_normal = info.wAttributes;
637
638 if (w32_use_full_screen_buffer)
639 {
640 FRAME_LINES (SELECTED_FRAME ()) = info.dwSize.Y; /* lines per page */
641 SET_FRAME_COLS (SELECTED_FRAME (), info.dwSize.X); /* characters per line */
642 }
643 else
644 {
645 /* Lines per page. Use buffer coords instead of buffer size. */
646 FRAME_LINES (SELECTED_FRAME ()) = 1 + info.srWindow.Bottom -
647 info.srWindow.Top;
648 /* Characters per line. Use buffer coords instead of buffer size. */
649 SET_FRAME_COLS (SELECTED_FRAME (), 1 + info.srWindow.Right -
650 info.srWindow.Left);
651 }
652
653 /* Setup w32_display_info structure for this frame. */
654
655 w32_initialize_display_info (build_string ("Console"));
656
657 }
658
659 DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
660 doc: /* Set screen colors. */)
661 (foreground, background)
662 Lisp_Object foreground;
663 Lisp_Object background;
664 {
665 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
666
667 Frecenter (Qnil);
668 return Qt;
669 }
670
671 DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
672 doc: /* Set cursor size. */)
673 (size)
674 Lisp_Object size;
675 {
676 CONSOLE_CURSOR_INFO cci;
677 cci.dwSize = XFASTINT (size);
678 cci.bVisible = TRUE;
679 (void) SetConsoleCursorInfo (cur_screen, &cci);
680
681 return Qt;
682 }
683
684 void
685 syms_of_ntterm ()
686 {
687 DEFVAR_BOOL ("w32-use-full-screen-buffer",
688 &w32_use_full_screen_buffer,
689 doc: /* Non-nil means make terminal frames use the full screen buffer dimensions.
690 This is desirable when running Emacs over telnet, and is the default.
691 A value of nil means use the current console window dimensions; this
692 may be preferrable when working directly at the console with a large
693 scroll-back buffer. */);
694 w32_use_full_screen_buffer = 1;
695
696 defsubr (&Sset_screen_color);
697 defsubr (&Sset_cursor_size);
698 defsubr (&Sset_message_beep);
699 }
700
701 /* arch-tag: a390a07f-f661-42bc-aeb4-e6d8bf860337
702 (do not change this comment) */