Patch of Alan Shutko <ats@acm.org> by way of rms.
[emacs.git] / src / w32console.c
blob490c1e10373a24f7440d5f094d49dfdd17520ac1
1 /* Terminal hooks for GNU Emacs on the Microsoft W32 API.
2 Copyright (C) 1992, 1999 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
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.
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.
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.
21 Tim Fleehart (apollo@online.com) 1-17-92
22 Geoff Voelker (voelker@cs.washington.edu) 9-12-93
26 #include <config.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <windows.h>
31 #include <string.h>
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"
44 /* from window.c */
45 extern Lisp_Object Frecenter ();
47 /* from keyboard.c */
48 extern int detect_input_pending ();
50 /* from sysdep.c */
51 extern int read_input_pending ();
53 extern struct frame * updating_frame;
54 extern int meta_key;
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);
72 static COORD cursor_coords;
73 static HANDLE prev_screen, cur_screen;
74 static WORD char_attr_normal;
75 static DWORD prev_console_mode;
77 #ifndef USE_SEPARATE_SCREEN
78 static CONSOLE_CURSOR_INFO prev_console_cursor;
79 #endif
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;
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. */
93 BOOL
94 ctrl_c_handler (unsigned long type)
96 /* Only ignore "interrupt" events when running interactively. */
97 return (!noninteractive
98 && (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT));
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 ())
105 /* Move the cursor to (row, col). */
106 void
107 move_cursor (int row, int col)
109 cursor_coords.X = col;
110 cursor_coords.Y = row;
112 if (updating_frame == (struct frame *) NULL)
114 SetConsoleCursorPosition (cur_screen, cursor_coords);
118 /* Clear from cursor to end of screen. */
119 void
120 clear_to_end (void)
122 struct frame * f = PICK_FRAME ();
124 clear_end_of_line (FRAME_WIDTH (f) - 1);
125 ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
128 /* Clear the frame. */
129 void
130 clear_frame (void)
132 struct frame * f = PICK_FRAME ();
133 COORD dest;
134 int n;
135 DWORD r;
136 CONSOLE_SCREEN_BUFFER_INFO info;
138 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
140 /* Remember that the screen buffer might be wider than the window. */
141 n = FRAME_HEIGHT (f) * info.dwSize.X;
142 dest.X = dest.Y = 0;
144 FillConsoleOutputAttribute (cur_screen, char_attr_normal, n, dest, &r);
145 FillConsoleOutputCharacter (cur_screen, ' ', n, dest, &r);
147 move_cursor (0, 0);
151 static struct glyph glyph_base[256];
152 static BOOL ceol_initialized = FALSE;
154 /* Clear from Cursor to end (what's "standout marker"?). */
155 void
156 clear_end_of_line (int end)
158 if (!ceol_initialized)
160 int i;
161 for (i = 0; i < 256; i++)
163 memcpy (&glyph_base[i], &space_glyph, sizeof (struct glyph));
165 ceol_initialized = TRUE;
167 write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
170 /* Insert n lines at vpos. if n is negative delete -n lines. */
171 void
172 ins_del_lines (int vpos, int n)
174 int i, nb;
175 SMALL_RECT scroll;
176 COORD dest;
177 CHAR_INFO fill;
178 struct frame * f = PICK_FRAME ();
180 if (n < 0)
182 scroll.Top = vpos - n;
183 scroll.Bottom = FRAME_HEIGHT (f);
184 dest.Y = vpos;
186 else
188 scroll.Top = vpos;
189 scroll.Bottom = FRAME_HEIGHT (f) - n;
190 dest.Y = vpos + n;
192 scroll.Left = 0;
193 scroll.Right = FRAME_WIDTH (f);
195 dest.X = 0;
197 fill.Char.AsciiChar = 0x20;
198 fill.Attributes = char_attr_normal;
200 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
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. */
209 if (n > 0)
211 if (scroll.Bottom < dest.Y)
213 for (i = scroll.Bottom; i < dest.Y; i++)
215 move_cursor (i, 0);
216 clear_end_of_line (FRAME_WIDTH (f));
220 else
222 nb = dest.Y + (scroll.Bottom - scroll.Top) + 1;
224 if (nb < scroll.Top)
226 for (i = nb; i < scroll.Top; i++)
228 move_cursor (i, 0);
229 clear_end_of_line (FRAME_WIDTH (f));
234 cursor_coords.X = 0;
235 cursor_coords.Y = vpos;
238 #undef LEFT
239 #undef RIGHT
240 #define LEFT 1
241 #define RIGHT 0
243 void
244 scroll_line (int dist, int direction)
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 ();
253 scroll.Top = cursor_coords.Y;
254 scroll.Bottom = cursor_coords.Y;
256 if (direction == LEFT)
258 scroll.Left = cursor_coords.X + dist;
259 scroll.Right = FRAME_WIDTH (f) - 1;
261 else
263 scroll.Left = cursor_coords.X;
264 scroll.Right = FRAME_WIDTH (f) - dist - 1;
267 dest.X = cursor_coords.X;
268 dest.Y = cursor_coords.Y;
270 fill.Char.AsciiChar = 0x20;
271 fill.Attributes = char_attr_normal;
273 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
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)
281 scroll_line (len, RIGHT);
283 /* Move len chars to the right starting at cursor_coords, fill with blanks */
284 if (start)
286 /* Print the first len characters of start, cursor_coords.X adjusted
287 by write_glyphs. */
289 write_glyphs (start, len);
291 else
293 clear_end_of_line (cursor_coords.X + len);
297 void
298 write_glyphs (register struct glyph *string, register int len)
300 int produced, consumed;
301 DWORD r;
302 struct frame * f = PICK_FRAME ();
303 WORD char_attr;
304 unsigned char conversion_buffer[1024];
305 int conversion_buffer_size = sizeof conversion_buffer;
307 if (len <= 0)
308 return;
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;
314 while (len > 0)
316 /* Identify a run of glyphs with the same face. */
317 int face_id = string->face_id;
318 int n;
320 for (n = 1; n < len; ++n)
321 if (string[n].face_id != face_id)
322 break;
324 /* Turn appearance modes of the face of the run on. */
325 char_attr = w32_face_attributes (f, face_id);
327 while (n > 0)
329 /* We use a fixed size (1024 bytes) of conversion buffer.
330 Usually it is sufficient, but if not, we just repeat the
331 loop. */
332 produced = encode_terminal_code (string, conversion_buffer,
333 n, conversion_buffer_size,
334 &consumed);
335 if (produced > 0)
337 /* Set the attribute for these characters. */
338 if (!FillConsoleOutputAttribute (cur_screen, char_attr,
339 produced, cursor_coords, &r))
341 printf ("Failed writing console attributes: %d\n",
342 GetLastError ());
343 fflush (stdout);
346 /* Write the characters. */
347 if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer,
348 produced, cursor_coords, &r))
350 printf ("Failed writing console characters: %d\n",
351 GetLastError ());
352 fflush (stdout);
355 cursor_coords.X += produced;
356 move_cursor (cursor_coords.Y, cursor_coords.X);
358 len -= consumed;
359 n -= consumed;
360 string += consumed;
364 /* We may have to output some codes to terminate the writing. */
365 if (CODING_REQUIRE_FLUSHING (&terminal_coding))
367 terminal_coding.mode |= CODING_MODE_LAST_BLOCK;
368 encode_coding (&terminal_coding, "", conversion_buffer,
369 0, conversion_buffer_size);
370 if (terminal_coding.produced > 0)
372 if (!FillConsoleOutputAttribute (cur_screen, char_attr_normal,
373 terminal_coding.produced,
374 cursor_coords, &r))
376 printf ("Failed writing console attributes: %d\n",
377 GetLastError ());
378 fflush (stdout);
381 /* Write the characters. */
382 if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer,
383 produced, cursor_coords, &r))
385 printf ("Failed writing console characters: %d\n",
386 GetLastError ());
387 fflush (stdout);
394 void
395 delete_glyphs (int n)
397 /* delete chars means scroll chars from cursor_coords.X + n to
398 cursor_coords.X, anything beyond the edge of the screen should
399 come out empty... */
401 scroll_line (n, LEFT);
404 static unsigned int sound_type = 0xFFFFFFFF;
405 #define MB_EMACS_SILENT (0xFFFFFFFF - 1)
407 void
408 w32_sys_ring_bell (void)
410 if (sound_type == 0xFFFFFFFF)
412 Beep (666, 100);
414 else if (sound_type == MB_EMACS_SILENT)
416 /* Do nothing. */
418 else
419 MessageBeep (sound_type);
422 DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
423 doc: /* Set the sound generated when the bell is rung.
424 SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent
425 to use the corresponding system sound for the bell. The 'silent sound
426 prevents Emacs from making any sound at all.
427 SOUND is nil to use the normal beep. */)
428 (sound)
429 Lisp_Object sound;
431 CHECK_SYMBOL (sound);
433 if (NILP (sound))
434 sound_type = 0xFFFFFFFF;
435 else if (EQ (sound, intern ("asterisk")))
436 sound_type = MB_ICONASTERISK;
437 else if (EQ (sound, intern ("exclamation")))
438 sound_type = MB_ICONEXCLAMATION;
439 else if (EQ (sound, intern ("hand")))
440 sound_type = MB_ICONHAND;
441 else if (EQ (sound, intern ("question")))
442 sound_type = MB_ICONQUESTION;
443 else if (EQ (sound, intern ("ok")))
444 sound_type = MB_OK;
445 else if (EQ (sound, intern ("silent")))
446 sound_type = MB_EMACS_SILENT;
447 else
448 sound_type = 0xFFFFFFFF;
450 return sound;
453 void
454 reset_terminal_modes (void)
456 #ifdef USE_SEPARATE_SCREEN
457 SetConsoleActiveScreenBuffer (prev_screen);
458 #else
459 SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
460 #endif
461 SetConsoleMode (keyboard_handle, prev_console_mode);
464 void
465 set_terminal_modes (void)
467 CONSOLE_CURSOR_INFO cci;
469 /* make cursor big and visible (100 on Win95 makes it disappear) */
470 cci.dwSize = 99;
471 cci.bVisible = TRUE;
472 (void) SetConsoleCursorInfo (cur_screen, &cci);
474 SetConsoleActiveScreenBuffer (cur_screen);
476 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
478 /* Initialize input mode: interrupt_input off, no flow control, allow
479 8 bit character input, standard quit char. */
480 Fset_input_mode (Qnil, Qnil, make_number (2), Qnil);
483 /* hmmm... perhaps these let us bracket screen changes so that we can flush
484 clumps rather than one-character-at-a-time...
486 we'll start with not moving the cursor while an update is in progress. */
487 void
488 update_begin (struct frame * f)
492 void
493 update_end (struct frame * f)
495 SetConsoleCursorPosition (cur_screen, cursor_coords);
498 void
499 set_terminal_window (int size)
503 /***********************************************************************
504 Faces
505 ***********************************************************************/
508 /* Turn appearances of face FACE_ID on tty frame F on. */
510 static WORD
511 w32_face_attributes (f, face_id)
512 struct frame *f;
513 int face_id;
515 WORD char_attr;
516 struct face *face = FACE_FROM_ID (f, face_id);
518 xassert (face != NULL);
520 char_attr = char_attr_normal;
522 if (face->foreground != FACE_TTY_DEFAULT_FG_COLOR
523 && face->foreground != FACE_TTY_DEFAULT_COLOR)
524 char_attr = (char_attr & 0xfff0) + (face->foreground % 16);
526 if (face->background != FACE_TTY_DEFAULT_BG_COLOR
527 && face->background != FACE_TTY_DEFAULT_COLOR)
528 char_attr = (char_attr & 0xff0f) + ((face->background % 16) << 4);
531 /* NTEMACS_TODO: Faces defined during startup get both foreground
532 and background of 0. Need a better way around this - for now detect
533 the problem and invert one of the faces to make the text readable. */
534 if (((char_attr & 0x00f0) >> 4) == (char_attr & 0x000f))
535 char_attr ^= 0x0007;
537 if (face->tty_reverse_p)
538 char_attr = (char_attr & 0xff00) + ((char_attr & 0x000f) << 4)
539 + ((char_attr & 0x00f0) >> 4);
541 return char_attr;
545 /* Emulation of some X window features from xfns.c and xfaces.c. */
547 extern char unspecified_fg[], unspecified_bg[];
550 /* Given a color index, return its standard name. */
551 Lisp_Object
552 vga_stdcolor_name (int idx)
554 /* Standard VGA colors, in the order of their standard numbering
555 in the default VGA palette. */
556 static char *vga_colors[16] = {
557 "black", "blue", "green", "cyan", "red", "magenta", "brown",
558 "lightgray", "darkgray", "lightblue", "lightgreen", "lightcyan",
559 "lightred", "lightmagenta", "yellow", "white"
562 extern Lisp_Object Qunspecified;
564 if (idx >= 0 && idx < sizeof (vga_colors) / sizeof (vga_colors[0]))
565 return build_string (vga_colors[idx]);
566 else
567 return Qunspecified; /* meaning the default */
570 typedef int (*term_hook) ();
572 void
573 initialize_w32_display (void)
575 CONSOLE_SCREEN_BUFFER_INFO info;
577 cursor_to_hook = move_cursor;
578 raw_cursor_to_hook = move_cursor;
579 clear_to_end_hook = clear_to_end;
580 clear_frame_hook = clear_frame;
581 clear_end_of_line_hook = clear_end_of_line;
582 ins_del_lines_hook = ins_del_lines;
583 insert_glyphs_hook = insert_glyphs;
584 write_glyphs_hook = write_glyphs;
585 delete_glyphs_hook = delete_glyphs;
586 ring_bell_hook = w32_sys_ring_bell;
587 reset_terminal_modes_hook = reset_terminal_modes;
588 set_terminal_modes_hook = set_terminal_modes;
589 set_terminal_window_hook = set_terminal_window;
590 update_begin_hook = update_begin;
591 update_end_hook = update_end;
593 read_socket_hook = w32_console_read_socket;
594 mouse_position_hook = w32_console_mouse_position;
595 estimate_mode_line_height_hook = 0;
597 /* Initialize interrupt_handle. */
598 init_crit ();
600 /* Remember original console settings. */
601 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
602 GetConsoleMode (keyboard_handle, &prev_console_mode);
604 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
606 #ifdef USE_SEPARATE_SCREEN
607 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
608 0, NULL,
609 CONSOLE_TEXTMODE_BUFFER,
610 NULL);
612 if (cur_screen == INVALID_HANDLE_VALUE)
614 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
615 printf ("LastError = 0x%lx\n", GetLastError ());
616 fflush (stdout);
617 exit (0);
619 #else
620 cur_screen = prev_screen;
621 GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
622 #endif
624 /* Respect setting of LINES and COLUMNS environment variables. */
626 char * lines = getenv("LINES");
627 char * columns = getenv("COLUMNS");
629 if (lines != NULL && columns != NULL)
631 SMALL_RECT new_win_dims;
632 COORD new_size;
634 new_size.X = atoi (columns);
635 new_size.Y = atoi (lines);
637 GetConsoleScreenBufferInfo (cur_screen, &info);
639 /* Shrink the window first, so the buffer dimensions can be
640 reduced if necessary. */
641 new_win_dims.Top = 0;
642 new_win_dims.Left = 0;
643 new_win_dims.Bottom = min (new_size.Y, info.dwSize.Y) - 1;
644 new_win_dims.Right = min (new_size.X, info.dwSize.X) - 1;
645 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
647 SetConsoleScreenBufferSize (cur_screen, new_size);
649 /* Set the window size to match the buffer dimension. */
650 new_win_dims.Top = 0;
651 new_win_dims.Left = 0;
652 new_win_dims.Bottom = new_size.Y - 1;
653 new_win_dims.Right = new_size.X - 1;
654 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
658 GetConsoleScreenBufferInfo (cur_screen, &info);
660 meta_key = 1;
661 char_attr_normal = info.wAttributes;
663 if (w32_use_full_screen_buffer)
665 FRAME_HEIGHT (SELECTED_FRAME ()) = info.dwSize.Y; /* lines per page */
666 SET_FRAME_WIDTH (SELECTED_FRAME (), info.dwSize.X); /* characters per line */
668 else
670 /* Lines per page. Use buffer coords instead of buffer size. */
671 FRAME_HEIGHT (SELECTED_FRAME ()) = 1 + info.srWindow.Bottom -
672 info.srWindow.Top;
673 /* Characters per line. Use buffer coords instead of buffer size. */
674 SET_FRAME_WIDTH (SELECTED_FRAME (), 1 + info.srWindow.Right -
675 info.srWindow.Left);
678 /* Setup w32_display_info structure for this frame. */
680 w32_initialize_display_info (build_string ("Console"));
684 DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
685 doc: /* Set screen colors. */)
686 (foreground, background)
687 Lisp_Object foreground;
688 Lisp_Object background;
690 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
692 Frecenter (Qnil);
693 return Qt;
696 DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
697 doc: /* Set cursor size. */)
698 (size)
699 Lisp_Object size;
701 CONSOLE_CURSOR_INFO cci;
702 cci.dwSize = XFASTINT (size);
703 cci.bVisible = TRUE;
704 (void) SetConsoleCursorInfo (cur_screen, &cci);
706 return Qt;
709 #ifndef HAVE_NTGUI
710 void
711 pixel_to_glyph_coords (struct frame * f, int pix_x, int pix_y, int *x, int *y,
712 void *bounds, int noclip)
714 *x = pix_x;
715 *y = pix_y;
718 void
719 glyph_to_pixel_coords (struct window * f, int x, int y, int *pix_x, int *pix_y)
721 *pix_x = x;
722 *pix_y = y;
724 #endif /* !HAVE_NTGUI */
726 void
727 syms_of_ntterm ()
729 DEFVAR_BOOL ("w32-use-full-screen-buffer",
730 &w32_use_full_screen_buffer,
731 doc: /* Non-nil means make terminal frames use the full screen buffer dimensions.
732 This is desirable when running Emacs over telnet, and is the default.
733 A value of nil means use the current console window dimensions; this
734 may be preferrable when working directly at the console with a large
735 scroll-back buffer. */);
736 w32_use_full_screen_buffer = 1;
738 defsubr (&Sset_screen_color);
739 defsubr (&Sset_cursor_size);
740 defsubr (&Sset_message_beep);