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