(Vwin32_enable_italics, Vwin32_enable_palette):
[bpt/emacs.git] / src / w32console.c
CommitLineData
6cdfb6e6
RS
1/* Terminal hooks for Windows NT port of GNU Emacs.
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"
33#include "frame.h"
34#include "disptab.h"
35#include "termhooks.h"
36
37#include "ntinevt.h"
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);
6e72ba86 61void nt_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);
67static void reset_kbd (void);
68static void unset_kbd (void);
69static int hl_mode (int new_highlight);
70
71void
72DebPrint ()
73{
74}
75
76/* Init hook called in init_keyboard. */
77void (*keyboard_init_hook)(void) = reset_kbd;
78
79COORD cursor_coords;
80HANDLE prev_screen, cur_screen;
81UCHAR char_attr, char_attr_normal, char_attr_reverse;
82HANDLE keyboard_handle;
83
84
85/* Setting this as the ctrl handler prevents emacs from being killed when
40d578c9
RS
86 someone hits ^C in a 'suspended' session (child shell).
87 Also ignore Ctrl-Break signals. */
88
6cdfb6e6
RS
89BOOL
90ctrl_c_handler (unsigned long type)
91{
40d578c9 92 return (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT);
6cdfb6e6
RS
93}
94
95/* If we're updating a frame, use it as the current frame
96 Otherwise, use the selected frame. */
97#define PICK_FRAME() (updating_frame ? updating_frame : selected_frame)
98
99/* Move the cursor to (row, col). */
100void
101move_cursor (int row, int col)
102{
103 cursor_coords.X = col;
104 cursor_coords.Y = row;
105
0534d577 106 if (updating_frame == (FRAME_PTR) NULL)
6cdfb6e6
RS
107 {
108 SetConsoleCursorPosition (cur_screen, cursor_coords);
109 }
110}
111
112/* Clear from cursor to end of screen. */
113void
114clear_to_end (void)
115{
116 FRAME_PTR f = PICK_FRAME ();
117
118 clear_end_of_line (FRAME_WIDTH (f) - 1);
119 ins_del_lines (cursor_coords.Y, FRAME_HEIGHT (f) - cursor_coords.Y - 1);
120}
121
122/* Clear the frame. */
123void
124clear_frame (void)
125{
126 SMALL_RECT scroll;
127 COORD dest;
128 CHAR_INFO fill;
129 FRAME_PTR f = PICK_FRAME ();
130
131 hl_mode (0);
132
133 scroll.Top = 0;
134 scroll.Bottom = FRAME_HEIGHT (f) - 1;
135 scroll.Left = 0;
136 scroll.Right = FRAME_WIDTH (f) - 1;
137
138 dest.Y = FRAME_HEIGHT (f);
139 dest.X = 0;
140
141 fill.Char.AsciiChar = 0x20;
142 fill.Attributes = char_attr;
143
144 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
145 move_cursor (0, 0);
146}
147
148
149static GLYPH glyph_base[256];
150static BOOL ceol_initialized = FALSE;
151
152/* Clear from Cursor to end (what's "standout marker"?). */
153void
154clear_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. */
169void
170ins_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 win32 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. */
241static int
242hl_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. */
262void
263change_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. */
272void
273reassert_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
284void
285scroll_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 ?. */
319void
320insert_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
338void
339write_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 WORD *attrs;
347 char *chars;
348 int i;
349
350 attrs = alloca (len * sizeof (*attrs));
351 chars = alloca (len * sizeof (*chars));
352 if (attrs == NULL || chars == NULL)
353 {
354 printf ("alloca failed in write_glyphs\n");
355 return;
356 }
357
358 /* We have to deal with the glyph indirection...go over the glyph
359 buffer and extract the characters. */
360 ptr = chars;
361 while (--len >= 0)
362 {
363 glyph = *string++;
364
365 if (glyph > glyph_len)
366 {
367 *ptr++ = glyph & 0xFF;
368 continue;
369 }
370 GLYPH_FOLLOW_ALIASES (glyph_table, glyph_len, glyph);
6e72ba86 371#ifndef HAVE_NTGUI
6cdfb6e6
RS
372 if (GLYPH_FACE (fixfix, glyph) != 0)
373 printf ("Glyph face is %d\n", GLYPH_FACE (fixfix, glyph));
6e72ba86 374#endif /* !HAVE_NTGUI */
6cdfb6e6
RS
375 if (GLYPH_SIMPLE_P (glyph_table, glyph_len, glyph))
376 {
377 *ptr++ = glyph & 0xFF;
378 continue;
379 }
380 for (i = 0; i < GLYPH_LENGTH (glyph_table, glyph); i++)
381 {
382 *ptr++ = (GLYPH_STRING (glyph_table, glyph))[i];
383 }
384 }
385
386 /* Number of characters we have in the buffer. */
387 len = ptr-chars;
388
389 /* Fill in the attributes for these characters. */
e312961b
GV
390 for (i = 0; i < len; i++)
391 attrs[i] = char_attr;
6cdfb6e6
RS
392
393 /* Write the attributes. */
394 if (!WriteConsoleOutputAttribute (cur_screen, attrs, len, cursor_coords, &i))
395 {
0534d577 396 printf ("Failed writing console attributes: %d\n", GetLastError ());
6cdfb6e6
RS
397 fflush (stdout);
398 }
399
400 /* Write the characters. */
401 if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
402 {
0534d577 403 printf ("Failed writing console characters: %d\n", GetLastError ());
6cdfb6e6
RS
404 fflush (stdout);
405 }
406
407 cursor_coords.X += len;
408 move_cursor (cursor_coords.Y, cursor_coords.X);
409}
410
411void
412delete_glyphs (int n)
413{
414 /* delete chars means scroll chars from cursor_coords.X + n to
415 cursor_coords.X, anything beyond the edge of the screen should
416 come out empty... */
417
418 scroll_line (n, LEFT);
419}
420
0534d577
KH
421static unsigned int sound_type = 0xFFFFFFFF;
422
6cdfb6e6 423void
6e72ba86 424nt_ring_bell (void)
6cdfb6e6 425{
0534d577
KH
426 if (sound_type == 0xFFFFFFFF)
427 Beep (666, 100);
428 else
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\
434SOUND is 'asterisk, 'exclamation, 'hand, 'question, or 'ok\n\
435to use the corresponding system sound for the bell.\n\
436SOUND is nil to use the normal beep.")
437 (sound)
438 Lisp_Object sound;
6cdfb6e6 439{
0534d577
KH
440 CHECK_SYMBOL (sound, 0);
441
442 if (NILP (sound))
443 sound_type = 0xFFFFFFFF;
444 else if (EQ (sound, intern ("asterisk")))
445 sound_type = MB_ICONASTERISK;
446 else if (EQ (sound, intern ("exclamation")))
447 sound_type = MB_ICONEXCLAMATION;
448 else if (EQ (sound, intern ("hand")))
449 sound_type = MB_ICONHAND;
450 else if (EQ (sound, intern ("question")))
451 sound_type = MB_ICONQUESTION;
452 else if (EQ (sound, intern ("ok")))
453 sound_type = MB_OK;
454 else
455 sound_type = 0xFFFFFFFF;
456
457 return sound;
6cdfb6e6
RS
458}
459
460/* Put our console back up, for ending a suspended session. */
461void
462take_console (void)
463{
464 reset_kbd ();
465 SetConsoleActiveScreenBuffer (cur_screen);
466}
467
468void
469reset_terminal_modes (void)
470{
471 unset_kbd ();
472 SetConsoleActiveScreenBuffer (prev_screen);
6cdfb6e6
RS
473}
474
475void
476set_terminal_modes (void)
477{
478 CONSOLE_CURSOR_INFO cci;
479
480 if (cur_screen == NULL)
481 {
482 reset_kbd ();
483 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
484 0, NULL,
485 CONSOLE_TEXTMODE_BUFFER,
486 NULL);
487
488 if (cur_screen == INVALID_HANDLE_VALUE)
489 {
490 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
491 printf ("LastError = 0x%lx\n", GetLastError ());
492 fflush (stdout);
493 exit (0);
494 }
495
496 SetConsoleActiveScreenBuffer (cur_screen);
497
e312961b
GV
498 /* make cursor big and visible (100 on Win95 makes it disappear) */
499 cci.dwSize = 99;
6cdfb6e6
RS
500 cci.bVisible = TRUE;
501 (void) SetConsoleCursorInfo (cur_screen, &cci);
502 }
503}
504
505/* hmmm... perhaps these let us bracket screen changes so that we can flush
506 clumps rather than one-character-at-a-time...
507
508 we'll start with not moving the cursor while an update is in progress. */
509void
510update_begin (FRAME_PTR f)
511{
512}
513
514void
515update_end (FRAME_PTR f)
516{
517 SetConsoleCursorPosition (cur_screen, cursor_coords);
518}
519
520void
521set_terminal_window (int size)
522{
523}
524
525void
526unset_kbd (void)
527{
528 SetConsoleMode (keyboard_handle, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
529 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT);
530}
531
532void
533reset_kbd (void)
534{
535 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
536 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
537}
538
539typedef int (*term_hook) ();
540
541void
542initialize_win_nt_display (void)
543{
544 CONSOLE_SCREEN_BUFFER_INFO info;
545
546 cursor_to_hook = (term_hook) move_cursor;
547 raw_cursor_to_hook = (term_hook) move_cursor;
548 clear_to_end_hook = (term_hook) clear_to_end;
549 clear_frame_hook = (term_hook) clear_frame;
550 clear_end_of_line_hook = (term_hook) clear_end_of_line;
551 ins_del_lines_hook = (term_hook) ins_del_lines;
552 change_line_highlight_hook = (term_hook) change_line_highlight;
553 reassert_line_highlight_hook = (term_hook) reassert_line_highlight;
554 insert_glyphs_hook = (term_hook) insert_glyphs;
555 write_glyphs_hook = (term_hook) write_glyphs;
556 delete_glyphs_hook = (term_hook) delete_glyphs;
6e72ba86 557 ring_bell_hook = (term_hook) nt_ring_bell;
6cdfb6e6
RS
558 reset_terminal_modes_hook = (term_hook) reset_terminal_modes;
559 set_terminal_modes_hook = (term_hook) set_terminal_modes;
560 set_terminal_window_hook = (term_hook) set_terminal_window;
561 update_begin_hook = (term_hook) update_begin;
562 update_end_hook = (term_hook) update_end;
563
564 read_socket_hook = win32_read_socket;
565 mouse_position_hook = win32_mouse_position;
566
567 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
568
569 set_terminal_modes ();
570
571 GetConsoleScreenBufferInfo (cur_screen, &info);
572
573 meta_key = 1;
574 char_attr = info.wAttributes & 0xFF;
575 char_attr_normal = char_attr;
576 char_attr_reverse = ((char_attr & 0xf) << 4) + ((char_attr & 0xf0) >> 4);
577
578 FRAME_HEIGHT (selected_frame) = info.dwSize.Y; /* lines per page */
579 FRAME_WIDTH (selected_frame) = info.dwSize.X; /* characters per line */
580
581 move_cursor (0, 0);
582
583 clear_frame ();
584}
585
586DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
587 "Set screen colors.")
588 (foreground, background)
589 Lisp_Object foreground;
590 Lisp_Object background;
591{
592 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
593 char_attr_reverse = XFASTINT (background) + (XFASTINT (foreground) << 4);
594
595 Frecenter (Qnil);
596 return Qt;
597}
598
599DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
600 "Set cursor size.")
601 (size)
602 Lisp_Object size;
603{
604 CONSOLE_CURSOR_INFO cci;
605 cci.dwSize = XFASTINT (size);
606 cci.bVisible = TRUE;
607 (void) SetConsoleCursorInfo (cur_screen, &cci);
608
609 return Qt;
610}
611
6e72ba86 612#ifndef HAVE_NTGUI
6cdfb6e6
RS
613void
614pixel_to_glyph_coords (FRAME_PTR f, int pix_x, int pix_y, int *x, int *y,
615 void *bounds, int noclip)
616{
617 *x = pix_x;
618 *y = pix_y;
619}
620
621void
622glyph_to_pixel_coords (FRAME_PTR f, int x, int y, int *pix_x, int *pix_y)
623{
624 *pix_x = x;
625 *pix_y = y;
626}
6e72ba86 627#endif /* !HAVE_NTGUI */
6cdfb6e6 628
0534d577 629void
6cdfb6e6
RS
630syms_of_ntterm ()
631{
632 defsubr (&Sset_screen_color);
633 defsubr (&Sset_cursor_size);
0534d577 634 defsubr (&Sset_message_beep);
6cdfb6e6 635}