.
[emacs.git] / src / w32console.c
blob6b7331fbd3c34664f53a24af9a5a22740b3b189a
1 /* Terminal hooks for GNU Emacs on the Microsoft W32 API.
2 Copyright (C) 1992 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>
32 #include "lisp.h"
33 #include "charset.h"
34 #include "frame.h"
35 #include "disptab.h"
36 #include "termhooks.h"
37 #include "w32inevt.h"
39 /* from window.c */
40 extern Lisp_Object Frecenter ();
42 /* from keyboard.c */
43 extern int detect_input_pending ();
45 /* from sysdep.c */
46 extern int read_input_pending ();
48 extern FRAME_PTR updating_frame;
49 extern int meta_key;
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 void w32_sys_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 int hl_mode (int new_highlight);
69 COORD cursor_coords;
70 HANDLE prev_screen, cur_screen;
71 UCHAR char_attr, char_attr_normal, char_attr_reverse;
72 HANDLE keyboard_handle;
73 DWORD prev_console_mode;
75 #ifndef USE_SEPARATE_SCREEN
76 CONSOLE_CURSOR_INFO prev_console_cursor;
77 #endif
79 /* Determine whether to make frame dimensions match the screen buffer,
80 or the current window size. The former is desirable when running
81 over telnet, while the latter is more useful when working directly at
82 the console with a large scroll-back buffer. */
83 int w32_use_full_screen_buffer;
86 /* Setting this as the ctrl handler prevents emacs from being killed when
87 someone hits ^C in a 'suspended' session (child shell).
88 Also ignore Ctrl-Break signals. */
90 BOOL
91 ctrl_c_handler (unsigned long type)
93 /* Only ignore "interrupt" events when running interactively. */
94 return (!noninteractive
95 && (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT));
98 /* If we're updating a frame, use it as the current frame
99 Otherwise, use the selected frame. */
100 #define PICK_FRAME() (updating_frame ? updating_frame : selected_frame)
102 /* Move the cursor to (row, col). */
103 void
104 move_cursor (int row, int col)
106 cursor_coords.X = col;
107 cursor_coords.Y = row;
109 if (updating_frame == (FRAME_PTR) NULL)
111 SetConsoleCursorPosition (cur_screen, cursor_coords);
115 /* Clear from cursor to end of screen. */
116 void
117 clear_to_end (void)
119 FRAME_PTR f = PICK_FRAME ();
121 clear_end_of_line (FRAME_WIDTH (f) - 1);
122 ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
125 /* Clear the frame. */
126 void
127 clear_frame (void)
129 FRAME_PTR f = PICK_FRAME ();
130 COORD dest;
131 int n, r;
132 CONSOLE_SCREEN_BUFFER_INFO info;
134 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
136 hl_mode (0);
138 /* Remember that the screen buffer might be wider than the window. */
139 n = FRAME_HEIGHT (f) * info.dwSize.X;
140 dest.X = dest.Y = 0;
142 FillConsoleOutputAttribute (cur_screen, char_attr, n, dest, &r);
143 FillConsoleOutputCharacter (cur_screen, ' ', n, dest, &r);
145 move_cursor (0, 0);
149 static GLYPH glyph_base[256];
150 static BOOL ceol_initialized = FALSE;
152 /* Clear from Cursor to end (what's "standout marker"?). */
153 void
154 clear_end_of_line (int end)
156 if (!ceol_initialized)
158 int i;
159 for (i = 0; i < 256; i++)
161 glyph_base[i] = SPACEGLYPH; /* empty space */
163 ceol_initialized = TRUE;
165 write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
168 /* Insert n lines at vpos. if n is negative delete -n lines. */
169 void
170 ins_del_lines (int vpos, int n)
172 int i, nb, save_highlight;
173 SMALL_RECT scroll;
174 COORD dest;
175 CHAR_INFO fill;
176 FRAME_PTR f = PICK_FRAME ();
178 if (n < 0)
180 scroll.Top = vpos - n;
181 scroll.Bottom = FRAME_HEIGHT (f);
182 dest.Y = vpos;
184 else
186 scroll.Top = vpos;
187 scroll.Bottom = FRAME_HEIGHT (f) - n;
188 dest.Y = vpos + n;
190 scroll.Left = 0;
191 scroll.Right = FRAME_WIDTH (f);
193 dest.X = 0;
195 save_highlight = hl_mode (0);
197 fill.Char.AsciiChar = 0x20;
198 fill.Attributes = char_attr;
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;
237 hl_mode (save_highlight);
240 /* Changes attribute to use when drawing characters to control. */
241 static int
242 hl_mode (int new_highlight)
244 static int highlight = 0;
245 int old_highlight;
247 old_highlight = highlight;
248 highlight = (new_highlight != 0);
249 if (highlight)
251 char_attr = char_attr_reverse;
253 else
255 char_attr = char_attr_normal;
257 return old_highlight;
260 /* Call this when about to modify line at position VPOS and change whether it
261 is highlighted. */
262 void
263 change_line_highlight (int new_highlight, int vpos, 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 FRAME_PTR 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;
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 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 GLYPH *string, register int len)
341 register unsigned int glyph_len = GLYPH_TABLE_LENGTH;
342 Lisp_Object *glyph_table = GLYPH_TABLE_BASE;
343 FRAME_PTR f = PICK_FRAME ();
344 register char *ptr;
345 GLYPH glyph;
346 char *chars;
347 int i;
349 if (len <= 0)
350 return;
352 chars = alloca (len * sizeof (*chars));
353 if (chars == NULL)
355 printf ("alloca failed in write_glyphs\n");
356 return;
359 /* We have to deal with the glyph indirection...go over the glyph
360 buffer and extract the characters. */
361 ptr = chars;
362 while (--len >= 0)
364 glyph = *string++;
366 if (glyph > glyph_len)
368 *ptr++ = glyph & 0xFF;
369 continue;
371 GLYPH_FOLLOW_ALIASES (glyph_table, glyph_len, glyph);
372 #ifndef HAVE_NTGUI
373 if (GLYPH_FACE (fixfix, glyph) != 0)
374 printf ("Glyph face is %d\n", GLYPH_FACE (fixfix, glyph));
375 #endif /* !HAVE_NTGUI */
376 if (GLYPH_SIMPLE_P (glyph_table, glyph_len, glyph))
378 *ptr++ = glyph & 0xFF;
379 continue;
381 for (i = 0; i < GLYPH_LENGTH (glyph_table, glyph); i++)
383 *ptr++ = (GLYPH_STRING (glyph_table, glyph))[i];
387 /* Number of characters we have in the buffer. */
388 len = ptr-chars;
390 /* Set the attribute for these characters. */
391 if (!FillConsoleOutputAttribute (cur_screen, char_attr, len, cursor_coords, &i))
393 printf ("Failed writing console attributes: %d\n", GetLastError ());
394 fflush (stdout);
397 /* Write the characters. */
398 if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
400 printf ("Failed writing console characters: %d\n", GetLastError ());
401 fflush (stdout);
404 cursor_coords.X += len;
405 move_cursor (cursor_coords.Y, cursor_coords.X);
408 void
409 delete_glyphs (int n)
411 /* delete chars means scroll chars from cursor_coords.X + n to
412 cursor_coords.X, anything beyond the edge of the screen should
413 come out empty... */
415 scroll_line (n, LEFT);
418 static unsigned int sound_type = 0xFFFFFFFF;
419 #define MB_EMACS_SILENT (0xFFFFFFFF - 1)
421 void
422 w32_sys_ring_bell (void)
424 if (sound_type == 0xFFFFFFFF)
426 Beep (666, 100);
428 else if (sound_type == MB_EMACS_SILENT)
430 /* Do nothing. */
432 else
433 MessageBeep (sound_type);
436 DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
437 "Set the sound generated when the bell is rung.\n\
438 SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent\n\
439 to use the corresponding system sound for the bell. The 'silent sound\n\
440 prevents Emacs from making any sound at all.\n\
441 SOUND is nil to use the normal beep.")
442 (sound)
443 Lisp_Object sound;
445 CHECK_SYMBOL (sound, 0);
447 if (NILP (sound))
448 sound_type = 0xFFFFFFFF;
449 else if (EQ (sound, intern ("asterisk")))
450 sound_type = MB_ICONASTERISK;
451 else if (EQ (sound, intern ("exclamation")))
452 sound_type = MB_ICONEXCLAMATION;
453 else if (EQ (sound, intern ("hand")))
454 sound_type = MB_ICONHAND;
455 else if (EQ (sound, intern ("question")))
456 sound_type = MB_ICONQUESTION;
457 else if (EQ (sound, intern ("ok")))
458 sound_type = MB_OK;
459 else if (EQ (sound, intern ("silent")))
460 sound_type = MB_EMACS_SILENT;
461 else
462 sound_type = 0xFFFFFFFF;
464 return sound;
467 void
468 reset_terminal_modes (void)
470 #ifdef USE_SEPARATE_SCREEN
471 SetConsoleActiveScreenBuffer (prev_screen);
472 #else
473 SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
474 #endif
475 SetConsoleMode (keyboard_handle, prev_console_mode);
478 void
479 set_terminal_modes (void)
481 CONSOLE_CURSOR_INFO cci;
483 /* make cursor big and visible (100 on Win95 makes it disappear) */
484 cci.dwSize = 99;
485 cci.bVisible = TRUE;
486 (void) SetConsoleCursorInfo (cur_screen, &cci);
488 SetConsoleActiveScreenBuffer (cur_screen);
490 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
492 /* Initialize input mode: interrupt_input off, no flow control, allow
493 8 bit character input, standard quit char. */
494 Fset_input_mode (Qnil, Qnil, make_number (2), Qnil);
497 /* hmmm... perhaps these let us bracket screen changes so that we can flush
498 clumps rather than one-character-at-a-time...
500 we'll start with not moving the cursor while an update is in progress. */
501 void
502 update_begin (FRAME_PTR f)
506 void
507 update_end (FRAME_PTR f)
509 SetConsoleCursorPosition (cur_screen, cursor_coords);
512 void
513 set_terminal_window (int size)
517 typedef int (*term_hook) ();
519 void
520 initialize_w32_display (void)
522 CONSOLE_SCREEN_BUFFER_INFO info;
524 cursor_to_hook = move_cursor;
525 raw_cursor_to_hook = move_cursor;
526 clear_to_end_hook = clear_to_end;
527 clear_frame_hook = clear_frame;
528 clear_end_of_line_hook = clear_end_of_line;
529 ins_del_lines_hook = ins_del_lines;
530 change_line_highlight_hook = change_line_highlight;
531 reassert_line_highlight_hook = reassert_line_highlight;
532 insert_glyphs_hook = insert_glyphs;
533 write_glyphs_hook = write_glyphs;
534 delete_glyphs_hook = delete_glyphs;
535 ring_bell_hook = w32_sys_ring_bell;
536 reset_terminal_modes_hook = reset_terminal_modes;
537 set_terminal_modes_hook = set_terminal_modes;
538 set_terminal_window_hook = set_terminal_window;
539 update_begin_hook = update_begin;
540 update_end_hook = update_end;
542 read_socket_hook = w32_console_read_socket;
543 mouse_position_hook = w32_console_mouse_position;
545 /* Initialize interrupt_handle. */
546 init_crit ();
548 /* Remember original console settings. */
549 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
550 GetConsoleMode (keyboard_handle, &prev_console_mode);
552 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
554 #ifdef USE_SEPARATE_SCREEN
555 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
556 0, NULL,
557 CONSOLE_TEXTMODE_BUFFER,
558 NULL);
560 if (cur_screen == INVALID_HANDLE_VALUE)
562 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
563 printf ("LastError = 0x%lx\n", GetLastError ());
564 fflush (stdout);
565 exit (0);
567 #else
568 cur_screen = prev_screen;
569 GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
570 #endif
572 /* Respect setting of LINES and COLUMNS environment variables. */
574 char * lines = getenv("LINES");
575 char * columns = getenv("COLUMNS");
577 if (lines != NULL && columns != NULL)
579 SMALL_RECT new_win_dims;
580 COORD new_size;
582 new_size.X = atoi (columns);
583 new_size.Y = atoi (lines);
585 GetConsoleScreenBufferInfo (cur_screen, &info);
587 /* Shrink the window first, so the buffer dimensions can be
588 reduced if necessary. */
589 new_win_dims.Top = 0;
590 new_win_dims.Left = 0;
591 new_win_dims.Bottom = min (new_size.Y, info.dwSize.Y) - 1;
592 new_win_dims.Right = min (new_size.X, info.dwSize.X) - 1;
593 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
595 SetConsoleScreenBufferSize (cur_screen, new_size);
597 /* Set the window size to match the buffer dimension. */
598 new_win_dims.Top = 0;
599 new_win_dims.Left = 0;
600 new_win_dims.Bottom = new_size.Y - 1;
601 new_win_dims.Right = new_size.X - 1;
602 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
606 GetConsoleScreenBufferInfo (cur_screen, &info);
608 meta_key = 1;
609 char_attr = info.wAttributes & 0xFF;
610 char_attr_normal = char_attr;
611 char_attr_reverse = ((char_attr & 0xf) << 4) + ((char_attr & 0xf0) >> 4);
613 if (w32_use_full_screen_buffer)
615 FRAME_HEIGHT (selected_frame) = info.dwSize.Y; /* lines per page */
616 SET_FRAME_WIDTH (selected_frame, info.dwSize.X); /* characters per line */
618 else
620 /* Lines per page. Use buffer coords instead of buffer size. */
621 FRAME_HEIGHT (selected_frame) = 1 + info.srWindow.Bottom -
622 info.srWindow.Top;
623 /* Characters per line. Use buffer coords instead of buffer size. */
624 SET_FRAME_WIDTH (selected_frame, 1 + info.srWindow.Right -
625 info.srWindow.Left);
629 DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
630 "Set screen colors.")
631 (foreground, background)
632 Lisp_Object foreground;
633 Lisp_Object background;
635 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
636 char_attr_reverse = XFASTINT (background) + (XFASTINT (foreground) << 4);
638 Frecenter (Qnil);
639 return Qt;
642 DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
643 "Set cursor size.")
644 (size)
645 Lisp_Object size;
647 CONSOLE_CURSOR_INFO cci;
648 cci.dwSize = XFASTINT (size);
649 cci.bVisible = TRUE;
650 (void) SetConsoleCursorInfo (cur_screen, &cci);
652 return Qt;
655 #ifndef HAVE_NTGUI
656 void
657 pixel_to_glyph_coords (FRAME_PTR f, int pix_x, int pix_y, int *x, int *y,
658 void *bounds, int noclip)
660 *x = pix_x;
661 *y = pix_y;
664 void
665 glyph_to_pixel_coords (FRAME_PTR f, int x, int y, int *pix_x, int *pix_y)
667 *pix_x = x;
668 *pix_y = y;
670 #endif /* !HAVE_NTGUI */
672 void
673 syms_of_ntterm ()
675 DEFVAR_BOOL ("w32-use-full-screen-buffer",
676 &w32_use_full_screen_buffer,
677 "Non-nil means make terminal frames use the full screen buffer dimensions.\n\
678 This is desirable when running Emacs over telnet, and is the default.\n\
679 A value of nil means use the current console window dimensions; this\n\
680 may be preferrable when working directly at the console with a large\n\
681 scroll-back buffer.");
682 w32_use_full_screen_buffer = 1;
684 defsubr (&Sset_screen_color);
685 defsubr (&Sset_cursor_size);
686 defsubr (&Sset_message_beep);