Sync to HEAD
[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 "character.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 move_cursor (int row, int col);
57 static void clear_to_end (void);
58 static void clear_frame (void);
59 static void clear_end_of_line (int);
60 static void ins_del_lines (int vpos, int n);
61 static void insert_glyphs (struct glyph *start, int len);
62 static void write_glyphs (struct glyph *string, int len);
63 static void delete_glyphs (int n);
64 void w32_sys_ring_bell (void);
65 static void reset_terminal_modes (void);
66 static void set_terminal_modes (void);
67 static void set_terminal_window (int size);
68 static void update_begin (struct frame * f);
69 static void 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 void
107 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 void
120 clear_to_end (void)
121 {
122 struct frame * f = PICK_FRAME ();
123
124 clear_end_of_line (FRAME_COLS (f) - 1);
125 ins_del_lines (cursor_coords.Y, FRAME_LINES (f) - cursor_coords.Y - 1);
126 }
127
128 /* Clear the frame. */
129 void
130 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 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 void
156 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 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 void
172 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 move_cursor (i, 0);
216 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 move_cursor (i, 0);
229 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 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 void
279 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 write_glyphs (start, len);
290 }
291 else
292 {
293 clear_end_of_line (cursor_coords.X + len);
294 }
295 }
296
297 extern unsigned char *terminal_encode_buffer;
298
299 void
300 write_glyphs (register struct glyph *string, register int len)
301 {
302 int produced, consumed;
303 DWORD r;
304 struct frame * f = PICK_FRAME ();
305 WORD char_attr;
306
307 if (len <= 0)
308 return;
309
310 /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
311 the tail. */
312 terminal_coding.mode &= ~CODING_MODE_LAST_BLOCK;
313
314 while (len > 0)
315 {
316 /* Identify a run of glyphs with the same face. */
317 int face_id = string->face_id;
318 int n;
319
320 for (n = 1; n < len; ++n)
321 if (string[n].face_id != face_id)
322 break;
323
324 /* Turn appearance modes of the face of the run on. */
325 char_attr = w32_face_attributes (f, face_id);
326
327 while (n > 0)
328 {
329 produced = encode_terminal_code (string,
330 n,
331 &consumed);
332 if (produced > 0)
333 {
334 /* Set the attribute for these characters. */
335 if (!FillConsoleOutputAttribute (cur_screen, char_attr,
336 produced, cursor_coords, &r))
337 {
338 printf ("Failed writing console attributes: %d\n",
339 GetLastError ());
340 fflush (stdout);
341 }
342
343 /* Write the characters. */
344 if (!WriteConsoleOutputCharacter (cur_screen, terminal_encode_buffer,
345 produced, cursor_coords, &r))
346 {
347 printf ("Failed writing console characters: %d\n",
348 GetLastError ());
349 fflush (stdout);
350 }
351
352 cursor_coords.X += produced;
353 move_cursor (cursor_coords.Y, cursor_coords.X);
354 }
355 len -= consumed;
356 n -= consumed;
357 string += consumed;
358 }
359 }
360
361 /* We may have to output some codes to terminate the writing. */
362 if (CODING_REQUIRE_FLUSHING (&terminal_coding))
363 {
364 Lisp_Object blank_string = build_string ("");
365 int conversion_buffer_size = 1024;
366
367 terminal_coding.mode |= CODING_MODE_LAST_BLOCK;
368 terminal_coding.destination = (unsigned char *) xmalloc (conversion_buffer_size);
369 encode_coding_object (&terminal_coding, blank_string, 0, 0,
370 0, conversion_buffer_size, Qnil);
371 if (terminal_coding.produced > 0)
372 {
373 if (!FillConsoleOutputAttribute (cur_screen, char_attr_normal,
374 terminal_coding.produced,
375 cursor_coords, &r))
376 {
377 printf ("Failed writing console attributes: %d\n",
378 GetLastError ());
379 fflush (stdout);
380 }
381
382 /* Write the characters. */
383 if (!WriteConsoleOutputCharacter (cur_screen, terminal_coding.destination,
384 produced, cursor_coords, &r))
385 {
386 printf ("Failed writing console characters: %d\n",
387 GetLastError ());
388 fflush (stdout);
389 }
390 }
391 xfree (terminal_coding.destination);
392 }
393 }
394
395
396 void
397 delete_glyphs (int n)
398 {
399 /* delete chars means scroll chars from cursor_coords.X + n to
400 cursor_coords.X, anything beyond the edge of the screen should
401 come out empty... */
402
403 scroll_line (n, LEFT);
404 }
405
406 static unsigned int sound_type = 0xFFFFFFFF;
407 #define MB_EMACS_SILENT (0xFFFFFFFF - 1)
408
409 void
410 w32_sys_ring_bell (void)
411 {
412 if (sound_type == 0xFFFFFFFF)
413 {
414 Beep (666, 100);
415 }
416 else if (sound_type == MB_EMACS_SILENT)
417 {
418 /* Do nothing. */
419 }
420 else
421 MessageBeep (sound_type);
422 }
423
424 DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
425 doc: /* Set the sound generated when the bell is rung.
426 SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent
427 to use the corresponding system sound for the bell. The 'silent sound
428 prevents Emacs from making any sound at all.
429 SOUND is nil to use the normal beep. */)
430 (sound)
431 Lisp_Object sound;
432 {
433 CHECK_SYMBOL (sound);
434
435 if (NILP (sound))
436 sound_type = 0xFFFFFFFF;
437 else if (EQ (sound, intern ("asterisk")))
438 sound_type = MB_ICONASTERISK;
439 else if (EQ (sound, intern ("exclamation")))
440 sound_type = MB_ICONEXCLAMATION;
441 else if (EQ (sound, intern ("hand")))
442 sound_type = MB_ICONHAND;
443 else if (EQ (sound, intern ("question")))
444 sound_type = MB_ICONQUESTION;
445 else if (EQ (sound, intern ("ok")))
446 sound_type = MB_OK;
447 else if (EQ (sound, intern ("silent")))
448 sound_type = MB_EMACS_SILENT;
449 else
450 sound_type = 0xFFFFFFFF;
451
452 return sound;
453 }
454
455 void
456 reset_terminal_modes (void)
457 {
458 #ifdef USE_SEPARATE_SCREEN
459 SetConsoleActiveScreenBuffer (prev_screen);
460 #else
461 SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
462 #endif
463 SetConsoleMode (keyboard_handle, prev_console_mode);
464 }
465
466 void
467 set_terminal_modes (void)
468 {
469 CONSOLE_CURSOR_INFO cci;
470
471 /* make cursor big and visible (100 on Win95 makes it disappear) */
472 cci.dwSize = 99;
473 cci.bVisible = TRUE;
474 (void) SetConsoleCursorInfo (cur_screen, &cci);
475
476 SetConsoleActiveScreenBuffer (cur_screen);
477
478 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
479
480 /* Initialize input mode: interrupt_input off, no flow control, allow
481 8 bit character input, standard quit char. */
482 Fset_input_mode (Qnil, Qnil, make_number (2), Qnil);
483 }
484
485 /* hmmm... perhaps these let us bracket screen changes so that we can flush
486 clumps rather than one-character-at-a-time...
487
488 we'll start with not moving the cursor while an update is in progress. */
489 void
490 update_begin (struct frame * f)
491 {
492 }
493
494 void
495 update_end (struct frame * f)
496 {
497 SetConsoleCursorPosition (cur_screen, cursor_coords);
498 }
499
500 void
501 set_terminal_window (int size)
502 {
503 }
504
505 /***********************************************************************
506 Faces
507 ***********************************************************************/
508
509
510 /* Turn appearances of face FACE_ID on tty frame F on. */
511
512 static WORD
513 w32_face_attributes (f, face_id)
514 struct frame *f;
515 int face_id;
516 {
517 WORD char_attr;
518 struct face *face = FACE_FROM_ID (f, face_id);
519
520 xassert (face != NULL);
521
522 char_attr = char_attr_normal;
523
524 if (face->foreground != FACE_TTY_DEFAULT_FG_COLOR
525 && face->foreground != FACE_TTY_DEFAULT_COLOR)
526 char_attr = (char_attr & 0xfff0) + (face->foreground % 16);
527
528 if (face->background != FACE_TTY_DEFAULT_BG_COLOR
529 && face->background != FACE_TTY_DEFAULT_COLOR)
530 char_attr = (char_attr & 0xff0f) + ((face->background % 16) << 4);
531
532
533 /* NTEMACS_TODO: Faces defined during startup get both foreground
534 and background of 0. Need a better way around this - for now detect
535 the problem and invert one of the faces to make the text readable. */
536 if (((char_attr & 0x00f0) >> 4) == (char_attr & 0x000f))
537 char_attr ^= 0x0007;
538
539 if (face->tty_reverse_p)
540 char_attr = (char_attr & 0xff00) + ((char_attr & 0x000f) << 4)
541 + ((char_attr & 0x00f0) >> 4);
542
543 return char_attr;
544 }
545
546
547 /* Emulation of some X window features from xfns.c and xfaces.c. */
548
549 extern char unspecified_fg[], unspecified_bg[];
550
551
552 /* Given a color index, return its standard name. */
553 Lisp_Object
554 vga_stdcolor_name (int idx)
555 {
556 /* Standard VGA colors, in the order of their standard numbering
557 in the default VGA palette. */
558 static char *vga_colors[16] = {
559 "black", "blue", "green", "cyan", "red", "magenta", "brown",
560 "lightgray", "darkgray", "lightblue", "lightgreen", "lightcyan",
561 "lightred", "lightmagenta", "yellow", "white"
562 };
563
564 extern Lisp_Object Qunspecified;
565
566 if (idx >= 0 && idx < sizeof (vga_colors) / sizeof (vga_colors[0]))
567 return build_string (vga_colors[idx]);
568 else
569 return Qunspecified; /* meaning the default */
570 }
571
572 typedef int (*term_hook) ();
573
574 void
575 initialize_w32_display (void)
576 {
577 CONSOLE_SCREEN_BUFFER_INFO info;
578
579 cursor_to_hook = move_cursor;
580 raw_cursor_to_hook = move_cursor;
581 clear_to_end_hook = clear_to_end;
582 clear_frame_hook = clear_frame;
583 clear_end_of_line_hook = clear_end_of_line;
584 ins_del_lines_hook = ins_del_lines;
585 insert_glyphs_hook = insert_glyphs;
586 write_glyphs_hook = write_glyphs;
587 delete_glyphs_hook = delete_glyphs;
588 ring_bell_hook = w32_sys_ring_bell;
589 reset_terminal_modes_hook = reset_terminal_modes;
590 set_terminal_modes_hook = set_terminal_modes;
591 set_terminal_window_hook = set_terminal_window;
592 update_begin_hook = update_begin;
593 update_end_hook = update_end;
594
595 read_socket_hook = w32_console_read_socket;
596 mouse_position_hook = w32_console_mouse_position;
597
598 /* Initialize interrupt_handle. */
599 init_crit ();
600
601 /* Remember original console settings. */
602 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
603 GetConsoleMode (keyboard_handle, &prev_console_mode);
604
605 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
606
607 #ifdef USE_SEPARATE_SCREEN
608 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
609 0, NULL,
610 CONSOLE_TEXTMODE_BUFFER,
611 NULL);
612
613 if (cur_screen == INVALID_HANDLE_VALUE)
614 {
615 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
616 printf ("LastError = 0x%lx\n", GetLastError ());
617 fflush (stdout);
618 exit (0);
619 }
620 #else
621 cur_screen = prev_screen;
622 GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
623 #endif
624
625 /* Respect setting of LINES and COLUMNS environment variables. */
626 {
627 char * lines = getenv("LINES");
628 char * columns = getenv("COLUMNS");
629
630 if (lines != NULL && columns != NULL)
631 {
632 SMALL_RECT new_win_dims;
633 COORD new_size;
634
635 new_size.X = atoi (columns);
636 new_size.Y = atoi (lines);
637
638 GetConsoleScreenBufferInfo (cur_screen, &info);
639
640 /* Shrink the window first, so the buffer dimensions can be
641 reduced if necessary. */
642 new_win_dims.Top = 0;
643 new_win_dims.Left = 0;
644 new_win_dims.Bottom = min (new_size.Y, info.dwSize.Y) - 1;
645 new_win_dims.Right = min (new_size.X, info.dwSize.X) - 1;
646 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
647
648 SetConsoleScreenBufferSize (cur_screen, new_size);
649
650 /* Set the window size to match the buffer dimension. */
651 new_win_dims.Top = 0;
652 new_win_dims.Left = 0;
653 new_win_dims.Bottom = new_size.Y - 1;
654 new_win_dims.Right = new_size.X - 1;
655 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
656 }
657 }
658
659 GetConsoleScreenBufferInfo (cur_screen, &info);
660
661 meta_key = 1;
662 char_attr_normal = info.wAttributes;
663
664 if (w32_use_full_screen_buffer)
665 {
666 FRAME_LINES (SELECTED_FRAME ()) = info.dwSize.Y; /* lines per page */
667 SET_FRAME_COLS (SELECTED_FRAME (), info.dwSize.X); /* characters per line */
668 }
669 else
670 {
671 /* Lines per page. Use buffer coords instead of buffer size. */
672 FRAME_LINES (SELECTED_FRAME ()) = 1 + info.srWindow.Bottom -
673 info.srWindow.Top;
674 /* Characters per line. Use buffer coords instead of buffer size. */
675 SET_FRAME_COLS (SELECTED_FRAME (), 1 + info.srWindow.Right -
676 info.srWindow.Left);
677 }
678
679 /* Setup w32_display_info structure for this frame. */
680
681 w32_initialize_display_info (build_string ("Console"));
682
683 }
684
685 DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
686 doc: /* Set screen colors. */)
687 (foreground, background)
688 Lisp_Object foreground;
689 Lisp_Object background;
690 {
691 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
692
693 Frecenter (Qnil);
694 return Qt;
695 }
696
697 DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
698 doc: /* Set cursor size. */)
699 (size)
700 Lisp_Object size;
701 {
702 CONSOLE_CURSOR_INFO cci;
703 cci.dwSize = XFASTINT (size);
704 cci.bVisible = TRUE;
705 (void) SetConsoleCursorInfo (cur_screen, &cci);
706
707 return Qt;
708 }
709
710 void
711 syms_of_ntterm ()
712 {
713 DEFVAR_BOOL ("w32-use-full-screen-buffer",
714 &w32_use_full_screen_buffer,
715 doc: /* Non-nil means make terminal frames use the full screen buffer dimensions.
716 This is desirable when running Emacs over telnet, and is the default.
717 A value of nil means use the current console window dimensions; this
718 may be preferrable when working directly at the console with a large
719 scroll-back buffer. */);
720 w32_use_full_screen_buffer = 1;
721
722 defsubr (&Sset_screen_color);
723 defsubr (&Sset_cursor_size);
724 defsubr (&Sset_message_beep);
725 }
726
727 /* arch-tag: a390a07f-f661-42bc-aeb4-e6d8bf860337
728 (do not change this comment) */