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