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
59 /* These headers #define all kinds of common words like "columns"...
60 What a bunch of losers. If we were to include them, we'd have to
61 include them last to prevent them from messing up our own header
62 files (struct slot names, etc.). But it turns out that there are
63 other conflicts as well on some systems, so screw it: we'll just
64 re-declare the routines we use and assume the code in this file is
65 invoking them correctly. */
66 /* # include <curses.h> */
67 /* # include <term.h> */
69 EXTERN_C int tgetent(const char *, const char *);
70 EXTERN_C int tgetflag(const char *);
71 EXTERN_C int tgetnum(const char *);
72 EXTERN_C char *tgetstr(const char *, char **);
75 #ifndef TERMCAP_H_FILE
76 EXTERN_C void tputs(const char *, int, int (*)(int));
79 char *emacs_tparam(const char *string, char *outstring, int len, int arg1,
80 int arg2, int arg3, int arg4, int arg5, int arg6, int arg7,
83 typedef int (*tputs_fun)(int);
85 #define FORCE_CURSOR_UPDATE(c) send_string_to_tty_console ((c), 0, 0)
86 #define OUTPUTN(c, a, n) \
88 cmputc_console = (c); \
89 FORCE_CURSOR_UPDATE(c); \
90 tputs ((a), (n), (tputs_fun)cmputc); \
92 #define OUTPUT1(c, a) OUTPUTN ((c), (a), 1)
93 #define OUTPUTN_IF(c, a, n) \
95 cmputc_console = (c); \
96 FORCE_CURSOR_UPDATE(c); \
98 tputs ((a), (n), (tputs_fun)cmputc); \
100 #define OUTPUT1_IF(c, a) OUTPUTN_IF ((c), (a), 1)
102 static void tty_output_emchar_dynarr(struct window *w,
103 struct display_line *dl,
104 Emchar_dynarr * buf, int xpos,
105 face_index findex, int cursor);
106 static void tty_output_bufbyte_string(struct window *w,
107 struct display_line *dl,
108 Bufbyte * str, Bytecount len,
109 int xpos, face_index findex, int cursor);
110 static void tty_turn_on_face(struct window *w, face_index findex);
111 static void tty_turn_off_face(struct window *w, face_index findex);
112 static void tty_turn_on_frame_face(struct frame *f, Lisp_Object face);
113 static void tty_turn_off_frame_face(struct frame *f, Lisp_Object face);
115 static void term_get_fkeys(Lisp_Object keymap, char **address);
117 extern int assume_colorterm;
119 /*****************************************************************************
122 Non-Mule tty's don't have fonts (that we use at least), so everything
123 is considered to be fixed width -- in other words, we return LEN.
124 Under Mule, however, a character can still cover more than one
125 column, so we use emchar_string_displayed_columns().
126 ****************************************************************************/
128 tty_text_width(struct frame *f, struct face_cachel *cachel, const Emchar * str,
131 return emchar_string_displayed_columns(str, len);
134 /*****************************************************************************
137 Return the width of the horizontal divider. This is a function
138 because divider_height is a console method.
139 ****************************************************************************/
140 static int tty_divider_height(void)
145 /*****************************************************************************
148 Return the width of the end-of-line cursor. This is a function
149 because eol_cursor_width is a console method.
150 ****************************************************************************/
151 static int tty_eol_cursor_width(void)
156 /*****************************************************************************
157 tty_frame_output_begin
159 Perform any necessary initialization prior to an update.
160 ****************************************************************************/
162 void tty_frame_output_begin(struct frame *f);
167 tty_frame_output_begin(struct frame *f)
170 /* Termcap requires `ospeed' to be a global variable so we have to
171 always set it for whatever tty console we are actually currently
173 ospeed = DEVICE_TTY_DATA(XDEVICE(FRAME_DEVICE(f)))->ospeed;
177 /*****************************************************************************
180 Perform any necessary flushing of queues when an update has completed.
181 ****************************************************************************/
183 void tty_frame_output_end(struct frame *f);
188 tty_frame_output_end(struct frame *f)
190 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
192 CONSOLE_TTY_CURSOR_X(c) = CONSOLE_TTY_FINAL_CURSOR_X(c);
193 CONSOLE_TTY_CURSOR_Y(c) = CONSOLE_TTY_FINAL_CURSOR_Y(c);
194 FORCE_CURSOR_UPDATE(c);
195 Lstream_flush(XLSTREAM(CONSOLE_TTY_DATA(c)->outstream));
198 static void tty_set_final_cursor_coords(struct frame *f, int y, int x)
200 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
202 CONSOLE_TTY_FINAL_CURSOR_X(c) = x;
203 CONSOLE_TTY_FINAL_CURSOR_Y(c) = y;
206 /*****************************************************************************
207 tty_output_display_block
209 Given a display line, a block number for that start line, output all
210 runes between start and end in the specified display block.
211 ****************************************************************************/
213 tty_output_display_block(struct window *w, struct display_line *dl, int block,
214 int start, int end, int start_pixpos,
215 int cursor_start, int cursor_width, int cursor_height)
217 struct frame *f = XFRAME(w->frame);
218 Emchar_dynarr *buf = NULL;
220 struct display_block *db = Dynarr_atp(dl->display_blocks, block);
221 rune_dynarr *rba = db->runes;
228 rb = Dynarr_atp(rba, elt);
231 /* Nothing to do so don't do anything. */
239 end = Dynarr_length(rba);
241 buf = Dynarr_new(Emchar);
243 while (elt < end && Dynarr_atp(rba, elt)->xpos < start_pixpos) {
245 findex = Dynarr_atp(rba, elt)->findex;
246 xpos = Dynarr_atp(rba, elt)->xpos;
250 rb = Dynarr_atp(rba, elt);
252 if (rb->findex == findex && rb->type == RUNE_CHAR
253 && rb->object.chr.ch != '\n'
254 && (rb->cursor_type != CURSOR_ON
255 || NILP(w->text_cursor_visible_p))) {
256 Dynarr_add(buf, rb->object.chr.ch);
259 if (Dynarr_length(buf)) {
260 tty_output_emchar_dynarr(w, dl, buf, xpos,
266 if (rb->type == RUNE_CHAR) {
270 if (rb->object.chr.ch == '\n') {
271 /* Clear in case a cursor was formerly here. */
273 Dynarr_add(buf, ' ');
274 tty_output_emchar_dynarr(w, dl, buf,
280 cmgoto(f, dl->ypos - 1, rb->xpos);
283 } else if (rb->cursor_type == CURSOR_ON) {
284 /* There is not a distinct eol cursor on tty's. */
286 Dynarr_add(buf, rb->object.chr.ch);
287 tty_output_emchar_dynarr(w, dl, buf,
292 cmgoto(f, dl->ypos - 1, xpos);
298 /* #### RUNE_HLINE is actually a little more complicated than this
299 but at the moment it is only used to draw a turned off
300 modeline and this will suffice for that. */
301 else if (rb->type == RUNE_BLANK
302 || rb->type == RUNE_HLINE) {
304 int size = rb->width;
306 if (rb->type == RUNE_BLANK)
312 Dynarr_add(buf, ch_to_add);
313 tty_output_emchar_dynarr(w, dl, buf, rb->xpos,
316 if (xpos >= cursor_start
317 && cursor_start < xpos + Dynarr_length(buf))
319 cmgoto(f, dl->ypos - 1, cursor_start);
326 rb = Dynarr_atp(rba, elt);
331 } else if (rb->type == RUNE_DGLYPH) {
333 Lisp_Object instance;
335 XSETWINDOW(window, w);
337 glyph_image_instance(rb->object.dglyph.
341 if (IMAGE_INSTANCEP(instance)) {
342 switch (XIMAGE_INSTANCE_TYPE(instance)) {
343 case IMAGE_MONO_PIXMAP:
344 case IMAGE_COLOR_PIXMAP:
345 case IMAGE_SUBWINDOW:
347 /* just do nothing here */
351 /* nothing is as nothing does */
360 IMAGE_INSTANCE_OPTIMIZE_OUTPUT
361 (XIMAGE_INSTANCE(instance)) = 0;
371 if (Dynarr_length(buf))
372 tty_output_emchar_dynarr(w, dl, buf, xpos, findex, 0);
377 /*****************************************************************************
378 tty_output_vertical_divider
380 Draw a vertical divider down the right side of the given window.
381 ****************************************************************************/
383 tty_output_vertical_divider(struct window *w, int SXE_UNUSED(clearp))
385 /* Divider width can either be 0 or 1 on TTYs */
386 if (window_divider_width(w)) {
387 struct frame *f = XFRAME(w->frame);
388 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
390 int y_top = WINDOW_TEXT_TOP(w);
391 int y_bot = WINDOW_TEXT_BOTTOM(w);
392 unsigned char divv = '|';
394 tty_turn_on_face(w, MODELINE_INDEX);
395 for (line = y_top; line < y_bot; line++) {
396 cmgoto(f, line, WINDOW_TEXT_RIGHT(w));
397 send_string_to_tty_console(c, &divv, 1);
398 TTY_INC_CURSOR_X(c, 1);
401 /* Draw the divider in the modeline. */
402 cmgoto(f, y_bot, WINDOW_TEXT_RIGHT(w));
403 send_string_to_tty_console(c, &divv, 1);
404 TTY_INC_CURSOR_X(c, 1);
405 tty_turn_off_face(w, MODELINE_INDEX);
410 /****************************************************************************
413 Clear the area in the box defined by the given parameters.
414 ****************************************************************************/
416 tty_clear_region(Lisp_Object window, struct device *d, struct frame *f,
417 face_index findex, int x, int y,
418 int width, int height, Lisp_Object fcolor, Lisp_Object bcolor,
419 Lisp_Object background_pixmap)
421 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
423 struct window *w = XWINDOW(window);
425 tty_turn_on_face(w, findex);
426 for (line = y; line < y + height; line++) {
431 if (window_is_leftmost(w)
432 && window_is_rightmost(w)
433 && TTY_SE(c).clr_to_eol) {
434 OUTPUT1(c, TTY_SE(c).clr_to_eol);
436 unsigned char sp = ' ';
437 /* #### Of course, this is all complete and utter crap. */
438 for (col = x; col < x + width; col++)
439 send_string_to_tty_console(c, &sp, 1);
440 TTY_INC_CURSOR_X(c, width);
443 tty_turn_off_face(w, findex);
447 /*****************************************************************************
448 tty_clear_to_window_end
450 Clear the area between ypos1 and ypos2. Each margin area and the
451 text area is handled separately since they may each have their own
453 ****************************************************************************/
454 static void tty_clear_to_window_end(struct window *w, int ypos1, int ypos2)
456 struct frame *f = XFRAME(w->frame);
457 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
460 x = WINDOW_TEXT_LEFT(w);
461 width = WINDOW_TEXT_WIDTH(w);
463 if (window_is_rightmost(w)) {
464 /* #### Optimize to use clr_to_eol function of tty if available, if
465 the window is the entire width of the frame. */
466 /* #### Is this actually an optimization? */
468 tty_turn_on_face(w, DEFAULT_INDEX);
469 for (line = ypos1; line < ypos2; line++) {
470 cmgoto(XFRAME(w->frame), line, x);
471 OUTPUT1(c, TTY_SE(c).clr_to_eol);
473 tty_turn_off_face(w, DEFAULT_INDEX);
477 XSETWINDOW(window, w);
478 redisplay_clear_region(window, DEFAULT_INDEX, x, ypos1, width,
483 /****************************************************************************
486 Clear the entire frame.
487 ****************************************************************************/
488 static void tty_clear_frame(struct frame *f)
490 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
492 tty_turn_on_frame_face(f, Vdefault_face);
493 if (TTY_SE(c).clr_frame) {
494 OUTPUT1(c, TTY_SE(c).clr_frame);
495 CONSOLE_TTY_REAL_CURSOR_X(c) = 0;
496 CONSOLE_TTY_REAL_CURSOR_Y(c) = 0;
498 FRAME_CURSOR_X(f) = 0;
499 FRAME_CURSOR_Y(f) = 0;
503 internal_cursor_to(f, 0, 0);
506 /* #### Not implemented. */
507 stderr_out("Not yet.\n");
510 tty_turn_off_frame_face(f, Vdefault_face);
514 tty_output_bufbyte_string(struct window *w, struct display_line *dl,
515 Bufbyte * str, Bytecount len, int xpos,
516 face_index findex, int cursor)
518 struct frame *f = XFRAME(w->frame);
519 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
521 /* First position the cursor. */
522 cmgoto(f, dl->ypos - 1, xpos);
524 /* Enable any face properties. */
525 tty_turn_on_face(w, findex);
527 send_string_to_tty_console(c, str, len);
528 TTY_INC_CURSOR_X(c, bufbyte_string_displayed_columns(str, len));
530 /* Turn the face properties back off. */
531 tty_turn_off_face(w, findex);
534 static Bufbyte_dynarr *tty_output_emchar_dynarr_dynarr;
536 /*****************************************************************************
537 tty_output_emchar_dynarr
539 Given a string and a starting position, output that string in the
540 given face. If cursor is true, draw a cursor around the string.
541 ****************************************************************************/
543 tty_output_emchar_dynarr(struct window *w, struct display_line *dl,
544 Emchar_dynarr * buf, int xpos, face_index findex,
547 if (!tty_output_emchar_dynarr_dynarr)
548 tty_output_emchar_dynarr_dynarr = Dynarr_new(Bufbyte);
550 Dynarr_reset(tty_output_emchar_dynarr_dynarr);
552 convert_emchar_string_into_bufbyte_dynarr(Dynarr_atp(buf, 0),
554 tty_output_emchar_dynarr_dynarr);
556 tty_output_bufbyte_string(w, dl,
557 Dynarr_atp(tty_output_emchar_dynarr_dynarr,
560 (tty_output_emchar_dynarr_dynarr), xpos,
564 #include <semaphore.h>
566 static int tty_mutex_inited = 0;
567 static sxe_mutex_t tty_mutex;
568 static Lstream *tty_stream = NULL;
570 static void tty_stream_putc( int c )
573 Lstream_putc(tty_stream, c);
576 static void send_tty_escseq(struct console *c, char *escseq)
578 if ( ! tty_mutex_inited ) {
579 SXE_MUTEX_INIT(&tty_mutex);
580 tty_mutex_inited = 1;
582 WITH_SXE_MUTEX(&tty_mutex,
584 tty_stream = XLSTREAM(CONSOLE_TTY_DATA(c)->outstream);
585 tputs(escseq,1,(tputs_fun)tty_stream_putc);
594 set_foreground_to(struct console *c, Lisp_Object sym)
599 result = Ffind_tty_color(sym,c->selected_device,Qt);
600 if ( !NILP(result) ) {
601 Lisp_Object idx = XCAR(result);
602 Lisp_Object bold = XCAR(XCDR(result));
604 if (TTY_SD(c).set_afore_ ) {
605 /* inevitable warning? */
607 emacs_tparam((TTY_SD(c).set_afore_),
613 if ( ! EQ(bold,Qnil) &&
614 CONSOLE_TTY_DATA(c)->is_bold_brighter ) {
615 OUTPUT1_IF(c, TTY_SD(c).turn_on_bold_);
617 send_tty_escseq(c,(char*)escseq);
625 set_background_to(struct console *c, Lisp_Object sym)
630 result = Ffind_tty_color(sym,c->selected_device,Qt);
631 if ( !NILP(result) ) {
632 Lisp_Object idx = XCAR(result);
634 if (TTY_SD(c).set_aback_ ) {
635 /* inevitable warning */
637 emacs_tparam((TTY_SD(c).set_aback_),
643 send_tty_escseq(c,(char*)escseq);
651 tty_turn_on_face_1(struct console *c, int highlight_p,
652 int blinking_p, int dim_p, int underline_p,
653 int reverse_p, Lisp_Object cinst_fore,
654 Lisp_Object cinst_back)
657 OUTPUT1_IF(c, TTY_SD(c).turn_on_bold_);
661 OUTPUT1_IF(c, TTY_SD(c).turn_on_blinking_);
665 OUTPUT1_IF(c, TTY_SD(c).turn_on_dim_);
669 /* #### punt for now if underline mode is glitchy */
670 if (!TTY_FLAGS(c).underline_width) {
671 OUTPUT1_IF(c, TTY_SD(c).begin_underline_);
676 /* #### punt for now if standout mode is glitchy */
677 if (!TTY_FLAGS(c).standout_width) {
678 OUTPUT1_IF(c, TTY_SD(c).begin_standout_);
684 Lisp_Object temp = cinst_fore;
685 cinst_fore = cinst_back;
690 int color_instancep_fore = COLOR_INSTANCEP(cinst_fore);
691 int cinst_fore_eq_null_color = EQ(cinst_fore, Vthe_null_color_instance);
692 if ( color_instancep_fore
693 && !cinst_fore_eq_null_color ) {
694 Lisp_Object cinst_sym = COLOR_INSTANCE_TTY_SYMBOL
695 (XCOLOR_INSTANCE(cinst_fore));
696 set_foreground_to(c, cinst_sym);
699 int color_instancep_back = COLOR_INSTANCEP(cinst_back);
700 int cinst_back_eq_null_color = EQ(cinst_back, Vthe_null_color_instance);
701 if ( color_instancep_back
702 && !cinst_back_eq_null_color ) {
703 Lisp_Object cinst_sym = COLOR_INSTANCE_TTY_SYMBOL
704 (XCOLOR_INSTANCE(cinst_back));
705 set_background_to(c, cinst_sym);
709 /*****************************************************************************
712 Turn on all set properties of the given face.
713 ****************************************************************************/
714 static void tty_turn_on_face(struct window *w, face_index findex)
716 struct frame *f = XFRAME(w->frame);
717 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
719 tty_turn_on_face_1(c,
720 WINDOW_FACE_CACHEL_HIGHLIGHT_P(w, findex),
721 WINDOW_FACE_CACHEL_BLINKING_P(w, findex),
722 WINDOW_FACE_CACHEL_DIM_P(w, findex),
723 WINDOW_FACE_CACHEL_UNDERLINE_P(w, findex),
724 WINDOW_FACE_CACHEL_REVERSE_P(w, findex),
725 WINDOW_FACE_CACHEL_FOREGROUND(w, findex),
726 WINDOW_FACE_CACHEL_BACKGROUND(w, findex));
729 /*****************************************************************************
732 Turn off all set properties of the given face (revert to default
733 face). We assume that tty_turn_on_face has been called for the given
734 face so that its properties are actually active.
735 ****************************************************************************/
736 static void tty_turn_off_face(struct window *w, face_index findex)
738 struct frame *f = XFRAME(w->frame);
739 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
741 if (WINDOW_FACE_CACHEL_REVERSE_P(w, findex)) {
742 /* #### punt for now if standout mode is glitchy */
743 if (!TTY_FLAGS(c).standout_width) {
744 OUTPUT1_IF(c, TTY_SD(c).end_standout_);
748 if (WINDOW_FACE_CACHEL_UNDERLINE_P(w, findex)) {
749 /* #### punt for now if underline mode is glitchy */
750 if (!TTY_FLAGS(c).underline_width) {
751 OUTPUT1_IF(c, TTY_SD(c).end_underline_);
755 if (WINDOW_FACE_CACHEL_HIGHLIGHT_P(w, findex) ||
756 WINDOW_FACE_CACHEL_BLINKING_P(w, findex) ||
757 WINDOW_FACE_CACHEL_DIM_P(w, findex) ||
758 !EQ(WINDOW_FACE_CACHEL_FOREGROUND(w, findex),
759 Vthe_null_color_instance) ||
760 !EQ(WINDOW_FACE_CACHEL_BACKGROUND(w, findex),
761 Vthe_null_color_instance)) {
762 OUTPUT1_IF(c, TTY_SD(c).turn_off_attributes_);
766 /*****************************************************************************
767 tty_turn_on_frame_face
769 Turn on all set properties of the given face.
770 ****************************************************************************/
771 static void tty_turn_on_frame_face(struct frame *f, Lisp_Object face)
774 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
777 tty_turn_on_face_1(c,
778 FACE_HIGHLIGHT_P(face, frame),
779 FACE_BLINKING_P(face, frame),
780 FACE_DIM_P(face, frame),
781 FACE_UNDERLINE_P(face, frame),
782 FACE_REVERSE_P(face, frame),
783 FACE_FOREGROUND(face, frame),
784 FACE_BACKGROUND(face, frame));
787 /*****************************************************************************
788 tty_turn_off_frame_face
790 Turn off all set properties of the given face (revert to default
791 face). We assume that tty_turn_on_face has been called for the given
792 face so that its properties are actually active.
793 ****************************************************************************/
794 static void tty_turn_off_frame_face(struct frame *f, Lisp_Object face)
797 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
801 if (FACE_REVERSE_P(face, frame)) {
802 /* #### punt for now if standout mode is glitchy */
803 if (!TTY_FLAGS(c).standout_width) {
804 OUTPUT1_IF(c, TTY_SD(c).end_standout_);
808 if (FACE_UNDERLINE_P(face, frame)) {
809 /* #### punt for now if underline mode is glitchy */
810 if (!TTY_FLAGS(c).underline_width) {
811 OUTPUT1_IF(c, TTY_SD(c).end_underline_);
815 if (FACE_HIGHLIGHT_P(face, frame) ||
816 FACE_BLINKING_P(face, frame) ||
817 FACE_DIM_P(face, frame) ||
818 !EQ(FACE_FOREGROUND(face, frame), Vthe_null_color_instance) ||
819 !EQ(FACE_BACKGROUND(face, frame), Vthe_null_color_instance)) {
820 OUTPUT1_IF(c, TTY_SD(c).turn_off_attributes_);
824 /*****************************************************************************
827 Sets up various parameters on tty modes.
828 ****************************************************************************/
829 void set_tty_modes(struct console *c)
831 if (!CONSOLE_TTY_P(c))
834 OUTPUT1_IF(c, (TTY_SD(c).init_motion_));
835 OUTPUT1_IF(c, (TTY_SD(c).cursor_visible_));
836 OUTPUT1_IF(c, (TTY_SD(c).keypad_on_));
839 /*****************************************************************************
842 Restore default state of tty.
843 ****************************************************************************/
844 void reset_tty_modes(struct console *c)
846 if (!CONSOLE_TTY_P(c))
849 OUTPUT1_IF(c, (TTY_SD(c).orig_pair_));
850 OUTPUT1_IF(c, (TTY_SD(c).keypad_off_));
851 OUTPUT1_IF(c, (TTY_SD(c).cursor_normal_));
852 OUTPUT1_IF(c, (TTY_SD(c).end_motion_));
855 Lisp_Object frm = CONSOLE_SELECTED_FRAME(c);
858 tty_frame_output_end(XFRAME(frm));
862 /*****************************************************************************
863 tty_redisplay_shutdown
865 Clear the frame and position the cursor properly for exiting.
866 ****************************************************************************/
867 void tty_redisplay_shutdown(struct console *c)
869 Lisp_Object dev = CONSOLE_SELECTED_DEVICE(c);
872 Lisp_Object frm = DEVICE_SELECTED_FRAME(XDEVICE(dev));
875 struct frame *f = XFRAME(frm);
877 /* Clear the bottom line of the frame. */
878 redisplay_clear_region(FRAME_SELECTED_WINDOW(f),
879 DEFAULT_INDEX, 0, f->height,
882 /* And then stick the cursor there. */
883 tty_set_final_cursor_coords(f, f->height, 0);
884 tty_frame_output_end(f);
889 /* #### Everything below here is old shit. It should either be moved
893 /* FLAGS - these don't need to be console local since only one console
894 can be being updated at a time. */
895 static int insert_mode_on; /* nonzero if in insert mode */
896 static int standout_mode_on; /* nonzero if in standout mode */
897 static int underline_mode_on; /* nonzero if in underline mode */
898 static int alternate_mode_on; /* nonzero if in alternate char set */
899 static int attributes_on; /* nonzero if any attributes on */
901 static void turn_on_insert(struct frame *f)
903 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
906 OUTPUT1_IF(c, TTY_SE(c).begin_ins_mode);
910 static void turn_off_insert(struct frame *f)
912 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
915 OUTPUT1(c, TTY_SE(c).end_ins_mode);
919 static void internal_cursor_to(struct frame *f, int row, int col)
921 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
923 if (!TTY_FLAGS(c).insert_mode_motion)
925 if (!TTY_FLAGS(c).standout_motion) {
926 turn_off_standout(f);
927 turn_off_underline(f);
928 turn_off_alternate(f);
934 static void clear_to_end(struct frame *f)
936 struct console *c = XCONSOLE(FRAME_CONSOLE(f));
938 /* assumes cursor is already positioned */
939 if (TTY_SE(c).clr_from_cursor) {
940 OUTPUT1(c, TTY_SE(c).clr_from_cursor);
942 int line = FRAME_CURSOR_Y(f);
944 while (line < FRAME_HEIGHT(f)) {
945 internal_cursor_to(f, line, 0);
946 OUTPUT1(c, TTY_SE(c).clr_to_eol);
954 * clear from last visible line on window to window end (presumably
955 * the line above window's modeline
957 static void tty_clear_window_end(struct window *w, int ystart, int yend)
959 struct console *c = XCONSOLE(WINDOW_CONSOLE(w));
962 for (line = ystart; line < yend; line++) {
963 cmgoto(XFRAME(w->frame), line, 0);
964 OUTPUT1(c, TTY_SE(c).clr_to_eol);
970 static int tty_flash(struct device *d)
972 struct console *c = XCONSOLE(DEVICE_CONSOLE(d));
973 if (TTY_SD(c).visual_bell_) {
974 OUTPUT1(c, TTY_SD(c).visual_bell_);
975 Lstream_flush(XLSTREAM(CONSOLE_TTY_DATA(c)->outstream));
982 * tty_ring_bell - sound an audio beep.
984 static void tty_ring_bell(struct device *d, int volume, int pitch, int duration)
986 struct console *c = XCONSOLE(DEVICE_CONSOLE(d));
989 OUTPUT1(c, TTY_SD(c).audio_bell_);
990 Lstream_flush(XLSTREAM(CONSOLE_TTY_DATA(c)->outstream));
994 int init_tty_for_redisplay(struct device *d, char *terminal_type)
996 int force_colorterm = assume_colorterm ||
997 getenv("COLOR_TERM") != NULL ||
998 getenv("COLORTERM") != NULL;
1001 /* According to the man page a terminfo/termcap cannot be
1002 longer than 4096 so we should be golden here
1004 char entry_buffer[4098];
1006 struct console *c = XCONSOLE(DEVICE_CONSOLE(d));
1007 char *ttype = terminal_type;
1009 /* What we should really do is allocate just enough space for
1010 the actual strings that are stored; but this would require
1011 doing this after all the tgetstr()s and adjusting all the
1013 CONSOLE_TTY_DATA(c)->term_entry_buffer =
1014 xmalloc_atomic(countof(entry_buffer));
1015 bufptr = CONSOLE_TTY_DATA(c)->term_entry_buffer;
1019 EMACS_BLOCK_SIGNAL(SIGTTOU);
1021 status = tgetent(entry_buffer, ttype);
1023 EMACS_UNBLOCK_SIGNAL(SIGTTOU);
1025 /* Under Linux at least, <0 is returned for TTY_TYPE_UNDEFINED. --ben
1028 if ( ! strcmp( ttype, "xterm-256color" ) ) {
1029 ttype = "xterm-color";
1031 } else if ( ! strcmp( ttype, "xterm-color" ) ) {
1034 } else if ( ! strcmp( ttype, "xterm" ) ) {
1038 return TTY_TYPE_UNDEFINED;
1040 } while( status <= 0 );
1042 if ( strcmp(terminal_type,ttype) ) {
1043 stderr_out("Could not find an entry for terminal type '");
1044 stderr_out("%s",terminal_type);
1045 stderr_out("' fallback using '");
1046 stderr_out("%s",ttype);
1050 CONSOLE_TTY_DATA(c)->term_cmap = Qnil;
1051 CONSOLE_TTY_DATA(c)->term_crgb = Qnil;
1053 * Establish the terminal size.
1055 /* First try to get the info from the system. If that fails, check
1056 the termcap entry. */
1057 get_tty_device_size(d, &CONSOLE_TTY_DATA(c)->width,
1058 &CONSOLE_TTY_DATA(c)->height);
1060 if (CONSOLE_TTY_DATA(c)->width <= 0)
1061 CONSOLE_TTY_DATA(c)->width = tgetnum("co");
1062 if (CONSOLE_TTY_DATA(c)->height <= 0)
1063 CONSOLE_TTY_DATA(c)->height = tgetnum("li");
1065 if (CONSOLE_TTY_DATA(c)->width <= 0 || CONSOLE_TTY_DATA(c)->height <= 0)
1066 return TTY_SIZE_UNSPECIFIED;
1069 * Initialize cursor motion information.
1072 /* local cursor movement */
1073 TTY_CM(c).up = tgetstr("up", &bufptr);
1074 TTY_CM(c).down = tgetstr("do", &bufptr);
1075 TTY_CM(c).left = tgetstr("le", &bufptr);
1076 TTY_CM(c).right = tgetstr("nd", &bufptr);
1077 TTY_CM(c).home = tgetstr("ho", &bufptr);
1078 TTY_CM(c).low_left = tgetstr("ll", &bufptr);
1079 TTY_CM(c).car_return = tgetstr("cr", &bufptr);
1081 /* absolute cursor motion */
1082 TTY_CM(c).abs = tgetstr("cm", &bufptr);
1083 TTY_CM(c).hor_abs = tgetstr("ch", &bufptr);
1084 TTY_CM(c).ver_abs = tgetstr("cv", &bufptr);
1086 /* Verify that the terminal is powerful enough to run Emacs */
1087 if (!TTY_CM(c).abs) {
1088 if (!TTY_CM(c).up || !TTY_CM(c).down
1089 || !TTY_CM(c).left || !TTY_CM(c).right)
1090 return TTY_TYPE_INSUFFICIENT;
1093 /* parameterized local cursor movement */
1094 TTY_CM(c).multi_up = tgetstr("UP", &bufptr);
1095 TTY_CM(c).multi_down = tgetstr("DO", &bufptr);
1096 TTY_CM(c).multi_left = tgetstr("LE", &bufptr);
1097 TTY_CM(c).multi_right = tgetstr("RI", &bufptr);
1100 TTY_CM(c).scroll_forw = tgetstr("sf", &bufptr);
1101 TTY_CM(c).scroll_back = tgetstr("sr", &bufptr);
1102 TTY_CM(c).multi_scroll_forw = tgetstr("SF", &bufptr);
1103 TTY_CM(c).multi_scroll_back = tgetstr("SR", &bufptr);
1104 TTY_CM(c).set_scroll_region = tgetstr("cs", &bufptr);
1107 * Initialize screen editing information.
1110 /* adding to the screen */
1111 TTY_SE(c).ins_line = tgetstr("al", &bufptr);
1112 TTY_SE(c).multi_ins_line = tgetstr("AL", &bufptr);
1113 TTY_SE(c).repeat = tgetstr("rp", &bufptr);
1114 TTY_SE(c).begin_ins_mode = tgetstr("im", &bufptr);
1115 TTY_SE(c).end_ins_mode = tgetstr("ei", &bufptr);
1116 TTY_SE(c).ins_char = tgetstr("ic", &bufptr);
1117 TTY_SE(c).multi_ins_char = tgetstr("IC", &bufptr);
1118 TTY_SE(c).insert_pad = tgetstr("ip", &bufptr);
1120 /* deleting from the screen */
1121 TTY_SE(c).clr_frame = tgetstr("cl", &bufptr);
1122 TTY_SE(c).clr_from_cursor = tgetstr("cd", &bufptr);
1123 TTY_SE(c).clr_to_eol = tgetstr("ce", &bufptr);
1124 TTY_SE(c).del_line = tgetstr("dl", &bufptr);
1125 TTY_SE(c).multi_del_line = tgetstr("DL", &bufptr);
1126 TTY_SE(c).del_char = tgetstr("dc", &bufptr);
1127 TTY_SE(c).multi_del_char = tgetstr("DC", &bufptr);
1128 TTY_SE(c).begin_del_mode = tgetstr("dm", &bufptr);
1129 TTY_SE(c).end_del_mode = tgetstr("ed", &bufptr);
1130 TTY_SE(c).erase_at_cursor = tgetstr("ec", &bufptr);
1133 * Initialize screen display information.
1135 TTY_SD(c).begin_standout_ = tgetstr("so", &bufptr);
1136 TTY_SD(c).end_standout_ = tgetstr("se", &bufptr);
1137 TTY_SD(c).begin_underline_ = tgetstr("us", &bufptr);
1138 TTY_SD(c).end_underline_ = tgetstr("ue", &bufptr);
1139 TTY_SD(c).begin_alternate_ = tgetstr("as", &bufptr);
1140 TTY_SD(c).end_alternate_ = tgetstr("ae", &bufptr);
1141 TTY_SD(c).turn_on_reverse_ = tgetstr("mr", &bufptr);
1142 TTY_SD(c).turn_on_blinking_ = tgetstr("mb", &bufptr);
1143 TTY_SD(c).turn_on_bold_ = tgetstr("md", &bufptr);
1144 TTY_SD(c).turn_on_dim_ = tgetstr("mh", &bufptr);
1145 TTY_SD(c).turn_off_attributes_ = tgetstr("me", &bufptr);
1146 TTY_SD(c).set_aback_ = tgetstr("AB", &bufptr);
1147 TTY_SD(c).set_afore_ = tgetstr("AF", &bufptr);
1148 TTY_SD(c).orig_pair_ = tgetstr("op", &bufptr);
1150 TTY_SD(c).visual_bell_ = tgetstr("vb", &bufptr);
1151 TTY_SD(c).audio_bell_ = tgetstr("bl", &bufptr);
1152 if (!TTY_SD(c).audio_bell_) {
1153 /* If audio_bell doesn't get set, then assume C-g. This is gross and
1154 ugly but is what Emacs has done from time immortal. */
1155 TTY_SD(c).audio_bell_ = "\07";
1158 TTY_SD(c).cursor_visible_ = tgetstr("ve", &bufptr);
1159 TTY_SD(c).cursor_normal_ = tgetstr("vs", &bufptr);
1160 TTY_SD(c).init_motion_ = tgetstr("ti", &bufptr);
1161 TTY_SD(c).end_motion_ = tgetstr("te", &bufptr);
1162 TTY_SD(c).keypad_on_ = tgetstr("ks", &bufptr);
1163 TTY_SD(c).keypad_off_ = tgetstr("ke", &bufptr);
1166 * Initialize additional terminal information.
1168 TTY_FLAGS(c).must_write_spaces = tgetflag("in");
1169 TTY_FLAGS(c).insert_mode_motion = tgetflag("mi");
1170 TTY_FLAGS(c).standout_motion = tgetflag("ms");
1171 TTY_FLAGS(c).memory_above_frame = tgetflag("da");
1172 TTY_FLAGS(c).memory_below_frame = tgetflag("db");
1173 TTY_FLAGS(c).standout_width = tgetnum("sg");
1174 TTY_FLAGS(c).underline_width = tgetnum("ug");
1176 if (TTY_FLAGS(c).standout_width == -1)
1177 TTY_FLAGS(c).standout_width = 0;
1178 if (TTY_FLAGS(c).underline_width == -1)
1179 TTY_FLAGS(c).underline_width = 0;
1181 TTY_FLAGS(c).meta_key =
1182 eight_bit_tty(d) ? tgetflag("km") || tgetflag("MT") ? 1 : 2 : 0;
1185 * Setup the costs tables for this tty console.
1191 * Initialize local flags.
1194 standout_mode_on = 0;
1195 underline_mode_on = 0;
1196 alternate_mode_on = 0;
1201 * Attempt to initialize the function_key_map to
1202 * some kind of sensible value
1205 term_get_fkeys(c->function_key_map, &bufptr);
1208 /* check for ANSI set-foreground and set-background strings,
1209 and assume color if so.
1211 #### we should support the other (non-ANSI) ways of specifying
1215 char *SXE_UNUSED(fooptr) = foobuf;
1216 int colors = tgetnum("Co");
1217 int pairs = tgetnum("pa");
1218 if ((TTY_SD(c).set_aback_ && TTY_SD(c).set_afore_) ||
1219 ((colors > 0) || (pairs > 0))) {
1220 DEVICE_CLASS(d) = Qcolor;
1221 /* These cases should be rare. Most termcap
1222 * entries will have both colors and pairs
1223 * defined. However let's play it safe :)
1225 if ( colors == 0 && pairs > 0 ) {
1226 /* try to determine colors from pairs.
1227 * Most common cases first.
1231 else if ( pairs == 256 )
1234 /* Naive isqrt algorithm. */
1236 colors*colors < pairs;
1239 if ( pairs == 0 && colors > 0 )
1240 /* try to determine pairs from colors */
1241 pairs = colors * colors;
1242 CONSOLE_TTY_DATA(c)->maxcolors = colors;
1243 CONSOLE_TTY_DATA(c)->maxpairs = pairs;
1245 /* Most terminals that report 8 colors will
1246 make bold brighter */
1247 CONSOLE_TTY_DATA(c)->is_bold_brighter =
1248 (colors == 8 && TTY_SD(c).turn_on_bold_ )
1250 /* Most terminals do not have brighter background
1251 colors, however the REAL X11R6 xterm and rxvt do
1252 allow reverse to achieve this. Sadly there
1253 is no way to autodetect. Since the Hue is very
1254 different on the 8 basic colors and most users
1255 will hopefully use xterm or rxvt.
1257 CONSOLE_TTY_DATA(c)->is_reverse_brighter =
1258 (colors == 8 && TTY_SD(c).turn_on_reverse_)
1261 else if ( force_colorterm ) {
1262 DEVICE_CLASS(d) = Qcolor;
1263 TTY_SD(c).set_aback_ = "\e[4%p1%dm";
1264 TTY_SD(c).set_afore_ = "\e[3%p1%dm";
1265 CONSOLE_TTY_DATA(c)->maxcolors = 8;
1266 CONSOLE_TTY_DATA(c)->maxpairs = 64;
1267 CONSOLE_TTY_DATA(c)->is_bold_brighter =
1268 (TTY_SD(c).turn_on_bold_ ) ? 1 : 0;
1269 if ( ! TTY_SD(c).orig_pair_ )
1270 TTY_SD(c).orig_pair_ = "\e[39;49m";
1273 DEVICE_CLASS(d) = Qmono;
1274 CONSOLE_TTY_DATA(c)->maxcolors = 2;
1275 CONSOLE_TTY_DATA(c)->maxpairs = 4;
1276 CONSOLE_TTY_DATA(c)->is_bold_brighter = 0;
1277 CONSOLE_TTY_DATA(c)->is_reverse_brighter = 0;
1281 return TTY_INIT_SUCCESS;
1285 /* tgetstr will disobey this qualifier */
1290 /* Termcap capability names that correspond directly to X keysyms.
1291 Some of these (marked "terminfo") aren't supplied by old-style
1292 (Berkeley) termcap entries. They're listed in X keysym order;
1293 except we put the keypad keys first, so that if they clash with
1294 other keys (as on the IBM PC keyboard) they get overridden.
1297 static struct fkey_table keys[] = {
1298 {"kh", "home"}, /* termcap */
1299 {"kl", "left"}, /* termcap */
1300 {"ku", "up"}, /* termcap */
1301 {"kr", "right"}, /* termcap */
1302 {"kd", "down"}, /* termcap */
1303 {"%8", "prior"}, /* terminfo */
1304 {"%5", "next"}, /* terminfo */
1305 {"@7", "end"}, /* terminfo */
1306 {"@1", "begin"}, /* terminfo */
1307 {"*6", "select"}, /* terminfo */
1308 {"%9", "print"}, /* terminfo */
1309 {"@4", "execute"}, /* terminfo --- actually the `command' key */
1311 * "insert" --- see below
1313 {"&8", "undo"}, /* terminfo */
1314 {"%0", "redo"}, /* terminfo */
1315 {"%7", "menu"}, /* terminfo --- actually the `options' key */
1316 {"@0", "find"}, /* terminfo */
1317 {"@2", "cancel"}, /* terminfo */
1318 {"%1", "help"}, /* terminfo */
1320 * "break" goes here, but can't be reliably intercepted with termcap
1322 {"&4", "reset"}, /* terminfo --- actually `restart' */
1324 * "system" and "user" --- no termcaps
1326 {"kE", "clearline"}, /* terminfo */
1327 {"kA", "insertline"}, /* terminfo */
1328 {"kL", "deleteline"}, /* terminfo */
1329 {"kI", "insertchar"}, /* terminfo */
1330 {"kD", "delete"}, /* terminfo */
1331 {"kB", "backtab"}, /* terminfo */
1333 * "kp-backtab", "kp-space", "kp-tab" --- no termcaps
1335 {"@8", "kp-enter"}, /* terminfo */
1337 * "kp-f1", "kp-f2", "kp-f3" "kp-f4",
1338 * "kp-multiply", "kp-add", "kp-separator",
1339 * "kp-subtract", "kp-decimal", "kp-divide", "kp-0";
1340 * --- no termcaps for any of these.
1342 {"K4", "kp-1"}, /* terminfo */
1344 * "kp-2" --- no termcap
1346 {"K5", "kp-3"}, /* terminfo */
1348 * "kp-4" --- no termcap
1350 {"K2", "kp-5"}, /* terminfo */
1352 * "kp-6" --- no termcap
1354 {"K1", "kp-7"}, /* terminfo */
1356 * "kp-8" --- no termcap
1358 {"K3", "kp-9"}, /* terminfo */
1360 * "kp-equal" --- no termcap
1373 static char **term_get_fkeys_arg;
1375 static Lisp_Object term_get_fkeys_1(Lisp_Object keymap);
1376 static Lisp_Object term_get_fkeys_error(Lisp_Object err, Lisp_Object arg);
1378 /* Find the escape codes sent by the function keys for Vfunction_key_map.
1379 This function scans the termcap function key sequence entries, and
1380 adds entries to Vfunction_key_map for each function key it finds. */
1382 static void term_get_fkeys(Lisp_Object keymap, char **address)
1384 /* We run the body of the function (term_get_fkeys_1) and ignore all Lisp
1385 errors during the call. The only errors should be from Fdefine_key
1386 when given a key sequence containing an invalid prefix key. If the
1387 termcap defines function keys which use a prefix that is already bound
1388 to a command by the default bindings, we should silently ignore that
1389 function key specification, rather than giving the user an error and
1390 refusing to run at all on such a terminal. */
1392 term_get_fkeys_arg = address;
1394 condition_case_1(Qerror,
1395 term_get_fkeys_1, keymap, term_get_fkeys_error, Qnil);
1398 static Lisp_Object term_get_fkeys_error(Lisp_Object err, Lisp_Object arg)
1403 static Lisp_Object term_get_fkeys_1(Lisp_Object function_key_map)
1407 char **address = term_get_fkeys_arg;
1409 for (i = 0; i < countof(keys); i++) {
1410 const char *sequence = tgetstr(keys[i].cap, address);
1412 Fdefine_key(function_key_map,
1413 build_ext_string(sequence, Qbinary),
1414 vector1(intern(keys[i].name)));
1418 /* The uses of the "k0" capability are inconsistent; sometimes it
1419 describes F10, whereas othertimes it describes F0 and "k;" describes F10.
1420 We will attempt to politely accommodate both systems by testing for
1421 "k;", and if it is present, assuming that "k0" denotes F0, otherwise F10.
1424 const char *k_semi = tgetstr("k;", address);
1425 const char *k0 = tgetstr("k0", address);
1428 Fdefine_key(function_key_map,
1429 build_ext_string(k_semi, Qbinary),
1430 vector1(intern("f10")));
1433 Fdefine_key(function_key_map,
1434 build_ext_string(k0, Qbinary),
1435 vector1(intern(k_semi ? "f0" : "f10")));
1438 /* Set up cookies for numbered function keys above f10. */
1440 char fcap[3], fkey[4];
1444 for (i = 11; i < 64; i++) {
1446 fcap[1] = '1' + i - 11;
1448 fcap[1] = 'A' + i - 20;
1450 fcap[1] = 'a' + i - 46;
1453 char *sequence = tgetstr(fcap, address);
1455 int sz = snprintf(fkey, sizeof(fkey), "f%d", i);
1456 assert(sz >= 0 && (size_t)sz < sizeof(fkey));
1457 Fdefine_key(function_key_map,
1458 build_ext_string(sequence,
1460 vector1(intern(fkey)));
1467 * Various mappings to try and get a better fit.
1469 #define CONDITIONAL_REASSIGN(cap1, cap2, keyname) do { \
1470 if (!tgetstr (cap1, address)) \
1472 char *sequence = tgetstr (cap2, address); \
1474 Fdefine_key (function_key_map, \
1475 build_ext_string (sequence, Qbinary), \
1476 vector1 (intern (keyname))); \
1480 /* if there's no key_next keycap, map key_npage to `next' keysym */
1481 CONDITIONAL_REASSIGN("%5", "kN", "next");
1482 /* if there's no key_prev keycap, map key_ppage to `previous' keysym */
1483 CONDITIONAL_REASSIGN("%8", "kP", "prior");
1484 /* if there's no key_dc keycap, map key_ic to `insert' keysym */
1485 CONDITIONAL_REASSIGN("kD", "kI", "insert");
1487 /* IBM has their own non-standard dialect of terminfo.
1488 If the standard name isn't found, try the IBM name. */
1489 CONDITIONAL_REASSIGN("kB", "KO", "backtab");
1490 CONDITIONAL_REASSIGN("@4", "kJ", "execute"); /* actually "action" */
1491 CONDITIONAL_REASSIGN("@4", "kc", "execute"); /* actually "command" */
1492 CONDITIONAL_REASSIGN("%7", "ki", "menu");
1493 CONDITIONAL_REASSIGN("@7", "kw", "end");
1494 CONDITIONAL_REASSIGN("F1", "k<", "f11");
1495 CONDITIONAL_REASSIGN("F2", "k>", "f12");
1496 CONDITIONAL_REASSIGN("%1", "kq", "help");
1497 CONDITIONAL_REASSIGN("*6", "kU", "select");
1498 #undef CONDITIONAL_REASSIGN
1503 /************************************************************************/
1504 /* initialization */
1505 /************************************************************************/
1507 void console_type_create_redisplay_tty(void)
1509 /* redisplay methods */
1510 CONSOLE_HAS_METHOD(tty, text_width);
1511 CONSOLE_HAS_METHOD(tty, output_display_block);
1512 CONSOLE_HAS_METHOD(tty, output_vertical_divider);
1513 CONSOLE_HAS_METHOD(tty, divider_height);
1514 CONSOLE_HAS_METHOD(tty, eol_cursor_width);
1515 CONSOLE_HAS_METHOD(tty, clear_to_window_end);
1516 CONSOLE_HAS_METHOD(tty, clear_region);
1517 CONSOLE_HAS_METHOD(tty, clear_frame);
1518 CONSOLE_HAS_METHOD(tty, frame_output_begin);
1519 CONSOLE_HAS_METHOD(tty, frame_output_end);
1520 CONSOLE_HAS_METHOD(tty, flash);
1521 CONSOLE_HAS_METHOD(tty, ring_bell);
1522 CONSOLE_HAS_METHOD(tty, set_final_cursor_coords);