Merged from miles@gnu.org--gnu-2005 (patch 83-87, 449-468)
[bpt/emacs.git] / src / term.c
1 /* Terminal control module for terminals described by TERMCAP
2 Copyright (C) 1985, 86, 87, 93, 94, 95, 98, 2000, 2001, 2002, 2005
3 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22 /* New redisplay, TTY faces by Gerd Moellmann <gerd@gnu.org>. */
23
24 #include <config.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <string.h>
28
29 #include <sys/file.h>
30
31 #include <unistd.h> /* For isatty. */
32
33 #if HAVE_TERMIOS_H
34 #include <termios.h> /* For TIOCNOTTY. */
35 #endif
36
37 #include <signal.h>
38
39 #include "lisp.h"
40 #include "termchar.h"
41 #include "termopts.h"
42 #include "charset.h"
43 #include "coding.h"
44 #include "keyboard.h"
45 #include "frame.h"
46 #include "disptab.h"
47 #include "termhooks.h"
48 #include "dispextern.h"
49 #include "window.h"
50 #include "keymap.h"
51 #include "syssignal.h"
52 #include "systty.h"
53
54 /* For now, don't try to include termcap.h. On some systems,
55 configure finds a non-standard termcap.h that the main build
56 won't find. */
57
58 #if defined HAVE_TERMCAP_H && 0
59 #include <termcap.h>
60 #else
61 extern void tputs P_ ((const char *, int, int (*)(int)));
62 extern int tgetent P_ ((char *, const char *));
63 extern int tgetflag P_ ((char *id));
64 extern int tgetnum P_ ((char *id));
65 #endif
66
67 #include "cm.h"
68 #ifdef HAVE_X_WINDOWS
69 #include "xterm.h"
70 #endif
71 #ifdef MAC_OS
72 #include "macterm.h"
73 #endif
74
75 #ifndef O_RDWR
76 #define O_RDWR 2
77 #endif
78
79 #ifndef O_NOCTTY
80 #define O_NOCTTY 0
81 #endif
82
83 static void turn_on_face P_ ((struct frame *, int face_id));
84 static void turn_off_face P_ ((struct frame *, int face_id));
85 static void tty_show_cursor P_ ((struct tty_display_info *));
86 static void tty_hide_cursor P_ ((struct tty_display_info *));
87
88 static struct display *get_tty_display (Lisp_Object display);
89
90 void delete_initial_display P_ ((struct display *));
91 void create_tty_output P_ ((struct frame *));
92 void delete_tty_output P_ ((struct frame *));
93
94 #define OUTPUT(tty, a) \
95 emacs_tputs ((tty), a, \
96 (int) (FRAME_LINES (XFRAME (selected_frame)) \
97 - curY (tty)), \
98 cmputc)
99
100 #define OUTPUT1(tty, a) emacs_tputs ((tty), a, 1, cmputc)
101 #define OUTPUTL(tty, a, lines) emacs_tputs ((tty), a, lines, cmputc)
102
103 #define OUTPUT_IF(tty, a) \
104 do { \
105 if (a) \
106 emacs_tputs ((tty), a, \
107 (int) (FRAME_LINES (XFRAME (selected_frame)) \
108 - curY (tty) ), \
109 cmputc); \
110 } while (0)
111
112 #define OUTPUT1_IF(tty, a) do { if (a) emacs_tputs ((tty), a, 1, cmputc); } while (0)
113
114 /* Display space properties */
115
116 extern Lisp_Object Qspace, QCalign_to, QCwidth;
117
118 /* Function to use to ring the bell. */
119
120 Lisp_Object Vring_bell_function;
121
122 /* Functions to call after suspending a tty. */
123 Lisp_Object Vsuspend_tty_functions;
124
125 /* Functions to call after resuming a tty. */
126 Lisp_Object Vresume_tty_functions;
127
128 /* Chain of all displays currently in use. */
129 struct display *display_list;
130
131 /* The initial display device, created by initial_term_init. */
132 struct display *initial_display;
133
134 /* Chain of all tty device parameters. */
135 struct tty_display_info *tty_list;
136
137 /* Nonzero means no need to redraw the entire frame on resuming a
138 suspended Emacs. This is useful on terminals with multiple
139 pages, where one page is used for Emacs and another for all
140 else. */
141 int no_redraw_on_reenter;
142
143
144 /* Meaning of bits in no_color_video. Each bit set means that the
145 corresponding attribute cannot be combined with colors. */
146
147 enum no_color_bit
148 {
149 NC_STANDOUT = 1 << 0,
150 NC_UNDERLINE = 1 << 1,
151 NC_REVERSE = 1 << 2,
152 NC_BLINK = 1 << 3,
153 NC_DIM = 1 << 4,
154 NC_BOLD = 1 << 5,
155 NC_INVIS = 1 << 6,
156 NC_PROTECT = 1 << 7,
157 NC_ALT_CHARSET = 1 << 8
158 };
159
160 /* internal state */
161
162 /* The largest frame width in any call to calculate_costs. */
163
164 int max_frame_cols;
165
166 /* The largest frame height in any call to calculate_costs. */
167
168 int max_frame_lines;
169
170 /* Non-zero if we have dropped our controlling tty and therefore
171 should not open a frame on stdout. */
172 static int no_controlling_tty;
173
174 /* The first unallocated display id. */
175 static int next_display_id;
176
177 /* Provided for lisp packages. */
178
179 static int system_uses_terminfo;
180
181 char *tparam ();
182
183 extern char *tgetstr ();
184 \f
185
186 #ifdef WINDOWSNT
187 /* We aren't X windows, but we aren't termcap either. This makes me
188 uncertain as to what value to use for frame.output_method. For
189 this file, we'll define FRAME_TERMCAP_P to be zero so that our
190 output hooks get called instead of the termcap functions. Probably
191 the best long-term solution is to define an output_windows_nt... */
192
193 #undef FRAME_TERMCAP_P
194 #define FRAME_TERMCAP_P(_f_) 0
195 #endif /* WINDOWSNT */
196
197 void
198 ring_bell (struct frame *f)
199 {
200 if (!NILP (Vring_bell_function))
201 {
202 Lisp_Object function;
203
204 /* Temporarily set the global variable to nil
205 so that if we get an error, it stays nil
206 and we don't call it over and over.
207
208 We don't specbind it, because that would carefully
209 restore the bad value if there's an error
210 and make the loop of errors happen anyway. */
211
212 function = Vring_bell_function;
213 Vring_bell_function = Qnil;
214
215 call0 (function);
216
217 Vring_bell_function = function;
218 }
219 else if (FRAME_DISPLAY (f)->ring_bell_hook)
220 (*FRAME_DISPLAY (f)->ring_bell_hook) (f);
221 }
222
223 /* Ring the bell on a tty. */
224
225 void
226 tty_ring_bell (struct frame *f)
227 {
228 struct tty_display_info *tty = FRAME_TTY (f);
229
230 if (tty->output)
231 {
232 OUTPUT (tty, (tty->TS_visible_bell && visible_bell
233 ? tty->TS_visible_bell
234 : tty->TS_bell));
235 fflush (tty->output);
236 }
237 }
238
239 /* Set up termcap modes for Emacs. */
240
241 void
242 tty_set_terminal_modes (struct display *display)
243 {
244 struct tty_display_info *tty = display->display_info.tty;
245
246 if (tty->output)
247 {
248 OUTPUT_IF (tty, tty->TS_termcap_modes);
249 OUTPUT_IF (tty, tty->TS_cursor_visible);
250 OUTPUT_IF (tty, tty->TS_keypad_mode);
251 losecursor (tty);
252 fflush (tty->output);
253 }
254 }
255
256 /* Reset termcap modes before exiting Emacs. */
257
258 void
259 tty_reset_terminal_modes (struct display *display)
260 {
261 struct tty_display_info *tty = display->display_info.tty;
262
263 if (tty->output)
264 {
265 turn_off_highlight (tty);
266 turn_off_insert (tty);
267 OUTPUT_IF (tty, tty->TS_end_keypad_mode);
268 OUTPUT_IF (tty, tty->TS_cursor_normal);
269 OUTPUT_IF (tty, tty->TS_end_termcap_modes);
270 OUTPUT_IF (tty, tty->TS_orig_pair);
271 /* Output raw CR so kernel can track the cursor hpos. */
272 current_tty = tty;
273 cmputc ('\r');
274 fflush (tty->output);
275 }
276 }
277
278 void
279 update_begin (struct frame *f)
280 {
281 if (FRAME_DISPLAY (f)->update_begin_hook)
282 (*FRAME_DISPLAY (f)->update_begin_hook) (f);
283 }
284
285 void
286 update_end (struct frame *f)
287 {
288 if (FRAME_DISPLAY (f)->update_end_hook)
289 (*FRAME_DISPLAY (f)->update_end_hook) (f);
290 }
291
292 /* Flag the end of a display update on a termcap display. */
293
294 void
295 tty_update_end (struct frame *f)
296 {
297 struct tty_display_info *tty = FRAME_TTY (f);
298
299 if (!XWINDOW (selected_window)->cursor_off_p)
300 tty_show_cursor (tty);
301 turn_off_insert (tty);
302 background_highlight (tty);
303 }
304
305 /* Specify how many text lines, from the top of the window,
306 should be affected by insert-lines and delete-lines operations.
307 This, and those operations, are used only within an update
308 that is bounded by calls to update_begin and update_end. */
309
310 void
311 set_terminal_window (struct frame *f, int size)
312 {
313 if (FRAME_DISPLAY (f)->set_terminal_window_hook)
314 (*FRAME_DISPLAY (f)->set_terminal_window_hook) (f, size);
315 }
316
317 /* The implementation of set_terminal_window for termcap frames. */
318
319 void
320 tty_set_terminal_window (struct frame *f, int size)
321 {
322 struct tty_display_info *tty = FRAME_TTY (f);
323
324 tty->specified_window = size ? size : FRAME_LINES (f);
325 if (FRAME_SCROLL_REGION_OK (f))
326 set_scroll_region (f, 0, tty->specified_window);
327 }
328
329 void
330 set_scroll_region (struct frame *f, int start, int stop)
331 {
332 char *buf;
333 struct tty_display_info *tty = FRAME_TTY (f);
334
335 if (tty->TS_set_scroll_region)
336 buf = tparam (tty->TS_set_scroll_region, 0, 0, start, stop - 1);
337 else if (tty->TS_set_scroll_region_1)
338 buf = tparam (tty->TS_set_scroll_region_1, 0, 0,
339 FRAME_LINES (f), start,
340 FRAME_LINES (f) - stop,
341 FRAME_LINES (f));
342 else
343 buf = tparam (tty->TS_set_window, 0, 0, start, 0, stop, FRAME_COLS (f));
344
345 OUTPUT (tty, buf);
346 xfree (buf);
347 losecursor (tty);
348 }
349
350 \f
351 static void
352 turn_on_insert (struct tty_display_info *tty)
353 {
354 if (!tty->insert_mode)
355 OUTPUT (tty, tty->TS_insert_mode);
356 tty->insert_mode = 1;
357 }
358
359 void
360 turn_off_insert (struct tty_display_info *tty)
361 {
362 if (tty->insert_mode)
363 OUTPUT (tty, tty->TS_end_insert_mode);
364 tty->insert_mode = 0;
365 }
366 \f
367 /* Handle highlighting. */
368
369 void
370 turn_off_highlight (struct tty_display_info *tty)
371 {
372 if (tty->standout_mode)
373 OUTPUT_IF (tty, tty->TS_end_standout_mode);
374 tty->standout_mode = 0;
375 }
376
377 static void
378 turn_on_highlight (struct tty_display_info *tty)
379 {
380 if (!tty->standout_mode)
381 OUTPUT_IF (tty, tty->TS_standout_mode);
382 tty->standout_mode = 1;
383 }
384
385 static void
386 toggle_highlight (struct tty_display_info *tty)
387 {
388 if (tty->standout_mode)
389 turn_off_highlight (tty);
390 else
391 turn_on_highlight (tty);
392 }
393
394
395 /* Make cursor invisible. */
396
397 static void
398 tty_hide_cursor (struct tty_display_info *tty)
399 {
400 if (tty->cursor_hidden == 0)
401 {
402 tty->cursor_hidden = 1;
403 OUTPUT_IF (tty, tty->TS_cursor_invisible);
404 }
405 }
406
407
408 /* Ensure that cursor is visible. */
409
410 static void
411 tty_show_cursor (struct tty_display_info *tty)
412 {
413 if (tty->cursor_hidden)
414 {
415 tty->cursor_hidden = 0;
416 OUTPUT_IF (tty, tty->TS_cursor_normal);
417 OUTPUT_IF (tty, tty->TS_cursor_visible);
418 }
419 }
420
421
422 /* Set standout mode to the state it should be in for
423 empty space inside windows. What this is,
424 depends on the user option inverse-video. */
425
426 void
427 background_highlight (struct tty_display_info *tty)
428 {
429 if (inverse_video)
430 turn_on_highlight (tty);
431 else
432 turn_off_highlight (tty);
433 }
434
435 /* Set standout mode to the mode specified for the text to be output. */
436
437 static void
438 highlight_if_desired (struct tty_display_info *tty)
439 {
440 if (inverse_video)
441 turn_on_highlight (tty);
442 else
443 turn_off_highlight (tty);
444 }
445 \f
446
447 /* Move cursor to row/column position VPOS/HPOS. HPOS/VPOS are
448 frame-relative coordinates. */
449
450 void
451 cursor_to (struct frame *f, int vpos, int hpos)
452 {
453 if (FRAME_DISPLAY (f)->cursor_to_hook)
454 (*FRAME_DISPLAY (f)->cursor_to_hook) (f, vpos, hpos);
455 }
456
457 void
458 tty_cursor_to (struct frame *f, int vpos, int hpos)
459 {
460 struct tty_display_info *tty = FRAME_TTY (f);
461
462 /* Detect the case where we are called from reset_sys_modes
463 and the costs have never been calculated. Do nothing. */
464 if (! tty->costs_set)
465 return;
466
467 if (curY (tty) == vpos
468 && curX (tty) == hpos)
469 return;
470 if (!tty->TF_standout_motion)
471 background_highlight (tty);
472 if (!tty->TF_insmode_motion)
473 turn_off_insert (tty);
474 cmgoto (tty, vpos, hpos);
475 }
476
477 /* Similar but don't take any account of the wasted characters. */
478
479 void
480 raw_cursor_to (struct frame *f, int row, int col)
481 {
482 if (FRAME_DISPLAY (f)->raw_cursor_to_hook)
483 (*FRAME_DISPLAY (f)->raw_cursor_to_hook) (f, row, col);
484 }
485
486 void
487 tty_raw_cursor_to (struct frame *f, int row, int col)
488 {
489 struct tty_display_info *tty = FRAME_TTY (f);
490
491 if (curY (tty) == row
492 && curX (tty) == col)
493 return;
494 if (!tty->TF_standout_motion)
495 background_highlight (tty);
496 if (!tty->TF_insmode_motion)
497 turn_off_insert (tty);
498 cmgoto (tty, row, col);
499 }
500 \f
501 /* Erase operations */
502
503 /* Clear from cursor to end of frame. */
504 void
505 clear_to_end (struct frame *f)
506 {
507 if (FRAME_DISPLAY (f)->clear_to_end_hook)
508 (*FRAME_DISPLAY (f)->clear_to_end_hook) (f);
509 }
510
511 /* Clear from cursor to end of frame on a termcap device. */
512
513 void
514 tty_clear_to_end (struct frame *f)
515 {
516 register int i;
517 struct tty_display_info *tty = FRAME_TTY (f);
518
519 if (tty->TS_clr_to_bottom)
520 {
521 background_highlight (tty);
522 OUTPUT (tty, tty->TS_clr_to_bottom);
523 }
524 else
525 {
526 for (i = curY (tty); i < FRAME_LINES (f); i++)
527 {
528 cursor_to (f, i, 0);
529 clear_end_of_line (f, FRAME_COLS (f));
530 }
531 }
532 }
533
534 /* Clear entire frame */
535
536 void
537 clear_frame (struct frame *f)
538 {
539 if (FRAME_DISPLAY (f)->clear_frame_hook)
540 (*FRAME_DISPLAY (f)->clear_frame_hook) (f);
541 }
542
543 /* Clear an entire termcap frame. */
544
545 void
546 tty_clear_frame (struct frame *f)
547 {
548 struct tty_display_info *tty = FRAME_TTY (f);
549
550 if (tty->TS_clr_frame)
551 {
552 background_highlight (tty);
553 OUTPUT (tty, tty->TS_clr_frame);
554 cmat (tty, 0, 0);
555 }
556 else
557 {
558 cursor_to (f, 0, 0);
559 clear_to_end (f);
560 }
561 }
562
563 /* Clear from cursor to end of line.
564 Assume that the line is already clear starting at column first_unused_hpos.
565
566 Note that the cursor may be moved, on terminals lacking a `ce' string. */
567
568 void
569 clear_end_of_line (struct frame *f, int first_unused_hpos)
570 {
571 if (FRAME_DISPLAY (f)->clear_end_of_line_hook)
572 (*FRAME_DISPLAY (f)->clear_end_of_line_hook) (f, first_unused_hpos);
573 }
574
575 /* An implementation of clear_end_of_line for termcap frames.
576
577 Note that the cursor may be moved, on terminals lacking a `ce' string. */
578
579 void
580 tty_clear_end_of_line (struct frame *f, int first_unused_hpos)
581 {
582 register int i;
583 struct tty_display_info *tty = FRAME_TTY (f);
584
585 /* Detect the case where we are called from reset_sys_modes
586 and the costs have never been calculated. Do nothing. */
587 if (! tty->costs_set)
588 return;
589
590 if (curX (tty) >= first_unused_hpos)
591 return;
592 background_highlight (tty);
593 if (tty->TS_clr_line)
594 {
595 OUTPUT1 (tty, tty->TS_clr_line);
596 }
597 else
598 { /* have to do it the hard way */
599 turn_off_insert (tty);
600
601 /* Do not write in last row last col with Auto-wrap on. */
602 if (AutoWrap (tty)
603 && curY (tty) == FrameRows (tty) - 1
604 && first_unused_hpos == FrameCols (tty))
605 first_unused_hpos--;
606
607 for (i = curX (tty); i < first_unused_hpos; i++)
608 {
609 if (tty->termscript)
610 fputc (' ', tty->termscript);
611 fputc (' ', tty->output);
612 }
613 cmplus (tty, first_unused_hpos - curX (tty));
614 }
615 }
616 \f
617 /* Buffer to store the source and result of code conversion for terminal. */
618 static unsigned char *encode_terminal_buf;
619 /* Allocated size of the above buffer. */
620 static int encode_terminal_bufsize;
621
622 /* Encode SRC_LEN glyphs starting at SRC to terminal output codes.
623 Set CODING->produced to the byte-length of the resulting byte
624 sequence, and return a pointer to that byte sequence. */
625
626 unsigned char *
627 encode_terminal_code (src, src_len, coding)
628 struct glyph *src;
629 int src_len;
630 struct coding_system *coding;
631 {
632 struct glyph *src_start = src, *src_end = src + src_len;
633 register GLYPH g;
634 unsigned char *buf;
635 int nchars, nbytes, required;
636 register int tlen = GLYPH_TABLE_LENGTH;
637 register Lisp_Object *tbase = GLYPH_TABLE_BASE;
638
639 /* Allocate sufficient size of buffer to store all characters in
640 multibyte-form. But, it may be enlarged on demand if
641 Vglyph_table contains a string. */
642 required = MAX_MULTIBYTE_LENGTH * src_len;
643 if (encode_terminal_bufsize < required)
644 {
645 if (encode_terminal_bufsize == 0)
646 encode_terminal_buf = xmalloc (required);
647 else
648 encode_terminal_buf = xrealloc (encode_terminal_buf, required);
649 encode_terminal_bufsize = required;
650 }
651
652 buf = encode_terminal_buf;
653 nchars = 0;
654 while (src < src_end)
655 {
656 /* We must skip glyphs to be padded for a wide character. */
657 if (! CHAR_GLYPH_PADDING_P (*src))
658 {
659 g = GLYPH_FROM_CHAR_GLYPH (src[0]);
660
661 if (g < 0 || g >= tlen)
662 {
663 /* This glyph doesn't has an entry in Vglyph_table. */
664 if (CHAR_VALID_P (src->u.ch, 0))
665 buf += CHAR_STRING (src->u.ch, buf);
666 else
667 *buf++ = SPACEGLYPH;
668 nchars++;
669 }
670 else
671 {
672 /* This glyph has an entry in Vglyph_table,
673 so process any alias before testing for simpleness. */
674 GLYPH_FOLLOW_ALIASES (tbase, tlen, g);
675
676 if (GLYPH_SIMPLE_P (tbase, tlen, g))
677 {
678 int c = FAST_GLYPH_CHAR (g);
679
680 if (CHAR_VALID_P (c, 0))
681 buf += CHAR_STRING (c, buf);
682 else
683 *buf++ = SPACEGLYPH;
684 nchars++;
685 }
686 else
687 {
688 /* We have a string in Vglyph_table. */
689 Lisp_Object string;
690
691 string = tbase[g];
692 if (! STRING_MULTIBYTE (string))
693 string = string_to_multibyte (string);
694 nbytes = buf - encode_terminal_buf;
695 if (encode_terminal_bufsize < nbytes + SBYTES (string))
696 {
697 encode_terminal_bufsize = nbytes + SBYTES (string);
698 encode_terminal_buf = xrealloc (encode_terminal_buf,
699 encode_terminal_bufsize);
700 buf = encode_terminal_buf + nbytes;
701 }
702 bcopy (SDATA (string), buf, SBYTES (string));
703 buf += SBYTES (string);
704 nchars += SCHARS (string);
705 }
706 }
707 }
708 src++;
709 }
710
711 nbytes = buf - encode_terminal_buf;
712 coding->src_multibyte = 1;
713 coding->dst_multibyte = 0;
714 if (SYMBOLP (coding->pre_write_conversion)
715 && ! NILP (Ffboundp (coding->pre_write_conversion)))
716 {
717 run_pre_write_conversin_on_c_str (&encode_terminal_buf,
718 &encode_terminal_bufsize,
719 nchars, nbytes, coding);
720 nchars = coding->produced_char;
721 nbytes = coding->produced;
722 }
723 required = nbytes + encoding_buffer_size (coding, nbytes);
724 if (encode_terminal_bufsize < required)
725 {
726 encode_terminal_bufsize = required;
727 encode_terminal_buf = xrealloc (encode_terminal_buf, required);
728 }
729
730 encode_coding (coding, encode_terminal_buf, encode_terminal_buf + nbytes,
731 nbytes, encode_terminal_bufsize - nbytes);
732 return encode_terminal_buf + nbytes;
733 }
734
735
736 /* Output LEN glyphs starting at STRING at the nominal cursor position.
737 Advance the nominal cursor over the text. */
738
739 void
740 write_glyphs (struct frame *f, struct glyph *string, int len)
741 {
742 if (FRAME_DISPLAY (f)->write_glyphs_hook)
743 (*FRAME_DISPLAY (f)->write_glyphs_hook) (f, string, len);
744 }
745
746 /* An implementation of write_glyphs for termcap frames. */
747
748 void
749 tty_write_glyphs (struct frame *f, struct glyph *string, int len)
750 {
751 unsigned char *conversion_buffer;
752 struct coding_system *coding;
753
754 struct tty_display_info *tty = FRAME_TTY (f);
755
756 turn_off_insert (tty);
757 tty_hide_cursor (tty);
758
759 /* Don't dare write in last column of bottom line, if Auto-Wrap,
760 since that would scroll the whole frame on some terminals. */
761
762 if (AutoWrap (tty)
763 && curY (tty) + 1 == FRAME_LINES (f)
764 && (curX (tty) + len) == FRAME_COLS (f))
765 len --;
766 if (len <= 0)
767 return;
768
769 cmplus (tty, len);
770
771 /* If terminal_coding does any conversion, use it, otherwise use
772 safe_terminal_coding. We can't use CODING_REQUIRE_ENCODING here
773 because it always return 1 if the member src_multibyte is 1. */
774 coding = (FRAME_TERMINAL_CODING (f)->common_flags & CODING_REQUIRE_ENCODING_MASK
775 ? FRAME_TERMINAL_CODING (f) : &safe_terminal_coding);
776 /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
777 the tail. */
778 coding->mode &= ~CODING_MODE_LAST_BLOCK;
779
780 while (len > 0)
781 {
782 /* Identify a run of glyphs with the same face. */
783 int face_id = string->face_id;
784 int n;
785
786 for (n = 1; n < len; ++n)
787 if (string[n].face_id != face_id)
788 break;
789
790 /* Turn appearance modes of the face of the run on. */
791 highlight_if_desired (tty);
792 turn_on_face (f, face_id);
793
794 if (n == len)
795 /* This is the last run. */
796 coding->mode |= CODING_MODE_LAST_BLOCK;
797 conversion_buffer = encode_terminal_code (string, n, coding);
798 if (coding->produced > 0)
799 {
800 fwrite (conversion_buffer, 1, coding->produced, tty->output);
801 if (ferror (tty->output))
802 clearerr (tty->output);
803 if (tty->termscript)
804 fwrite (conversion_buffer, 1, coding->produced, tty->termscript);
805 }
806 len -= n;
807 string += n;
808
809 /* Turn appearance modes off. */
810 turn_off_face (f, face_id);
811 turn_off_highlight (tty);
812 }
813
814 cmcheckmagic (tty);
815 }
816
817 /* Insert LEN glyphs from START at the nominal cursor position.
818
819 If start is zero, insert blanks instead of a string at start */
820
821 void
822 insert_glyphs (struct frame *f, struct glyph *start, int len)
823 {
824 if (len <= 0)
825 return;
826
827 if (FRAME_DISPLAY (f)->insert_glyphs_hook)
828 (*FRAME_DISPLAY (f)->insert_glyphs_hook) (f, start, len);
829 }
830
831 /* An implementation of insert_glyphs for termcap frames. */
832
833 void
834 tty_insert_glyphs (struct frame *f, struct glyph *start, int len)
835 {
836 char *buf;
837 struct glyph *glyph = NULL;
838 unsigned char *conversion_buffer;
839 unsigned char space[1];
840 struct coding_system *coding;
841
842 struct tty_display_info *tty = FRAME_TTY (f);
843
844 if (tty->TS_ins_multi_chars)
845 {
846 buf = tparam (tty->TS_ins_multi_chars, 0, 0, len);
847 OUTPUT1 (tty, buf);
848 xfree (buf);
849 if (start)
850 write_glyphs (f, start, len);
851 return;
852 }
853
854 turn_on_insert (tty);
855 cmplus (tty, len);
856
857 if (! start)
858 space[0] = SPACEGLYPH;
859
860 /* If terminal_coding does any conversion, use it, otherwise use
861 safe_terminal_coding. We can't use CODING_REQUIRE_ENCODING here
862 because it always return 1 if the member src_multibyte is 1. */
863 coding = (FRAME_TERMINAL_CODING (f)->common_flags & CODING_REQUIRE_ENCODING_MASK
864 ? FRAME_TERMINAL_CODING (f) : &safe_terminal_coding);
865 /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
866 the tail. */
867 coding->mode &= ~CODING_MODE_LAST_BLOCK;
868
869 while (len-- > 0)
870 {
871 OUTPUT1_IF (tty, tty->TS_ins_char);
872 if (!start)
873 {
874 conversion_buffer = space;
875 coding->produced = 1;
876 }
877 else
878 {
879 highlight_if_desired (tty);
880 turn_on_face (f, start->face_id);
881 glyph = start;
882 ++start;
883 /* We must open sufficient space for a character which
884 occupies more than one column. */
885 while (len && CHAR_GLYPH_PADDING_P (*start))
886 {
887 OUTPUT1_IF (tty, tty->TS_ins_char);
888 start++, len--;
889 }
890
891 if (len <= 0)
892 /* This is the last glyph. */
893 coding->mode |= CODING_MODE_LAST_BLOCK;
894
895 conversion_buffer = encode_terminal_code (glyph, 1, coding);
896 }
897
898 if (coding->produced > 0)
899 {
900 fwrite (conversion_buffer, 1, coding->produced, tty->output);
901 if (ferror (tty->output))
902 clearerr (tty->output);
903 if (tty->termscript)
904 fwrite (conversion_buffer, 1, coding->produced, tty->termscript);
905 }
906
907 OUTPUT1_IF (tty, tty->TS_pad_inserted_char);
908 if (start)
909 {
910 turn_off_face (f, glyph->face_id);
911 turn_off_highlight (tty);
912 }
913 }
914
915 cmcheckmagic (tty);
916 }
917
918 /* Delete N glyphs at the nominal cursor position. */
919
920 void
921 delete_glyphs (struct frame *f, int n)
922 {
923 if (FRAME_DISPLAY (f)->delete_glyphs_hook)
924 (*FRAME_DISPLAY (f)->delete_glyphs_hook) (f, n);
925 }
926
927 /* An implementation of delete_glyphs for termcap frames. */
928
929 void
930 tty_delete_glyphs (struct frame *f, int n)
931 {
932 char *buf;
933 register int i;
934
935 struct tty_display_info *tty = FRAME_TTY (f);
936
937 if (tty->delete_in_insert_mode)
938 {
939 turn_on_insert (tty);
940 }
941 else
942 {
943 turn_off_insert (tty);
944 OUTPUT_IF (tty, tty->TS_delete_mode);
945 }
946
947 if (tty->TS_del_multi_chars)
948 {
949 buf = tparam (tty->TS_del_multi_chars, 0, 0, n);
950 OUTPUT1 (tty, buf);
951 xfree (buf);
952 }
953 else
954 for (i = 0; i < n; i++)
955 OUTPUT1 (tty, tty->TS_del_char);
956 if (!tty->delete_in_insert_mode)
957 OUTPUT_IF (tty, tty->TS_end_delete_mode);
958 }
959 \f
960 /* Insert N lines at vpos VPOS. If N is negative, delete -N lines. */
961
962 void
963 ins_del_lines (struct frame *f, int vpos, int n)
964 {
965 if (FRAME_DISPLAY (f)->ins_del_lines_hook)
966 (*FRAME_DISPLAY (f)->ins_del_lines_hook) (f, vpos, n);
967 }
968
969 /* An implementation of ins_del_lines for termcap frames. */
970
971 void
972 tty_ins_del_lines (struct frame *f, int vpos, int n)
973 {
974 struct tty_display_info *tty = FRAME_TTY (f);
975 char *multi = n > 0 ? tty->TS_ins_multi_lines : tty->TS_del_multi_lines;
976 char *single = n > 0 ? tty->TS_ins_line : tty->TS_del_line;
977 char *scroll = n > 0 ? tty->TS_rev_scroll : tty->TS_fwd_scroll;
978
979 register int i = n > 0 ? n : -n;
980 register char *buf;
981
982 /* If the lines below the insertion are being pushed
983 into the end of the window, this is the same as clearing;
984 and we know the lines are already clear, since the matching
985 deletion has already been done. So can ignore this. */
986 /* If the lines below the deletion are blank lines coming
987 out of the end of the window, don't bother,
988 as there will be a matching inslines later that will flush them. */
989 if (FRAME_SCROLL_REGION_OK (f)
990 && vpos + i >= tty->specified_window)
991 return;
992 if (!FRAME_MEMORY_BELOW_FRAME (f)
993 && vpos + i >= FRAME_LINES (f))
994 return;
995
996 if (multi)
997 {
998 raw_cursor_to (f, vpos, 0);
999 background_highlight (tty);
1000 buf = tparam (multi, 0, 0, i);
1001 OUTPUT (tty, buf);
1002 xfree (buf);
1003 }
1004 else if (single)
1005 {
1006 raw_cursor_to (f, vpos, 0);
1007 background_highlight (tty);
1008 while (--i >= 0)
1009 OUTPUT (tty, single);
1010 if (tty->TF_teleray)
1011 curX (tty) = 0;
1012 }
1013 else
1014 {
1015 set_scroll_region (f, vpos, tty->specified_window);
1016 if (n < 0)
1017 raw_cursor_to (f, tty->specified_window - 1, 0);
1018 else
1019 raw_cursor_to (f, vpos, 0);
1020 background_highlight (tty);
1021 while (--i >= 0)
1022 OUTPUTL (tty, scroll, tty->specified_window - vpos);
1023 set_scroll_region (f, 0, tty->specified_window);
1024 }
1025
1026 if (!FRAME_SCROLL_REGION_OK (f)
1027 && FRAME_MEMORY_BELOW_FRAME (f)
1028 && n < 0)
1029 {
1030 cursor_to (f, FRAME_LINES (f) + n, 0);
1031 clear_to_end (f);
1032 }
1033 }
1034 \f
1035 /* Compute cost of sending "str", in characters,
1036 not counting any line-dependent padding. */
1037
1038 int
1039 string_cost (char *str)
1040 {
1041 cost = 0;
1042 if (str)
1043 tputs (str, 0, evalcost);
1044 return cost;
1045 }
1046
1047 /* Compute cost of sending "str", in characters,
1048 counting any line-dependent padding at one line. */
1049
1050 static int
1051 string_cost_one_line (char *str)
1052 {
1053 cost = 0;
1054 if (str)
1055 tputs (str, 1, evalcost);
1056 return cost;
1057 }
1058
1059 /* Compute per line amount of line-dependent padding,
1060 in tenths of characters. */
1061
1062 int
1063 per_line_cost (char *str)
1064 {
1065 cost = 0;
1066 if (str)
1067 tputs (str, 0, evalcost);
1068 cost = - cost;
1069 if (str)
1070 tputs (str, 10, evalcost);
1071 return cost;
1072 }
1073
1074 #ifndef old
1075 /* char_ins_del_cost[n] is cost of inserting N characters.
1076 char_ins_del_cost[-n] is cost of deleting N characters.
1077 The length of this vector is based on max_frame_cols. */
1078
1079 int *char_ins_del_vector;
1080
1081 #define char_ins_del_cost(f) (&char_ins_del_vector[FRAME_COLS ((f))])
1082 #endif
1083
1084 /* ARGSUSED */
1085 static void
1086 calculate_ins_del_char_costs (struct frame *f)
1087 {
1088 struct tty_display_info *tty = FRAME_TTY (f);
1089 int ins_startup_cost, del_startup_cost;
1090 int ins_cost_per_char, del_cost_per_char;
1091 register int i;
1092 register int *p;
1093
1094 if (tty->TS_ins_multi_chars)
1095 {
1096 ins_cost_per_char = 0;
1097 ins_startup_cost = string_cost_one_line (tty->TS_ins_multi_chars);
1098 }
1099 else if (tty->TS_ins_char || tty->TS_pad_inserted_char
1100 || (tty->TS_insert_mode && tty->TS_end_insert_mode))
1101 {
1102 ins_startup_cost = (30 * (string_cost (tty->TS_insert_mode)
1103 + string_cost (tty->TS_end_insert_mode))) / 100;
1104 ins_cost_per_char = (string_cost_one_line (tty->TS_ins_char)
1105 + string_cost_one_line (tty->TS_pad_inserted_char));
1106 }
1107 else
1108 {
1109 ins_startup_cost = 9999;
1110 ins_cost_per_char = 0;
1111 }
1112
1113 if (tty->TS_del_multi_chars)
1114 {
1115 del_cost_per_char = 0;
1116 del_startup_cost = string_cost_one_line (tty->TS_del_multi_chars);
1117 }
1118 else if (tty->TS_del_char)
1119 {
1120 del_startup_cost = (string_cost (tty->TS_delete_mode)
1121 + string_cost (tty->TS_end_delete_mode));
1122 if (tty->delete_in_insert_mode)
1123 del_startup_cost /= 2;
1124 del_cost_per_char = string_cost_one_line (tty->TS_del_char);
1125 }
1126 else
1127 {
1128 del_startup_cost = 9999;
1129 del_cost_per_char = 0;
1130 }
1131
1132 /* Delete costs are at negative offsets */
1133 p = &char_ins_del_cost (f)[0];
1134 for (i = FRAME_COLS (f); --i >= 0;)
1135 *--p = (del_startup_cost += del_cost_per_char);
1136
1137 /* Doing nothing is free */
1138 p = &char_ins_del_cost (f)[0];
1139 *p++ = 0;
1140
1141 /* Insert costs are at positive offsets */
1142 for (i = FRAME_COLS (f); --i >= 0;)
1143 *p++ = (ins_startup_cost += ins_cost_per_char);
1144 }
1145
1146 void
1147 calculate_costs (struct frame *frame)
1148 {
1149 FRAME_COST_BAUD_RATE (frame) = baud_rate;
1150
1151 if (FRAME_TERMCAP_P (frame))
1152 {
1153 struct tty_display_info *tty = FRAME_TTY (frame);
1154 register char *f = (tty->TS_set_scroll_region
1155 ? tty->TS_set_scroll_region
1156 : tty->TS_set_scroll_region_1);
1157
1158 FRAME_SCROLL_REGION_COST (frame) = string_cost (f);
1159
1160 tty->costs_set = 1;
1161
1162 /* These variables are only used for terminal stuff. They are
1163 allocated once for the terminal frame of X-windows emacs, but not
1164 used afterwards.
1165
1166 char_ins_del_vector (i.e., char_ins_del_cost) isn't used because
1167 X turns off char_ins_del_ok. */
1168
1169 max_frame_lines = max (max_frame_lines, FRAME_LINES (frame));
1170 max_frame_cols = max (max_frame_cols, FRAME_COLS (frame));
1171
1172 if (char_ins_del_vector != 0)
1173 char_ins_del_vector
1174 = (int *) xrealloc (char_ins_del_vector,
1175 (sizeof (int)
1176 + 2 * max_frame_cols * sizeof (int)));
1177 else
1178 char_ins_del_vector
1179 = (int *) xmalloc (sizeof (int)
1180 + 2 * max_frame_cols * sizeof (int));
1181
1182 bzero (char_ins_del_vector, (sizeof (int)
1183 + 2 * max_frame_cols * sizeof (int)));
1184
1185
1186 if (f && (!tty->TS_ins_line && !tty->TS_del_line))
1187 do_line_insertion_deletion_costs (frame,
1188 tty->TS_rev_scroll, tty->TS_ins_multi_lines,
1189 tty->TS_fwd_scroll, tty->TS_del_multi_lines,
1190 f, f, 1);
1191 else
1192 do_line_insertion_deletion_costs (frame,
1193 tty->TS_ins_line, tty->TS_ins_multi_lines,
1194 tty->TS_del_line, tty->TS_del_multi_lines,
1195 0, 0, 1);
1196
1197 calculate_ins_del_char_costs (frame);
1198
1199 /* Don't use TS_repeat if its padding is worse than sending the chars */
1200 if (tty->TS_repeat && per_line_cost (tty->TS_repeat) * baud_rate < 9000)
1201 tty->RPov = string_cost (tty->TS_repeat);
1202 else
1203 tty->RPov = FRAME_COLS (frame) * 2;
1204
1205 cmcostinit (FRAME_TTY (frame)); /* set up cursor motion costs */
1206 }
1207 }
1208 \f
1209 struct fkey_table {
1210 char *cap, *name;
1211 };
1212
1213 /* Termcap capability names that correspond directly to X keysyms.
1214 Some of these (marked "terminfo") aren't supplied by old-style
1215 (Berkeley) termcap entries. They're listed in X keysym order;
1216 except we put the keypad keys first, so that if they clash with
1217 other keys (as on the IBM PC keyboard) they get overridden.
1218 */
1219
1220 static struct fkey_table keys[] =
1221 {
1222 {"kh", "home"}, /* termcap */
1223 {"kl", "left"}, /* termcap */
1224 {"ku", "up"}, /* termcap */
1225 {"kr", "right"}, /* termcap */
1226 {"kd", "down"}, /* termcap */
1227 {"%8", "prior"}, /* terminfo */
1228 {"%5", "next"}, /* terminfo */
1229 {"@7", "end"}, /* terminfo */
1230 {"@1", "begin"}, /* terminfo */
1231 {"*6", "select"}, /* terminfo */
1232 {"%9", "print"}, /* terminfo */
1233 {"@4", "execute"}, /* terminfo --- actually the `command' key */
1234 /*
1235 * "insert" --- see below
1236 */
1237 {"&8", "undo"}, /* terminfo */
1238 {"%0", "redo"}, /* terminfo */
1239 {"%7", "menu"}, /* terminfo --- actually the `options' key */
1240 {"@0", "find"}, /* terminfo */
1241 {"@2", "cancel"}, /* terminfo */
1242 {"%1", "help"}, /* terminfo */
1243 /*
1244 * "break" goes here, but can't be reliably intercepted with termcap
1245 */
1246 {"&4", "reset"}, /* terminfo --- actually `restart' */
1247 /*
1248 * "system" and "user" --- no termcaps
1249 */
1250 {"kE", "clearline"}, /* terminfo */
1251 {"kA", "insertline"}, /* terminfo */
1252 {"kL", "deleteline"}, /* terminfo */
1253 {"kI", "insertchar"}, /* terminfo */
1254 {"kD", "deletechar"}, /* terminfo */
1255 {"kB", "backtab"}, /* terminfo */
1256 /*
1257 * "kp_backtab", "kp-space", "kp-tab" --- no termcaps
1258 */
1259 {"@8", "kp-enter"}, /* terminfo */
1260 /*
1261 * "kp-f1", "kp-f2", "kp-f3" "kp-f4",
1262 * "kp-multiply", "kp-add", "kp-separator",
1263 * "kp-subtract", "kp-decimal", "kp-divide", "kp-0";
1264 * --- no termcaps for any of these.
1265 */
1266 {"K4", "kp-1"}, /* terminfo */
1267 /*
1268 * "kp-2" --- no termcap
1269 */
1270 {"K5", "kp-3"}, /* terminfo */
1271 /*
1272 * "kp-4" --- no termcap
1273 */
1274 {"K2", "kp-5"}, /* terminfo */
1275 /*
1276 * "kp-6" --- no termcap
1277 */
1278 {"K1", "kp-7"}, /* terminfo */
1279 /*
1280 * "kp-8" --- no termcap
1281 */
1282 {"K3", "kp-9"}, /* terminfo */
1283 /*
1284 * "kp-equal" --- no termcap
1285 */
1286 {"k1", "f1"},
1287 {"k2", "f2"},
1288 {"k3", "f3"},
1289 {"k4", "f4"},
1290 {"k5", "f5"},
1291 {"k6", "f6"},
1292 {"k7", "f7"},
1293 {"k8", "f8"},
1294 {"k9", "f9"},
1295
1296 {"&0", "S-cancel"}, /*shifted cancel key*/
1297 {"&9", "S-begin"}, /*shifted begin key*/
1298 {"*0", "S-find"}, /*shifted find key*/
1299 {"*1", "S-execute"}, /*shifted execute? actually shifted command key*/
1300 {"*4", "S-delete"}, /*shifted delete-character key*/
1301 {"*7", "S-end"}, /*shifted end key*/
1302 {"*8", "S-clearline"}, /*shifted clear-to end-of-line key*/
1303 {"#1", "S-help"}, /*shifted help key*/
1304 {"#2", "S-home"}, /*shifted home key*/
1305 {"#3", "S-insert"}, /*shifted insert-character key*/
1306 {"#4", "S-left"}, /*shifted left-arrow key*/
1307 {"%d", "S-menu"}, /*shifted menu? actually shifted options key*/
1308 {"%c", "S-next"}, /*shifted next key*/
1309 {"%e", "S-prior"}, /*shifted previous key*/
1310 {"%f", "S-print"}, /*shifted print key*/
1311 {"%g", "S-redo"}, /*shifted redo key*/
1312 {"%i", "S-right"}, /*shifted right-arrow key*/
1313 {"!3", "S-undo"} /*shifted undo key*/
1314 };
1315
1316 static char **term_get_fkeys_address;
1317 static KBOARD *term_get_fkeys_kboard;
1318 static Lisp_Object term_get_fkeys_1 ();
1319
1320 /* Find the escape codes sent by the function keys for Vfunction_key_map.
1321 This function scans the termcap function key sequence entries, and
1322 adds entries to Vfunction_key_map for each function key it finds. */
1323
1324 void
1325 term_get_fkeys (address, kboard)
1326 char **address;
1327 KBOARD *kboard;
1328 {
1329 /* We run the body of the function (term_get_fkeys_1) and ignore all Lisp
1330 errors during the call. The only errors should be from Fdefine_key
1331 when given a key sequence containing an invalid prefix key. If the
1332 termcap defines function keys which use a prefix that is already bound
1333 to a command by the default bindings, we should silently ignore that
1334 function key specification, rather than giving the user an error and
1335 refusing to run at all on such a terminal. */
1336
1337 extern Lisp_Object Fidentity ();
1338 term_get_fkeys_address = address;
1339 term_get_fkeys_kboard = kboard;
1340 internal_condition_case (term_get_fkeys_1, Qerror, Fidentity);
1341 }
1342
1343 static Lisp_Object
1344 term_get_fkeys_1 ()
1345 {
1346 int i;
1347
1348 char **address = term_get_fkeys_address;
1349 KBOARD *kboard = term_get_fkeys_kboard;
1350
1351 /* This can happen if CANNOT_DUMP or with strange options. */
1352 if (!initialized)
1353 kboard->Vfunction_key_map = Fmake_sparse_keymap (Qnil);
1354
1355 for (i = 0; i < (sizeof (keys)/sizeof (keys[0])); i++)
1356 {
1357 char *sequence = tgetstr (keys[i].cap, address);
1358 if (sequence)
1359 Fdefine_key (kboard->Vfunction_key_map, build_string (sequence),
1360 Fmake_vector (make_number (1),
1361 intern (keys[i].name)));
1362 }
1363
1364 /* The uses of the "k0" capability are inconsistent; sometimes it
1365 describes F10, whereas othertimes it describes F0 and "k;" describes F10.
1366 We will attempt to politely accommodate both systems by testing for
1367 "k;", and if it is present, assuming that "k0" denotes F0, otherwise F10.
1368 */
1369 {
1370 char *k_semi = tgetstr ("k;", address);
1371 char *k0 = tgetstr ("k0", address);
1372 char *k0_name = "f10";
1373
1374 if (k_semi)
1375 {
1376 if (k0)
1377 /* Define f0 first, so that f10 takes precedence in case the
1378 key sequences happens to be the same. */
1379 Fdefine_key (kboard->Vfunction_key_map, build_string (k0),
1380 Fmake_vector (make_number (1), intern ("f0")));
1381 Fdefine_key (kboard->Vfunction_key_map, build_string (k_semi),
1382 Fmake_vector (make_number (1), intern ("f10")));
1383 }
1384 else if (k0)
1385 Fdefine_key (kboard->Vfunction_key_map, build_string (k0),
1386 Fmake_vector (make_number (1), intern (k0_name)));
1387 }
1388
1389 /* Set up cookies for numbered function keys above f10. */
1390 {
1391 char fcap[3], fkey[4];
1392
1393 fcap[0] = 'F'; fcap[2] = '\0';
1394 for (i = 11; i < 64; i++)
1395 {
1396 if (i <= 19)
1397 fcap[1] = '1' + i - 11;
1398 else if (i <= 45)
1399 fcap[1] = 'A' + i - 20;
1400 else
1401 fcap[1] = 'a' + i - 46;
1402
1403 {
1404 char *sequence = tgetstr (fcap, address);
1405 if (sequence)
1406 {
1407 sprintf (fkey, "f%d", i);
1408 Fdefine_key (kboard->Vfunction_key_map, build_string (sequence),
1409 Fmake_vector (make_number (1),
1410 intern (fkey)));
1411 }
1412 }
1413 }
1414 }
1415
1416 /*
1417 * Various mappings to try and get a better fit.
1418 */
1419 {
1420 #define CONDITIONAL_REASSIGN(cap1, cap2, sym) \
1421 if (!tgetstr (cap1, address)) \
1422 { \
1423 char *sequence = tgetstr (cap2, address); \
1424 if (sequence) \
1425 Fdefine_key (kboard->Vfunction_key_map, build_string (sequence), \
1426 Fmake_vector (make_number (1), \
1427 intern (sym))); \
1428 }
1429
1430 /* if there's no key_next keycap, map key_npage to `next' keysym */
1431 CONDITIONAL_REASSIGN ("%5", "kN", "next");
1432 /* if there's no key_prev keycap, map key_ppage to `previous' keysym */
1433 CONDITIONAL_REASSIGN ("%8", "kP", "prior");
1434 /* if there's no key_dc keycap, map key_ic to `insert' keysym */
1435 CONDITIONAL_REASSIGN ("kD", "kI", "insert");
1436 /* if there's no key_end keycap, map key_ll to 'end' keysym */
1437 CONDITIONAL_REASSIGN ("@7", "kH", "end");
1438
1439 /* IBM has their own non-standard dialect of terminfo.
1440 If the standard name isn't found, try the IBM name. */
1441 CONDITIONAL_REASSIGN ("kB", "KO", "backtab");
1442 CONDITIONAL_REASSIGN ("@4", "kJ", "execute"); /* actually "action" */
1443 CONDITIONAL_REASSIGN ("@4", "kc", "execute"); /* actually "command" */
1444 CONDITIONAL_REASSIGN ("%7", "ki", "menu");
1445 CONDITIONAL_REASSIGN ("@7", "kw", "end");
1446 CONDITIONAL_REASSIGN ("F1", "k<", "f11");
1447 CONDITIONAL_REASSIGN ("F2", "k>", "f12");
1448 CONDITIONAL_REASSIGN ("%1", "kq", "help");
1449 CONDITIONAL_REASSIGN ("*6", "kU", "select");
1450 #undef CONDITIONAL_REASSIGN
1451 }
1452
1453 return Qnil;
1454 }
1455
1456 \f
1457 /***********************************************************************
1458 Character Display Information
1459 ***********************************************************************/
1460
1461 static void append_glyph P_ ((struct it *));
1462 static void produce_stretch_glyph P_ ((struct it *));
1463
1464
1465 /* Append glyphs to IT's glyph_row. Called from produce_glyphs for
1466 terminal frames if IT->glyph_row != NULL. IT->c is the character
1467 for which to produce glyphs; IT->face_id contains the character's
1468 face. Padding glyphs are appended if IT->c has a IT->pixel_width >
1469 1. */
1470
1471 static void
1472 append_glyph (it)
1473 struct it *it;
1474 {
1475 struct glyph *glyph, *end;
1476 int i;
1477
1478 xassert (it->glyph_row);
1479 glyph = (it->glyph_row->glyphs[it->area]
1480 + it->glyph_row->used[it->area]);
1481 end = it->glyph_row->glyphs[1 + it->area];
1482
1483 for (i = 0;
1484 i < it->pixel_width && glyph < end;
1485 ++i)
1486 {
1487 glyph->type = CHAR_GLYPH;
1488 glyph->pixel_width = 1;
1489 glyph->u.ch = it->c;
1490 glyph->face_id = it->face_id;
1491 glyph->padding_p = i > 0;
1492 glyph->charpos = CHARPOS (it->position);
1493 glyph->object = it->object;
1494
1495 ++it->glyph_row->used[it->area];
1496 ++glyph;
1497 }
1498 }
1499
1500
1501 /* Produce glyphs for the display element described by IT. *IT
1502 specifies what we want to produce a glyph for (character, image, ...),
1503 and where in the glyph matrix we currently are (glyph row and hpos).
1504 produce_glyphs fills in output fields of *IT with information such as the
1505 pixel width and height of a character, and maybe output actual glyphs at
1506 the same time if IT->glyph_row is non-null. See the explanation of
1507 struct display_iterator in dispextern.h for an overview.
1508
1509 produce_glyphs also stores the result of glyph width, ascent
1510 etc. computations in *IT.
1511
1512 IT->glyph_row may be null, in which case produce_glyphs does not
1513 actually fill in the glyphs. This is used in the move_* functions
1514 in xdisp.c for text width and height computations.
1515
1516 Callers usually don't call produce_glyphs directly;
1517 instead they use the macro PRODUCE_GLYPHS. */
1518
1519 void
1520 produce_glyphs (it)
1521 struct it *it;
1522 {
1523 /* If a hook is installed, let it do the work. */
1524 xassert (it->what == IT_CHARACTER
1525 || it->what == IT_COMPOSITION
1526 || it->what == IT_STRETCH);
1527
1528 if (it->what == IT_STRETCH)
1529 {
1530 produce_stretch_glyph (it);
1531 goto done;
1532 }
1533
1534 /* Nothing but characters are supported on terminal frames. For a
1535 composition sequence, it->c is the first character of the
1536 sequence. */
1537 xassert (it->what == IT_CHARACTER
1538 || it->what == IT_COMPOSITION);
1539
1540 if (it->c >= 040 && it->c < 0177)
1541 {
1542 it->pixel_width = it->nglyphs = 1;
1543 if (it->glyph_row)
1544 append_glyph (it);
1545 }
1546 else if (it->c == '\n')
1547 it->pixel_width = it->nglyphs = 0;
1548 else if (it->c == '\t')
1549 {
1550 int absolute_x = (it->current_x
1551 + it->continuation_lines_width);
1552 int next_tab_x
1553 = (((1 + absolute_x + it->tab_width - 1)
1554 / it->tab_width)
1555 * it->tab_width);
1556 int nspaces;
1557
1558 /* If part of the TAB has been displayed on the previous line
1559 which is continued now, continuation_lines_width will have
1560 been incremented already by the part that fitted on the
1561 continued line. So, we will get the right number of spaces
1562 here. */
1563 nspaces = next_tab_x - absolute_x;
1564
1565 if (it->glyph_row)
1566 {
1567 int n = nspaces;
1568
1569 it->c = ' ';
1570 it->pixel_width = it->len = 1;
1571
1572 while (n--)
1573 append_glyph (it);
1574
1575 it->c = '\t';
1576 }
1577
1578 it->pixel_width = nspaces;
1579 it->nglyphs = nspaces;
1580 }
1581 else if (SINGLE_BYTE_CHAR_P (it->c))
1582 {
1583 /* Coming here means that it->c is from display table, thus we
1584 must send the code as is to the terminal. Although there's
1585 no way to know how many columns it occupies on a screen, it
1586 is a good assumption that a single byte code has 1-column
1587 width. */
1588 it->pixel_width = it->nglyphs = 1;
1589 if (it->glyph_row)
1590 append_glyph (it);
1591 }
1592 else
1593 {
1594 /* A multi-byte character. The display width is fixed for all
1595 characters of the set. Some of the glyphs may have to be
1596 ignored because they are already displayed in a continued
1597 line. */
1598 int charset = CHAR_CHARSET (it->c);
1599
1600 it->pixel_width = CHARSET_WIDTH (charset);
1601 it->nglyphs = it->pixel_width;
1602
1603 if (it->glyph_row)
1604 append_glyph (it);
1605 }
1606
1607 done:
1608 /* Advance current_x by the pixel width as a convenience for
1609 the caller. */
1610 if (it->area == TEXT_AREA)
1611 it->current_x += it->pixel_width;
1612 it->ascent = it->max_ascent = it->phys_ascent = it->max_phys_ascent = 0;
1613 it->descent = it->max_descent = it->phys_descent = it->max_phys_descent = 1;
1614 }
1615
1616
1617 /* Produce a stretch glyph for iterator IT. IT->object is the value
1618 of the glyph property displayed. The value must be a list
1619 `(space KEYWORD VALUE ...)' with the following KEYWORD/VALUE pairs
1620 being recognized:
1621
1622 1. `:width WIDTH' specifies that the space should be WIDTH *
1623 canonical char width wide. WIDTH may be an integer or floating
1624 point number.
1625
1626 2. `:align-to HPOS' specifies that the space should be wide enough
1627 to reach HPOS, a value in canonical character units. */
1628
1629 static void
1630 produce_stretch_glyph (it)
1631 struct it *it;
1632 {
1633 /* (space :width WIDTH ...) */
1634 Lisp_Object prop, plist;
1635 int width = 0, align_to = -1;
1636 int zero_width_ok_p = 0;
1637 double tem;
1638
1639 /* List should start with `space'. */
1640 xassert (CONSP (it->object) && EQ (XCAR (it->object), Qspace));
1641 plist = XCDR (it->object);
1642
1643 /* Compute the width of the stretch. */
1644 if ((prop = Fplist_get (plist, QCwidth), !NILP (prop))
1645 && calc_pixel_width_or_height (&tem, it, prop, 0, 1, 0))
1646 {
1647 /* Absolute width `:width WIDTH' specified and valid. */
1648 zero_width_ok_p = 1;
1649 width = (int)(tem + 0.5);
1650 }
1651 else if ((prop = Fplist_get (plist, QCalign_to), !NILP (prop))
1652 && calc_pixel_width_or_height (&tem, it, prop, 0, 1, &align_to))
1653 {
1654 if (it->glyph_row == NULL || !it->glyph_row->mode_line_p)
1655 align_to = (align_to < 0
1656 ? 0
1657 : align_to - window_box_left_offset (it->w, TEXT_AREA));
1658 else if (align_to < 0)
1659 align_to = window_box_left_offset (it->w, TEXT_AREA);
1660 width = max (0, (int)(tem + 0.5) + align_to - it->current_x);
1661 zero_width_ok_p = 1;
1662 }
1663 else
1664 /* Nothing specified -> width defaults to canonical char width. */
1665 width = FRAME_COLUMN_WIDTH (it->f);
1666
1667 if (width <= 0 && (width < 0 || !zero_width_ok_p))
1668 width = 1;
1669
1670 if (width > 0 && it->glyph_row)
1671 {
1672 Lisp_Object o_object = it->object;
1673 Lisp_Object object = it->stack[it->sp - 1].string;
1674 int n = width;
1675 int c = it->c;
1676
1677 if (!STRINGP (object))
1678 object = it->w->buffer;
1679 it->object = object;
1680 it->c = ' ';
1681 it->pixel_width = it->len = 1;
1682 while (n--)
1683 append_glyph (it);
1684 it->object = o_object;
1685 it->c = c;
1686 }
1687 it->pixel_width = width;
1688 it->nglyphs = width;
1689 }
1690
1691
1692 /* Get information about special display element WHAT in an
1693 environment described by IT. WHAT is one of IT_TRUNCATION or
1694 IT_CONTINUATION. Maybe produce glyphs for WHAT if IT has a
1695 non-null glyph_row member. This function ensures that fields like
1696 face_id, c, len of IT are left untouched. */
1697
1698 void
1699 produce_special_glyphs (it, what)
1700 struct it *it;
1701 enum display_element_type what;
1702 {
1703 struct it temp_it;
1704 GLYPH glyph;
1705
1706 temp_it = *it;
1707 temp_it.dp = NULL;
1708 temp_it.what = IT_CHARACTER;
1709 temp_it.len = 1;
1710 temp_it.object = make_number (0);
1711 bzero (&temp_it.current, sizeof temp_it.current);
1712
1713 if (what == IT_CONTINUATION)
1714 {
1715 /* Continuation glyph. */
1716 if (it->dp
1717 && INTEGERP (DISP_CONTINUE_GLYPH (it->dp))
1718 && GLYPH_CHAR_VALID_P (XINT (DISP_CONTINUE_GLYPH (it->dp))))
1719 {
1720 glyph = XINT (DISP_CONTINUE_GLYPH (it->dp));
1721 glyph = spec_glyph_lookup_face (XWINDOW (it->window), glyph);
1722 }
1723 else
1724 glyph = '\\';
1725 }
1726 else if (what == IT_TRUNCATION)
1727 {
1728 /* Truncation glyph. */
1729 if (it->dp
1730 && INTEGERP (DISP_TRUNC_GLYPH (it->dp))
1731 && GLYPH_CHAR_VALID_P (XINT (DISP_TRUNC_GLYPH (it->dp))))
1732 {
1733 glyph = XINT (DISP_TRUNC_GLYPH (it->dp));
1734 glyph = spec_glyph_lookup_face (XWINDOW (it->window), glyph);
1735 }
1736 else
1737 glyph = '$';
1738 }
1739 else
1740 abort ();
1741
1742 temp_it.c = FAST_GLYPH_CHAR (glyph);
1743 temp_it.face_id = FAST_GLYPH_FACE (glyph);
1744 temp_it.len = CHAR_BYTES (temp_it.c);
1745
1746 produce_glyphs (&temp_it);
1747 it->pixel_width = temp_it.pixel_width;
1748 it->nglyphs = temp_it.pixel_width;
1749 }
1750
1751
1752 \f
1753 /***********************************************************************
1754 Faces
1755 ***********************************************************************/
1756
1757 /* Value is non-zero if attribute ATTR may be used. ATTR should be
1758 one of the enumerators from enum no_color_bit, or a bit set built
1759 from them. Some display attributes may not be used together with
1760 color; the termcap capability `NC' specifies which ones. */
1761
1762 #define MAY_USE_WITH_COLORS_P(tty, ATTR) \
1763 (tty->TN_max_colors > 0 \
1764 ? (tty->TN_no_color_video & (ATTR)) == 0 \
1765 : 1)
1766
1767 /* Turn appearances of face FACE_ID on tty frame F on.
1768 FACE_ID is a realized face ID number, in the face cache. */
1769
1770 static void
1771 turn_on_face (f, face_id)
1772 struct frame *f;
1773 int face_id;
1774 {
1775 struct face *face = FACE_FROM_ID (f, face_id);
1776 long fg = face->foreground;
1777 long bg = face->background;
1778 struct tty_display_info *tty = FRAME_TTY (f);
1779
1780 /* Do this first because TS_end_standout_mode may be the same
1781 as TS_exit_attribute_mode, which turns all appearances off. */
1782 if (MAY_USE_WITH_COLORS_P (tty, NC_REVERSE))
1783 {
1784 if (tty->TN_max_colors > 0)
1785 {
1786 if (fg >= 0 && bg >= 0)
1787 {
1788 /* If the terminal supports colors, we can set them
1789 below without using reverse video. The face's fg
1790 and bg colors are set as they should appear on
1791 the screen, i.e. they take the inverse-video'ness
1792 of the face already into account. */
1793 }
1794 else if (inverse_video)
1795 {
1796 if (fg == FACE_TTY_DEFAULT_FG_COLOR
1797 || bg == FACE_TTY_DEFAULT_BG_COLOR)
1798 toggle_highlight (tty);
1799 }
1800 else
1801 {
1802 if (fg == FACE_TTY_DEFAULT_BG_COLOR
1803 || bg == FACE_TTY_DEFAULT_FG_COLOR)
1804 toggle_highlight (tty);
1805 }
1806 }
1807 else
1808 {
1809 /* If we can't display colors, use reverse video
1810 if the face specifies that. */
1811 if (inverse_video)
1812 {
1813 if (fg == FACE_TTY_DEFAULT_FG_COLOR
1814 || bg == FACE_TTY_DEFAULT_BG_COLOR)
1815 toggle_highlight (tty);
1816 }
1817 else
1818 {
1819 if (fg == FACE_TTY_DEFAULT_BG_COLOR
1820 || bg == FACE_TTY_DEFAULT_FG_COLOR)
1821 toggle_highlight (tty);
1822 }
1823 }
1824 }
1825
1826 if (face->tty_bold_p)
1827 {
1828 if (MAY_USE_WITH_COLORS_P (tty, NC_BOLD))
1829 OUTPUT1_IF (tty, tty->TS_enter_bold_mode);
1830 }
1831 else if (face->tty_dim_p)
1832 if (MAY_USE_WITH_COLORS_P (tty, NC_DIM))
1833 OUTPUT1_IF (tty, tty->TS_enter_dim_mode);
1834
1835 /* Alternate charset and blinking not yet used. */
1836 if (face->tty_alt_charset_p
1837 && MAY_USE_WITH_COLORS_P (tty, NC_ALT_CHARSET))
1838 OUTPUT1_IF (tty, tty->TS_enter_alt_charset_mode);
1839
1840 if (face->tty_blinking_p
1841 && MAY_USE_WITH_COLORS_P (tty, NC_BLINK))
1842 OUTPUT1_IF (tty, tty->TS_enter_blink_mode);
1843
1844 if (face->tty_underline_p && MAY_USE_WITH_COLORS_P (tty, NC_UNDERLINE))
1845 OUTPUT1_IF (tty, tty->TS_enter_underline_mode);
1846
1847 if (tty->TN_max_colors > 0)
1848 {
1849 char *p;
1850
1851 if (fg >= 0 && tty->TS_set_foreground)
1852 {
1853 if (tty->standout_mode)
1854 p = tparam (tty->TS_set_background, NULL, 0, (int) fg);
1855 else
1856 p = tparam (tty->TS_set_foreground, NULL, 0, (int) fg);
1857 OUTPUT (tty, p);
1858 xfree (p);
1859 }
1860
1861 if (bg >= 0 && tty->TS_set_background)
1862 {
1863 if (tty->standout_mode)
1864 p = tparam (tty->TS_set_foreground, NULL, 0, (int) bg);
1865 else
1866 p = tparam (tty->TS_set_background, NULL, 0, (int) bg);
1867 OUTPUT (tty, p);
1868 xfree (p);
1869 }
1870 }
1871 }
1872
1873
1874 /* Turn off appearances of face FACE_ID on tty frame F. */
1875
1876 static void
1877 turn_off_face (f, face_id)
1878 struct frame *f;
1879 int face_id;
1880 {
1881 struct face *face = FACE_FROM_ID (f, face_id);
1882 struct tty_display_info *tty = FRAME_TTY (f);
1883
1884 xassert (face != NULL);
1885
1886 if (tty->TS_exit_attribute_mode)
1887 {
1888 /* Capability "me" will turn off appearance modes double-bright,
1889 half-bright, reverse-video, standout, underline. It may or
1890 may not turn off alt-char-mode. */
1891 if (face->tty_bold_p
1892 || face->tty_dim_p
1893 || face->tty_reverse_p
1894 || face->tty_alt_charset_p
1895 || face->tty_blinking_p
1896 || face->tty_underline_p)
1897 {
1898 OUTPUT1_IF (tty, tty->TS_exit_attribute_mode);
1899 if (strcmp (tty->TS_exit_attribute_mode, tty->TS_end_standout_mode) == 0)
1900 tty->standout_mode = 0;
1901 }
1902
1903 if (face->tty_alt_charset_p)
1904 OUTPUT_IF (tty, tty->TS_exit_alt_charset_mode);
1905 }
1906 else
1907 {
1908 /* If we don't have "me" we can only have those appearances
1909 that have exit sequences defined. */
1910 if (face->tty_alt_charset_p)
1911 OUTPUT_IF (tty, tty->TS_exit_alt_charset_mode);
1912
1913 if (face->tty_underline_p)
1914 OUTPUT_IF (tty, tty->TS_exit_underline_mode);
1915 }
1916
1917 /* Switch back to default colors. */
1918 if (tty->TN_max_colors > 0
1919 && ((face->foreground != FACE_TTY_DEFAULT_COLOR
1920 && face->foreground != FACE_TTY_DEFAULT_FG_COLOR)
1921 || (face->background != FACE_TTY_DEFAULT_COLOR
1922 && face->background != FACE_TTY_DEFAULT_BG_COLOR)))
1923 OUTPUT1_IF (tty, tty->TS_orig_pair);
1924 }
1925
1926
1927 /* Return non-zero if the terminal on frame F supports all of the
1928 capabilities in CAPS simultaneously, with foreground and background
1929 colors FG and BG. */
1930
1931 int
1932 tty_capable_p (tty, caps, fg, bg)
1933 struct tty_display_info *tty;
1934 unsigned caps;
1935 unsigned long fg, bg;
1936 {
1937 #define TTY_CAPABLE_P_TRY(tty, cap, TS, NC_bit) \
1938 if ((caps & (cap)) && (!(TS) || !MAY_USE_WITH_COLORS_P(tty, NC_bit))) \
1939 return 0;
1940
1941 TTY_CAPABLE_P_TRY (tty, TTY_CAP_INVERSE, tty->TS_standout_mode, NC_REVERSE);
1942 TTY_CAPABLE_P_TRY (tty, TTY_CAP_UNDERLINE, tty->TS_enter_underline_mode, NC_UNDERLINE);
1943 TTY_CAPABLE_P_TRY (tty, TTY_CAP_BOLD, tty->TS_enter_bold_mode, NC_BOLD);
1944 TTY_CAPABLE_P_TRY (tty, TTY_CAP_DIM, tty->TS_enter_dim_mode, NC_DIM);
1945 TTY_CAPABLE_P_TRY (tty, TTY_CAP_BLINK, tty->TS_enter_blink_mode, NC_BLINK);
1946 TTY_CAPABLE_P_TRY (tty, TTY_CAP_ALT_CHARSET, tty->TS_enter_alt_charset_mode, NC_ALT_CHARSET);
1947
1948 /* We can do it! */
1949 return 1;
1950 }
1951
1952 /* Return non-zero if the terminal is capable to display colors. */
1953
1954 DEFUN ("tty-display-color-p", Ftty_display_color_p, Stty_display_color_p,
1955 0, 1, 0,
1956 doc: /* Return non-nil if the tty device that DISPLAY uses can display colors. */)
1957 (display)
1958 Lisp_Object display;
1959 {
1960 struct display *d = get_tty_display (display);
1961 if (!d)
1962 return Qnil;
1963 else
1964 return d->display_info.tty->TN_max_colors > 0 ? Qt : Qnil;
1965 }
1966
1967 /* Return the number of supported colors. */
1968 DEFUN ("tty-display-color-cells", Ftty_display_color_cells,
1969 Stty_display_color_cells, 0, 1, 0,
1970 doc: /* Return the number of colors supported by the tty device that DISPLAY uses. */)
1971 (display)
1972 Lisp_Object display;
1973 {
1974 struct display *d = get_tty_display (display);
1975 if (!d)
1976 return make_number (0);
1977 else
1978 return make_number (d->display_info.tty->TN_max_colors);
1979 }
1980
1981 #ifndef WINDOWSNT
1982
1983 /* Save or restore the default color-related capabilities of this
1984 terminal. */
1985 static void
1986 tty_default_color_capabilities (struct tty_display_info *tty, int save)
1987 {
1988 static char
1989 *default_orig_pair, *default_set_foreground, *default_set_background;
1990 static int default_max_colors, default_max_pairs, default_no_color_video;
1991
1992 if (save)
1993 {
1994 if (default_orig_pair)
1995 xfree (default_orig_pair);
1996 default_orig_pair = tty->TS_orig_pair ? xstrdup (tty->TS_orig_pair) : NULL;
1997
1998 if (default_set_foreground)
1999 xfree (default_set_foreground);
2000 default_set_foreground = tty->TS_set_foreground ? xstrdup (tty->TS_set_foreground)
2001 : NULL;
2002
2003 if (default_set_background)
2004 xfree (default_set_background);
2005 default_set_background = tty->TS_set_background ? xstrdup (tty->TS_set_background)
2006 : NULL;
2007
2008 default_max_colors = tty->TN_max_colors;
2009 default_max_pairs = tty->TN_max_pairs;
2010 default_no_color_video = tty->TN_no_color_video;
2011 }
2012 else
2013 {
2014 tty->TS_orig_pair = default_orig_pair;
2015 tty->TS_set_foreground = default_set_foreground;
2016 tty->TS_set_background = default_set_background;
2017 tty->TN_max_colors = default_max_colors;
2018 tty->TN_max_pairs = default_max_pairs;
2019 tty->TN_no_color_video = default_no_color_video;
2020 }
2021 }
2022
2023 /* Setup one of the standard tty color schemes according to MODE.
2024 MODE's value is generally the number of colors which we want to
2025 support; zero means set up for the default capabilities, the ones
2026 we saw at term_init time; -1 means turn off color support. */
2027 void
2028 tty_setup_colors (struct tty_display_info *tty, int mode)
2029 {
2030 /* Canonicalize all negative values of MODE. */
2031 if (mode < -1)
2032 mode = -1;
2033
2034 switch (mode)
2035 {
2036 case -1: /* no colors at all */
2037 tty->TN_max_colors = 0;
2038 tty->TN_max_pairs = 0;
2039 tty->TN_no_color_video = 0;
2040 tty->TS_set_foreground = tty->TS_set_background = tty->TS_orig_pair = NULL;
2041 break;
2042 case 0: /* default colors, if any */
2043 default:
2044 tty_default_color_capabilities (tty, 0);
2045 break;
2046 case 8: /* 8 standard ANSI colors */
2047 tty->TS_orig_pair = "\033[0m";
2048 #ifdef TERMINFO
2049 tty->TS_set_foreground = "\033[3%p1%dm";
2050 tty->TS_set_background = "\033[4%p1%dm";
2051 #else
2052 tty->TS_set_foreground = "\033[3%dm";
2053 tty->TS_set_background = "\033[4%dm";
2054 #endif
2055 tty->TN_max_colors = 8;
2056 tty->TN_max_pairs = 64;
2057 tty->TN_no_color_video = 0;
2058 break;
2059 }
2060 }
2061
2062 void
2063 set_tty_color_mode (f, val)
2064 struct frame *f;
2065 Lisp_Object val;
2066 {
2067 Lisp_Object color_mode_spec, current_mode_spec;
2068 Lisp_Object color_mode, current_mode;
2069 int mode, old_mode;
2070 extern Lisp_Object Qtty_color_mode;
2071 Lisp_Object tty_color_mode_alist;
2072
2073 tty_color_mode_alist = Fintern_soft (build_string ("tty-color-mode-alist"),
2074 Qnil);
2075
2076 if (INTEGERP (val))
2077 color_mode = val;
2078 else
2079 {
2080 if (NILP (tty_color_mode_alist))
2081 color_mode_spec = Qnil;
2082 else
2083 color_mode_spec = Fassq (val, XSYMBOL (tty_color_mode_alist)->value);
2084
2085 if (CONSP (color_mode_spec))
2086 color_mode = XCDR (color_mode_spec);
2087 else
2088 color_mode = Qnil;
2089 }
2090
2091 current_mode_spec = assq_no_quit (Qtty_color_mode, f->param_alist);
2092
2093 if (CONSP (current_mode_spec))
2094 current_mode = XCDR (current_mode_spec);
2095 else
2096 current_mode = Qnil;
2097 if (INTEGERP (color_mode))
2098 mode = XINT (color_mode);
2099 else
2100 mode = 0; /* meaning default */
2101 if (INTEGERP (current_mode))
2102 old_mode = XINT (current_mode);
2103 else
2104 old_mode = 0;
2105
2106 if (mode != old_mode)
2107 {
2108 tty_setup_colors (FRAME_TTY (f), mode);
2109 /* This recomputes all the faces given the new color
2110 definitions. */
2111 call0 (intern ("tty-set-up-initial-frame-faces"));
2112 redraw_frame (f);
2113 }
2114 }
2115
2116 #endif /* !WINDOWSNT */
2117
2118 \f
2119
2120 /* Return the display object specified by DISPLAY. DISPLAY may be a
2121 display id, a frame, or nil for the display device of the current
2122 frame. If THROW is zero, return NULL for failure, otherwise throw
2123 an error. */
2124
2125 struct display *
2126 get_display (Lisp_Object display, int throw)
2127 {
2128 struct display *result = NULL;
2129
2130 if (NILP (display))
2131 display = selected_frame;
2132
2133 if (INTEGERP (display))
2134 {
2135 struct display *d;
2136
2137 for (d = display_list; d; d = d->next_display)
2138 {
2139 if (d->id == XINT (display))
2140 {
2141 result = d;
2142 break;
2143 }
2144 }
2145 }
2146 else if (FRAMEP (display))
2147 {
2148 result = FRAME_DISPLAY (XFRAME (display));
2149 }
2150
2151 if (result == NULL && throw)
2152 wrong_type_argument (Qdisplay_live_p, display);
2153
2154 return result;
2155 }
2156
2157 /* Return the tty display object specified by DISPLAY. */
2158
2159 static struct display *
2160 get_tty_display (Lisp_Object display)
2161 {
2162 struct display *d = get_display (display, 0);
2163
2164 if (d && d->type == output_initial)
2165 d = NULL;
2166
2167 if (d && d->type != output_termcap)
2168 {
2169 #if 0 /* XXX We need a predicate as the first argument; find one. */
2170 wrong_type_argument ("Not a termcap display", display);
2171 #else /* Until we fix the wrong_type_argument call above, simply throw
2172 a dumb error. */
2173 error ("DISPLAY is not a termcap display");
2174 #endif
2175 }
2176
2177 return d;
2178 }
2179
2180 /* Return the active termcap display that uses the tty device with the
2181 given name. If NAME is NULL, return the display corresponding to
2182 our controlling terminal.
2183
2184 This function ignores suspended displays.
2185
2186 Returns NULL if the named terminal device is not opened. */
2187
2188 struct display *
2189 get_named_tty_display (name)
2190 char *name;
2191 {
2192 struct display *d;
2193
2194 for (d = display_list; d; d = d->next_display) {
2195 if (d->type == output_termcap
2196 && ((d->display_info.tty->name == 0 && name == 0)
2197 || (name && d->display_info.tty->name
2198 && !strcmp (d->display_info.tty->name, name)))
2199 && DISPLAY_ACTIVE_P (d))
2200 return d;
2201 };
2202
2203 return 0;
2204 }
2205
2206 \f
2207
2208 DEFUN ("display-name", Fdisplay_name, Sdisplay_name, 0, 1, 0,
2209 doc: /* Return the name of the device that DISPLAY uses.
2210 It is not guaranteed that the returned value is unique among opened displays.
2211
2212 DISPLAY may be a display, a frame, or nil (meaning the selected
2213 frame's display). */)
2214 (display)
2215 Lisp_Object display;
2216 {
2217 struct display *d = get_display (display, 1);
2218
2219 if (d->name)
2220 return build_string (d->name);
2221 else
2222 return Qnil;
2223 }
2224
2225 DEFUN ("display-tty-type", Fdisplay_tty_type, Sdisplay_tty_type, 0, 1, 0,
2226 doc: /* Return the type of the TTY device that DISPLAY uses.
2227
2228 DISPLAY may be a display, a frame, or nil (meaning the selected
2229 frame's display). */)
2230 (display)
2231 Lisp_Object display;
2232 {
2233 struct display *d = get_display (display, 1);
2234
2235 if (d->type != output_termcap)
2236 error ("Display %d is not a termcap display", d->id);
2237
2238 if (d->display_info.tty->type)
2239 return build_string (d->display_info.tty->type);
2240 else
2241 return Qnil;
2242 }
2243
2244 DEFUN ("display-controlling-tty-p", Fdisplay_controlling_tty_p, Sdisplay_controlling_tty_p, 0, 1, 0,
2245 doc: /* Return non-nil if DISPLAY is on the controlling tty of the Emacs process.
2246
2247 DISPLAY may be a display, a frame, or nil (meaning the selected
2248 frame's display). */)
2249 (display)
2250 Lisp_Object display;
2251 {
2252 struct display *d = get_display (display, 1);
2253
2254 if (d->type != output_termcap || d->display_info.tty->name)
2255 return Qnil;
2256 else
2257 return Qt;
2258 }
2259
2260 DEFUN ("tty-no-underline", Ftty_no_underline, Stty_no_underline, 0, 1, 0,
2261 doc: /* Declare that the tty used by DISPLAY does not handle underlining.
2262 This is used to override the terminfo data, for certain terminals that
2263 do not really do underlining, but say that they do. This function has
2264 no effect if used on a non-tty display.
2265
2266 DISPLAY may be a display, a frame, or nil (meaning the selected
2267 frame's display). */)
2268 (display)
2269 Lisp_Object display;
2270 {
2271 struct display *d = get_display (display, 1);
2272
2273 if (d->type == output_termcap)
2274 d->display_info.tty->TS_enter_underline_mode = 0;
2275 return Qnil;
2276 }
2277
2278
2279 \f
2280 /***********************************************************************
2281 Initialization
2282 ***********************************************************************/
2283
2284 /* Create the bootstrap display device for the initial frame.
2285 Returns a display of type output_initial. */
2286
2287 struct display *
2288 init_initial_display (void)
2289 {
2290 if (initialized || display_list || tty_list)
2291 abort ();
2292
2293 initial_display = create_display ();
2294 initial_display->type = output_initial;
2295 initial_display->name = xstrdup ("initial_display");
2296 initial_display->kboard = initial_kboard;
2297
2298 initial_display->delete_display_hook = &delete_initial_display;
2299 /* All other hooks are NULL. */
2300
2301 return initial_display;
2302 }
2303
2304 /* Deletes the bootstrap display device.
2305 Called through delete_display_hook. */
2306
2307 void
2308 delete_initial_display (struct display *display)
2309 {
2310 if (display != initial_display)
2311 abort ();
2312
2313 delete_display (display);
2314 initial_display = NULL;
2315 }
2316
2317 /* Drop the controlling terminal if fd is the same device. */
2318 void
2319 dissociate_if_controlling_tty (int fd)
2320 {
2321 int pgid;
2322 EMACS_GET_TTY_PGRP (fd, &pgid); /* If tcgetpgrp succeeds, fd is the ctty. */
2323 if (pgid != -1)
2324 {
2325 #if defined (USG) && !defined (BSD_PGRPS)
2326 setpgrp ();
2327 no_controlling_tty = 1;
2328 #else
2329 #ifdef TIOCNOTTY /* Try BSD ioctls. */
2330 sigblock (sigmask (SIGTTOU));
2331 fd = emacs_open ("/dev/tty", O_RDWR, 0);
2332 if (fd != -1 && ioctl (fd, TIOCNOTTY, 0) != -1)
2333 {
2334 no_controlling_tty = 1;
2335 }
2336 if (fd != -1)
2337 emacs_close (fd);
2338 sigunblock (sigmask (SIGTTOU));
2339 #else
2340 /* Unknown system. */
2341 croak ();
2342 #endif /* ! TIOCNOTTY */
2343 #endif /* ! USG */
2344 }
2345 }
2346
2347 /* Create a termcap display on the tty device with the given name and
2348 type.
2349
2350 If NAME is NULL, then use the controlling tty, i.e., stdin/stdout.
2351 Otherwise NAME should be a path to the tty device file,
2352 e.g. "/dev/pts/7".
2353
2354 TERMINAL_TYPE is the termcap type of the device, e.g. "vt100".
2355
2356 If MUST_SUCCEED is true, then all errors are fatal. */
2357
2358 struct display *
2359 term_init (char *name, char *terminal_type, int must_succeed)
2360 {
2361 char *area;
2362 char **address = &area;
2363 char *buffer = NULL;
2364 int buffer_size = 4096;
2365 register char *p;
2366 int status;
2367 struct tty_display_info *tty;
2368 struct display *display;
2369
2370 static void maybe_fatal();
2371
2372 if (!terminal_type)
2373 maybe_fatal (must_succeed, 0, 0,
2374 "Unknown terminal type",
2375 "Unknown terminal type");
2376
2377 /* If we already have an active display on the given device, use that.
2378 If all displays are suspended, create a new one instead. */
2379 /* XXX Perhaps this should be made explicit by having term_init
2380 always create a new display and separating display and frame
2381 creation on Lisp level. */
2382 display = get_named_tty_display (name);
2383 if (display)
2384 return display;
2385
2386 display = create_display ();
2387 tty = (struct tty_display_info *) xmalloc (sizeof (struct tty_display_info));
2388 bzero (tty, sizeof (struct tty_display_info));
2389 tty->next = tty_list;
2390 tty_list = tty;
2391
2392 display->type = output_termcap;
2393 display->display_info.tty = tty;
2394 tty->display = display;
2395
2396 tty->Wcm = (struct cm *) xmalloc (sizeof (struct cm));
2397 Wcm_clear (tty);
2398
2399 display->rif = 0; /* ttys don't support window-based redisplay. */
2400
2401 display->cursor_to_hook = &tty_cursor_to;
2402 display->raw_cursor_to_hook = &tty_raw_cursor_to;
2403
2404 display->clear_to_end_hook = &tty_clear_to_end;
2405 display->clear_frame_hook = &tty_clear_frame;
2406 display->clear_end_of_line_hook = &tty_clear_end_of_line;
2407
2408 display->ins_del_lines_hook = &tty_ins_del_lines;
2409
2410 display->insert_glyphs_hook = &tty_insert_glyphs;
2411 display->write_glyphs_hook = &tty_write_glyphs;
2412 display->delete_glyphs_hook = &tty_delete_glyphs;
2413
2414 display->ring_bell_hook = &tty_ring_bell;
2415
2416 display->reset_terminal_modes_hook = &tty_reset_terminal_modes;
2417 display->set_terminal_modes_hook = &tty_set_terminal_modes;
2418 display->update_begin_hook = 0; /* Not needed. */
2419 display->update_end_hook = &tty_update_end;
2420 display->set_terminal_window_hook = &tty_set_terminal_window;
2421
2422 display->mouse_position_hook = 0; /* Not needed. */
2423 display->frame_rehighlight_hook = 0; /* Not needed. */
2424 display->frame_raise_lower_hook = 0; /* Not needed. */
2425
2426 display->set_vertical_scroll_bar_hook = 0; /* Not needed. */
2427 display->condemn_scroll_bars_hook = 0; /* Not needed. */
2428 display->redeem_scroll_bar_hook = 0; /* Not needed. */
2429 display->judge_scroll_bars_hook = 0; /* Not needed. */
2430
2431 display->read_socket_hook = &tty_read_avail_input; /* keyboard.c */
2432 display->frame_up_to_date_hook = 0; /* Not needed. */
2433
2434 display->delete_frame_hook = &delete_tty_output;
2435 display->delete_display_hook = &delete_tty;
2436
2437 if (name)
2438 {
2439 int fd;
2440 FILE *file;
2441
2442 #ifdef O_IGNORE_CTTY
2443 /* Open the terminal device. Don't recognize it as our
2444 controlling terminal, and don't make it the controlling tty
2445 if we don't have one at the moment. */
2446 fd = emacs_open (name, O_RDWR | O_IGNORE_CTTY | O_NOCTTY, 0);
2447 #else
2448 /* Alas, O_IGNORE_CTTY is a GNU extension that seems to be only
2449 defined on Hurd. On other systems, we need to dissociate
2450 ourselves from the controlling tty when we want to open a
2451 frame on the same terminal. */
2452
2453 fd = emacs_open (name, O_RDWR | O_NOCTTY, 0);
2454
2455 #endif /* O_IGNORE_CTTY */
2456
2457 if (fd < 0)
2458 {
2459 delete_tty (display);
2460 error ("Could not open file: %s", name);
2461 }
2462 if (!isatty (fd))
2463 {
2464 close (fd);
2465 error ("Not a tty device: %s", name);
2466 }
2467
2468 dissociate_if_controlling_tty (fd);
2469
2470 file = fdopen (fd, "w+");
2471 tty->name = xstrdup (name);
2472 display->name = xstrdup (name);
2473 tty->input = file;
2474 tty->output = file;
2475 }
2476 else
2477 {
2478 if (no_controlling_tty)
2479 {
2480 /* Opening a frame on stdout is unsafe if we have
2481 disconnected our controlling terminal. */
2482 error ("There is no controlling terminal any more");
2483 }
2484 tty->name = 0;
2485 display->name = xstrdup (ttyname (0));
2486 tty->input = stdin;
2487 tty->output = stdout;
2488 }
2489
2490 tty->type = xstrdup (terminal_type);
2491
2492 add_keyboard_wait_descriptor (fileno (tty->input));
2493
2494 encode_terminal_bufsize = 0;
2495
2496 #ifdef WINDOWSNT
2497 initialize_w32_display ();
2498
2499 Wcm_clear (tty);
2500
2501 area = (char *) xmalloc (2044);
2502
2503 FrameRows (tty) = FRAME_LINES (f); /* XXX */
2504 FrameCols (tty) = FRAME_COLS (f); /* XXX */
2505 tty->specified_window = FRAME_LINES (f); /* XXX */
2506
2507 tty->display->delete_in_insert_mode = 1;
2508
2509 UseTabs (tty) = 0;
2510 display->scroll_region_ok = 0;
2511
2512 /* Seems to insert lines when it's not supposed to, messing
2513 up the display. In doing a trace, it didn't seem to be
2514 called much, so I don't think we're losing anything by
2515 turning it off. */
2516 display->line_ins_del_ok = 0;
2517 display->char_ins_del_ok = 1;
2518
2519 baud_rate = 19200;
2520
2521 FRAME_CAN_HAVE_SCROLL_BARS (f) = 0; /* XXX */
2522 FRAME_VERTICAL_SCROLL_BAR_TYPE (f) = vertical_scroll_bar_none; /* XXX */
2523 TN_max_colors = 16; /* Required to be non-zero for tty-display-color-p */
2524
2525 return display;
2526 #else /* not WINDOWSNT */
2527
2528 Wcm_clear (tty);
2529
2530 buffer = (char *) xmalloc (buffer_size);
2531
2532 /* On some systems, tgetent tries to access the controlling
2533 terminal. */
2534 sigblock (sigmask (SIGTTOU));
2535 status = tgetent (buffer, terminal_type);
2536 sigunblock (sigmask (SIGTTOU));
2537
2538 if (status < 0)
2539 {
2540 #ifdef TERMINFO
2541 maybe_fatal (must_succeed, buffer, display,
2542 "Cannot open terminfo database file",
2543 "Cannot open terminfo database file");
2544 #else
2545 maybe_fatal (must_succeed, buffer, display,
2546 "Cannot open termcap database file",
2547 "Cannot open termcap database file");
2548 #endif
2549 }
2550 if (status == 0)
2551 {
2552 #ifdef TERMINFO
2553 maybe_fatal (must_succeed, buffer, display,
2554 "Terminal type %s is not defined",
2555 "Terminal type %s is not defined.\n\
2556 If that is not the actual type of terminal you have,\n\
2557 use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
2558 `setenv TERM ...') to specify the correct type. It may be necessary\n\
2559 to do `unset TERMINFO' (C-shell: `unsetenv TERMINFO') as well.",
2560 terminal_type);
2561 #else
2562 maybe_fatal (must_succeed, buffer, display,
2563 "Terminal type %s is not defined",
2564 "Terminal type %s is not defined.\n\
2565 If that is not the actual type of terminal you have,\n\
2566 use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
2567 `setenv TERM ...') to specify the correct type. It may be necessary\n\
2568 to do `unset TERMCAP' (C-shell: `unsetenv TERMCAP') as well.",
2569 terminal_type);
2570 #endif
2571 }
2572
2573 #ifndef TERMINFO
2574 if (strlen (buffer) >= buffer_size)
2575 abort ();
2576 buffer_size = strlen (buffer);
2577 #endif
2578 area = (char *) xmalloc (buffer_size);
2579
2580 tty->TS_ins_line = tgetstr ("al", address);
2581 tty->TS_ins_multi_lines = tgetstr ("AL", address);
2582 tty->TS_bell = tgetstr ("bl", address);
2583 BackTab (tty) = tgetstr ("bt", address);
2584 tty->TS_clr_to_bottom = tgetstr ("cd", address);
2585 tty->TS_clr_line = tgetstr ("ce", address);
2586 tty->TS_clr_frame = tgetstr ("cl", address);
2587 ColPosition (tty) = NULL; /* tgetstr ("ch", address); */
2588 AbsPosition (tty) = tgetstr ("cm", address);
2589 CR (tty) = tgetstr ("cr", address);
2590 tty->TS_set_scroll_region = tgetstr ("cs", address);
2591 tty->TS_set_scroll_region_1 = tgetstr ("cS", address);
2592 RowPosition (tty) = tgetstr ("cv", address);
2593 tty->TS_del_char = tgetstr ("dc", address);
2594 tty->TS_del_multi_chars = tgetstr ("DC", address);
2595 tty->TS_del_line = tgetstr ("dl", address);
2596 tty->TS_del_multi_lines = tgetstr ("DL", address);
2597 tty->TS_delete_mode = tgetstr ("dm", address);
2598 tty->TS_end_delete_mode = tgetstr ("ed", address);
2599 tty->TS_end_insert_mode = tgetstr ("ei", address);
2600 Home (tty) = tgetstr ("ho", address);
2601 tty->TS_ins_char = tgetstr ("ic", address);
2602 tty->TS_ins_multi_chars = tgetstr ("IC", address);
2603 tty->TS_insert_mode = tgetstr ("im", address);
2604 tty->TS_pad_inserted_char = tgetstr ("ip", address);
2605 tty->TS_end_keypad_mode = tgetstr ("ke", address);
2606 tty->TS_keypad_mode = tgetstr ("ks", address);
2607 LastLine (tty) = tgetstr ("ll", address);
2608 Right (tty) = tgetstr ("nd", address);
2609 Down (tty) = tgetstr ("do", address);
2610 if (!Down (tty))
2611 Down (tty) = tgetstr ("nl", address); /* Obsolete name for "do" */
2612 #ifdef VMS
2613 /* VMS puts a carriage return before each linefeed,
2614 so it is not safe to use linefeeds. */
2615 if (Down (tty) && Down (tty)[0] == '\n' && Down (tty)[1] == '\0')
2616 Down (tty) = 0;
2617 #endif /* VMS */
2618 if (tgetflag ("bs"))
2619 Left (tty) = "\b"; /* can't possibly be longer! */
2620 else /* (Actually, "bs" is obsolete...) */
2621 Left (tty) = tgetstr ("le", address);
2622 if (!Left (tty))
2623 Left (tty) = tgetstr ("bc", address); /* Obsolete name for "le" */
2624 tty->TS_pad_char = tgetstr ("pc", address);
2625 tty->TS_repeat = tgetstr ("rp", address);
2626 tty->TS_end_standout_mode = tgetstr ("se", address);
2627 tty->TS_fwd_scroll = tgetstr ("sf", address);
2628 tty->TS_standout_mode = tgetstr ("so", address);
2629 tty->TS_rev_scroll = tgetstr ("sr", address);
2630 tty->Wcm->cm_tab = tgetstr ("ta", address);
2631 tty->TS_end_termcap_modes = tgetstr ("te", address);
2632 tty->TS_termcap_modes = tgetstr ("ti", address);
2633 Up (tty) = tgetstr ("up", address);
2634 tty->TS_visible_bell = tgetstr ("vb", address);
2635 tty->TS_cursor_normal = tgetstr ("ve", address);
2636 tty->TS_cursor_visible = tgetstr ("vs", address);
2637 tty->TS_cursor_invisible = tgetstr ("vi", address);
2638 tty->TS_set_window = tgetstr ("wi", address);
2639
2640 tty->TS_enter_underline_mode = tgetstr ("us", address);
2641 tty->TS_exit_underline_mode = tgetstr ("ue", address);
2642 tty->TS_enter_bold_mode = tgetstr ("md", address);
2643 tty->TS_enter_dim_mode = tgetstr ("mh", address);
2644 tty->TS_enter_blink_mode = tgetstr ("mb", address);
2645 tty->TS_enter_reverse_mode = tgetstr ("mr", address);
2646 tty->TS_enter_alt_charset_mode = tgetstr ("as", address);
2647 tty->TS_exit_alt_charset_mode = tgetstr ("ae", address);
2648 tty->TS_exit_attribute_mode = tgetstr ("me", address);
2649
2650 MultiUp (tty) = tgetstr ("UP", address);
2651 MultiDown (tty) = tgetstr ("DO", address);
2652 MultiLeft (tty) = tgetstr ("LE", address);
2653 MultiRight (tty) = tgetstr ("RI", address);
2654
2655 /* SVr4/ANSI color suppert. If "op" isn't available, don't support
2656 color because we can't switch back to the default foreground and
2657 background. */
2658 tty->TS_orig_pair = tgetstr ("op", address);
2659 if (tty->TS_orig_pair)
2660 {
2661 tty->TS_set_foreground = tgetstr ("AF", address);
2662 tty->TS_set_background = tgetstr ("AB", address);
2663 if (!tty->TS_set_foreground)
2664 {
2665 /* SVr4. */
2666 tty->TS_set_foreground = tgetstr ("Sf", address);
2667 tty->TS_set_background = tgetstr ("Sb", address);
2668 }
2669
2670 tty->TN_max_colors = tgetnum ("Co");
2671 tty->TN_max_pairs = tgetnum ("pa");
2672
2673 tty->TN_no_color_video = tgetnum ("NC");
2674 if (tty->TN_no_color_video == -1)
2675 tty->TN_no_color_video = 0;
2676 }
2677
2678 tty_default_color_capabilities (tty, 1);
2679
2680 MagicWrap (tty) = tgetflag ("xn");
2681 /* Since we make MagicWrap terminals look like AutoWrap, we need to have
2682 the former flag imply the latter. */
2683 AutoWrap (tty) = MagicWrap (tty) || tgetflag ("am");
2684 display->memory_below_frame = tgetflag ("db");
2685 tty->TF_hazeltine = tgetflag ("hz");
2686 display->must_write_spaces = tgetflag ("in");
2687 tty->meta_key = tgetflag ("km") || tgetflag ("MT");
2688 tty->TF_insmode_motion = tgetflag ("mi");
2689 tty->TF_standout_motion = tgetflag ("ms");
2690 tty->TF_underscore = tgetflag ("ul");
2691 tty->TF_teleray = tgetflag ("xt");
2692
2693 #ifdef MULTI_KBOARD
2694 display->kboard = (KBOARD *) xmalloc (sizeof (KBOARD));
2695 init_kboard (display->kboard);
2696 display->kboard->next_kboard = all_kboards;
2697 all_kboards = display->kboard;
2698 display->kboard->reference_count++;
2699 /* Don't let the initial kboard remain current longer than necessary.
2700 That would cause problems if a file loaded on startup tries to
2701 prompt in the mini-buffer. */
2702 if (current_kboard == initial_kboard)
2703 current_kboard = display->kboard;
2704 #endif
2705
2706 term_get_fkeys (address, display->kboard);
2707
2708 /* Get frame size from system, or else from termcap. */
2709 {
2710 int height, width;
2711 get_tty_size (fileno (tty->input), &width, &height);
2712 FrameCols (tty) = width;
2713 FrameRows (tty) = height;
2714 }
2715
2716 if (FrameCols (tty) <= 0)
2717 FrameCols (tty) = tgetnum ("co");
2718 if (FrameRows (tty) <= 0)
2719 FrameRows (tty) = tgetnum ("li");
2720
2721 if (FrameRows (tty) < 3 || FrameCols (tty) < 3)
2722 maybe_fatal (must_succeed, NULL, display,
2723 "Screen size %dx%d is too small"
2724 "Screen size %dx%d is too small",
2725 FrameCols (tty), FrameRows (tty));
2726
2727 #if 0 /* This is not used anywhere. */
2728 tty->display->min_padding_speed = tgetnum ("pb");
2729 #endif
2730
2731 TabWidth (tty) = tgetnum ("tw");
2732
2733 #ifdef VMS
2734 /* These capabilities commonly use ^J.
2735 I don't know why, but sending them on VMS does not work;
2736 it causes following spaces to be lost, sometimes.
2737 For now, the simplest fix is to avoid using these capabilities ever. */
2738 if (Down (tty) && Down (tty)[0] == '\n')
2739 Down (tty) = 0;
2740 #endif /* VMS */
2741
2742 if (!tty->TS_bell)
2743 tty->TS_bell = "\07";
2744
2745 if (!tty->TS_fwd_scroll)
2746 tty->TS_fwd_scroll = Down (tty);
2747
2748 PC = tty->TS_pad_char ? *tty->TS_pad_char : 0;
2749
2750 if (TabWidth (tty) < 0)
2751 TabWidth (tty) = 8;
2752
2753 /* Turned off since /etc/termcap seems to have :ta= for most terminals
2754 and newer termcap doc does not seem to say there is a default.
2755 if (!tty->Wcm->cm_tab)
2756 tty->Wcm->cm_tab = "\t";
2757 */
2758
2759 /* We don't support standout modes that use `magic cookies', so
2760 turn off any that do. */
2761 if (tty->TS_standout_mode && tgetnum ("sg") >= 0)
2762 {
2763 tty->TS_standout_mode = 0;
2764 tty->TS_end_standout_mode = 0;
2765 }
2766 if (tty->TS_enter_underline_mode && tgetnum ("ug") >= 0)
2767 {
2768 tty->TS_enter_underline_mode = 0;
2769 tty->TS_exit_underline_mode = 0;
2770 }
2771
2772 /* If there's no standout mode, try to use underlining instead. */
2773 if (tty->TS_standout_mode == 0)
2774 {
2775 tty->TS_standout_mode = tty->TS_enter_underline_mode;
2776 tty->TS_end_standout_mode = tty->TS_exit_underline_mode;
2777 }
2778
2779 /* If no `se' string, try using a `me' string instead.
2780 If that fails, we can't use standout mode at all. */
2781 if (tty->TS_end_standout_mode == 0)
2782 {
2783 char *s = tgetstr ("me", address);
2784 if (s != 0)
2785 tty->TS_end_standout_mode = s;
2786 else
2787 tty->TS_standout_mode = 0;
2788 }
2789
2790 if (tty->TF_teleray)
2791 {
2792 tty->Wcm->cm_tab = 0;
2793 /* We can't support standout mode, because it uses magic cookies. */
2794 tty->TS_standout_mode = 0;
2795 /* But that means we cannot rely on ^M to go to column zero! */
2796 CR (tty) = 0;
2797 /* LF can't be trusted either -- can alter hpos */
2798 /* if move at column 0 thru a line with TS_standout_mode */
2799 Down (tty) = 0;
2800 }
2801
2802 /* Special handling for certain terminal types known to need it */
2803
2804 if (!strcmp (terminal_type, "supdup"))
2805 {
2806 display->memory_below_frame = 1;
2807 tty->Wcm->cm_losewrap = 1;
2808 }
2809 if (!strncmp (terminal_type, "c10", 3)
2810 || !strcmp (terminal_type, "perq"))
2811 {
2812 /* Supply a makeshift :wi string.
2813 This string is not valid in general since it works only
2814 for windows starting at the upper left corner;
2815 but that is all Emacs uses.
2816
2817 This string works only if the frame is using
2818 the top of the video memory, because addressing is memory-relative.
2819 So first check the :ti string to see if that is true.
2820
2821 It would be simpler if the :wi string could go in the termcap
2822 entry, but it can't because it is not fully valid.
2823 If it were in the termcap entry, it would confuse other programs. */
2824 if (!tty->TS_set_window)
2825 {
2826 p = tty->TS_termcap_modes;
2827 while (*p && strcmp (p, "\033v "))
2828 p++;
2829 if (*p)
2830 tty->TS_set_window = "\033v%C %C %C %C ";
2831 }
2832 /* Termcap entry often fails to have :in: flag */
2833 display->must_write_spaces = 1;
2834 /* :ti string typically fails to have \E^G! in it */
2835 /* This limits scope of insert-char to one line. */
2836 strcpy (area, tty->TS_termcap_modes);
2837 strcat (area, "\033\007!");
2838 tty->TS_termcap_modes = area;
2839 area += strlen (area) + 1;
2840 p = AbsPosition (tty);
2841 /* Change all %+ parameters to %C, to handle
2842 values above 96 correctly for the C100. */
2843 while (*p)
2844 {
2845 if (p[0] == '%' && p[1] == '+')
2846 p[1] = 'C';
2847 p++;
2848 }
2849 }
2850
2851 tty->specified_window = FrameRows (tty);
2852
2853 if (Wcm_init (tty) == -1) /* can't do cursor motion */
2854 {
2855 maybe_fatal (must_succeed, NULL, display,
2856 "Terminal type \"%s\" is not powerful enough to run Emacs",
2857 #ifdef VMS
2858 "Terminal type \"%s\" is not powerful enough to run Emacs.\n\
2859 It lacks the ability to position the cursor.\n\
2860 If that is not the actual type of terminal you have, use either the\n\
2861 DCL command `SET TERMINAL/DEVICE= ...' for DEC-compatible terminals,\n\
2862 or `define EMACS_TERM \"terminal type\"' for non-DEC terminals.",
2863 #else /* not VMS */
2864 # ifdef TERMINFO
2865 "Terminal type \"%s\" is not powerful enough to run Emacs.\n\
2866 It lacks the ability to position the cursor.\n\
2867 If that is not the actual type of terminal you have,\n\
2868 use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
2869 `setenv TERM ...') to specify the correct type. It may be necessary\n\
2870 to do `unset TERMINFO' (C-shell: `unsetenv TERMINFO') as well.",
2871 # else /* TERMCAP */
2872 "Terminal type \"%s\" is not powerful enough to run Emacs.\n\
2873 It lacks the ability to position the cursor.\n\
2874 If that is not the actual type of terminal you have,\n\
2875 use the Bourne shell command `TERM=... export TERM' (C-shell:\n\
2876 `setenv TERM ...') to specify the correct type. It may be necessary\n\
2877 to do `unset TERMCAP' (C-shell: `unsetenv TERMCAP') as well.",
2878 # endif /* TERMINFO */
2879 #endif /*VMS */
2880 terminal_type);
2881 }
2882
2883 if (FrameRows (tty) <= 0 || FrameCols (tty) <= 0)
2884 maybe_fatal (must_succeed, NULL, display,
2885 "Could not determine the frame size",
2886 "Could not determine the frame size");
2887
2888 tty->delete_in_insert_mode
2889 = tty->TS_delete_mode && tty->TS_insert_mode
2890 && !strcmp (tty->TS_delete_mode, tty->TS_insert_mode);
2891
2892 tty->se_is_so = (tty->TS_standout_mode
2893 && tty->TS_end_standout_mode
2894 && !strcmp (tty->TS_standout_mode, tty->TS_end_standout_mode));
2895
2896 UseTabs (tty) = tabs_safe_p (fileno (tty->input)) && TabWidth (tty) == 8;
2897
2898 display->scroll_region_ok
2899 = (tty->Wcm->cm_abs
2900 && (tty->TS_set_window || tty->TS_set_scroll_region || tty->TS_set_scroll_region_1));
2901
2902 display->line_ins_del_ok
2903 = (((tty->TS_ins_line || tty->TS_ins_multi_lines)
2904 && (tty->TS_del_line || tty->TS_del_multi_lines))
2905 || (display->scroll_region_ok
2906 && tty->TS_fwd_scroll && tty->TS_rev_scroll));
2907
2908 display->char_ins_del_ok
2909 = ((tty->TS_ins_char || tty->TS_insert_mode
2910 || tty->TS_pad_inserted_char || tty->TS_ins_multi_chars)
2911 && (tty->TS_del_char || tty->TS_del_multi_chars));
2912
2913 display->fast_clear_end_of_line = tty->TS_clr_line != 0;
2914
2915 init_baud_rate (fileno (tty->input));
2916
2917 #ifdef AIXHFT
2918 /* The HFT system on AIX doesn't optimize for scrolling, so it's
2919 really ugly at times. */
2920 display->line_ins_del_ok = 0;
2921 display->char_ins_del_ok = 0;
2922 #endif
2923
2924 /* Don't do this. I think termcap may still need the buffer. */
2925 /* xfree (buffer); */
2926
2927 /* Init system terminal modes (RAW or CBREAK, etc.). */
2928 init_sys_modes (tty);
2929
2930 return display;
2931 #endif /* not WINDOWSNT */
2932 }
2933
2934 /* Auxiliary error-handling function for term_init.
2935 Free BUFFER and delete DISPLAY, then call error or fatal
2936 with str1 or str2, respectively, according to MUST_SUCCEED. */
2937
2938 static void
2939 maybe_fatal (must_succeed, buffer, display, str1, str2, arg1, arg2)
2940 int must_succeed;
2941 char *buffer;
2942 struct display *display;
2943 char *str1, *str2, *arg1, *arg2;
2944 {
2945 if (buffer)
2946 xfree (buffer);
2947
2948 if (display)
2949 delete_tty (display);
2950
2951 if (must_succeed)
2952 fatal (str2, arg1, arg2);
2953 else
2954 error (str1, arg1, arg2);
2955
2956 abort ();
2957 }
2958
2959 /* VARARGS 1 */
2960 void
2961 fatal (str, arg1, arg2)
2962 char *str, *arg1, *arg2;
2963 {
2964 fprintf (stderr, "emacs: ");
2965 fprintf (stderr, str, arg1, arg2);
2966 fprintf (stderr, "\n");
2967 fflush (stderr);
2968 exit (1);
2969 }
2970
2971 \f
2972
2973 static int deleting_tty = 0;
2974
2975
2976 /* Delete the given terminal device, closing all frames on it. */
2977
2978 void
2979 delete_tty (struct display *display)
2980 {
2981 struct tty_display_info *tty;
2982 Lisp_Object tail, frame;
2983 char *tty_name;
2984 int last_display;
2985
2986 if (deleting_tty)
2987 /* We get a recursive call when we delete the last frame on this
2988 display. */
2989 return;
2990
2991 if (display->type != output_termcap)
2992 abort ();
2993
2994 tty = display->display_info.tty;
2995
2996 last_display = 1;
2997 FOR_EACH_FRAME (tail, frame)
2998 {
2999 struct frame *f = XFRAME (frame);
3000 if (FRAME_LIVE_P (f) && (!FRAME_TERMCAP_P (f) || FRAME_TTY (f) != tty))
3001 {
3002 last_display = 0;
3003 break;
3004 }
3005 }
3006 if (last_display)
3007 error ("Attempt to delete the sole display with live frames");
3008
3009 if (tty == tty_list)
3010 tty_list = tty->next;
3011 else
3012 {
3013 struct tty_display_info *p;
3014 for (p = tty_list; p && p->next != tty; p = p->next)
3015 ;
3016
3017 if (! p)
3018 /* This should not happen. */
3019 abort ();
3020
3021 p->next = tty->next;
3022 tty->next = 0;
3023 }
3024
3025 deleting_tty = 1;
3026
3027 FOR_EACH_FRAME (tail, frame)
3028 {
3029 struct frame *f = XFRAME (frame);
3030 if (FRAME_TERMCAP_P (f) && FRAME_LIVE_P (f) && FRAME_TTY (f) == tty)
3031 {
3032 Fdelete_frame (frame, Qt);
3033 }
3034 }
3035
3036 /* reset_sys_modes needs a valid display, so this call needs to be
3037 before delete_display. */
3038 reset_sys_modes (tty);
3039
3040 delete_display (display);
3041
3042 tty_name = tty->name;
3043 if (tty->type)
3044 xfree (tty->type);
3045
3046 if (tty->input)
3047 {
3048 delete_keyboard_wait_descriptor (fileno (tty->input));
3049 if (tty->input != stdin)
3050 fclose (tty->input);
3051 }
3052 if (tty->output && tty->output != stdout && tty->output != tty->input)
3053 fclose (tty->output);
3054 if (tty->termscript)
3055 fclose (tty->termscript);
3056
3057 if (tty->old_tty)
3058 xfree (tty->old_tty);
3059
3060 if (tty->Wcm)
3061 xfree (tty->Wcm);
3062
3063 bzero (tty, sizeof (struct tty_display_info));
3064 xfree (tty);
3065 deleting_tty = 0;
3066 }
3067
3068 \f
3069
3070 /* Initialize the tty-dependent part of frame F. The frame must
3071 already have its display initialized. */
3072
3073 void
3074 create_tty_output (struct frame *f)
3075 {
3076 struct tty_output *t;
3077
3078 if (! FRAME_TERMCAP_P (f))
3079 abort ();
3080
3081 t = xmalloc (sizeof (struct tty_output));
3082 bzero (t, sizeof (struct tty_output));
3083
3084 t->display_info = FRAME_DISPLAY (f)->display_info.tty;
3085
3086 f->output_data.tty = t;
3087 }
3088
3089 /* Delete the tty-dependent part of frame F. */
3090
3091 void
3092 delete_tty_output (struct frame *f)
3093 {
3094 if (! FRAME_TERMCAP_P (f))
3095 abort ();
3096
3097 xfree (f->output_data.tty);
3098 }
3099
3100
3101 \f
3102
3103 /* Mark the pointers in the tty_display_info objects.
3104 Called by the Fgarbage_collector. */
3105
3106 void
3107 mark_ttys ()
3108 {
3109 struct tty_display_info *tty;
3110
3111 for (tty = tty_list; tty; tty = tty->next)
3112 {
3113 if (tty->top_frame)
3114 mark_object (tty->top_frame);
3115 }
3116 }
3117
3118 \f
3119
3120 /* Create a new display object and add it to the display list. */
3121
3122 struct display *
3123 create_display (void)
3124 {
3125 struct display *display = (struct display *) xmalloc (sizeof (struct display));
3126
3127 bzero (display, sizeof (struct display));
3128 display->next_display = display_list;
3129 display_list = display;
3130
3131 display->id = next_display_id++;
3132
3133 display->keyboard_coding =
3134 (struct coding_system *) xmalloc (sizeof (struct coding_system));
3135 display->terminal_coding =
3136 (struct coding_system *) xmalloc (sizeof (struct coding_system));
3137
3138 setup_coding_system (Qnil, display->keyboard_coding);
3139 setup_coding_system (Qnil, display->terminal_coding);
3140
3141 return display;
3142 }
3143
3144 /* Remove a display from the display list and free its memory. */
3145
3146 void
3147 delete_display (struct display *display)
3148 {
3149 struct display **dp;
3150 Lisp_Object tail, frame;
3151
3152 /* Check for and close live frames that are still on this
3153 display. */
3154 FOR_EACH_FRAME (tail, frame)
3155 {
3156 struct frame *f = XFRAME (frame);
3157 if (FRAME_LIVE_P (f) && f->display == display)
3158 {
3159 Fdelete_frame (frame, Qt);
3160 }
3161 }
3162
3163 for (dp = &display_list; *dp != display; dp = &(*dp)->next_display)
3164 if (! *dp)
3165 abort ();
3166 *dp = display->next_display;
3167
3168 if (display->keyboard_coding)
3169 xfree (display->keyboard_coding);
3170 if (display->terminal_coding)
3171 xfree (display->terminal_coding);
3172 if (display->name)
3173 xfree (display->name);
3174
3175 #ifdef MULTI_KBOARD
3176 if (display->kboard && --display->kboard->reference_count == 0)
3177 delete_kboard (display->kboard);
3178 #endif
3179
3180 bzero (display, sizeof (struct display));
3181 xfree (display);
3182 }
3183
3184 DEFUN ("delete-display", Fdelete_display, Sdelete_display, 0, 2, 0,
3185 doc: /* Delete DISPLAY by deleting all frames on it and closing the device.
3186 DISPLAY may be a display id, a frame, or nil for the display
3187 device of the current frame.
3188
3189 Normally, you may not delete a display if all other displays are suspended,
3190 but if the second argument FORCE is non-nil, you may do so. */)
3191 (display, force)
3192 Lisp_Object display, force;
3193 {
3194 struct display *d, *p;
3195
3196 d = get_display (display, 0);
3197
3198 if (!d)
3199 return Qnil;
3200
3201 p = display_list;
3202 while (p && (p == d || !DISPLAY_ACTIVE_P (p)))
3203 p = p->next_display;
3204
3205 if (NILP (force) && !p)
3206 error ("Attempt to delete the sole active display");
3207
3208 if (d->delete_display_hook)
3209 (*d->delete_display_hook) (d);
3210 else
3211 delete_display (d);
3212
3213 return Qnil;
3214 }
3215
3216 DEFUN ("display-live-p", Fdisplay_live_p, Sdisplay_live_p, 1, 1, 0,
3217 doc: /* Return non-nil if OBJECT is a display which has not been deleted.
3218 Value is nil if OBJECT is not a live display.
3219 If object is a live display, the return value indicates what sort of
3220 output device it uses. See the documentation of `framep' for possible
3221 return values.
3222
3223 Displays are represented by their integer identifiers. */)
3224 (object)
3225 Lisp_Object object;
3226 {
3227 struct display *d;
3228
3229 if (!INTEGERP (object))
3230 return Qnil;
3231
3232 d = get_display (object, 0);
3233
3234 if (!d)
3235 return Qnil;
3236
3237 switch (d->type)
3238 {
3239 case output_initial: /* The initial frame is like a termcap frame. */
3240 case output_termcap:
3241 return Qt;
3242 case output_x_window:
3243 return Qx;
3244 case output_w32:
3245 return Qw32;
3246 case output_msdos_raw:
3247 return Qpc;
3248 case output_mac:
3249 return Qmac;
3250 default:
3251 abort ();
3252 }
3253 }
3254
3255 DEFUN ("display-list", Fdisplay_list, Sdisplay_list, 0, 0, 0,
3256 doc: /* Return a list of all displays.
3257 Displays are represented by their integer identifiers. */)
3258 ()
3259 {
3260 Lisp_Object displays = Qnil;
3261 struct display *d;
3262
3263 for (d = display_list; d; d = d->next_display)
3264 displays = Fcons (make_number (d->id), displays);
3265
3266 return displays;
3267 }
3268
3269
3270 \f
3271
3272 DEFUN ("suspend-tty", Fsuspend_tty, Ssuspend_tty, 0, 1, 0,
3273 doc: /* Suspend the terminal device TTY.
3274 The terminal is restored to its default state, and Emacs ceases all
3275 access to the terminal device. Frames that use the device are not
3276 deleted, but input is not read from them and if they change, their
3277 display is not updated.
3278
3279 TTY may be a display id, a frame, or nil for the display device of the
3280 currently selected frame.
3281
3282 This function runs `suspend-tty-functions' after suspending the
3283 device. The functions are run with one arg, the id of the suspended
3284 display device.
3285
3286 `suspend-tty' does nothing if it is called on an already suspended
3287 device.
3288
3289 A suspended terminal device may be resumed by calling `resume-tty' on
3290 it. */)
3291 (tty)
3292 Lisp_Object tty;
3293 {
3294 struct display *d = get_tty_display (tty);
3295 FILE *f;
3296
3297 if (!d)
3298 error ("Unknown tty device");
3299
3300 f = d->display_info.tty->input;
3301
3302 if (f)
3303 {
3304 reset_sys_modes (d->display_info.tty);
3305
3306 delete_keyboard_wait_descriptor (fileno (f));
3307
3308 fclose (f);
3309 if (f != d->display_info.tty->output)
3310 fclose (d->display_info.tty->output);
3311
3312 d->display_info.tty->input = 0;
3313 d->display_info.tty->output = 0;
3314
3315 if (FRAMEP (d->display_info.tty->top_frame))
3316 FRAME_SET_VISIBLE (XFRAME (d->display_info.tty->top_frame), 0);
3317
3318 /* Run `suspend-tty-functions'. */
3319 if (!NILP (Vrun_hooks))
3320 {
3321 Lisp_Object args[2];
3322 args[0] = intern ("suspend-tty-functions");
3323 args[1] = make_number (d->id);
3324 Frun_hook_with_args (2, args);
3325 }
3326 }
3327
3328 return Qnil;
3329 }
3330
3331
3332 DEFUN ("resume-tty", Fresume_tty, Sresume_tty, 0, 1, 0,
3333 doc: /* Resume the previously suspended terminal device TTY.
3334 The terminal is opened and reinitialized. Frames that are on the
3335 suspended display are revived.
3336
3337 It is an error to resume a display while another display is active on
3338 the same device.
3339
3340 This function runs `resume-tty-functions' after resuming the device.
3341 The functions are run with one arg, the id of the resumed display
3342 device.
3343
3344 `resume-tty' does nothing if it is called on a device that is not
3345 suspended.
3346
3347 TTY may be a display id, a frame, or nil for the display device of the
3348 currently selected frame. */)
3349 (tty)
3350 Lisp_Object tty;
3351 {
3352 struct display *d = get_tty_display (tty);
3353 int fd;
3354
3355 if (!d)
3356 error ("Unknown tty device");
3357
3358 if (!d->display_info.tty->input)
3359 {
3360 if (get_named_tty_display (d->display_info.tty->name))
3361 error ("Cannot resume display while another display is active on the same device");
3362
3363 fd = emacs_open (d->display_info.tty->name, O_RDWR | O_NOCTTY, 0);
3364
3365 /* XXX What if open fails? */
3366
3367 dissociate_if_controlling_tty (fd);
3368
3369 d->display_info.tty->output = fdopen (fd, "w+");
3370 d->display_info.tty->input = d->display_info.tty->output;
3371
3372 add_keyboard_wait_descriptor (fd);
3373
3374 if (FRAMEP (d->display_info.tty->top_frame))
3375 FRAME_SET_VISIBLE (XFRAME (d->display_info.tty->top_frame), 1);
3376
3377 init_sys_modes (d->display_info.tty);
3378
3379 /* Run `suspend-tty-functions'. */
3380 if (!NILP (Vrun_hooks))
3381 {
3382 Lisp_Object args[2];
3383 args[0] = intern ("resume-tty-functions");
3384 args[1] = make_number (d->id);
3385 Frun_hook_with_args (2, args);
3386 }
3387 }
3388
3389 return Qnil;
3390 }
3391
3392 \f
3393 void
3394 syms_of_term ()
3395 {
3396 DEFVAR_BOOL ("system-uses-terminfo", &system_uses_terminfo,
3397 doc: /* Non-nil means the system uses terminfo rather than termcap.
3398 This variable can be used by terminal emulator packages. */);
3399 #ifdef TERMINFO
3400 system_uses_terminfo = 1;
3401 #else
3402 system_uses_terminfo = 0;
3403 #endif
3404
3405 DEFVAR_LISP ("ring-bell-function", &Vring_bell_function,
3406 doc: /* Non-nil means call this function to ring the bell.
3407 The function should accept no arguments. */);
3408 Vring_bell_function = Qnil;
3409
3410 DEFVAR_LISP ("suspend-tty-functions", &Vsuspend_tty_functions,
3411 doc: /* Functions to be run after suspending a tty.
3412 The functions are run with one argument, the name of the tty to be suspended.
3413 See `suspend-tty'. */);
3414 Vsuspend_tty_functions = Qnil;
3415
3416
3417 DEFVAR_LISP ("resume-tty-functions", &Vresume_tty_functions,
3418 doc: /* Functions to be run after resuming a tty.
3419 The functions are run with one argument, the name of the tty that was revived.
3420 See `resume-tty'. */);
3421 Vresume_tty_functions = Qnil;
3422
3423 defsubr (&Stty_display_color_p);
3424 defsubr (&Stty_display_color_cells);
3425 defsubr (&Stty_no_underline);
3426 defsubr (&Sdisplay_name);
3427 defsubr (&Sdisplay_tty_type);
3428 defsubr (&Sdisplay_controlling_tty_p);
3429 defsubr (&Sdelete_display);
3430 defsubr (&Sdisplay_live_p);
3431 defsubr (&Sdisplay_list);
3432 defsubr (&Ssuspend_tty);
3433 defsubr (&Sresume_tty);
3434
3435 Fprovide (intern ("multi-tty"), Qnil);
3436
3437 }
3438
3439
3440
3441 /* arch-tag: 498e7449-6f2e-45e2-91dd-b7d4ca488193
3442 (do not change this comment) */