(combine-run-hooks): New function.
[emacs.git] / src / w32console.c
blobac33abd650170b778ee32be9cb852a426b1fa876
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 /* Disable features in dispextern.h that require a Window System. */
39 #undef HAVE_WINDOW_SYSTEM
40 #include "frame.h"
41 #include "w32inevt.h"
42 #include "dispextern.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 change_line_highlight (int, int, int, int);
62 static void reassert_line_highlight (int, int);
63 static void insert_glyphs (struct glyph *start, int len);
64 static void write_glyphs (struct glyph *string, int len);
65 static void delete_glyphs (int n);
66 void w32_sys_ring_bell (void);
67 static void reset_terminal_modes (void);
68 static void set_terminal_modes (void);
69 static void set_terminal_window (int size);
70 static void update_begin (struct frame * f);
71 static void update_end (struct frame * f);
72 static WORD w32_face_attributes (struct frame *f, int face_id);
73 static int hl_mode (int new_highlight);
75 static COORD cursor_coords;
76 static HANDLE prev_screen, cur_screen;
77 static WORD char_attr_normal;
78 static DWORD prev_console_mode;
80 #ifndef USE_SEPARATE_SCREEN
81 static CONSOLE_CURSOR_INFO prev_console_cursor;
82 #endif
84 /* Determine whether to make frame dimensions match the screen buffer,
85 or the current window size. The former is desirable when running
86 over telnet, while the latter is more useful when working directly at
87 the console with a large scroll-back buffer. */
88 int w32_use_full_screen_buffer;
89 HANDLE keyboard_handle;
92 /* Setting this as the ctrl handler prevents emacs from being killed when
93 someone hits ^C in a 'suspended' session (child shell).
94 Also ignore Ctrl-Break signals. */
96 BOOL
97 ctrl_c_handler (unsigned long type)
99 /* Only ignore "interrupt" events when running interactively. */
100 return (!noninteractive
101 && (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT));
104 /* If we're updating a frame, use it as the current frame
105 Otherwise, use the selected frame. */
106 #define PICK_FRAME() (updating_frame ? updating_frame : SELECTED_FRAME ())
108 /* Move the cursor to (row, col). */
109 void
110 move_cursor (int row, int col)
112 cursor_coords.X = col;
113 cursor_coords.Y = row;
115 if (updating_frame == (struct frame *) NULL)
117 SetConsoleCursorPosition (cur_screen, cursor_coords);
121 /* Clear from cursor to end of screen. */
122 void
123 clear_to_end (void)
125 struct frame * f = PICK_FRAME ();
127 clear_end_of_line (FRAME_WIDTH (f) - 1);
128 ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
131 /* Clear the frame. */
132 void
133 clear_frame (void)
135 struct frame * f = PICK_FRAME ();
136 COORD dest;
137 int n, r;
138 CONSOLE_SCREEN_BUFFER_INFO info;
140 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
142 hl_mode (0);
144 /* Remember that the screen buffer might be wider than the window. */
145 n = FRAME_HEIGHT (f) * info.dwSize.X;
146 dest.X = dest.Y = 0;
148 FillConsoleOutputAttribute (cur_screen, char_attr_normal, n, dest, &r);
149 FillConsoleOutputCharacter (cur_screen, ' ', n, dest, &r);
151 move_cursor (0, 0);
155 static struct glyph glyph_base[256];
156 static BOOL ceol_initialized = FALSE;
158 /* Clear from Cursor to end (what's "standout marker"?). */
159 void
160 clear_end_of_line (int end)
162 if (!ceol_initialized)
164 int i;
165 for (i = 0; i < 256; i++)
167 memcpy (&glyph_base[i], &space_glyph, sizeof (struct glyph));
169 ceol_initialized = TRUE;
171 write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
174 /* Insert n lines at vpos. if n is negative delete -n lines. */
175 void
176 ins_del_lines (int vpos, int n)
178 int i, nb, save_highlight;
179 SMALL_RECT scroll;
180 COORD dest;
181 CHAR_INFO fill;
182 struct frame * f = PICK_FRAME ();
184 if (n < 0)
186 scroll.Top = vpos - n;
187 scroll.Bottom = FRAME_HEIGHT (f);
188 dest.Y = vpos;
190 else
192 scroll.Top = vpos;
193 scroll.Bottom = FRAME_HEIGHT (f) - n;
194 dest.Y = vpos + n;
196 scroll.Left = 0;
197 scroll.Right = FRAME_WIDTH (f);
199 dest.X = 0;
201 save_highlight = hl_mode (0);
203 fill.Char.AsciiChar = 0x20;
204 fill.Attributes = char_attr_normal;
206 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
208 /* Here we have to deal with a w32 console flake: If the scroll
209 region looks like abc and we scroll c to a and fill with d we get
210 cbd... if we scroll block c one line at a time to a, we get cdd...
211 Emacs expects cdd consistently... So we have to deal with that
212 here... (this also occurs scrolling the same way in the other
213 direction. */
215 if (n > 0)
217 if (scroll.Bottom < dest.Y)
219 for (i = scroll.Bottom; i < dest.Y; i++)
221 move_cursor (i, 0);
222 clear_end_of_line (FRAME_WIDTH (f));
226 else
228 nb = dest.Y + (scroll.Bottom - scroll.Top) + 1;
230 if (nb < scroll.Top)
232 for (i = nb; i < scroll.Top; i++)
234 move_cursor (i, 0);
235 clear_end_of_line (FRAME_WIDTH (f));
240 cursor_coords.X = 0;
241 cursor_coords.Y = vpos;
243 hl_mode (save_highlight);
246 /* Changes attribute to use when drawing characters to control. */
247 static int
248 hl_mode (int new_highlight)
250 static int highlight = 0;
251 int old_highlight;
253 old_highlight = highlight;
254 highlight = (new_highlight != 0);
256 return old_highlight;
259 /* Call this when about to modify line at position VPOS and change whether it
260 is highlighted. */
261 void
262 change_line_highlight (int new_highlight, int vpos, int y,
263 int first_unused_hpos)
265 hl_mode (new_highlight);
266 move_cursor (vpos, 0);
267 clear_end_of_line (first_unused_hpos);
270 /* External interface to control of standout mode. Call this when about to
271 * modify line at position VPOS and not change whether it is highlighted. */
272 void
273 reassert_line_highlight (int highlight, int vpos)
275 hl_mode (highlight);
276 vpos; /* pedantic compiler silencer */
279 #undef LEFT
280 #undef RIGHT
281 #define LEFT 1
282 #define RIGHT 0
284 void
285 scroll_line (int dist, int direction)
287 /* The idea here is to implement a horizontal scroll in one line to
288 implement delete and half of insert. */
289 SMALL_RECT scroll;
290 COORD dest;
291 CHAR_INFO fill;
292 struct frame * f = PICK_FRAME ();
294 scroll.Top = cursor_coords.Y;
295 scroll.Bottom = cursor_coords.Y;
297 if (direction == LEFT)
299 scroll.Left = cursor_coords.X + dist;
300 scroll.Right = FRAME_WIDTH (f) - 1;
302 else
304 scroll.Left = cursor_coords.X;
305 scroll.Right = FRAME_WIDTH (f) - dist - 1;
308 dest.X = cursor_coords.X;
309 dest.Y = cursor_coords.Y;
311 fill.Char.AsciiChar = 0x20;
312 fill.Attributes = char_attr_normal;
314 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
318 /* If start is zero insert blanks instead of a string at start ?. */
319 void
320 insert_glyphs (register struct glyph *start, register int len)
322 scroll_line (len, RIGHT);
324 /* Move len chars to the right starting at cursor_coords, fill with blanks */
325 if (start)
327 /* Print the first len characters of start, cursor_coords.X adjusted
328 by write_glyphs. */
330 write_glyphs (start, len);
332 else
334 clear_end_of_line (cursor_coords.X + len);
338 void
339 write_glyphs (register struct glyph *string, register int len)
341 int produced, consumed, i;
342 struct frame * f = PICK_FRAME ();
343 WORD char_attr;
345 if (len <= 0)
346 return;
348 /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
349 the tail. */
350 terminal_coding.mode &= ~CODING_MODE_LAST_BLOCK;
352 while (len > 0)
354 /* Identify a run of glyphs with the same face. */
355 int face_id = string->face_id;
356 int n;
358 for (n = 1; n < len; ++n)
359 if (string[n].face_id != face_id)
360 break;
362 /* Turn appearance modes of the face of the run on. */
363 char_attr = w32_face_attributes (f, face_id);
365 while (n > 0)
367 /* We use a shared conversion buffer of the current size
368 (1024 bytes at least). Usually it is sufficient, but if
369 not, we just repeat the loop. */
370 produced = encode_terminal_code (string, conversion_buffer,
371 n, conversion_buffer_size,
372 &consumed);
373 if (produced > 0)
375 /* Set the attribute for these characters. */
376 if (!FillConsoleOutputAttribute (cur_screen, char_attr,
377 produced, cursor_coords, &i))
379 printf ("Failed writing console attributes: %d\n",
380 GetLastError ());
381 fflush (stdout);
384 /* Write the characters. */
385 if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer,
386 produced, cursor_coords, &i))
388 printf ("Failed writing console characters: %d\n",
389 GetLastError ());
390 fflush (stdout);
393 cursor_coords.X += produced;
394 move_cursor (cursor_coords.Y, cursor_coords.X);
396 len -= consumed;
397 n -= consumed;
398 string += consumed;
402 /* We may have to output some codes to terminate the writing. */
403 if (CODING_REQUIRE_FLUSHING (&terminal_coding))
405 terminal_coding.mode |= CODING_MODE_LAST_BLOCK;
406 encode_coding (&terminal_coding, "", conversion_buffer,
407 0, conversion_buffer_size);
408 if (terminal_coding.produced > 0)
410 if (!FillConsoleOutputAttribute (cur_screen, char_attr_normal,
411 terminal_coding.produced,
412 cursor_coords, &i))
414 printf ("Failed writing console attributes: %d\n",
415 GetLastError ());
416 fflush (stdout);
419 /* Write the characters. */
420 if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer,
421 produced, cursor_coords, &i))
423 printf ("Failed writing console characters: %d\n",
424 GetLastError ());
425 fflush (stdout);
432 void
433 delete_glyphs (int n)
435 /* delete chars means scroll chars from cursor_coords.X + n to
436 cursor_coords.X, anything beyond the edge of the screen should
437 come out empty... */
439 scroll_line (n, LEFT);
442 static unsigned int sound_type = 0xFFFFFFFF;
443 #define MB_EMACS_SILENT (0xFFFFFFFF - 1)
445 void
446 w32_sys_ring_bell (void)
448 if (sound_type == 0xFFFFFFFF)
450 Beep (666, 100);
452 else if (sound_type == MB_EMACS_SILENT)
454 /* Do nothing. */
456 else
457 MessageBeep (sound_type);
460 DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
461 "Set the sound generated when the bell is rung.\n\
462 SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent\n\
463 to use the corresponding system sound for the bell. The 'silent sound\n\
464 prevents Emacs from making any sound at all.\n\
465 SOUND is nil to use the normal beep.")
466 (sound)
467 Lisp_Object sound;
469 CHECK_SYMBOL (sound, 0);
471 if (NILP (sound))
472 sound_type = 0xFFFFFFFF;
473 else if (EQ (sound, intern ("asterisk")))
474 sound_type = MB_ICONASTERISK;
475 else if (EQ (sound, intern ("exclamation")))
476 sound_type = MB_ICONEXCLAMATION;
477 else if (EQ (sound, intern ("hand")))
478 sound_type = MB_ICONHAND;
479 else if (EQ (sound, intern ("question")))
480 sound_type = MB_ICONQUESTION;
481 else if (EQ (sound, intern ("ok")))
482 sound_type = MB_OK;
483 else if (EQ (sound, intern ("silent")))
484 sound_type = MB_EMACS_SILENT;
485 else
486 sound_type = 0xFFFFFFFF;
488 return sound;
491 void
492 reset_terminal_modes (void)
494 hl_mode (0);
496 #ifdef USE_SEPARATE_SCREEN
497 SetConsoleActiveScreenBuffer (prev_screen);
498 #else
499 SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
500 #endif
501 SetConsoleMode (keyboard_handle, prev_console_mode);
504 void
505 set_terminal_modes (void)
507 CONSOLE_CURSOR_INFO cci;
509 hl_mode (0);
511 /* make cursor big and visible (100 on Win95 makes it disappear) */
512 cci.dwSize = 99;
513 cci.bVisible = TRUE;
514 (void) SetConsoleCursorInfo (cur_screen, &cci);
516 SetConsoleActiveScreenBuffer (cur_screen);
518 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
520 /* Initialize input mode: interrupt_input off, no flow control, allow
521 8 bit character input, standard quit char. */
522 Fset_input_mode (Qnil, Qnil, make_number (2), Qnil);
525 /* hmmm... perhaps these let us bracket screen changes so that we can flush
526 clumps rather than one-character-at-a-time...
528 we'll start with not moving the cursor while an update is in progress. */
529 void
530 update_begin (struct frame * f)
532 hl_mode (0);
535 void
536 update_end (struct frame * f)
538 hl_mode (0);
539 SetConsoleCursorPosition (cur_screen, cursor_coords);
542 void
543 set_terminal_window (int size)
547 /***********************************************************************
548 Faces
549 ***********************************************************************/
552 /* Turn appearances of face FACE_ID on tty frame F on. */
554 static WORD
555 w32_face_attributes (f, face_id)
556 struct frame *f;
557 int face_id;
559 WORD char_attr;
560 int highlight_on_p;
561 struct face *face = FACE_FROM_ID (f, face_id);
563 highlight_on_p = hl_mode (0);
564 hl_mode (highlight_on_p);
566 xassert (face != NULL);
568 char_attr = char_attr_normal;
570 if (face->foreground != FACE_TTY_DEFAULT_FG_COLOR
571 && face->foreground != FACE_TTY_DEFAULT_COLOR)
572 char_attr = (char_attr & 0xfff0) + (face->foreground % 16);
574 if (face->background != FACE_TTY_DEFAULT_BG_COLOR
575 && face->background != FACE_TTY_DEFAULT_COLOR)
576 char_attr = (char_attr & 0xff0f) + ((face->background % 16) * 16);
579 /* Ensure readability (temporary measure until this all works) */
580 if (((char_attr & 0x00f0) >> 4) == (char_attr & 0x000f))
581 char_attr ^= 0x0007;
583 if (face->tty_reverse_p || highlight_on_p)
584 char_attr = (char_attr & 0xff00) + ((char_attr & 0x000f) << 4)
585 + ((char_attr & 0x00f0) >> 4);
587 return char_attr;
591 /* Emulation of some X window features from xfns.c and xfaces.c. */
593 extern char unspecified_fg[], unspecified_bg[];
596 /* Given a color index, return its standard name. */
597 Lisp_Object
598 vga_stdcolor_name (int idx)
600 /* Standard VGA colors, in the order of their standard numbering
601 in the default VGA palette. */
602 static char *vga_colors[16] = {
603 "black", "blue", "green", "cyan", "red", "magenta", "brown",
604 "lightgray", "darkgray", "lightblue", "lightgreen", "lightcyan",
605 "lightred", "lightmagenta", "yellow", "white"
608 extern Lisp_Object Qunspecified;
610 if (idx >= 0 && idx < sizeof (vga_colors) / sizeof (vga_colors[0]))
611 return build_string (vga_colors[idx]);
612 else
613 return Qunspecified; /* meaning the default */
616 typedef int (*term_hook) ();
618 void
619 initialize_w32_display (void)
621 CONSOLE_SCREEN_BUFFER_INFO info;
623 cursor_to_hook = move_cursor;
624 raw_cursor_to_hook = move_cursor;
625 clear_to_end_hook = clear_to_end;
626 clear_frame_hook = clear_frame;
627 clear_end_of_line_hook = clear_end_of_line;
628 ins_del_lines_hook = ins_del_lines;
629 change_line_highlight_hook = change_line_highlight;
630 reassert_line_highlight_hook = reassert_line_highlight;
631 insert_glyphs_hook = insert_glyphs;
632 write_glyphs_hook = write_glyphs;
633 delete_glyphs_hook = delete_glyphs;
634 ring_bell_hook = w32_sys_ring_bell;
635 reset_terminal_modes_hook = reset_terminal_modes;
636 set_terminal_modes_hook = set_terminal_modes;
637 set_terminal_window_hook = set_terminal_window;
638 update_begin_hook = update_begin;
639 update_end_hook = update_end;
641 read_socket_hook = w32_console_read_socket;
642 mouse_position_hook = w32_console_mouse_position;
643 estimate_mode_line_height_hook = 0;
645 /* Initialize interrupt_handle. */
646 init_crit ();
648 /* Remember original console settings. */
649 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
650 GetConsoleMode (keyboard_handle, &prev_console_mode);
652 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
654 #ifdef USE_SEPARATE_SCREEN
655 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
656 0, NULL,
657 CONSOLE_TEXTMODE_BUFFER,
658 NULL);
660 if (cur_screen == INVALID_HANDLE_VALUE)
662 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
663 printf ("LastError = 0x%lx\n", GetLastError ());
664 fflush (stdout);
665 exit (0);
667 #else
668 cur_screen = prev_screen;
669 GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
670 #endif
672 /* Respect setting of LINES and COLUMNS environment variables. */
674 char * lines = getenv("LINES");
675 char * columns = getenv("COLUMNS");
677 if (lines != NULL && columns != NULL)
679 SMALL_RECT new_win_dims;
680 COORD new_size;
682 new_size.X = atoi (columns);
683 new_size.Y = atoi (lines);
685 GetConsoleScreenBufferInfo (cur_screen, &info);
687 /* Shrink the window first, so the buffer dimensions can be
688 reduced if necessary. */
689 new_win_dims.Top = 0;
690 new_win_dims.Left = 0;
691 new_win_dims.Bottom = min (new_size.Y, info.dwSize.Y) - 1;
692 new_win_dims.Right = min (new_size.X, info.dwSize.X) - 1;
693 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
695 SetConsoleScreenBufferSize (cur_screen, new_size);
697 /* Set the window size to match the buffer dimension. */
698 new_win_dims.Top = 0;
699 new_win_dims.Left = 0;
700 new_win_dims.Bottom = new_size.Y - 1;
701 new_win_dims.Right = new_size.X - 1;
702 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
706 GetConsoleScreenBufferInfo (cur_screen, &info);
708 meta_key = 1;
709 char_attr_normal = info.wAttributes;
710 hl_mode (0);
712 if (w32_use_full_screen_buffer)
714 FRAME_HEIGHT (SELECTED_FRAME ()) = info.dwSize.Y; /* lines per page */
715 SET_FRAME_WIDTH (SELECTED_FRAME (), info.dwSize.X); /* characters per line */
717 else
719 /* Lines per page. Use buffer coords instead of buffer size. */
720 FRAME_HEIGHT (SELECTED_FRAME ()) = 1 + info.srWindow.Bottom -
721 info.srWindow.Top;
722 /* Characters per line. Use buffer coords instead of buffer size. */
723 SET_FRAME_WIDTH (SELECTED_FRAME (), 1 + info.srWindow.Right -
724 info.srWindow.Left);
727 /* Setup w32_display_info structure for this frame. */
729 w32_initialize_display_info (build_string ("Console"));
733 DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
734 "Set screen colors.")
735 (foreground, background)
736 Lisp_Object foreground;
737 Lisp_Object background;
739 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
741 Frecenter (Qnil);
742 return Qt;
745 DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
746 "Set cursor size.")
747 (size)
748 Lisp_Object size;
750 CONSOLE_CURSOR_INFO cci;
751 cci.dwSize = XFASTINT (size);
752 cci.bVisible = TRUE;
753 (void) SetConsoleCursorInfo (cur_screen, &cci);
755 return Qt;
758 #ifndef HAVE_NTGUI
759 void
760 pixel_to_glyph_coords (struct frame * f, int pix_x, int pix_y, int *x, int *y,
761 void *bounds, int noclip)
763 *x = pix_x;
764 *y = pix_y;
767 void
768 glyph_to_pixel_coords (struct frame * f, int x, int y, int *pix_x, int *pix_y)
770 *pix_x = x;
771 *pix_y = y;
773 #endif /* !HAVE_NTGUI */
775 void
776 syms_of_ntterm ()
778 DEFVAR_BOOL ("w32-use-full-screen-buffer",
779 &w32_use_full_screen_buffer,
780 "Non-nil means make terminal frames use the full screen buffer dimensions.\n\
781 This is desirable when running Emacs over telnet, and is the default.\n\
782 A value of nil means use the current console window dimensions; this\n\
783 may be preferrable when working directly at the console with a large\n\
784 scroll-back buffer.");
785 w32_use_full_screen_buffer = 1;
787 defsubr (&Sset_screen_color);
788 defsubr (&Sset_cursor_size);
789 defsubr (&Sset_message_beep);