1 /* Communication module for TTY terminals.
2 Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
3 Copyright (C) 1995 Sun Microsystems, Inc.
4 Copyright (C) 1995, 1996 Ben Wing.
5 Copyright (C) 1996 Chuck Thompson.
6 Copyright (C) 2007 Nelson Ferreira
8 This file is part of SXEmacs
10 SXEmacs is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
15 SXEmacs is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>. */
24 /* Synched up with: Not completely synched with FSF. Mostly divergent
27 /* This file has been Mule-ized. */
29 /* Written by Chuck Thompson. */
30 /* Color support added by Ben Wing and completely changed by Nelson Ferreira */
36 #include "console-tty.h"
37 #include "events/events.h"
40 #include "ui/glyphs.h"
42 #include "objects-tty.h"
43 #include "ui/redisplay.h"
45 #include "ui/window.h"
49 #include CURSES_H_FILE
55 #include TERMCAP_H_FILE
65 #ifndef HAVE_TGETENT_PROTOTYPE
66 EXTERN_C int tgetent(const char *, const char *);
69 #ifndef HAVE_TGETFLAG_PROTOTYPE
70 EXTERN_C int tgetflag(const char *);
73 #ifndef HAVE_TGETNUM_PROTOTYPE
74 EXTERN_C int tgetnum(const char *);
77 #ifndef HAVE_TGETSTR_PROTOTYPE
78 EXTERN_C char *tgetstr(const char *, char **);
81 #ifndef HAVE_TPUTS_PROTOTYPE
82 EXTERN_C void tputs(const char *, int, int (*)(int));
85 char *emacs_tparam(const char *string, char *outstring, int len, int arg1,
86 int arg2, int arg3, int arg4, int arg5, int arg6, int arg7,
89 typedef int (*tputs_fun)(int);
91 #define FORCE_CURSOR_UPDATE(c) send_string_to_tty_console ((c), 0, 0)
92 #define OUTPUTN(c, a, n) \
94 cmputc_console = (c); \
95 FORCE_CURSOR_UPDATE(c); \
96 tputs ((a), (n), (tputs_fun)cmputc); \
98 #define OUTPUT1(c, a) OUTPUTN ((c), (a), 1)
99 #define OUTPUTN_IF(c, a, n) \
101 cmputc_console = (c); \
102 FORCE_CURSOR_UPDATE(c); \
104 tputs ((a), (n), (tputs_fun)cmputc); \
106 #define OUTPUT1_IF(c, a) OUTPUTN_IF ((c), (a), 1)
108 static void tty_output_emchar_dynarr(struct window *w,
109 struct display_line *dl,
110 Emchar_dynarr * buf, int xpos,
111 face_index findex, int cursor);
112 static void tty_output_bufbyte_string(struct window *w,
113 struct display_line *dl,
114 Bufbyte * str, Bytecount len,
115 int xpos, face_index findex, int cursor);
116 static void tty_turn_on_face(struct window *w, face_index findex);
117 static void tty_turn_off_face(struct window *w, face_index findex);
118 static void tty_turn_on_frame_face(struct frame *f, Lisp_Object face);
119 static void tty_turn_off_frame_face(struct frame *f, Lisp_Object face);
121 static void term_get_fkeys(Lisp_Object keymap, char **address);
123 extern int assume_colorterm;
125 /*****************************************************************************
128 Non-Mule tty's don't have fonts (that we use at least), so everything
129 is considered to be fixed width -- in other words, we return LEN.
130 Under Mule, however, a character can still cover more than one
131 column, so we use emchar_string_displayed_columns().
132 ****************************************************************************/
134 tty_text_width(struct frame *f, struct face_cachel *cachel, const Emchar * str,
137 return emchar_string_displayed_columns(str, len);
140 /*****************************************************************************
143 Return the width of the horizontal divider. This is a function
144 because divider_height is a console method.
145 ****************************************************************************/
146 static int tty_divider_height(void)
151 /*****************************************************************************
154 Return the width of the end-of-line cursor. This is a function
155 because eol_cursor_width is a console method.
156 ****************************************************************************/
157 static int tty_eol_cursor_width(void)
162 /*****************************************************************************
163 tty_frame_output_begin
165 Perform any necessary initialization prior to an update.
166 ****************************************************************************/
168 void tty_frame_output_begin(struct frame *f);
173 tty_frame_output_begin(struct frame *f)
176 /* Termcap requires `ospeed' to be a global variable so we have to
177 always set it for whatever tty console we are actually currently
179 ospeed = DEVICE_TTY_DATA(XDEVICE(FRAME_DEVICE(f)))->ospeed;
183 /*****************************************************************************
186 Perform any necessary flushing of queues when an update has completed.
187 ****************************************************************************/
189 void tty_frame_output_end(struct frame *f);
194 tty_frame_output_end(struct frame *f)
196 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
198 CONSOLE_TTY_CURSOR_X(c) = CONSOLE_TTY_FINAL_CURSOR_X(c);
199 CONSOLE_TTY_CURSOR_Y(c) = CONSOLE_TTY_FINAL_CURSOR_Y(c);
200 FORCE_CURSOR_UPDATE(c);
201 Lstream_flush(XLSTREAM(CONSOLE_TTY_DATA(c)->outstream));
204 static void tty_set_final_cursor_coords(struct frame *f, int y, int x)
206 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
208 CONSOLE_TTY_FINAL_CURSOR_X(c) = x;
209 CONSOLE_TTY_FINAL_CURSOR_Y(c) = y;
212 /*****************************************************************************
213 tty_output_display_block
215 Given a display line, a block number for that start line, output all
216 runes between start and end in the specified display block.
217 ****************************************************************************/
219 tty_output_display_block(struct window *w, struct display_line *dl, int block,
220 int start, int end, int start_pixpos,
221 int cursor_start, int cursor_width, int cursor_height)
223 struct frame *f = XFRAME(w->frame);
224 Emchar_dynarr *buf = NULL;
226 struct display_block *db = Dynarr_atp(dl->display_blocks, block);
227 rune_dynarr *rba = db->runes;
234 rb = Dynarr_atp(rba, elt);
237 /* Nothing to do so don't do anything. */
245 end = Dynarr_length(rba);
247 buf = Dynarr_new(Emchar);
249 while (elt < end && Dynarr_atp(rba, elt)->xpos < start_pixpos) {
251 findex = Dynarr_atp(rba, elt)->findex;
252 xpos = Dynarr_atp(rba, elt)->xpos;
256 rb = Dynarr_atp(rba, elt);
258 if (rb->findex == findex && rb->type == RUNE_CHAR
259 && rb->object.chr.ch != '\n'
260 && (rb->cursor_type != CURSOR_ON
261 || NILP(w->text_cursor_visible_p))) {
262 Dynarr_add(buf, rb->object.chr.ch);
265 if (Dynarr_length(buf)) {
266 tty_output_emchar_dynarr(w, dl, buf, xpos,
272 if (rb->type == RUNE_CHAR) {
276 if (rb->object.chr.ch == '\n') {
277 /* Clear in case a cursor was formerly here. */
279 Dynarr_add(buf, ' ');
280 tty_output_emchar_dynarr(w, dl, buf,
286 cmgoto(f, dl->ypos - 1, rb->xpos);
289 } else if (rb->cursor_type == CURSOR_ON) {
290 /* There is not a distinct eol cursor on tty's. */
292 Dynarr_add(buf, rb->object.chr.ch);
293 tty_output_emchar_dynarr(w, dl, buf,
298 cmgoto(f, dl->ypos - 1, xpos);
304 /* #### RUNE_HLINE is actually a little more complicated than this
305 but at the moment it is only used to draw a turned off
306 modeline and this will suffice for that. */
307 else if (rb->type == RUNE_BLANK
308 || rb->type == RUNE_HLINE) {
310 int size = rb->width;
312 if (rb->type == RUNE_BLANK)
318 Dynarr_add(buf, ch_to_add);
319 tty_output_emchar_dynarr(w, dl, buf, rb->xpos,
322 if (xpos >= cursor_start
323 && cursor_start < xpos + Dynarr_length(buf))
325 cmgoto(f, dl->ypos - 1, cursor_start);
332 rb = Dynarr_atp(rba, elt);
337 } else if (rb->type == RUNE_DGLYPH) {
339 Lisp_Object instance;
341 XSETWINDOW(window, w);
343 glyph_image_instance(rb->object.dglyph.
347 if (IMAGE_INSTANCEP(instance)) {
348 switch (XIMAGE_INSTANCE_TYPE(instance)) {
349 case IMAGE_MONO_PIXMAP:
350 case IMAGE_COLOR_PIXMAP:
351 case IMAGE_SUBWINDOW:
353 /* just do nothing here */
357 /* nothing is as nothing does */
366 IMAGE_INSTANCE_OPTIMIZE_OUTPUT
367 (XIMAGE_INSTANCE(instance)) = 0;
377 if (Dynarr_length(buf))
378 tty_output_emchar_dynarr(w, dl, buf, xpos, findex, 0);
383 /*****************************************************************************
384 tty_output_vertical_divider
386 Draw a vertical divider down the right side of the given window.
387 ****************************************************************************/
389 tty_output_vertical_divider(struct window *w, int SXE_UNUSED(clearp))
391 /* Divider width can either be 0 or 1 on TTYs */
392 if (window_divider_width(w)) {
393 struct frame *f = XFRAME(w->frame);
394 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
396 int y_top = WINDOW_TEXT_TOP(w);
397 int y_bot = WINDOW_TEXT_BOTTOM(w);
398 unsigned char divv = '|';
400 tty_turn_on_face(w, MODELINE_INDEX);
401 for (line = y_top; line < y_bot; line++) {
402 cmgoto(f, line, WINDOW_TEXT_RIGHT(w));
403 send_string_to_tty_console(c, &divv, 1);
404 TTY_INC_CURSOR_X(c, 1);
407 /* Draw the divider in the modeline. */
408 cmgoto(f, y_bot, WINDOW_TEXT_RIGHT(w));
409 send_string_to_tty_console(c, &divv, 1);
410 TTY_INC_CURSOR_X(c, 1);
411 tty_turn_off_face(w, MODELINE_INDEX);
416 /****************************************************************************
419 Clear the area in the box defined by the given parameters.
420 ****************************************************************************/
422 tty_clear_region(Lisp_Object window, struct device *d, struct frame *f,
423 face_index findex, int x, int y,
424 int width, int height, Lisp_Object fcolor, Lisp_Object bcolor,
425 Lisp_Object background_pixmap)
427 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
429 struct window *w = XWINDOW(window);
431 tty_turn_on_face(w, findex);
432 for (line = y; line < y + height; line++) {
437 if (window_is_leftmost(w)
438 && window_is_rightmost(w)
439 && TTY_SE(c).clr_to_eol) {
440 OUTPUT1(c, TTY_SE(c).clr_to_eol);
442 unsigned char sp = ' ';
443 /* #### Of course, this is all complete and utter crap. */
444 for (col = x; col < x + width; col++)
445 send_string_to_tty_console(c, &sp, 1);
446 TTY_INC_CURSOR_X(c, width);
449 tty_turn_off_face(w, findex);
453 /*****************************************************************************
454 tty_clear_to_window_end
456 Clear the area between ypos1 and ypos2. Each margin area and the
457 text area is handled separately since they may each have their own
459 ****************************************************************************/
460 static void tty_clear_to_window_end(struct window *w, int ypos1, int ypos2)
462 struct frame *f = XFRAME(w->frame);
463 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
466 x = WINDOW_TEXT_LEFT(w);
467 width = WINDOW_TEXT_WIDTH(w);
469 if (window_is_rightmost(w)) {
470 /* #### Optimize to use clr_to_eol function of tty if available, if
471 the window is the entire width of the frame. */
472 /* #### Is this actually an optimization? */
474 tty_turn_on_face(w, DEFAULT_INDEX);
475 for (line = ypos1; line < ypos2; line++) {
476 cmgoto(XFRAME(w->frame), line, x);
477 OUTPUT1(c, TTY_SE(c).clr_to_eol);
479 tty_turn_off_face(w, DEFAULT_INDEX);
483 XSETWINDOW(window, w);
484 redisplay_clear_region(window, DEFAULT_INDEX, x, ypos1, width,
489 /****************************************************************************
492 Clear the entire frame.
493 ****************************************************************************/
494 static void tty_clear_frame(struct frame *f)
496 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
498 tty_turn_on_frame_face(f, Vdefault_face);
499 if (TTY_SE(c).clr_frame) {
500 OUTPUT1(c, TTY_SE(c).clr_frame);
501 CONSOLE_TTY_REAL_CURSOR_X(c) = 0;
502 CONSOLE_TTY_REAL_CURSOR_Y(c) = 0;
504 FRAME_CURSOR_X(f) = 0;
505 FRAME_CURSOR_Y(f) = 0;
509 internal_cursor_to(f, 0, 0);
512 /* #### Not implemented. */
513 stderr_out("Not yet.\n");
516 tty_turn_off_frame_face(f, Vdefault_face);
520 tty_output_bufbyte_string(struct window *w, struct display_line *dl,
521 Bufbyte * str, Bytecount len, int xpos,
522 face_index findex, int cursor)
524 struct frame *f = XFRAME(w->frame);
525 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
527 /* First position the cursor. */
528 cmgoto(f, dl->ypos - 1, xpos);
530 /* Enable any face properties. */
531 tty_turn_on_face(w, findex);
533 send_string_to_tty_console(c, str, len);
534 TTY_INC_CURSOR_X(c, bufbyte_string_displayed_columns(str, len));
536 /* Turn the face properties back off. */
537 tty_turn_off_face(w, findex);
540 static Bufbyte_dynarr *tty_output_emchar_dynarr_dynarr;
542 /*****************************************************************************
543 tty_output_emchar_dynarr
545 Given a string and a starting position, output that string in the
546 given face. If cursor is true, draw a cursor around the string.
547 ****************************************************************************/
549 tty_output_emchar_dynarr(struct window *w, struct display_line *dl,
550 Emchar_dynarr * buf, int xpos, face_index findex,
553 if (!tty_output_emchar_dynarr_dynarr)
554 tty_output_emchar_dynarr_dynarr = Dynarr_new(Bufbyte);
556 Dynarr_reset(tty_output_emchar_dynarr_dynarr);
558 convert_emchar_string_into_bufbyte_dynarr(Dynarr_atp(buf, 0),
560 tty_output_emchar_dynarr_dynarr);
562 tty_output_bufbyte_string(w, dl,
563 Dynarr_atp(tty_output_emchar_dynarr_dynarr,
566 (tty_output_emchar_dynarr_dynarr), xpos,
570 #include <semaphore.h>
572 static int tty_mutex_inited = 0;
573 static sxe_mutex_t tty_mutex;
574 static Lstream *tty_stream = NULL;
576 static void tty_stream_putc( int c )
579 Lstream_putc(tty_stream, c);
582 static void send_tty_escseq(struct console *c, char *escseq)
584 if ( ! tty_mutex_inited ) {
585 SXE_MUTEX_INIT(&tty_mutex);
586 tty_mutex_inited = 1;
588 WITH_SXE_MUTEX(&tty_mutex,
590 tty_stream = XLSTREAM(CONSOLE_TTY_DATA(c)->outstream);
591 tputs(escseq,1,(tputs_fun)tty_stream_putc);
600 set_foreground_to(struct console *c, Lisp_Object sym)
605 result = Ffind_tty_color(sym,c->selected_device,Qt);
606 if ( !NILP(result) ) {
607 Lisp_Object idx = XCAR(result);
608 Lisp_Object bold = XCAR(XCDR(result));
610 if (TTY_SD(c).set_afore_ ) {
611 /* inevitable warning? */
613 emacs_tparam((TTY_SD(c).set_afore_),
619 if ( ! EQ(bold,Qnil) &&
620 CONSOLE_TTY_DATA(c)->is_bold_brighter ) {
621 OUTPUT1_IF(c, TTY_SD(c).turn_on_bold_);
623 send_tty_escseq(c,(char*)escseq);
631 set_background_to(struct console *c, Lisp_Object sym)
636 result = Ffind_tty_color(sym,c->selected_device,Qt);
637 if ( !NILP(result) ) {
638 Lisp_Object idx = XCAR(result);
640 if (TTY_SD(c).set_aback_ ) {
641 /* inevitable warning */
643 emacs_tparam((TTY_SD(c).set_aback_),
649 send_tty_escseq(c,(char*)escseq);
657 tty_turn_on_face_1(struct console *c, int highlight_p,
658 int blinking_p, int dim_p, int underline_p,
659 int reverse_p, Lisp_Object cinst_fore,
660 Lisp_Object cinst_back)
663 OUTPUT1_IF(c, TTY_SD(c).turn_on_bold_);
667 OUTPUT1_IF(c, TTY_SD(c).turn_on_blinking_);
671 OUTPUT1_IF(c, TTY_SD(c).turn_on_dim_);
675 /* #### punt for now if underline mode is glitchy */
676 if (!TTY_FLAGS(c).underline_width) {
677 OUTPUT1_IF(c, TTY_SD(c).begin_underline_);
682 /* #### punt for now if standout mode is glitchy */
683 if (!TTY_FLAGS(c).standout_width) {
684 OUTPUT1_IF(c, TTY_SD(c).begin_standout_);
690 Lisp_Object temp = cinst_fore;
691 cinst_fore = cinst_back;
696 int color_instancep_fore = COLOR_INSTANCEP(cinst_fore);
697 int cinst_fore_eq_null_color = EQ(cinst_fore, Vthe_null_color_instance);
698 if ( color_instancep_fore
699 && !cinst_fore_eq_null_color ) {
700 Lisp_Object cinst_sym = COLOR_INSTANCE_TTY_SYMBOL
701 (XCOLOR_INSTANCE(cinst_fore));
702 set_foreground_to(c, cinst_sym);
705 int color_instancep_back = COLOR_INSTANCEP(cinst_back);
706 int cinst_back_eq_null_color = EQ(cinst_back, Vthe_null_color_instance);
707 if ( color_instancep_back
708 && !cinst_back_eq_null_color ) {
709 Lisp_Object cinst_sym = COLOR_INSTANCE_TTY_SYMBOL
710 (XCOLOR_INSTANCE(cinst_back));
711 set_background_to(c, cinst_sym);
715 /*****************************************************************************
718 Turn on all set properties of the given face.
719 ****************************************************************************/
720 static void tty_turn_on_face(struct window *w, face_index findex)
722 struct frame *f = XFRAME(w->frame);
723 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
725 tty_turn_on_face_1(c,
726 WINDOW_FACE_CACHEL_HIGHLIGHT_P(w, findex),
727 WINDOW_FACE_CACHEL_BLINKING_P(w, findex),
728 WINDOW_FACE_CACHEL_DIM_P(w, findex),
729 WINDOW_FACE_CACHEL_UNDERLINE_P(w, findex),
730 WINDOW_FACE_CACHEL_REVERSE_P(w, findex),
731 WINDOW_FACE_CACHEL_FOREGROUND(w, findex),
732 WINDOW_FACE_CACHEL_BACKGROUND(w, findex));
735 /*****************************************************************************
738 Turn off all set properties of the given face (revert to default
739 face). We assume that tty_turn_on_face has been called for the given
740 face so that its properties are actually active.
741 ****************************************************************************/
742 static void tty_turn_off_face(struct window *w, face_index findex)
744 struct frame *f = XFRAME(w->frame);
745 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
747 if (WINDOW_FACE_CACHEL_REVERSE_P(w, findex)) {
748 /* #### punt for now if standout mode is glitchy */
749 if (!TTY_FLAGS(c).standout_width) {
750 OUTPUT1_IF(c, TTY_SD(c).end_standout_);
754 if (WINDOW_FACE_CACHEL_UNDERLINE_P(w, findex)) {
755 /* #### punt for now if underline mode is glitchy */
756 if (!TTY_FLAGS(c).underline_width) {
757 OUTPUT1_IF(c, TTY_SD(c).end_underline_);
761 if (WINDOW_FACE_CACHEL_HIGHLIGHT_P(w, findex) ||
762 WINDOW_FACE_CACHEL_BLINKING_P(w, findex) ||
763 WINDOW_FACE_CACHEL_DIM_P(w, findex) ||
764 !EQ(WINDOW_FACE_CACHEL_FOREGROUND(w, findex),
765 Vthe_null_color_instance) ||
766 !EQ(WINDOW_FACE_CACHEL_BACKGROUND(w, findex),
767 Vthe_null_color_instance)) {
768 OUTPUT1_IF(c, TTY_SD(c).turn_off_attributes_);
772 /*****************************************************************************
773 tty_turn_on_frame_face
775 Turn on all set properties of the given face.
776 ****************************************************************************/
777 static void tty_turn_on_frame_face(struct frame *f, Lisp_Object face)
780 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
783 tty_turn_on_face_1(c,
784 FACE_HIGHLIGHT_P(face, frame),
785 FACE_BLINKING_P(face, frame),
786 FACE_DIM_P(face, frame),
787 FACE_UNDERLINE_P(face, frame),
788 FACE_REVERSE_P(face, frame),
789 FACE_FOREGROUND(face, frame),
790 FACE_BACKGROUND(face, frame));
793 /*****************************************************************************
794 tty_turn_off_frame_face
796 Turn off all set properties of the given face (revert to default
797 face). We assume that tty_turn_on_face has been called for the given
798 face so that its properties are actually active.
799 ****************************************************************************/
800 static void tty_turn_off_frame_face(struct frame *f, Lisp_Object face)
803 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
807 if (FACE_REVERSE_P(face, frame)) {
808 /* #### punt for now if standout mode is glitchy */
809 if (!TTY_FLAGS(c).standout_width) {
810 OUTPUT1_IF(c, TTY_SD(c).end_standout_);
814 if (FACE_UNDERLINE_P(face, frame)) {
815 /* #### punt for now if underline mode is glitchy */
816 if (!TTY_FLAGS(c).underline_width) {
817 OUTPUT1_IF(c, TTY_SD(c).end_underline_);
821 if (FACE_HIGHLIGHT_P(face, frame) ||
822 FACE_BLINKING_P(face, frame) ||
823 FACE_DIM_P(face, frame) ||
824 !EQ(FACE_FOREGROUND(face, frame), Vthe_null_color_instance) ||
825 !EQ(FACE_BACKGROUND(face, frame), Vthe_null_color_instance)) {
826 OUTPUT1_IF(c, TTY_SD(c).turn_off_attributes_);
830 /*****************************************************************************
833 Sets up various parameters on tty modes.
834 ****************************************************************************/
835 void set_tty_modes(struct console *c)
837 if (!CONSOLE_TTY_P(c))
840 OUTPUT1_IF(c, (TTY_SD(c).init_motion_));
841 OUTPUT1_IF(c, (TTY_SD(c).cursor_visible_));
842 OUTPUT1_IF(c, (TTY_SD(c).keypad_on_));
845 /*****************************************************************************
848 Restore default state of tty.
849 ****************************************************************************/
850 void reset_tty_modes(struct console *c)
852 if (!CONSOLE_TTY_P(c))
855 OUTPUT1_IF(c, (TTY_SD(c).orig_pair_));
856 OUTPUT1_IF(c, (TTY_SD(c).keypad_off_));
857 OUTPUT1_IF(c, (TTY_SD(c).cursor_normal_));
858 OUTPUT1_IF(c, (TTY_SD(c).end_motion_));
861 Lisp_Object frm = CONSOLE_SELECTED_FRAME(c);
864 tty_frame_output_end(XFRAME(frm));
868 /*****************************************************************************
869 tty_redisplay_shutdown
871 Clear the frame and position the cursor properly for exiting.
872 ****************************************************************************/
873 void tty_redisplay_shutdown(struct console *c)
875 Lisp_Object dev = CONSOLE_SELECTED_DEVICE(c);
878 Lisp_Object frm = DEVICE_SELECTED_FRAME(XDEVICE(dev));
881 struct frame *f = XFRAME(frm);
883 /* Clear the bottom line of the frame. */
884 redisplay_clear_region(FRAME_SELECTED_WINDOW(f),
885 DEFAULT_INDEX, 0, f->height,
888 /* And then stick the cursor there. */
889 tty_set_final_cursor_coords(f, f->height, 0);
890 tty_frame_output_end(f);
895 /* #### Everything below here is old shit. It should either be moved
899 /* FLAGS - these don't need to be console local since only one console
900 can be being updated at a time. */
901 static int insert_mode_on; /* nonzero if in insert mode */
902 static int standout_mode_on; /* nonzero if in standout mode */
903 static int underline_mode_on; /* nonzero if in underline mode */
904 static int alternate_mode_on; /* nonzero if in alternate char set */
905 static int attributes_on; /* nonzero if any attributes on */
907 static void turn_on_insert(struct frame *f)
909 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
912 OUTPUT1_IF(c, TTY_SE(c).begin_ins_mode);
916 static void turn_off_insert(struct frame *f)
918 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
921 OUTPUT1(c, TTY_SE(c).end_ins_mode);
925 static void internal_cursor_to(struct frame *f, int row, int col)
927 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
929 if (!TTY_FLAGS(c).insert_mode_motion)
931 if (!TTY_FLAGS(c).standout_motion) {
932 turn_off_standout(f);
933 turn_off_underline(f);
934 turn_off_alternate(f);
940 static void clear_to_end(struct frame *f)
942 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
944 /* assumes cursor is already positioned */
945 if (TTY_SE(c).clr_from_cursor) {
946 OUTPUT1(c, TTY_SE(c).clr_from_cursor);
948 int line = FRAME_CURSOR_Y(f);
950 while (line < FRAME_HEIGHT(f)) {
951 internal_cursor_to(f, line, 0);
952 OUTPUT1(c, TTY_SE(c).clr_to_eol);
960 * clear from last visible line on window to window end (presumably
961 * the line above window's modeline
963 static void tty_clear_window_end(struct window *w, int ystart, int yend)
965 struct console *c = XCONSOLE(WINDOW_CONSOLE(w));
968 for (line = ystart; line < yend; line++) {
969 cmgoto(XFRAME(w->frame), line, 0);
970 OUTPUT1(c, TTY_SE(c).clr_to_eol);
976 static int tty_flash(struct device *d)
978 struct console *c = XCONSOLE(DEVICE_CONSOLE(d));
979 if (TTY_SD(c).visual_bell_) {
980 OUTPUT1(c, TTY_SD(c).visual_bell_);
981 Lstream_flush(XLSTREAM(CONSOLE_TTY_DATA(c)->outstream));
988 * tty_ring_bell - sound an audio beep.
990 static void tty_ring_bell(struct device *d, int volume, int pitch, int duration)
992 struct console *c = XCONSOLE(DEVICE_CONSOLE(d));
995 OUTPUT1(c, TTY_SD(c).audio_bell_);
996 Lstream_flush(XLSTREAM(CONSOLE_TTY_DATA(c)->outstream));
1000 int init_tty_for_redisplay(struct device *d, char *terminal_type)
1002 int force_colorterm = assume_colorterm ||
1003 getenv("COLOR_TERM") != NULL ||
1004 getenv("COLORTERM") != NULL;
1007 /* According to the man page a terminfo/termcap cannot be
1008 longer than 4096 so we should be golden here
1010 char entry_buffer[4098];
1012 struct console *c = XCONSOLE(DEVICE_CONSOLE(d));
1013 char *ttype = terminal_type;
1015 /* What we should really do is allocate just enough space for
1016 the actual strings that are stored; but this would require
1017 doing this after all the tgetstr()s and adjusting all the
1019 CONSOLE_TTY_DATA(c)->term_entry_buffer =
1020 xmalloc_atomic(countof(entry_buffer));
1021 bufptr = CONSOLE_TTY_DATA(c)->term_entry_buffer;
1025 EMACS_BLOCK_SIGNAL(SIGTTOU);
1027 status = tgetent(entry_buffer, ttype);
1029 EMACS_UNBLOCK_SIGNAL(SIGTTOU);
1031 /* Under Linux at least, <0 is returned for TTY_TYPE_UNDEFINED. --ben
1034 if ( ! strcmp( ttype, "xterm-256color" ) ) {
1035 ttype = "xterm-color";
1037 } else if ( ! strcmp( ttype, "xterm-color" ) ) {
1040 } else if ( ! strcmp( ttype, "xterm" ) ) {
1044 return TTY_TYPE_UNDEFINED;
1046 } while( status <= 0 );
1048 if ( strcmp(terminal_type,ttype) ) {
1049 stderr_out("Could not find an entry for terminal type '");
1050 stderr_out("%s",terminal_type);
1051 stderr_out("' fallback using '");
1052 stderr_out("%s",ttype);
1056 CONSOLE_TTY_DATA(c)->term_cmap = Qnil;
1057 CONSOLE_TTY_DATA(c)->term_crgb = Qnil;
1059 * Establish the terminal size.
1061 /* First try to get the info from the system. If that fails, check
1062 the termcap entry. */
1063 get_tty_device_size(d, &CONSOLE_TTY_DATA(c)->width,
1064 &CONSOLE_TTY_DATA(c)->height);
1066 if (CONSOLE_TTY_DATA(c)->width <= 0)
1067 CONSOLE_TTY_DATA(c)->width = tgetnum("co");
1068 if (CONSOLE_TTY_DATA(c)->height <= 0)
1069 CONSOLE_TTY_DATA(c)->height = tgetnum("li");
1071 if (CONSOLE_TTY_DATA(c)->width <= 0 || CONSOLE_TTY_DATA(c)->height <= 0)
1072 return TTY_SIZE_UNSPECIFIED;
1075 * Initialize cursor motion information.
1078 /* local cursor movement */
1079 TTY_CM(c).up = tgetstr("up", &bufptr);
1080 TTY_CM(c).down = tgetstr("do", &bufptr);
1081 TTY_CM(c).left = tgetstr("le", &bufptr);
1082 TTY_CM(c).right = tgetstr("nd", &bufptr);
1083 TTY_CM(c).home = tgetstr("ho", &bufptr);
1084 TTY_CM(c).low_left = tgetstr("ll", &bufptr);
1085 TTY_CM(c).car_return = tgetstr("cr", &bufptr);
1087 /* absolute cursor motion */
1088 TTY_CM(c).abs = tgetstr("cm", &bufptr);
1089 TTY_CM(c).hor_abs = tgetstr("ch", &bufptr);
1090 TTY_CM(c).ver_abs = tgetstr("cv", &bufptr);
1092 /* Verify that the terminal is powerful enough to run Emacs */
1093 if (!TTY_CM(c).abs) {
1094 if (!TTY_CM(c).up || !TTY_CM(c).down
1095 || !TTY_CM(c).left || !TTY_CM(c).right)
1096 return TTY_TYPE_INSUFFICIENT;
1099 /* parameterized local cursor movement */
1100 TTY_CM(c).multi_up = tgetstr("UP", &bufptr);
1101 TTY_CM(c).multi_down = tgetstr("DO", &bufptr);
1102 TTY_CM(c).multi_left = tgetstr("LE", &bufptr);
1103 TTY_CM(c).multi_right = tgetstr("RI", &bufptr);
1106 TTY_CM(c).scroll_forw = tgetstr("sf", &bufptr);
1107 TTY_CM(c).scroll_back = tgetstr("sr", &bufptr);
1108 TTY_CM(c).multi_scroll_forw = tgetstr("SF", &bufptr);
1109 TTY_CM(c).multi_scroll_back = tgetstr("SR", &bufptr);
1110 TTY_CM(c).set_scroll_region = tgetstr("cs", &bufptr);
1113 * Initialize screen editing information.
1116 /* adding to the screen */
1117 TTY_SE(c).ins_line = tgetstr("al", &bufptr);
1118 TTY_SE(c).multi_ins_line = tgetstr("AL", &bufptr);
1119 TTY_SE(c).repeat = tgetstr("rp", &bufptr);
1120 TTY_SE(c).begin_ins_mode = tgetstr("im", &bufptr);
1121 TTY_SE(c).end_ins_mode = tgetstr("ei", &bufptr);
1122 TTY_SE(c).ins_char = tgetstr("ic", &bufptr);
1123 TTY_SE(c).multi_ins_char = tgetstr("IC", &bufptr);
1124 TTY_SE(c).insert_pad = tgetstr("ip", &bufptr);
1126 /* deleting from the screen */
1127 TTY_SE(c).clr_frame = tgetstr("cl", &bufptr);
1128 TTY_SE(c).clr_from_cursor = tgetstr("cd", &bufptr);
1129 TTY_SE(c).clr_to_eol = tgetstr("ce", &bufptr);
1130 TTY_SE(c).del_line = tgetstr("dl", &bufptr);
1131 TTY_SE(c).multi_del_line = tgetstr("DL", &bufptr);
1132 TTY_SE(c).del_char = tgetstr("dc", &bufptr);
1133 TTY_SE(c).multi_del_char = tgetstr("DC", &bufptr);
1134 TTY_SE(c).begin_del_mode = tgetstr("dm", &bufptr);
1135 TTY_SE(c).end_del_mode = tgetstr("ed", &bufptr);
1136 TTY_SE(c).erase_at_cursor = tgetstr("ec", &bufptr);
1139 * Initialize screen display information.
1141 TTY_SD(c).begin_standout_ = tgetstr("so", &bufptr);
1142 TTY_SD(c).end_standout_ = tgetstr("se", &bufptr);
1143 TTY_SD(c).begin_underline_ = tgetstr("us", &bufptr);
1144 TTY_SD(c).end_underline_ = tgetstr("ue", &bufptr);
1145 TTY_SD(c).begin_alternate_ = tgetstr("as", &bufptr);
1146 TTY_SD(c).end_alternate_ = tgetstr("ae", &bufptr);
1147 TTY_SD(c).turn_on_reverse_ = tgetstr("mr", &bufptr);
1148 TTY_SD(c).turn_on_blinking_ = tgetstr("mb", &bufptr);
1149 TTY_SD(c).turn_on_bold_ = tgetstr("md", &bufptr);
1150 TTY_SD(c).turn_on_dim_ = tgetstr("mh", &bufptr);
1151 TTY_SD(c).turn_off_attributes_ = tgetstr("me", &bufptr);
1152 TTY_SD(c).set_aback_ = tgetstr("AB", &bufptr);
1153 TTY_SD(c).set_afore_ = tgetstr("AF", &bufptr);
1154 TTY_SD(c).orig_pair_ = tgetstr("op", &bufptr);
1156 TTY_SD(c).visual_bell_ = tgetstr("vb", &bufptr);
1157 TTY_SD(c).audio_bell_ = tgetstr("bl", &bufptr);
1158 if (!TTY_SD(c).audio_bell_) {
1159 /* If audio_bell doesn't get set, then assume C-g. This is gross and
1160 ugly but is what Emacs has done from time immortal. */
1161 TTY_SD(c).audio_bell_ = "\07";
1164 TTY_SD(c).cursor_visible_ = tgetstr("vs", &bufptr);
1165 TTY_SD(c).cursor_invisible_ = tgetstr("vi", &bufptr);
1166 TTY_SD(c).cursor_normal_ = tgetstr("ve", &bufptr);
1167 TTY_SD(c).hard_cursor_ = tgetstr("HC", &bufptr);
1168 TTY_SD(c).init_motion_ = tgetstr("ti", &bufptr);
1169 TTY_SD(c).end_motion_ = tgetstr("te", &bufptr);
1170 TTY_SD(c).keypad_on_ = tgetstr("ks", &bufptr);
1171 TTY_SD(c).keypad_off_ = tgetstr("ke", &bufptr);
1174 * Initialize additional terminal information.
1176 TTY_FLAGS(c).must_write_spaces = tgetflag("in");
1177 TTY_FLAGS(c).insert_mode_motion = tgetflag("mi");
1178 TTY_FLAGS(c).standout_motion = tgetflag("ms");
1179 TTY_FLAGS(c).memory_above_frame = tgetflag("da");
1180 TTY_FLAGS(c).memory_below_frame = tgetflag("db");
1181 TTY_FLAGS(c).standout_width = tgetnum("sg");
1182 TTY_FLAGS(c).underline_width = tgetnum("ug");
1184 if (TTY_FLAGS(c).standout_width == -1)
1185 TTY_FLAGS(c).standout_width = 0;
1186 if (TTY_FLAGS(c).underline_width == -1)
1187 TTY_FLAGS(c).underline_width = 0;
1189 TTY_FLAGS(c).meta_key =
1190 eight_bit_tty(d) ? tgetflag("km") || tgetflag("MT") ? 1 : 2 : 0;
1193 * Setup the costs tables for this tty console.
1199 * Initialize local flags.
1202 standout_mode_on = 0;
1203 underline_mode_on = 0;
1204 alternate_mode_on = 0;
1209 * Attempt to initialize the function_key_map to
1210 * some kind of sensible value
1213 term_get_fkeys(c->function_key_map, &bufptr);
1216 /* check for ANSI set-foreground and set-background strings,
1217 and assume color if so.
1219 #### we should support the other (non-ANSI) ways of specifying
1223 char *SXE_UNUSED(fooptr) = foobuf;
1224 int colors = tgetnum("Co");
1225 int pairs = tgetnum("pa");
1226 if ((TTY_SD(c).set_aback_ && TTY_SD(c).set_afore_) ||
1227 ((colors > 0) || (pairs > 0))) {
1228 DEVICE_CLASS(d) = Qcolor;
1229 /* These cases should be rare. Most termcap
1230 * entries will have both colors and pairs
1231 * defined. However let's play it safe :)
1233 if ( colors == 0 && pairs > 0 ) {
1234 /* try to determine colors from pairs.
1235 * Most common cases first.
1239 else if ( pairs == 256 )
1242 /* Naive isqrt algorithm. */
1244 colors*colors < pairs;
1247 if ( pairs == 0 && colors > 0 )
1248 /* try to determine pairs from colors */
1249 pairs = colors * colors;
1250 CONSOLE_TTY_DATA(c)->maxcolors = colors;
1251 CONSOLE_TTY_DATA(c)->maxpairs = pairs;
1253 /* Most terminals that report 8 colors will
1254 make bold brighter */
1255 CONSOLE_TTY_DATA(c)->is_bold_brighter =
1256 (colors == 8 && TTY_SD(c).turn_on_bold_ )
1258 /* Most terminals do not have brighter background
1259 colors, however the REAL X11R6 xterm and rxvt do
1260 allow reverse to achieve this. Sadly there
1261 is no way to autodetect. Since the Hue is very
1262 different on the 8 basic colors and most users
1263 will hopefully use xterm or rxvt.
1265 CONSOLE_TTY_DATA(c)->is_reverse_brighter =
1266 (colors == 8 && TTY_SD(c).turn_on_reverse_)
1269 else if ( force_colorterm ) {
1270 DEVICE_CLASS(d) = Qcolor;
1271 TTY_SD(c).set_aback_ = "\e[4%p1%dm";
1272 TTY_SD(c).set_afore_ = "\e[3%p1%dm";
1273 CONSOLE_TTY_DATA(c)->maxcolors = 8;
1274 CONSOLE_TTY_DATA(c)->maxpairs = 64;
1275 CONSOLE_TTY_DATA(c)->is_bold_brighter =
1276 (TTY_SD(c).turn_on_bold_ ) ? 1 : 0;
1277 if ( ! TTY_SD(c).orig_pair_ )
1278 TTY_SD(c).orig_pair_ = "\e[39;49m";
1281 DEVICE_CLASS(d) = Qmono;
1282 CONSOLE_TTY_DATA(c)->maxcolors = 2;
1283 CONSOLE_TTY_DATA(c)->maxpairs = 4;
1284 CONSOLE_TTY_DATA(c)->is_bold_brighter = 0;
1285 CONSOLE_TTY_DATA(c)->is_reverse_brighter = 0;
1289 return TTY_INIT_SUCCESS;
1293 /* tgetstr will disobey this qualifier */
1298 /* Termcap capability names that correspond directly to X keysyms.
1299 Some of these (marked "terminfo") aren't supplied by old-style
1300 (Berkeley) termcap entries. They're listed in X keysym order;
1301 except we put the keypad keys first, so that if they clash with
1302 other keys (as on the IBM PC keyboard) they get overridden.
1305 static struct fkey_table keys[] = {
1306 {"kh", "home"}, /* termcap */
1307 {"kl", "left"}, /* termcap */
1308 {"ku", "up"}, /* termcap */
1309 {"kr", "right"}, /* termcap */
1310 {"kd", "down"}, /* termcap */
1311 {"%8", "prior"}, /* terminfo */
1312 {"%5", "next"}, /* terminfo */
1313 {"@7", "end"}, /* terminfo */
1314 {"@1", "begin"}, /* terminfo */
1315 {"*6", "select"}, /* terminfo */
1316 {"%9", "print"}, /* terminfo */
1317 {"@4", "execute"}, /* terminfo --- actually the `command' key */
1319 * "insert" --- see below
1321 {"&8", "undo"}, /* terminfo */
1322 {"%0", "redo"}, /* terminfo */
1323 {"%7", "menu"}, /* terminfo --- actually the `options' key */
1324 {"@0", "find"}, /* terminfo */
1325 {"@2", "cancel"}, /* terminfo */
1326 {"%1", "help"}, /* terminfo */
1328 * "break" goes here, but can't be reliably intercepted with termcap
1330 {"&4", "reset"}, /* terminfo --- actually `restart' */
1332 * "system" and "user" --- no termcaps
1334 {"kE", "clearline"}, /* terminfo */
1335 {"kA", "insertline"}, /* terminfo */
1336 {"kL", "deleteline"}, /* terminfo */
1337 {"kI", "insertchar"}, /* terminfo */
1338 {"kD", "delete"}, /* terminfo */
1339 {"kB", "backtab"}, /* terminfo */
1341 * "kp-backtab", "kp-space", "kp-tab" --- no termcaps
1343 {"@8", "kp-enter"}, /* terminfo */
1345 * "kp-f1", "kp-f2", "kp-f3" "kp-f4",
1346 * "kp-multiply", "kp-add", "kp-separator",
1347 * "kp-subtract", "kp-decimal", "kp-divide", "kp-0";
1348 * --- no termcaps for any of these.
1350 {"K4", "kp-1"}, /* terminfo */
1352 * "kp-2" --- no termcap
1354 {"K5", "kp-3"}, /* terminfo */
1356 * "kp-4" --- no termcap
1358 {"K2", "kp-5"}, /* terminfo */
1360 * "kp-6" --- no termcap
1362 {"K1", "kp-7"}, /* terminfo */
1364 * "kp-8" --- no termcap
1366 {"K3", "kp-9"}, /* terminfo */
1368 * "kp-equal" --- no termcap
1381 static char **term_get_fkeys_arg;
1383 static Lisp_Object term_get_fkeys_1(Lisp_Object keymap);
1384 static Lisp_Object term_get_fkeys_error(Lisp_Object err, Lisp_Object arg);
1386 /* Find the escape codes sent by the function keys for Vfunction_key_map.
1387 This function scans the termcap function key sequence entries, and
1388 adds entries to Vfunction_key_map for each function key it finds. */
1390 static void term_get_fkeys(Lisp_Object keymap, char **address)
1392 /* We run the body of the function (term_get_fkeys_1) and ignore all Lisp
1393 errors during the call. The only errors should be from Fdefine_key
1394 when given a key sequence containing an invalid prefix key. If the
1395 termcap defines function keys which use a prefix that is already bound
1396 to a command by the default bindings, we should silently ignore that
1397 function key specification, rather than giving the user an error and
1398 refusing to run at all on such a terminal. */
1400 term_get_fkeys_arg = address;
1402 condition_case_1(Qerror,
1403 term_get_fkeys_1, keymap, term_get_fkeys_error, Qnil);
1406 static Lisp_Object term_get_fkeys_error(Lisp_Object err, Lisp_Object arg)
1411 static Lisp_Object term_get_fkeys_1(Lisp_Object function_key_map)
1415 char **address = term_get_fkeys_arg;
1417 for (i = 0; i < countof(keys); i++) {
1418 const char *sequence = tgetstr(keys[i].cap, address);
1420 Fdefine_key(function_key_map,
1421 build_ext_string(sequence, Qbinary),
1422 vector1(intern(keys[i].name)));
1426 /* The uses of the "k0" capability are inconsistent; sometimes it
1427 describes F10, whereas othertimes it describes F0 and "k;" describes F10.
1428 We will attempt to politely accommodate both systems by testing for
1429 "k;", and if it is present, assuming that "k0" denotes F0, otherwise F10.
1432 const char *k_semi = tgetstr("k;", address);
1433 const char *k0 = tgetstr("k0", address);
1436 Fdefine_key(function_key_map,
1437 build_ext_string(k_semi, Qbinary),
1438 vector1(intern("f10")));
1441 Fdefine_key(function_key_map,
1442 build_ext_string(k0, Qbinary),
1443 vector1(intern(k_semi ? "f0" : "f10")));
1446 /* Set up cookies for numbered function keys above f10. */
1448 char fcap[3], fkey[4];
1452 for (i = 11; i < 64; i++) {
1454 fcap[1] = '1' + i - 11;
1456 fcap[1] = 'A' + i - 20;
1458 fcap[1] = 'a' + i - 46;
1461 char *sequence = tgetstr(fcap, address);
1463 int sz = snprintf(fkey, sizeof(fkey), "f%d", i);
1464 assert(sz >= 0 && (size_t)sz < sizeof(fkey));
1465 Fdefine_key(function_key_map,
1466 build_ext_string(sequence,
1468 vector1(intern(fkey)));
1475 * Various mappings to try and get a better fit.
1477 #define CONDITIONAL_REASSIGN(cap1, cap2, keyname) do { \
1478 if (!tgetstr (cap1, address)) \
1480 char *sequence = tgetstr (cap2, address); \
1482 Fdefine_key (function_key_map, \
1483 build_ext_string (sequence, Qbinary), \
1484 vector1 (intern (keyname))); \
1488 /* if there's no key_next keycap, map key_npage to `next' keysym */
1489 CONDITIONAL_REASSIGN("%5", "kN", "next");
1490 /* if there's no key_prev keycap, map key_ppage to `previous' keysym */
1491 CONDITIONAL_REASSIGN("%8", "kP", "prior");
1492 /* if there's no key_dc keycap, map key_ic to `insert' keysym */
1493 CONDITIONAL_REASSIGN("kD", "kI", "insert");
1495 /* IBM has their own non-standard dialect of terminfo.
1496 If the standard name isn't found, try the IBM name. */
1497 CONDITIONAL_REASSIGN("kB", "KO", "backtab");
1498 CONDITIONAL_REASSIGN("@4", "kJ", "execute"); /* actually "action" */
1499 CONDITIONAL_REASSIGN("@4", "kc", "execute"); /* actually "command" */
1500 CONDITIONAL_REASSIGN("%7", "ki", "menu");
1501 CONDITIONAL_REASSIGN("@7", "kw", "end");
1502 CONDITIONAL_REASSIGN("F1", "k<", "f11");
1503 CONDITIONAL_REASSIGN("F2", "k>", "f12");
1504 CONDITIONAL_REASSIGN("%1", "kq", "help");
1505 CONDITIONAL_REASSIGN("*6", "kU", "select");
1506 #undef CONDITIONAL_REASSIGN
1511 /************************************************************************/
1512 /* initialization */
1513 /************************************************************************/
1515 void console_type_create_redisplay_tty(void)
1517 /* redisplay methods */
1518 CONSOLE_HAS_METHOD(tty, text_width);
1519 CONSOLE_HAS_METHOD(tty, output_display_block);
1520 CONSOLE_HAS_METHOD(tty, output_vertical_divider);
1521 CONSOLE_HAS_METHOD(tty, divider_height);
1522 CONSOLE_HAS_METHOD(tty, eol_cursor_width);
1523 CONSOLE_HAS_METHOD(tty, clear_to_window_end);
1524 CONSOLE_HAS_METHOD(tty, clear_region);
1525 CONSOLE_HAS_METHOD(tty, clear_frame);
1526 CONSOLE_HAS_METHOD(tty, frame_output_begin);
1527 CONSOLE_HAS_METHOD(tty, frame_output_end);
1528 CONSOLE_HAS_METHOD(tty, flash);
1529 CONSOLE_HAS_METHOD(tty, ring_bell);
1530 CONSOLE_HAS_METHOD(tty, set_final_cursor_coords);