1999-05-22 Ben Elliston <bje@cygnus.com>
[bpt/emacs.git] / src / w32console.c
CommitLineData
e6b20d65 1/* Terminal hooks for GNU Emacs on the Microsoft W32 API.
6cdfb6e6
RS
2 Copyright (C) 1992 Free Software Foundation, Inc.
3
3b7ad313
EN
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.
6cdfb6e6
RS
20
21 Tim Fleehart (apollo@online.com) 1-17-92
22 Geoff Voelker (voelker@cs.washington.edu) 9-12-93
23*/
24
25
6816efce
GV
26#include <config.h>
27
6cdfb6e6
RS
28#include <stdlib.h>
29#include <stdio.h>
6cdfb6e6
RS
30#include <windows.h>
31
32#include "lisp.h"
2091aeb2 33#include "charset.h"
6cdfb6e6
RS
34#include "frame.h"
35#include "disptab.h"
36#include "termhooks.h"
489f9371 37#include "w32inevt.h"
6cdfb6e6 38
0534d577 39/* from window.c */
6cdfb6e6
RS
40extern Lisp_Object Frecenter ();
41
42/* from keyboard.c */
43extern int detect_input_pending ();
44
45/* from sysdep.c */
46extern int read_input_pending ();
47
48extern FRAME_PTR updating_frame;
49extern int meta_key;
50
51static void move_cursor (int row, int col);
52static void clear_to_end (void);
53static void clear_frame (void);
54static void clear_end_of_line (int);
55static void ins_del_lines (int vpos, int n);
56static void change_line_highlight (int, int, int);
57static void reassert_line_highlight (int, int);
58static void insert_glyphs (GLYPH *start, int len);
59static void write_glyphs (GLYPH *string, int len);
60static void delete_glyphs (int n);
fbd6baed 61void w32_sys_ring_bell (void);
6cdfb6e6
RS
62static void reset_terminal_modes (void);
63static void set_terminal_modes (void);
64static void set_terminal_window (int size);
65static void update_begin (FRAME_PTR f);
66static void update_end (FRAME_PTR f);
6cdfb6e6
RS
67static int hl_mode (int new_highlight);
68
6cdfb6e6
RS
69COORD cursor_coords;
70HANDLE prev_screen, cur_screen;
71UCHAR char_attr, char_attr_normal, char_attr_reverse;
72HANDLE keyboard_handle;
b6823b27 73DWORD prev_console_mode;
6cdfb6e6 74
3e671d90
GV
75#ifndef USE_SEPARATE_SCREEN
76CONSOLE_CURSOR_INFO prev_console_cursor;
77#endif
78
6cdfb6e6
RS
79
80/* Setting this as the ctrl handler prevents emacs from being killed when
40d578c9
RS
81 someone hits ^C in a 'suspended' session (child shell).
82 Also ignore Ctrl-Break signals. */
83
6cdfb6e6
RS
84BOOL
85ctrl_c_handler (unsigned long type)
86{
3e671d90
GV
87 /* Only ignore "interrupt" events when running interactively. */
88 return (!noninteractive
89 && (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT));
6cdfb6e6
RS
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). */
97void
98move_cursor (int row, int col)
99{
100 cursor_coords.X = col;
101 cursor_coords.Y = row;
102
0534d577 103 if (updating_frame == (FRAME_PTR) NULL)
6cdfb6e6
RS
104 {
105 SetConsoleCursorPosition (cur_screen, cursor_coords);
106 }
107}
108
109/* Clear from cursor to end of screen. */
110void
111clear_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. */
120void
121clear_frame (void)
122{
6cdfb6e6 123 FRAME_PTR f = PICK_FRAME ();
b47278e1
GV
124 COORD dest;
125 int n, r;
126
6cdfb6e6
RS
127 hl_mode (0);
128
b47278e1
GV
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
6cdfb6e6
RS
135 move_cursor (0, 0);
136}
137
138
139static GLYPH glyph_base[256];
140static BOOL ceol_initialized = FALSE;
141
142/* Clear from Cursor to end (what's "standout marker"?). */
143void
144clear_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. */
159void
160ins_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
fbd6baed 192 /* Here we have to deal with a w32 console flake: If the scroll
6cdfb6e6
RS
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. */
231static int
232hl_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. */
252void
253change_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. */
262void
263reassert_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
274void
275scroll_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 ?. */
309void
310insert_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
328void
329write_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
b47278e1
GV
340 if (len <= 0)
341 return;
342
6cdfb6e6
RS
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);
6e72ba86 364#ifndef HAVE_NTGUI
6cdfb6e6
RS
365 if (GLYPH_FACE (fixfix, glyph) != 0)
366 printf ("Glyph face is %d\n", GLYPH_FACE (fixfix, glyph));
6e72ba86 367#endif /* !HAVE_NTGUI */
6cdfb6e6
RS
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. */
e312961b
GV
383 for (i = 0; i < len; i++)
384 attrs[i] = char_attr;
6cdfb6e6
RS
385
386 /* Write the attributes. */
387 if (!WriteConsoleOutputAttribute (cur_screen, attrs, len, cursor_coords, &i))
388 {
0534d577 389 printf ("Failed writing console attributes: %d\n", GetLastError ());
6cdfb6e6
RS
390 fflush (stdout);
391 }
392
393 /* Write the characters. */
394 if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
395 {
0534d577 396 printf ("Failed writing console characters: %d\n", GetLastError ());
6cdfb6e6
RS
397 fflush (stdout);
398 }
399
400 cursor_coords.X += len;
401 move_cursor (cursor_coords.Y, cursor_coords.X);
402}
403
404void
405delete_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
0534d577 414static unsigned int sound_type = 0xFFFFFFFF;
78859b80 415#define MB_EMACS_SILENT (0xFFFFFFFF - 1)
0534d577 416
6cdfb6e6 417void
fbd6baed 418w32_sys_ring_bell (void)
6cdfb6e6 419{
0534d577 420 if (sound_type == 0xFFFFFFFF)
78859b80 421 {
0534d577 422 Beep (666, 100);
78859b80
GV
423 }
424 else if (sound_type == MB_EMACS_SILENT)
425 {
426 /* Do nothing. */
427 }
0534d577 428 else
78859b80 429 MessageBeep (sound_type);
6cdfb6e6
RS
430}
431
0534d577
KH
432DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
433 "Set the sound generated when the bell is rung.\n\
78859b80
GV
434SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent\n\
435to use the corresponding system sound for the bell. The 'silent sound\n\
436prevents Emacs from making any sound at all.\n\
0534d577
KH
437SOUND is nil to use the normal beep.")
438 (sound)
439 Lisp_Object sound;
6cdfb6e6 440{
0534d577
KH
441 CHECK_SYMBOL (sound, 0);
442
443 if (NILP (sound))
444 sound_type = 0xFFFFFFFF;
445 else if (EQ (sound, intern ("asterisk")))
446 sound_type = MB_ICONASTERISK;
447 else if (EQ (sound, intern ("exclamation")))
448 sound_type = MB_ICONEXCLAMATION;
449 else if (EQ (sound, intern ("hand")))
450 sound_type = MB_ICONHAND;
451 else if (EQ (sound, intern ("question")))
452 sound_type = MB_ICONQUESTION;
453 else if (EQ (sound, intern ("ok")))
454 sound_type = MB_OK;
78859b80
GV
455 else if (EQ (sound, intern ("silent")))
456 sound_type = MB_EMACS_SILENT;
0534d577
KH
457 else
458 sound_type = 0xFFFFFFFF;
459
460 return sound;
6cdfb6e6 461}
6cdfb6e6
RS
462
463void
464reset_terminal_modes (void)
465{
3e671d90 466#ifdef USE_SEPARATE_SCREEN
6cdfb6e6 467 SetConsoleActiveScreenBuffer (prev_screen);
3e671d90
GV
468#else
469 SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
470#endif
471 SetConsoleMode (keyboard_handle, prev_console_mode);
6cdfb6e6
RS
472}
473
474void
475set_terminal_modes (void)
476{
477 CONSOLE_CURSOR_INFO cci;
478
3e671d90
GV
479 /* make cursor big and visible (100 on Win95 makes it disappear) */
480 cci.dwSize = 99;
481 cci.bVisible = TRUE;
482 (void) SetConsoleCursorInfo (cur_screen, &cci);
6cdfb6e6 483
3e671d90 484 SetConsoleActiveScreenBuffer (cur_screen);
6cdfb6e6 485
3e671d90 486 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
6cdfb6e6 487
3e671d90
GV
488 /* Initialize input mode: interrupt_input off, no flow control, allow
489 8 bit character input, standard quit char. */
490 Fset_input_mode (Qnil, Qnil, make_number (2), Qnil);
6cdfb6e6
RS
491}
492
493/* hmmm... perhaps these let us bracket screen changes so that we can flush
494 clumps rather than one-character-at-a-time...
495
496 we'll start with not moving the cursor while an update is in progress. */
497void
498update_begin (FRAME_PTR f)
499{
500}
501
502void
503update_end (FRAME_PTR f)
504{
505 SetConsoleCursorPosition (cur_screen, cursor_coords);
506}
507
508void
509set_terminal_window (int size)
510{
511}
512
6cdfb6e6
RS
513typedef int (*term_hook) ();
514
515void
e6b20d65 516initialize_w32_display (void)
6cdfb6e6
RS
517{
518 CONSOLE_SCREEN_BUFFER_INFO info;
519
f3d268f9
GV
520 cursor_to_hook = move_cursor;
521 raw_cursor_to_hook = move_cursor;
522 clear_to_end_hook = clear_to_end;
523 clear_frame_hook = clear_frame;
524 clear_end_of_line_hook = clear_end_of_line;
525 ins_del_lines_hook = ins_del_lines;
526 change_line_highlight_hook = change_line_highlight;
527 reassert_line_highlight_hook = reassert_line_highlight;
528 insert_glyphs_hook = insert_glyphs;
529 write_glyphs_hook = write_glyphs;
530 delete_glyphs_hook = delete_glyphs;
531 ring_bell_hook = w32_sys_ring_bell;
532 reset_terminal_modes_hook = reset_terminal_modes;
533 set_terminal_modes_hook = set_terminal_modes;
534 set_terminal_window_hook = set_terminal_window;
535 update_begin_hook = update_begin;
536 update_end_hook = update_end;
6cdfb6e6 537
fbd6baed 538 read_socket_hook = w32_console_read_socket;
f3d268f9 539 mouse_position_hook = w32_console_mouse_position;
3e671d90 540
c1e06681
AI
541 /* Initialize interrupt_handle. */
542 init_crit ();
543
3e671d90
GV
544 /* Remember original console settings. */
545 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
546 GetConsoleMode (keyboard_handle, &prev_console_mode);
547
6cdfb6e6
RS
548 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
549
3e671d90
GV
550#ifdef USE_SEPARATE_SCREEN
551 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
552 0, NULL,
553 CONSOLE_TEXTMODE_BUFFER,
554 NULL);
555
556 if (cur_screen == INVALID_HANDLE_VALUE)
557 {
558 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
559 printf ("LastError = 0x%lx\n", GetLastError ());
560 fflush (stdout);
561 exit (0);
562 }
563#else
564 cur_screen = prev_screen;
565 GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
566#endif
567
6cdfb6e6
RS
568 GetConsoleScreenBufferInfo (cur_screen, &info);
569
570 meta_key = 1;
571 char_attr = info.wAttributes & 0xFF;
572 char_attr_normal = char_attr;
573 char_attr_reverse = ((char_attr & 0xf) << 4) + ((char_attr & 0xf0) >> 4);
938469f2
GV
574
575 /* Lines per page. Use buffer coords instead of buffer size. */
576 FRAME_HEIGHT (selected_frame) = 1 + info.srWindow.Bottom -
577 info.srWindow.Top;
578 /* Characters per line. Use buffer coords instead of buffer size. */
579 SET_FRAME_WIDTH (selected_frame, 1 + info.srWindow.Right -
580 info.srWindow.Left);
6cdfb6e6
RS
581}
582
583DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
584 "Set screen colors.")
585 (foreground, background)
586 Lisp_Object foreground;
587 Lisp_Object background;
588{
589 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
590 char_attr_reverse = XFASTINT (background) + (XFASTINT (foreground) << 4);
591
592 Frecenter (Qnil);
593 return Qt;
594}
595
596DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
597 "Set cursor size.")
598 (size)
599 Lisp_Object size;
600{
601 CONSOLE_CURSOR_INFO cci;
602 cci.dwSize = XFASTINT (size);
603 cci.bVisible = TRUE;
604 (void) SetConsoleCursorInfo (cur_screen, &cci);
605
606 return Qt;
607}
608
6e72ba86 609#ifndef HAVE_NTGUI
6cdfb6e6
RS
610void
611pixel_to_glyph_coords (FRAME_PTR f, int pix_x, int pix_y, int *x, int *y,
612 void *bounds, int noclip)
613{
614 *x = pix_x;
615 *y = pix_y;
616}
617
618void
619glyph_to_pixel_coords (FRAME_PTR f, int x, int y, int *pix_x, int *pix_y)
620{
621 *pix_x = x;
622 *pix_y = y;
623}
6e72ba86 624#endif /* !HAVE_NTGUI */
6cdfb6e6 625
0534d577 626void
6cdfb6e6
RS
627syms_of_ntterm ()
628{
629 defsubr (&Sset_screen_color);
630 defsubr (&Sset_cursor_size);
0534d577 631 defsubr (&Sset_message_beep);
6cdfb6e6 632}