(Vtemporary_file_directory): New variable.
[bpt/emacs.git] / src / w32console.c
1 /* Terminal hooks for GNU Emacs on the Microsoft W32 API.
2 Copyright (C) 1992 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
32 #include "lisp.h"
33 #include "charset.h"
34 #include "frame.h"
35 #include "disptab.h"
36 #include "termhooks.h"
37 #include "w32inevt.h"
38
39 /* from window.c */
40 extern Lisp_Object Frecenter ();
41
42 /* from keyboard.c */
43 extern int detect_input_pending ();
44
45 /* from sysdep.c */
46 extern int read_input_pending ();
47
48 extern FRAME_PTR updating_frame;
49 extern int meta_key;
50
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);
68
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;
74
75 #ifndef USE_SEPARATE_SCREEN
76 CONSOLE_CURSOR_INFO prev_console_cursor;
77 #endif
78
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;
84
85
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. */
89
90 BOOL
91 ctrl_c_handler (unsigned long type)
92 {
93 /* Only ignore "interrupt" events when running interactively. */
94 return (!noninteractive
95 && (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT));
96 }
97
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)
101
102 /* Move the cursor to (row, col). */
103 void
104 move_cursor (int row, int col)
105 {
106 cursor_coords.X = col;
107 cursor_coords.Y = row;
108
109 if (updating_frame == (FRAME_PTR) NULL)
110 {
111 SetConsoleCursorPosition (cur_screen, cursor_coords);
112 }
113 }
114
115 /* Clear from cursor to end of screen. */
116 void
117 clear_to_end (void)
118 {
119 FRAME_PTR f = PICK_FRAME ();
120
121 clear_end_of_line (FRAME_WIDTH (f) - 1);
122 ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
123 }
124
125 /* Clear the frame. */
126 void
127 clear_frame (void)
128 {
129 FRAME_PTR f = PICK_FRAME ();
130 COORD dest;
131 int n, r;
132 CONSOLE_SCREEN_BUFFER_INFO info;
133
134 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
135
136 hl_mode (0);
137
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;
141
142 FillConsoleOutputAttribute (cur_screen, char_attr, n, dest, &r);
143 FillConsoleOutputCharacter (cur_screen, ' ', n, dest, &r);
144
145 move_cursor (0, 0);
146 }
147
148
149 static GLYPH glyph_base[256];
150 static BOOL ceol_initialized = FALSE;
151
152 /* Clear from Cursor to end (what's "standout marker"?). */
153 void
154 clear_end_of_line (int end)
155 {
156 if (!ceol_initialized)
157 {
158 int i;
159 for (i = 0; i < 256; i++)
160 {
161 glyph_base[i] = SPACEGLYPH; /* empty space */
162 }
163 ceol_initialized = TRUE;
164 }
165 write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
166 }
167
168 /* Insert n lines at vpos. if n is negative delete -n lines. */
169 void
170 ins_del_lines (int vpos, int n)
171 {
172 int i, nb, save_highlight;
173 SMALL_RECT scroll;
174 COORD dest;
175 CHAR_INFO fill;
176 FRAME_PTR f = PICK_FRAME ();
177
178 if (n < 0)
179 {
180 scroll.Top = vpos - n;
181 scroll.Bottom = FRAME_HEIGHT (f);
182 dest.Y = vpos;
183 }
184 else
185 {
186 scroll.Top = vpos;
187 scroll.Bottom = FRAME_HEIGHT (f) - n;
188 dest.Y = vpos + n;
189 }
190 scroll.Left = 0;
191 scroll.Right = FRAME_WIDTH (f);
192
193 dest.X = 0;
194
195 save_highlight = hl_mode (0);
196
197 fill.Char.AsciiChar = 0x20;
198 fill.Attributes = char_attr;
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_WIDTH (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_WIDTH (f));
230 }
231 }
232 }
233
234 cursor_coords.X = 0;
235 cursor_coords.Y = vpos;
236
237 hl_mode (save_highlight);
238 }
239
240 /* Changes attribute to use when drawing characters to control. */
241 static int
242 hl_mode (int new_highlight)
243 {
244 static int highlight = 0;
245 int old_highlight;
246
247 old_highlight = highlight;
248 highlight = (new_highlight != 0);
249 if (highlight)
250 {
251 char_attr = char_attr_reverse;
252 }
253 else
254 {
255 char_attr = char_attr_normal;
256 }
257 return old_highlight;
258 }
259
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)
264 {
265 hl_mode (new_highlight);
266 move_cursor (vpos, 0);
267 clear_end_of_line (first_unused_hpos);
268 }
269
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)
274 {
275 hl_mode (highlight);
276 vpos; /* pedantic compiler silencer */
277 }
278
279 #undef LEFT
280 #undef RIGHT
281 #define LEFT 1
282 #define RIGHT 0
283
284 void
285 scroll_line (int dist, int direction)
286 {
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 ();
293
294 scroll.Top = cursor_coords.Y;
295 scroll.Bottom = cursor_coords.Y;
296
297 if (direction == LEFT)
298 {
299 scroll.Left = cursor_coords.X + dist;
300 scroll.Right = FRAME_WIDTH (f) - 1;
301 }
302 else
303 {
304 scroll.Left = cursor_coords.X;
305 scroll.Right = FRAME_WIDTH (f) - dist - 1;
306 }
307
308 dest.X = cursor_coords.X;
309 dest.Y = cursor_coords.Y;
310
311 fill.Char.AsciiChar = 0x20;
312 fill.Attributes = char_attr;
313
314 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
315 }
316
317
318 /* If start is zero insert blanks instead of a string at start ?. */
319 void
320 insert_glyphs (register GLYPH *start, register int len)
321 {
322 scroll_line (len, RIGHT);
323
324 /* Move len chars to the right starting at cursor_coords, fill with blanks */
325 if (start)
326 {
327 /* Print the first len characters of start, cursor_coords.X adjusted
328 by write_glyphs. */
329
330 write_glyphs (start, len);
331 }
332 else
333 {
334 clear_end_of_line (cursor_coords.X + len);
335 }
336 }
337
338 void
339 write_glyphs (register GLYPH *string, register int len)
340 {
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;
348
349 if (len <= 0)
350 return;
351
352 chars = alloca (len * sizeof (*chars));
353 if (chars == NULL)
354 {
355 printf ("alloca failed in write_glyphs\n");
356 return;
357 }
358
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)
363 {
364 glyph = *string++;
365
366 if (glyph > glyph_len)
367 {
368 *ptr++ = glyph & 0xFF;
369 continue;
370 }
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))
377 {
378 *ptr++ = glyph & 0xFF;
379 continue;
380 }
381 for (i = 0; i < GLYPH_LENGTH (glyph_table, glyph); i++)
382 {
383 *ptr++ = (GLYPH_STRING (glyph_table, glyph))[i];
384 }
385 }
386
387 /* Number of characters we have in the buffer. */
388 len = ptr-chars;
389
390 /* Set the attribute for these characters. */
391 if (!FillConsoleOutputAttribute (cur_screen, char_attr, len, cursor_coords, &i))
392 {
393 printf ("Failed writing console attributes: %d\n", GetLastError ());
394 fflush (stdout);
395 }
396
397 /* Write the characters. */
398 if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
399 {
400 printf ("Failed writing console characters: %d\n", GetLastError ());
401 fflush (stdout);
402 }
403
404 cursor_coords.X += len;
405 move_cursor (cursor_coords.Y, cursor_coords.X);
406 }
407
408 void
409 delete_glyphs (int n)
410 {
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... */
414
415 scroll_line (n, LEFT);
416 }
417
418 static unsigned int sound_type = 0xFFFFFFFF;
419 #define MB_EMACS_SILENT (0xFFFFFFFF - 1)
420
421 void
422 w32_sys_ring_bell (void)
423 {
424 if (sound_type == 0xFFFFFFFF)
425 {
426 Beep (666, 100);
427 }
428 else if (sound_type == MB_EMACS_SILENT)
429 {
430 /* Do nothing. */
431 }
432 else
433 MessageBeep (sound_type);
434 }
435
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;
444 {
445 CHECK_SYMBOL (sound, 0);
446
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;
463
464 return sound;
465 }
466
467 void
468 reset_terminal_modes (void)
469 {
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);
476 }
477
478 void
479 set_terminal_modes (void)
480 {
481 CONSOLE_CURSOR_INFO cci;
482
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);
487
488 SetConsoleActiveScreenBuffer (cur_screen);
489
490 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
491
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);
495 }
496
497 /* hmmm... perhaps these let us bracket screen changes so that we can flush
498 clumps rather than one-character-at-a-time...
499
500 we'll start with not moving the cursor while an update is in progress. */
501 void
502 update_begin (FRAME_PTR f)
503 {
504 }
505
506 void
507 update_end (FRAME_PTR f)
508 {
509 SetConsoleCursorPosition (cur_screen, cursor_coords);
510 }
511
512 void
513 set_terminal_window (int size)
514 {
515 }
516
517 typedef int (*term_hook) ();
518
519 void
520 initialize_w32_display (void)
521 {
522 CONSOLE_SCREEN_BUFFER_INFO info;
523
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;
541
542 read_socket_hook = w32_console_read_socket;
543 mouse_position_hook = w32_console_mouse_position;
544
545 /* Initialize interrupt_handle. */
546 init_crit ();
547
548 /* Remember original console settings. */
549 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
550 GetConsoleMode (keyboard_handle, &prev_console_mode);
551
552 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
553
554 #ifdef USE_SEPARATE_SCREEN
555 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
556 0, NULL,
557 CONSOLE_TEXTMODE_BUFFER,
558 NULL);
559
560 if (cur_screen == INVALID_HANDLE_VALUE)
561 {
562 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
563 printf ("LastError = 0x%lx\n", GetLastError ());
564 fflush (stdout);
565 exit (0);
566 }
567 #else
568 cur_screen = prev_screen;
569 GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
570 #endif
571
572 /* Respect setting of LINES and COLUMNS environment variables. */
573 {
574 char * lines = getenv("LINES");
575 char * columns = getenv("COLUMNS");
576
577 if (lines != NULL && columns != NULL)
578 {
579 SMALL_RECT new_win_dims;
580 COORD new_size;
581
582 new_size.X = atoi (columns);
583 new_size.Y = atoi (lines);
584
585 GetConsoleScreenBufferInfo (cur_screen, &info);
586
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);
594
595 SetConsoleScreenBufferSize (cur_screen, new_size);
596
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);
603 }
604 }
605
606 GetConsoleScreenBufferInfo (cur_screen, &info);
607
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);
612
613 if (w32_use_full_screen_buffer)
614 {
615 FRAME_HEIGHT (selected_frame) = info.dwSize.Y; /* lines per page */
616 SET_FRAME_WIDTH (selected_frame, info.dwSize.X); /* characters per line */
617 }
618 else
619 {
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);
626 }
627 }
628
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;
634 {
635 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
636 char_attr_reverse = XFASTINT (background) + (XFASTINT (foreground) << 4);
637
638 Frecenter (Qnil);
639 return Qt;
640 }
641
642 DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
643 "Set cursor size.")
644 (size)
645 Lisp_Object size;
646 {
647 CONSOLE_CURSOR_INFO cci;
648 cci.dwSize = XFASTINT (size);
649 cci.bVisible = TRUE;
650 (void) SetConsoleCursorInfo (cur_screen, &cci);
651
652 return Qt;
653 }
654
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)
659 {
660 *x = pix_x;
661 *y = pix_y;
662 }
663
664 void
665 glyph_to_pixel_coords (FRAME_PTR f, int x, int y, int *pix_x, int *pix_y)
666 {
667 *pix_x = x;
668 *pix_y = y;
669 }
670 #endif /* !HAVE_NTGUI */
671
672 void
673 syms_of_ntterm ()
674 {
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;
683
684 defsubr (&Sset_screen_color);
685 defsubr (&Sset_cursor_size);
686 defsubr (&Sset_message_beep);
687 }