Revision: miles@gnu.org--gnu-2004/emacs--unicode--0--patch-75
[bpt/emacs.git] / src / w32console.c
1 /* Terminal hooks for GNU Emacs on the Microsoft W32 API.
2 Copyright (C) 1992, 1999 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 #include <string.h>
32
33 #include "lisp.h"
34 #include "character.h"
35 #include "coding.h"
36 #include "disptab.h"
37 #include "termhooks.h"
38 #include "dispextern.h"
39 /* Disable features in frame.h that require a Window System. */
40 #undef HAVE_WINDOW_SYSTEM
41 #include "frame.h"
42 #include "w32inevt.h"
43
44 /* from window.c */
45 extern Lisp_Object Frecenter ();
46
47 /* from keyboard.c */
48 extern int detect_input_pending ();
49
50 /* from sysdep.c */
51 extern int read_input_pending ();
52
53 extern struct frame * updating_frame;
54 extern int meta_key;
55
56 static void w32con_move_cursor (int row, int col);
57 static void w32con_clear_to_end (void);
58 static void w32con_clear_frame (void);
59 static void w32con_clear_end_of_line (int);
60 static void w32con_ins_del_lines (int vpos, int n);
61 static void w32con_insert_glyphs (struct glyph *start, int len);
62 static void w32con_write_glyphs (struct glyph *string, int len);
63 static void w32con_delete_glyphs (int n);
64 void w32_sys_ring_bell (void);
65 static void w32con_reset_terminal_modes (void);
66 static void w32con_set_terminal_modes (void);
67 static void w32con_set_terminal_window (int size);
68 static void w32con_update_begin (struct frame * f);
69 static void w32con_update_end (struct frame * f);
70 static WORD w32_face_attributes (struct frame *f, int face_id);
71
72 static COORD cursor_coords;
73 static HANDLE prev_screen, cur_screen;
74 static WORD char_attr_normal;
75 static DWORD prev_console_mode;
76
77 #ifndef USE_SEPARATE_SCREEN
78 static CONSOLE_CURSOR_INFO prev_console_cursor;
79 #endif
80
81 /* Determine whether to make frame dimensions match the screen buffer,
82 or the current window size. The former is desirable when running
83 over telnet, while the latter is more useful when working directly at
84 the console with a large scroll-back buffer. */
85 int w32_use_full_screen_buffer;
86 HANDLE keyboard_handle;
87
88
89 /* Setting this as the ctrl handler prevents emacs from being killed when
90 someone hits ^C in a 'suspended' session (child shell).
91 Also ignore Ctrl-Break signals. */
92
93 BOOL
94 ctrl_c_handler (unsigned long type)
95 {
96 /* Only ignore "interrupt" events when running interactively. */
97 return (!noninteractive
98 && (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT));
99 }
100
101 /* If we're updating a frame, use it as the current frame
102 Otherwise, use the selected frame. */
103 #define PICK_FRAME() (updating_frame ? updating_frame : SELECTED_FRAME ())
104
105 /* Move the cursor to (row, col). */
106 static void
107 w32con_move_cursor (int row, int col)
108 {
109 cursor_coords.X = col;
110 cursor_coords.Y = row;
111
112 if (updating_frame == (struct frame *) NULL)
113 {
114 SetConsoleCursorPosition (cur_screen, cursor_coords);
115 }
116 }
117
118 /* Clear from cursor to end of screen. */
119 static void
120 w32con_clear_to_end (void)
121 {
122 struct frame * f = PICK_FRAME ();
123
124 w32con_clear_end_of_line (FRAME_COLS (f) - 1);
125 w32con_ins_del_lines (cursor_coords.Y, FRAME_LINES (f) - cursor_coords.Y - 1);
126 }
127
128 /* Clear the frame. */
129 static void
130 w32con_clear_frame (void)
131 {
132 struct frame * f = PICK_FRAME ();
133 COORD dest;
134 int n;
135 DWORD r;
136 CONSOLE_SCREEN_BUFFER_INFO info;
137
138 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info);
139
140 /* Remember that the screen buffer might be wider than the window. */
141 n = FRAME_LINES (f) * info.dwSize.X;
142 dest.X = dest.Y = 0;
143
144 FillConsoleOutputAttribute (cur_screen, char_attr_normal, n, dest, &r);
145 FillConsoleOutputCharacter (cur_screen, ' ', n, dest, &r);
146
147 w32con_move_cursor (0, 0);
148 }
149
150
151 static struct glyph glyph_base[256];
152 static BOOL ceol_initialized = FALSE;
153
154 /* Clear from Cursor to end (what's "standout marker"?). */
155 static void
156 w32con_clear_end_of_line (int end)
157 {
158 if (!ceol_initialized)
159 {
160 int i;
161 for (i = 0; i < 256; i++)
162 {
163 memcpy (&glyph_base[i], &space_glyph, sizeof (struct glyph));
164 }
165 ceol_initialized = TRUE;
166 }
167 w32con_write_glyphs (glyph_base, end - cursor_coords.X); /* fencepost ? */
168 }
169
170 /* Insert n lines at vpos. if n is negative delete -n lines. */
171 static void
172 w32con_ins_del_lines (int vpos, int n)
173 {
174 int i, nb;
175 SMALL_RECT scroll;
176 COORD dest;
177 CHAR_INFO fill;
178 struct frame * f = PICK_FRAME ();
179
180 if (n < 0)
181 {
182 scroll.Top = vpos - n;
183 scroll.Bottom = FRAME_LINES (f);
184 dest.Y = vpos;
185 }
186 else
187 {
188 scroll.Top = vpos;
189 scroll.Bottom = FRAME_LINES (f) - n;
190 dest.Y = vpos + n;
191 }
192 scroll.Left = 0;
193 scroll.Right = FRAME_COLS (f);
194
195 dest.X = 0;
196
197 fill.Char.AsciiChar = 0x20;
198 fill.Attributes = char_attr_normal;
199
200 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
201
202 /* Here we have to deal with a w32 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 w32con_move_cursor (i, 0);
216 w32con_clear_end_of_line (FRAME_COLS (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 w32con_move_cursor (i, 0);
229 w32con_clear_end_of_line (FRAME_COLS (f));
230 }
231 }
232 }
233
234 cursor_coords.X = 0;
235 cursor_coords.Y = vpos;
236 }
237
238 #undef LEFT
239 #undef RIGHT
240 #define LEFT 1
241 #define RIGHT 0
242
243 static void
244 scroll_line (int dist, int direction)
245 {
246 /* The idea here is to implement a horizontal scroll in one line to
247 implement delete and half of insert. */
248 SMALL_RECT scroll;
249 COORD dest;
250 CHAR_INFO fill;
251 struct frame * f = PICK_FRAME ();
252
253 scroll.Top = cursor_coords.Y;
254 scroll.Bottom = cursor_coords.Y;
255
256 if (direction == LEFT)
257 {
258 scroll.Left = cursor_coords.X + dist;
259 scroll.Right = FRAME_COLS (f) - 1;
260 }
261 else
262 {
263 scroll.Left = cursor_coords.X;
264 scroll.Right = FRAME_COLS (f) - dist - 1;
265 }
266
267 dest.X = cursor_coords.X;
268 dest.Y = cursor_coords.Y;
269
270 fill.Char.AsciiChar = 0x20;
271 fill.Attributes = char_attr_normal;
272
273 ScrollConsoleScreenBuffer (cur_screen, &scroll, NULL, dest, &fill);
274 }
275
276
277 /* If start is zero insert blanks instead of a string at start ?. */
278 static void
279 w32con_insert_glyphs (register struct glyph *start, register int len)
280 {
281 scroll_line (len, RIGHT);
282
283 /* Move len chars to the right starting at cursor_coords, fill with blanks */
284 if (start)
285 {
286 /* Print the first len characters of start, cursor_coords.X adjusted
287 by write_glyphs. */
288
289 w32con_write_glyphs (start, len);
290 }
291 else
292 {
293 w32con_clear_end_of_line (cursor_coords.X + len);
294 }
295 }
296
297 extern unsigned char *terminal_encode_buffer;
298
299 extern unsigned char *encode_terminal_code P_ ((struct glyph *, int,
300 struct coding_system *));
301
302 static void
303 w32con_write_glyphs (register struct glyph *string, register int len)
304 {
305 int produced, consumed;
306 DWORD r;
307 struct frame * f = PICK_FRAME ();
308 WORD char_attr;
309
310 if (len <= 0)
311 return;
312
313 /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
314 the tail. */
315 terminal_coding.mode &= ~CODING_MODE_LAST_BLOCK;
316
317 while (len > 0)
318 {
319 /* Identify a run of glyphs with the same face. */
320 int face_id = string->face_id;
321 int n;
322
323 for (n = 1; n < len; ++n)
324 if (string[n].face_id != face_id)
325 break;
326
327 /* Turn appearance modes of the face of the run on. */
328 char_attr = w32_face_attributes (f, face_id);
329
330 while (n > 0)
331 {
332 produced = encode_terminal_code (string,
333 n,
334 &consumed);
335 if (produced > 0)
336 {
337 /* Set the attribute for these characters. */
338 if (!FillConsoleOutputAttribute (cur_screen, char_attr,
339 produced, cursor_coords, &r))
340 {
341 printf ("Failed writing console attributes: %d\n",
342 GetLastError ());
343 fflush (stdout);
344 }
345
346 /* Write the characters. */
347 if (!WriteConsoleOutputCharacter (cur_screen, terminal_encode_buffer,
348 produced, cursor_coords, &r))
349 {
350 printf ("Failed writing console characters: %d\n",
351 GetLastError ());
352 fflush (stdout);
353 }
354
355 cursor_coords.X += produced;
356 w32con_move_cursor (cursor_coords.Y, cursor_coords.X);
357 }
358 len -= consumed;
359 n -= consumed;
360 string += consumed;
361 }
362 }
363
364 /* We may have to output some codes to terminate the writing. */
365 if (CODING_REQUIRE_FLUSHING (&terminal_coding))
366 {
367 Lisp_Object blank_string = build_string ("");
368 int conversion_buffer_size = 1024;
369
370 terminal_coding.mode |= CODING_MODE_LAST_BLOCK;
371 terminal_coding.destination = (unsigned char *) xmalloc (conversion_buffer_size);
372 encode_coding_object (&terminal_coding, blank_string, 0, 0,
373 0, conversion_buffer_size, Qnil);
374 if (terminal_coding.produced > 0)
375 {
376 if (!FillConsoleOutputAttribute (cur_screen, char_attr_normal,
377 terminal_coding.produced,
378 cursor_coords, &r))
379 {
380 printf ("Failed writing console attributes: %d\n",
381 GetLastError ());
382 fflush (stdout);
383 }
384
385 /* Write the characters. */
386 if (!WriteConsoleOutputCharacter (cur_screen, terminal_coding.destination,
387 produced, cursor_coords, &r))
388 {
389 printf ("Failed writing console characters: %d\n",
390 GetLastError ());
391 fflush (stdout);
392 }
393 }
394 xfree (terminal_coding.destination);
395 }
396 }
397
398
399 static void
400 w32con_delete_glyphs (int n)
401 {
402 /* delete chars means scroll chars from cursor_coords.X + n to
403 cursor_coords.X, anything beyond the edge of the screen should
404 come out empty... */
405
406 scroll_line (n, LEFT);
407 }
408
409 static unsigned int sound_type = 0xFFFFFFFF;
410 #define MB_EMACS_SILENT (0xFFFFFFFF - 1)
411
412 void
413 w32_sys_ring_bell (void)
414 {
415 if (sound_type == 0xFFFFFFFF)
416 {
417 Beep (666, 100);
418 }
419 else if (sound_type == MB_EMACS_SILENT)
420 {
421 /* Do nothing. */
422 }
423 else
424 MessageBeep (sound_type);
425 }
426
427 DEFUN ("set-message-beep", Fset_message_beep, Sset_message_beep, 1, 1, 0,
428 doc: /* Set the sound generated when the bell is rung.
429 SOUND is 'asterisk, 'exclamation, 'hand, 'question, 'ok, or 'silent
430 to use the corresponding system sound for the bell. The 'silent sound
431 prevents Emacs from making any sound at all.
432 SOUND is nil to use the normal beep. */)
433 (sound)
434 Lisp_Object sound;
435 {
436 CHECK_SYMBOL (sound);
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 if (EQ (sound, intern ("silent")))
451 sound_type = MB_EMACS_SILENT;
452 else
453 sound_type = 0xFFFFFFFF;
454
455 return sound;
456 }
457
458 static void
459 w32con_reset_terminal_modes (void)
460 {
461 #ifdef USE_SEPARATE_SCREEN
462 SetConsoleActiveScreenBuffer (prev_screen);
463 #else
464 SetConsoleCursorInfo (prev_screen, &prev_console_cursor);
465 #endif
466 SetConsoleMode (keyboard_handle, prev_console_mode);
467 }
468
469 static void
470 w32con_set_terminal_modes (void)
471 {
472 CONSOLE_CURSOR_INFO cci;
473
474 /* make cursor big and visible (100 on Win95 makes it disappear) */
475 cci.dwSize = 99;
476 cci.bVisible = TRUE;
477 (void) SetConsoleCursorInfo (cur_screen, &cci);
478
479 SetConsoleActiveScreenBuffer (cur_screen);
480
481 SetConsoleMode (keyboard_handle, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
482
483 /* Initialize input mode: interrupt_input off, no flow control, allow
484 8 bit character input, standard quit char. */
485 Fset_input_mode (Qnil, Qnil, make_number (2), Qnil);
486 }
487
488 /* hmmm... perhaps these let us bracket screen changes so that we can flush
489 clumps rather than one-character-at-a-time...
490
491 we'll start with not moving the cursor while an update is in progress. */
492 static void
493 w32con_update_begin (struct frame * f)
494 {
495 }
496
497 static void
498 w32con_update_end (struct frame * f)
499 {
500 SetConsoleCursorPosition (cur_screen, cursor_coords);
501 }
502
503 static void
504 w32con_set_terminal_window (int size)
505 {
506 }
507
508 /***********************************************************************
509 Faces
510 ***********************************************************************/
511
512
513 /* Turn appearances of face FACE_ID on tty frame F on. */
514
515 static WORD
516 w32_face_attributes (f, face_id)
517 struct frame *f;
518 int face_id;
519 {
520 WORD char_attr;
521 struct face *face = FACE_FROM_ID (f, face_id);
522
523 xassert (face != NULL);
524
525 char_attr = char_attr_normal;
526
527 if (face->foreground != FACE_TTY_DEFAULT_FG_COLOR
528 && face->foreground != FACE_TTY_DEFAULT_COLOR)
529 char_attr = (char_attr & 0xfff0) + (face->foreground % 16);
530
531 if (face->background != FACE_TTY_DEFAULT_BG_COLOR
532 && face->background != FACE_TTY_DEFAULT_COLOR)
533 char_attr = (char_attr & 0xff0f) + ((face->background % 16) << 4);
534
535
536 /* NTEMACS_TODO: Faces defined during startup get both foreground
537 and background of 0. Need a better way around this - for now detect
538 the problem and invert one of the faces to make the text readable. */
539 if (((char_attr & 0x00f0) >> 4) == (char_attr & 0x000f))
540 char_attr ^= 0x0007;
541
542 if (face->tty_reverse_p)
543 char_attr = (char_attr & 0xff00) + ((char_attr & 0x000f) << 4)
544 + ((char_attr & 0x00f0) >> 4);
545
546 return char_attr;
547 }
548
549
550 /* Emulation of some X window features from xfns.c and xfaces.c. */
551
552 extern char unspecified_fg[], unspecified_bg[];
553
554
555 /* Given a color index, return its standard name. */
556 Lisp_Object
557 vga_stdcolor_name (int idx)
558 {
559 /* Standard VGA colors, in the order of their standard numbering
560 in the default VGA palette. */
561 static char *vga_colors[16] = {
562 "black", "blue", "green", "cyan", "red", "magenta", "brown",
563 "lightgray", "darkgray", "lightblue", "lightgreen", "lightcyan",
564 "lightred", "lightmagenta", "yellow", "white"
565 };
566
567 extern Lisp_Object Qunspecified;
568
569 if (idx >= 0 && idx < sizeof (vga_colors) / sizeof (vga_colors[0]))
570 return build_string (vga_colors[idx]);
571 else
572 return Qunspecified; /* meaning the default */
573 }
574
575 typedef int (*term_hook) ();
576
577 void
578 initialize_w32_display (void)
579 {
580 CONSOLE_SCREEN_BUFFER_INFO info;
581
582 cursor_to_hook = w32con_move_cursor;
583 raw_cursor_to_hook = w32con_move_cursor;
584 clear_to_end_hook = w32con_clear_to_end;
585 clear_frame_hook = w32con_clear_frame;
586 clear_end_of_line_hook = w32con_clear_end_of_line;
587 ins_del_lines_hook = w32con_ins_del_lines;
588 insert_glyphs_hook = w32con_insert_glyphs;
589 write_glyphs_hook = w32con_write_glyphs;
590 delete_glyphs_hook = w32con_delete_glyphs;
591 ring_bell_hook = w32_sys_ring_bell;
592 reset_terminal_modes_hook = w32con_reset_terminal_modes;
593 set_terminal_modes_hook = w32con_set_terminal_modes;
594 set_terminal_window_hook = w32con_set_terminal_window;
595 update_begin_hook = w32con_update_begin;
596 update_end_hook = w32con_update_end;
597
598 read_socket_hook = w32_console_read_socket;
599 mouse_position_hook = w32_console_mouse_position;
600
601 /* Initialize interrupt_handle. */
602 init_crit ();
603
604 /* Remember original console settings. */
605 keyboard_handle = GetStdHandle (STD_INPUT_HANDLE);
606 GetConsoleMode (keyboard_handle, &prev_console_mode);
607
608 prev_screen = GetStdHandle (STD_OUTPUT_HANDLE);
609
610 #ifdef USE_SEPARATE_SCREEN
611 cur_screen = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
612 0, NULL,
613 CONSOLE_TEXTMODE_BUFFER,
614 NULL);
615
616 if (cur_screen == INVALID_HANDLE_VALUE)
617 {
618 printf ("CreateConsoleScreenBuffer failed in ResetTerm\n");
619 printf ("LastError = 0x%lx\n", GetLastError ());
620 fflush (stdout);
621 exit (0);
622 }
623 #else
624 cur_screen = prev_screen;
625 GetConsoleCursorInfo (prev_screen, &prev_console_cursor);
626 #endif
627
628 /* Respect setting of LINES and COLUMNS environment variables. */
629 {
630 char * lines = getenv("LINES");
631 char * columns = getenv("COLUMNS");
632
633 if (lines != NULL && columns != NULL)
634 {
635 SMALL_RECT new_win_dims;
636 COORD new_size;
637
638 new_size.X = atoi (columns);
639 new_size.Y = atoi (lines);
640
641 GetConsoleScreenBufferInfo (cur_screen, &info);
642
643 /* Shrink the window first, so the buffer dimensions can be
644 reduced if necessary. */
645 new_win_dims.Top = 0;
646 new_win_dims.Left = 0;
647 new_win_dims.Bottom = min (new_size.Y, info.dwSize.Y) - 1;
648 new_win_dims.Right = min (new_size.X, info.dwSize.X) - 1;
649 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
650
651 SetConsoleScreenBufferSize (cur_screen, new_size);
652
653 /* Set the window size to match the buffer dimension. */
654 new_win_dims.Top = 0;
655 new_win_dims.Left = 0;
656 new_win_dims.Bottom = new_size.Y - 1;
657 new_win_dims.Right = new_size.X - 1;
658 SetConsoleWindowInfo (cur_screen, TRUE, &new_win_dims);
659 }
660 }
661
662 GetConsoleScreenBufferInfo (cur_screen, &info);
663
664 meta_key = 1;
665 char_attr_normal = info.wAttributes;
666
667 if (w32_use_full_screen_buffer)
668 {
669 FRAME_LINES (SELECTED_FRAME ()) = info.dwSize.Y; /* lines per page */
670 SET_FRAME_COLS (SELECTED_FRAME (), info.dwSize.X); /* characters per line */
671 }
672 else
673 {
674 /* Lines per page. Use buffer coords instead of buffer size. */
675 FRAME_LINES (SELECTED_FRAME ()) = 1 + info.srWindow.Bottom -
676 info.srWindow.Top;
677 /* Characters per line. Use buffer coords instead of buffer size. */
678 SET_FRAME_COLS (SELECTED_FRAME (), 1 + info.srWindow.Right -
679 info.srWindow.Left);
680 }
681
682 /* Setup w32_display_info structure for this frame. */
683
684 w32_initialize_display_info (build_string ("Console"));
685
686 }
687
688 DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0,
689 doc: /* Set screen colors. */)
690 (foreground, background)
691 Lisp_Object foreground;
692 Lisp_Object background;
693 {
694 char_attr_normal = XFASTINT (foreground) + (XFASTINT (background) << 4);
695
696 Frecenter (Qnil);
697 return Qt;
698 }
699
700 DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0,
701 doc: /* Set cursor size. */)
702 (size)
703 Lisp_Object size;
704 {
705 CONSOLE_CURSOR_INFO cci;
706 cci.dwSize = XFASTINT (size);
707 cci.bVisible = TRUE;
708 (void) SetConsoleCursorInfo (cur_screen, &cci);
709
710 return Qt;
711 }
712
713 void
714 syms_of_ntterm ()
715 {
716 DEFVAR_BOOL ("w32-use-full-screen-buffer",
717 &w32_use_full_screen_buffer,
718 doc: /* Non-nil means make terminal frames use the full screen buffer dimensions.
719 This is desirable when running Emacs over telnet, and is the default.
720 A value of nil means use the current console window dimensions; this
721 may be preferrable when working directly at the console with a large
722 scroll-back buffer. */);
723 w32_use_full_screen_buffer = 1;
724
725 defsubr (&Sset_screen_color);
726 defsubr (&Sset_cursor_size);
727 defsubr (&Sset_message_beep);
728 }
729
730 /* arch-tag: a390a07f-f661-42bc-aeb4-e6d8bf860337
731 (do not change this comment) */