(Fread_file_name): Check type of DEFAULT_FILENAME.
[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
80 /* Setting this as the ctrl handler prevents emacs from being killed when
81 someone hits ^C in a 'suspended' session (child shell).
82 Also ignore Ctrl-Break signals. */
83
84 BOOL
85 ctrl_c_handler (unsigned long type)
86 {
87 /* Only ignore "interrupt" events when running interactively. */
88 return (!noninteractive
89 && (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT));
90 }
91
92 /* If we're updating a frame, use it as the current frame
93 Otherwise, use the selected frame. */
94 #define PICK_FRAME() (updating_frame ? updating_frame : selected_frame)
95
96 /* Move the cursor to (row, col). */
97 void
98 move_cursor (int row, int col)
99 {
100 cursor_coords.X = col;
101 cursor_coords.Y = row;
102
103 if (updating_frame == (FRAME_PTR) NULL)
104 {
105 SetConsoleCursorPosition (cur_screen, cursor_coords);
106 }
107 }
108
109 /* Clear from cursor to end of screen. */
110 void
111 clear_to_end (void)
112 {
113 FRAME_PTR f = PICK_FRAME ();
114
115 clear_end_of_line (FRAME_WIDTH (f) - 1);
116 ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
117 }
118
119 /* Clear the frame. */
120 void
121 clear_frame (void)
122 {
123 FRAME_PTR f = PICK_FRAME ();
124 COORD dest;
125 int n, r;
126 CONSOLE_SCREEN_BUFFER_INFO info;
127
128 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
129
130 hl_mode (0);
131
132 /* Remember that the screen buffer might be wider than the window. */
133 n = FRAME_HEIGHT (f) * info.dwSize.X;
134 dest.X = dest.Y = 0;
135
136 FillConsoleOutputAttribute (cur_screen, char_attr, n, dest, &r);
137 FillConsoleOutputCharacter (cur_screen, ' ', n, dest, &r);
138
139 move_cursor (0, 0);
140 }
141
142
143 static GLYPH glyph_base[256];
144 static BOOL ceol_initialized = FALSE;
145
146 /* Clear from Cursor to end (what's "standout marker"?). */
147 void
148 clear_end_of_line (int end)
149 {
150 if (!ceol_initialized)
151 {
152 int i;
153 for (i = 0; i < 256; i++)
154 {
155 glyph_base[i] = SPACEGLYPH; /* empty space */
156 }
157 ceol_initialized = TRUE;
158 }
159 write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
160 }
161
162 /* Insert n lines at vpos. if n is negative delete -n lines. */
163 void
164 ins_del_lines (int vpos, int n)
165 {
166 int i, nb, save_highlight;
167 SMALL_RECT scroll;
168 COORD dest;
169 CHAR_INFO fill;
170 FRAME_PTR f = PICK_FRAME ();
171
172 if (n < 0)
173 {
174 scroll.Top = vpos - n;
175 scroll.Bottom = FRAME_HEIGHT (f);
176 dest.Y = vpos;
177 }
178 else
179 {
180 scroll.Top = vpos;
181 scroll.Bottom = FRAME_HEIGHT (f) - n;
182 dest.Y = vpos + n;
183 }
184 scroll.Left = 0;
185 scroll.Right = FRAME_WIDTH (f);
186
187 dest.X = 0;
188
189 save_highlight = hl_mode (0);
190
191 fill.Char.AsciiChar = 0x20;
192 fill.Attributes = char_attr;
193
194 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
195
196 /* Here we have to deal with a w32 console flake: If the scroll
197 region looks like abc and we scroll c to a and fill with d we get
198 cbd... if we scroll block c one line at a time to a, we get cdd...
199 Emacs expects cdd consistently... So we have to deal with that
200 here... (this also occurs scrolling the same way in the other
201 direction. */
202
203 if (n > 0)
204 {
205 if (scroll.Bottom < dest.Y)
206 {
207 for (i = scroll.Bottom; i < dest.Y; i++)
208 {
209 move_cursor (i, 0);
210 clear_end_of_line (FRAME_WIDTH (f));
211 }
212 }
213 }
214 else
215 {
216 nb = dest.Y + (scroll.Bottom - scroll.Top) + 1;
217
218 if (nb < scroll.Top)
219 {
220 for (i = nb; i < scroll.Top; i++)
221 {
222 move_cursor (i, 0);
223 clear_end_of_line (FRAME_WIDTH (f));
224 }
225 }
226 }
227
228 cursor_coords.X = 0;
229 cursor_coords.Y = vpos;
230
231 hl_mode (save_highlight);
232 }
233
234 /* Changes attribute to use when drawing characters to control. */
235 static int
236 hl_mode (int new_highlight)
237 {
238 static int highlight = 0;
239 int old_highlight;
240
241 old_highlight = highlight;
242 highlight = (new_highlight != 0);
243 if (highlight)
244 {
245 char_attr = char_attr_reverse;
246 }
247 else
248 {
249 char_attr = char_attr_normal;
250 }
251 return old_highlight;
252 }
253
254 /* Call this when about to modify line at position VPOS and change whether it
255 is highlighted. */
256 void
257 change_line_highlight (int new_highlight, int vpos, int first_unused_hpos)
258 {
259 hl_mode (new_highlight);
260 move_cursor (vpos, 0);
261 clear_end_of_line (first_unused_hpos);
262 }
263
264 /* External interface to control of standout mode. Call this when about to
265 * modify line at position VPOS and not change whether it is highlighted. */
266 void
267 reassert_line_highlight (int highlight, int vpos)
268 {
269 hl_mode (highlight);
270 vpos; /* pedantic compiler silencer */
271 }
272
273 #undef LEFT
274 #undef RIGHT
275 #define LEFT 1
276 #define RIGHT 0
277
278 void
279 scroll_line (int dist, int direction)
280 {
281 /* The idea here is to implement a horizontal scroll in one line to
282 implement delete and half of insert. */
283 SMALL_RECT scroll;
284 COORD dest;
285 CHAR_INFO fill;
286 FRAME_PTR f = PICK_FRAME ();
287
288 scroll.Top = cursor_coords.Y;
289 scroll.Bottom = cursor_coords.Y;
290
291 if (direction == LEFT)
292 {
293 scroll.Left = cursor_coords.X + dist;
294 scroll.Right = FRAME_WIDTH (f) - 1;
295 }
296 else
297 {
298 scroll.Left = cursor_coords.X;
299 scroll.Right = FRAME_WIDTH (f) - dist - 1;
300 }
301
302 dest.X = cursor_coords.X;
303 dest.Y = cursor_coords.Y;
304
305 fill.Char.AsciiChar = 0x20;
306 fill.Attributes = char_attr;
307
308 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
309 }
310
311
312 /* If start is zero insert blanks instead of a string at start ?. */
313 void
314 insert_glyphs (register GLYPH *start, register int len)
315 {
316 scroll_line (len, RIGHT);
317
318 /* Move len chars to the right starting at cursor_coords, fill with blanks */
319 if (start)
320 {
321 /* Print the first len characters of start, cursor_coords.X adjusted
322 by write_glyphs. */
323
324 write_glyphs (start, len);
325 }
326 else
327 {
328 clear_end_of_line (cursor_coords.X + len);
329 }
330 }
331
332 void
333 write_glyphs (register GLYPH *string, register int len)
334 {
335 register unsigned int glyph_len = GLYPH_TABLE_LENGTH;
336 Lisp_Object *glyph_table = GLYPH_TABLE_BASE;
337 FRAME_PTR f = PICK_FRAME ();
338 register char *ptr;
339 GLYPH glyph;
340 char *chars;
341 int i;
342
343 if (len <= 0)
344 return;
345
346 chars = alloca (len * sizeof (*chars));
347 if (chars == NULL)
348 {
349 printf ("alloca failed in write_glyphs\n");
350 return;
351 }
352
353 /* We have to deal with the glyph indirection...go over the glyph
354 buffer and extract the characters. */
355 ptr = chars;
356 while (--len >= 0)
357 {
358 glyph = *string++;
359
360 if (glyph > glyph_len)
361 {
362 *ptr++ = glyph & 0xFF;
363 continue;
364 }
365 GLYPH_FOLLOW_ALIASES (glyph_table, glyph_len, glyph);
366 #ifndef HAVE_NTGUI
367 if (GLYPH_FACE (fixfix, glyph) != 0)
368 printf ("Glyph face is %d\n", GLYPH_FACE (fixfix, glyph));
369 #endif /* !HAVE_NTGUI */
370 if (GLYPH_SIMPLE_P (glyph_table, glyph_len, glyph))
371 {
372 *ptr++ = glyph & 0xFF;
373 continue;
374 }
375 for (i = 0; i < GLYPH_LENGTH (glyph_table, glyph); i++)
376 {
377 *ptr++ = (GLYPH_STRING (glyph_table, glyph))[i];
378 }
379 }
380
381 /* Number of characters we have in the buffer. */
382 len = ptr-chars;
383
384 /* Set the attribute for these characters. */
385 if (!FillConsoleOutputAttribute (cur_screen, char_attr, len, cursor_coords, &i))
386 {
387 printf ("Failed writing console attributes: %d\n", GetLastError ());
388 fflush (stdout);
389 }
390
391 /* Write the characters. */
392 if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
393 {
394 printf ("Failed writing console characters: %d\n", GetLastError ());
395 fflush (stdout);
396 }
397
398 cursor_coords.X += len;
399 move_cursor (cursor_coords.Y, cursor_coords.X);
400 }
401
402 void
403 delete_glyphs (int n)
404 {
405 /* delete chars means scroll chars from cursor_coords.X + n to
406 cursor_coords.X, anything beyond the edge of the screen should
407 come out empty... */
408
409 scroll_line (n, LEFT);
410 }
411
412 static unsigned int sound_type = 0xFFFFFFFF;
413 #define MB_EMACS_SILENT (0xFFFFFFFF - 1)
414
415 void
416 w32_sys_ring_bell (void)
417 {
418 if (sound_type == 0xFFFFFFFF)
419 {
420 Beep (666, 100);
421 }
422 else if (sound_type == MB_EMACS_SILENT)
423 {
424 /* Do nothing. */
425 }
426 else
427 MessageBeep (sound_type);
428 }
429
430 DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
431 "Set the sound generated when the bell is rung.\n\
432 SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent\n\
433 to use the corresponding system sound for the bell. The 'silent sound\n\
434 prevents Emacs from making any sound at all.\n\
435 SOUND is nil to use the normal beep.")
436 (sound)
437 Lisp_Object sound;
438 {
439 CHECK_SYMBOL (sound, 0);
440
441 if (NILP (sound))
442 sound_type = 0xFFFFFFFF;
443 else if (EQ (sound, intern ("asterisk")))
444 sound_type = MB_ICONASTERISK;
445 else if (EQ (sound, intern ("exclamation")))
446 sound_type = MB_ICONEXCLAMATION;
447 else if (EQ (sound, intern ("hand")))
448 sound_type = MB_ICONHAND;
449 else if (EQ (sound, intern ("question")))
450 sound_type = MB_ICONQUESTION;
451 else if (EQ (sound, intern ("ok")))
452 sound_type = MB_OK;
453 else if (EQ (sound, intern ("silent")))
454 sound_type = MB_EMACS_SILENT;
455 else
456 sound_type = 0xFFFFFFFF;
457
458 return sound;
459 }
460
461 void
462 reset_terminal_modes (void)
463 {
464 #ifdef USE_SEPARATE_SCREEN
465 SetConsoleActiveScreenBuffer (prev_screen);
466 #else
467 SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
468 #endif
469 SetConsoleMode (keyboard_handle, prev_console_mode);
470 }
471
472 void
473 set_terminal_modes (void)
474 {
475 CONSOLE_CURSOR_INFO cci;
476
477 /* make cursor big and visible (100 on Win95 makes it disappear) */
478 cci.dwSize = 99;
479 cci.bVisible = TRUE;
480 (void) SetConsoleCursorInfo (cur_screen, &cci);
481
482 SetConsoleActiveScreenBuffer (cur_screen);
483
484 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
485
486 /* Initialize input mode: interrupt_input off, no flow control, allow
487 8 bit character input, standard quit char. */
488 Fset_input_mode (Qnil, Qnil, make_number (2), Qnil);
489 }
490
491 /* hmmm... perhaps these let us bracket screen changes so that we can flush
492 clumps rather than one-character-at-a-time...
493
494 we'll start with not moving the cursor while an update is in progress. */
495 void
496 update_begin (FRAME_PTR f)
497 {
498 }
499
500 void
501 update_end (FRAME_PTR f)
502 {
503 SetConsoleCursorPosition (cur_screen, cursor_coords);
504 }
505
506 void
507 set_terminal_window (int size)
508 {
509 }
510
511 typedef int (*term_hook) ();
512
513 void
514 initialize_w32_display (void)
515 {
516 CONSOLE_SCREEN_BUFFER_INFO info;
517
518 cursor_to_hook = move_cursor;
519 raw_cursor_to_hook = move_cursor;
520 clear_to_end_hook = clear_to_end;
521 clear_frame_hook = clear_frame;
522 clear_end_of_line_hook = clear_end_of_line;
523 ins_del_lines_hook = ins_del_lines;
524 change_line_highlight_hook = change_line_highlight;
525 reassert_line_highlight_hook = reassert_line_highlight;
526 insert_glyphs_hook = insert_glyphs;
527 write_glyphs_hook = write_glyphs;
528 delete_glyphs_hook = delete_glyphs;
529 ring_bell_hook = w32_sys_ring_bell;
530 reset_terminal_modes_hook = reset_terminal_modes;
531 set_terminal_modes_hook = set_terminal_modes;
532 set_terminal_window_hook = set_terminal_window;
533 update_begin_hook = update_begin;
534 update_end_hook = update_end;
535
536 read_socket_hook = w32_console_read_socket;
537 mouse_position_hook = w32_console_mouse_position;
538
539 /* Initialize interrupt_handle. */
540 init_crit ();
541
542 /* Remember original console settings. */
543 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
544 GetConsoleMode (keyboard_handle, &prev_console_mode);
545
546 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
547
548 #ifdef USE_SEPARATE_SCREEN
549 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
550 0, NULL,
551 CONSOLE_TEXTMODE_BUFFER,
552 NULL);
553
554 if (cur_screen == INVALID_HANDLE_VALUE)
555 {
556 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
557 printf ("LastError = 0x%lx\n", GetLastError ());
558 fflush (stdout);
559 exit (0);
560 }
561 #else
562 cur_screen = prev_screen;
563 GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
564 #endif
565
566 GetConsoleScreenBufferInfo (cur_screen, &info);
567
568 meta_key = 1;
569 char_attr = info.wAttributes & 0xFF;
570 char_attr_normal = char_attr;
571 char_attr_reverse = ((char_attr & 0xf) << 4) + ((char_attr & 0xf0) >> 4);
572
573 /* Lines per page. Use buffer coords instead of buffer size. */
574 FRAME_HEIGHT (selected_frame) = 1 + info.srWindow.Bottom -
575 info.srWindow.Top;
576 /* Characters per line. Use buffer coords instead of buffer size. */
577 SET_FRAME_WIDTH (selected_frame, 1 + info.srWindow.Right -
578 info.srWindow.Left);
579 }
580
581 DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
582 "Set screen colors.")
583 (foreground, background)
584 Lisp_Object foreground;
585 Lisp_Object background;
586 {
587 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
588 char_attr_reverse = XFASTINT (background) + (XFASTINT (foreground) << 4);
589
590 Frecenter (Qnil);
591 return Qt;
592 }
593
594 DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
595 "Set cursor size.")
596 (size)
597 Lisp_Object size;
598 {
599 CONSOLE_CURSOR_INFO cci;
600 cci.dwSize = XFASTINT (size);
601 cci.bVisible = TRUE;
602 (void) SetConsoleCursorInfo (cur_screen, &cci);
603
604 return Qt;
605 }
606
607 #ifndef HAVE_NTGUI
608 void
609 pixel_to_glyph_coords (FRAME_PTR f, int pix_x, int pix_y, int *x, int *y,
610 void *bounds, int noclip)
611 {
612 *x = pix_x;
613 *y = pix_y;
614 }
615
616 void
617 glyph_to_pixel_coords (FRAME_PTR f, int x, int y, int *pix_x, int *pix_y)
618 {
619 *pix_x = x;
620 *pix_y = y;
621 }
622 #endif /* !HAVE_NTGUI */
623
624 void
625 syms_of_ntterm ()
626 {
627 defsubr (&Sset_screen_color);
628 defsubr (&Sset_cursor_size);
629 defsubr (&Sset_message_beep);
630 }