(compute_motion): Fix boundary case.
[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;
a5404e3a
AI
126 CONSOLE_SCREEN_BUFFER_INFO info;
127
128 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
b47278e1 129
6cdfb6e6
RS
130 hl_mode (0);
131
a5404e3a
AI
132 /* Remember that the screen buffer might be wider than the window. */
133 n = FRAME_HEIGHT (f) * info.dwSize.X;
b47278e1
GV
134 dest.X = dest.Y = 0;
135
136 FillConsoleOutputAttribute (cur_screen, char_attr, n, dest, &r);
137 FillConsoleOutputCharacter (cur_screen, ' ', n, dest, &r);
138
6cdfb6e6
RS
139 move_cursor (0, 0);
140}
141
142
143static GLYPH glyph_base[256];
144static BOOL ceol_initialized = FALSE;
145
146/* Clear from Cursor to end (what's "standout marker"?). */
147void
148clear_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. */
163void
164ins_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
fbd6baed 196 /* Here we have to deal with a w32 console flake: If the scroll
6cdfb6e6
RS
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. */
235static int
236hl_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. */
256void
257change_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. */
266void
267reassert_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
278void
279scroll_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 ?. */
313void
314insert_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
332void
333write_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;
6cdfb6e6
RS
340 char *chars;
341 int i;
342
b47278e1
GV
343 if (len <= 0)
344 return;
345
6cdfb6e6 346 chars = alloca (len * sizeof (*chars));
a5404e3a 347 if (chars == NULL)
6cdfb6e6
RS
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);
6e72ba86 366#ifndef HAVE_NTGUI
6cdfb6e6
RS
367 if (GLYPH_FACE (fixfix, glyph) != 0)
368 printf ("Glyph face is %d\n", GLYPH_FACE (fixfix, glyph));
6e72ba86 369#endif /* !HAVE_NTGUI */
6cdfb6e6
RS
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
a5404e3a
AI
384 /* Set the attribute for these characters. */
385 if (!FillConsoleOutputAttribute (cur_screen, char_attr, len, cursor_coords, &i))
6cdfb6e6 386 {
0534d577 387 printf ("Failed writing console attributes: %d\n", GetLastError ());
6cdfb6e6
RS
388 fflush (stdout);
389 }
390
391 /* Write the characters. */
392 if (!WriteConsoleOutputCharacter (cur_screen, chars, len, cursor_coords, &i))
393 {
0534d577 394 printf ("Failed writing console characters: %d\n", GetLastError ());
6cdfb6e6
RS
395 fflush (stdout);
396 }
397
398 cursor_coords.X += len;
399 move_cursor (cursor_coords.Y, cursor_coords.X);
400}
401
402void
403delete_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
0534d577 412static unsigned int sound_type = 0xFFFFFFFF;
78859b80 413#define MB_EMACS_SILENT (0xFFFFFFFF - 1)
0534d577 414
6cdfb6e6 415void
fbd6baed 416w32_sys_ring_bell (void)
6cdfb6e6 417{
0534d577 418 if (sound_type == 0xFFFFFFFF)
78859b80 419 {
0534d577 420 Beep (666, 100);
78859b80
GV
421 }
422 else if (sound_type == MB_EMACS_SILENT)
423 {
424 /* Do nothing. */
425 }
0534d577 426 else
78859b80 427 MessageBeep (sound_type);
6cdfb6e6
RS
428}
429
0534d577
KH
430DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
431 "Set the sound generated when the bell is rung.\n\
78859b80
GV
432SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent\n\
433to use the corresponding system sound for the bell. The 'silent sound\n\
434prevents Emacs from making any sound at all.\n\
0534d577
KH
435SOUND is nil to use the normal beep.")
436 (sound)
437 Lisp_Object sound;
6cdfb6e6 438{
0534d577
KH
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;
78859b80
GV
453 else if (EQ (sound, intern ("silent")))
454 sound_type = MB_EMACS_SILENT;
0534d577
KH
455 else
456 sound_type = 0xFFFFFFFF;
457
458 return sound;
6cdfb6e6 459}
6cdfb6e6
RS
460
461void
462reset_terminal_modes (void)
463{
3e671d90 464#ifdef USE_SEPARATE_SCREEN
6cdfb6e6 465 SetConsoleActiveScreenBuffer (prev_screen);
3e671d90
GV
466#else
467 SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
468#endif
469 SetConsoleMode (keyboard_handle, prev_console_mode);
6cdfb6e6
RS
470}
471
472void
473set_terminal_modes (void)
474{
475 CONSOLE_CURSOR_INFO cci;
476
3e671d90
GV
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);
6cdfb6e6 481
3e671d90 482 SetConsoleActiveScreenBuffer (cur_screen);
6cdfb6e6 483
3e671d90 484 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
6cdfb6e6 485
3e671d90
GV
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);
6cdfb6e6
RS
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. */
495void
496update_begin (FRAME_PTR f)
497{
498}
499
500void
501update_end (FRAME_PTR f)
502{
503 SetConsoleCursorPosition (cur_screen, cursor_coords);
504}
505
506void
507set_terminal_window (int size)
508{
509}
510
6cdfb6e6
RS
511typedef int (*term_hook) ();
512
513void
e6b20d65 514initialize_w32_display (void)
6cdfb6e6
RS
515{
516 CONSOLE_SCREEN_BUFFER_INFO info;
517
f3d268f9
GV
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;
6cdfb6e6 535
fbd6baed 536 read_socket_hook = w32_console_read_socket;
f3d268f9 537 mouse_position_hook = w32_console_mouse_position;
3e671d90 538
c1e06681
AI
539 /* Initialize interrupt_handle. */
540 init_crit ();
541
3e671d90
GV
542 /* Remember original console settings. */
543 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
544 GetConsoleMode (keyboard_handle, &prev_console_mode);
545
6cdfb6e6
RS
546 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
547
3e671d90
GV
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
6cdfb6e6
RS
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);
938469f2
GV
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);
6cdfb6e6
RS
579}
580
581DEFUN ("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
594DEFUN ("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
6e72ba86 607#ifndef HAVE_NTGUI
6cdfb6e6
RS
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}
6e72ba86 622#endif /* !HAVE_NTGUI */
6cdfb6e6 623
0534d577 624void
6cdfb6e6
RS
625syms_of_ntterm ()
626{
627 defsubr (&Sset_screen_color);
628 defsubr (&Sset_cursor_size);
0534d577 629 defsubr (&Sset_message_beep);
6cdfb6e6 630}