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