vt: add support for smput/rmput escape codes

Support "\e[?1049h" and "\e[?1049l" escape codes.
This patch allows programs to enter and leave alternate screens.
This feature is widely available in graphical terminal emulators and mostly
used by fullscreen terminal-based user interfaces such as text editors.
Most editors such as vim and nano assume this escape code in not supported
and will not try to print the escape sequence if TERM=linux.
To try out this patch, run `TERM=xterm-256color vim` inside a VT.

Signed-off-by: Calixte Pernot <calixte.pernot@grenoble-inp.org>
Link: https://lore.kernel.org/r/20250825125607.2478-3-calixte.pernot@grenoble-inp.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Calixte Pernot 2025-08-25 14:56:09 +02:00 committed by Greg Kroah-Hartman
parent cc4d900d0d
commit 23743ba647
2 changed files with 61 additions and 0 deletions

View file

@ -141,6 +141,7 @@ static const struct consw *con_driver_map[MAX_NR_CONSOLES];
static int con_open(struct tty_struct *, struct file *); static int con_open(struct tty_struct *, struct file *);
static void vc_init(struct vc_data *vc, int do_clear); static void vc_init(struct vc_data *vc, int do_clear);
static void gotoxy(struct vc_data *vc, int new_x, int new_y); static void gotoxy(struct vc_data *vc, int new_x, int new_y);
static void restore_cur(struct vc_data *vc);
static void save_cur(struct vc_data *vc); static void save_cur(struct vc_data *vc);
static void reset_terminal(struct vc_data *vc, int do_clear); static void reset_terminal(struct vc_data *vc, int do_clear);
static void con_flush_chars(struct tty_struct *tty); static void con_flush_chars(struct tty_struct *tty);
@ -1341,6 +1342,10 @@ struct vc_data *vc_deallocate(unsigned int currcons)
kfree(vc->vc_screenbuf); kfree(vc->vc_screenbuf);
vc_cons[currcons].d = NULL; vc_cons[currcons].d = NULL;
} }
if (vc->vc_saved_screen != NULL) {
kfree(vc->vc_saved_screen);
vc->vc_saved_screen = NULL;
}
return vc; return vc;
} }
@ -1875,6 +1880,45 @@ static int get_bracketed_paste(struct tty_struct *tty)
return vc->vc_bracketed_paste; return vc->vc_bracketed_paste;
} }
/* console_lock is held */
static void enter_alt_screen(struct vc_data *vc)
{
unsigned int size = vc->vc_rows * vc->vc_cols * 2;
if (vc->vc_saved_screen != NULL)
return; /* Already inside an alt-screen */
vc->vc_saved_screen = kmemdup((u16 *)vc->vc_origin, size, GFP_KERNEL);
if (vc->vc_saved_screen == NULL)
return;
vc->vc_saved_rows = vc->vc_rows;
vc->vc_saved_cols = vc->vc_cols;
save_cur(vc);
/* clear entire screen */
csi_J(vc, CSI_J_FULL);
}
/* console_lock is held */
static void leave_alt_screen(struct vc_data *vc)
{
unsigned int rows = min(vc->vc_saved_rows, vc->vc_rows);
unsigned int cols = min(vc->vc_saved_cols, vc->vc_cols);
u16 *src, *dest;
if (vc->vc_saved_screen == NULL)
return; /* Not inside an alt-screen */
for (unsigned int r = 0; r < rows; r++) {
src = vc->vc_saved_screen + r * vc->vc_saved_cols;
dest = ((u16 *)vc->vc_origin) + r * vc->vc_cols;
memcpy(dest, src, 2 * cols);
}
restore_cur(vc);
/* Update the entire screen */
if (con_should_update(vc))
do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
kfree(vc->vc_saved_screen);
vc->vc_saved_screen = NULL;
}
enum { enum {
CSI_DEC_hl_CURSOR_KEYS = 1, /* CKM: cursor keys send ^[Ox/^[[x */ CSI_DEC_hl_CURSOR_KEYS = 1, /* CKM: cursor keys send ^[Ox/^[[x */
CSI_DEC_hl_132_COLUMNS = 3, /* COLM: 80/132 mode switch */ CSI_DEC_hl_132_COLUMNS = 3, /* COLM: 80/132 mode switch */
@ -1885,6 +1929,7 @@ enum {
CSI_DEC_hl_MOUSE_X10 = 9, CSI_DEC_hl_MOUSE_X10 = 9,
CSI_DEC_hl_SHOW_CURSOR = 25, /* TCEM */ CSI_DEC_hl_SHOW_CURSOR = 25, /* TCEM */
CSI_DEC_hl_MOUSE_VT200 = 1000, CSI_DEC_hl_MOUSE_VT200 = 1000,
CSI_DEC_hl_ALT_SCREEN = 1049,
CSI_DEC_hl_BRACKETED_PASTE = 2004, CSI_DEC_hl_BRACKETED_PASTE = 2004,
}; };
@ -1941,6 +1986,12 @@ static void csi_DEC_hl(struct vc_data *vc, bool on_off)
case CSI_DEC_hl_BRACKETED_PASTE: case CSI_DEC_hl_BRACKETED_PASTE:
vc->vc_bracketed_paste = on_off; vc->vc_bracketed_paste = on_off;
break; break;
case CSI_DEC_hl_ALT_SCREEN:
if (on_off)
enter_alt_screen(vc);
else
leave_alt_screen(vc);
break;
} }
} }
@ -2179,6 +2230,13 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
vc->vc_deccm = global_cursor_default; vc->vc_deccm = global_cursor_default;
vc->vc_decim = 0; vc->vc_decim = 0;
if (vc->vc_saved_screen != NULL) {
kfree(vc->vc_saved_screen);
vc->vc_saved_screen = NULL;
vc->vc_saved_rows = 0;
vc->vc_saved_cols = 0;
}
vt_reset_keyboard(vc->vc_num); vt_reset_keyboard(vc->vc_num);
vc->vc_cursor_type = cur_default; vc->vc_cursor_type = cur_default;

View file

@ -159,6 +159,9 @@ struct vc_data {
struct uni_pagedict *uni_pagedict; struct uni_pagedict *uni_pagedict;
struct uni_pagedict **uni_pagedict_loc; /* [!] Location of uni_pagedict variable for this console */ struct uni_pagedict **uni_pagedict_loc; /* [!] Location of uni_pagedict variable for this console */
u32 **vc_uni_lines; /* unicode screen content */ u32 **vc_uni_lines; /* unicode screen content */
u16 *vc_saved_screen;
unsigned int vc_saved_cols;
unsigned int vc_saved_rows;
/* additional information is in vt_kern.h */ /* additional information is in vt_kern.h */
}; };