* [PATCH 0/2] vt: bracketed paste and cursor position
@ 2025-05-14 1:52 Nicolas Pitre
2025-05-14 1:52 ` [PATCH 1/2] vt: bracketed paste support Nicolas Pitre
2025-05-14 1:52 ` [PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position Nicolas Pitre
0 siblings, 2 replies; 6+ messages in thread
From: Nicolas Pitre @ 2025-05-14 1:52 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby; +Cc: Nicolas Pitre, linux-serial, linux-kernel
A different kind of VT console update this time. These patches:
- add bracketed paste support to the VT console
- overcome a /dev/vcsa limitation with cursor position retrieval
They are submitted together to avoid merge conflicts as they touch some
common code.
Tested on top of linux v6.15.0-rc4.
diffstat:
drivers/tty/vt/selection.c | 35 ++++++++++++++++++++++++++++++----
drivers/tty/vt/vt.c | 37 ++++++++++++++++++++++++++++++++++++
include/linux/console_struct.h | 1 +
include/uapi/linux/tiocl.h | 5 +++++
4 files changed, 74 insertions(+), 4 deletions(-)
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/2] vt: bracketed paste support
2025-05-14 1:52 [PATCH 0/2] vt: bracketed paste and cursor position Nicolas Pitre
@ 2025-05-14 1:52 ` Nicolas Pitre
2025-05-14 5:43 ` Jiri Slaby
2025-05-14 1:52 ` [PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position Nicolas Pitre
1 sibling, 1 reply; 6+ messages in thread
From: Nicolas Pitre @ 2025-05-14 1:52 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby; +Cc: Nicolas Pitre, linux-serial, linux-kernel
From: Nicolas Pitre <npitre@baylibre.com>
This is comprised of 3 aspects:
- Take note of when applications advertise bracketed paste support via
"\e[?2004h" and "\e[?2004l".
- Insert bracketed paste markers ("\e[200~" and "\e[201~") around pasted
content in paste_selection() when bracketed paste is active.
- Add TIOCL_GETBRACKETEDPASTE to return bracketed paste status so user
space daemons implementing cut-and-paste functionality (e.g. gpm,
BRLTTY) may know when to insert bracketed paste markers.
Link: https://en.wikipedia.org/wiki/Bracketed-paste
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
---
drivers/tty/vt/selection.c | 35 ++++++++++++++++++++++++++++++----
drivers/tty/vt/vt.c | 15 +++++++++++++++
include/linux/console_struct.h | 1 +
include/uapi/linux/tiocl.h | 1 +
4 files changed, 48 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c
index 791e2f1f7c0b..ac86b82411a8 100644
--- a/drivers/tty/vt/selection.c
+++ b/drivers/tty/vt/selection.c
@@ -403,6 +403,12 @@ int paste_selection(struct tty_struct *tty)
DECLARE_WAITQUEUE(wait, current);
int ret = 0;
+ bool bp = vc->vc_bracketed_paste;
+ static const char bracketed_paste_start[] = "\033[200~";
+ static const char bracketed_paste_end[] = "\033[201~";
+ const char *bps = bp ? bracketed_paste_start : NULL;
+ const char *bpe = bp ? bracketed_paste_end : NULL;
+
console_lock();
poke_blanked_console();
console_unlock();
@@ -414,7 +420,7 @@ int paste_selection(struct tty_struct *tty)
add_wait_queue(&vc->paste_wait, &wait);
mutex_lock(&vc_sel.lock);
- while (vc_sel.buffer && vc_sel.buf_len > pasted) {
+ while (vc_sel.buffer && (vc_sel.buf_len > pasted || bpe)) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
ret = -EINTR;
@@ -427,10 +433,31 @@ int paste_selection(struct tty_struct *tty)
continue;
}
__set_current_state(TASK_RUNNING);
+
+ if (bps) {
+ count = tty_ldisc_receive_buf(ld, bps, NULL, strlen(bps));
+ bps += count;
+ if (*bps == '\0')
+ bps = NULL;
+ else
+ continue;
+ }
+
count = vc_sel.buf_len - pasted;
- count = tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted, NULL,
- count);
- pasted += count;
+ if (count) {
+ count = tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted,
+ NULL, count);
+ pasted += count;
+ if (vc_sel.buf_len > pasted)
+ continue;
+ }
+
+ if (bpe) {
+ count = tty_ldisc_receive_buf(ld, bpe, NULL, strlen(bpe));
+ bpe += count;
+ if (*bpe == '\0')
+ bpe = NULL;
+ }
}
mutex_unlock(&vc_sel.lock);
remove_wait_queue(&vc->paste_wait, &wait);
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index efb761454166..ed39d9cb4432 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1870,6 +1870,14 @@ int mouse_reporting(void)
return vc_cons[fg_console].d->vc_report_mouse;
}
+/* invoked via ioctl(TIOCLINUX) */
+static int get_bracketed_paste(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+
+ return vc->vc_bracketed_paste;
+}
+
enum {
CSI_DEC_hl_CURSOR_KEYS = 1, /* CKM: cursor keys send ^[Ox/^[[x */
CSI_DEC_hl_132_COLUMNS = 3, /* COLM: 80/132 mode switch */
@@ -1880,6 +1888,7 @@ enum {
CSI_DEC_hl_MOUSE_X10 = 9,
CSI_DEC_hl_SHOW_CURSOR = 25, /* TCEM */
CSI_DEC_hl_MOUSE_VT200 = 1000,
+ CSI_DEC_hl_BRACKETED_PASTE = 2004,
};
/* console_lock is held */
@@ -1932,6 +1941,9 @@ static void csi_DEC_hl(struct vc_data *vc, bool on_off)
case CSI_DEC_hl_MOUSE_VT200:
vc->vc_report_mouse = on_off ? 2 : 0;
break;
+ case CSI_DEC_hl_BRACKETED_PASTE:
+ vc->vc_bracketed_paste = on_off;
+ break;
}
}
@@ -2157,6 +2169,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
vc->state.charset = 0;
vc->vc_need_wrap = 0;
vc->vc_report_mouse = 0;
+ vc->vc_bracketed_paste = 0;
vc->vc_utf = default_utf8;
vc->vc_utf_count = 0;
@@ -3483,6 +3496,8 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
break;
case TIOCL_BLANKEDSCREEN:
return console_blanked;
+ case TIOCL_GETBRACKETEDPASTE:
+ return get_bracketed_paste(tty);
default:
return -EINVAL;
}
diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
index 20f564e98552..59b4fec5f254 100644
--- a/include/linux/console_struct.h
+++ b/include/linux/console_struct.h
@@ -145,6 +145,7 @@ struct vc_data {
unsigned int vc_need_wrap : 1;
unsigned int vc_can_do_color : 1;
unsigned int vc_report_mouse : 2;
+ unsigned int vc_bracketed_paste : 1;
unsigned char vc_utf : 1; /* Unicode UTF-8 encoding */
unsigned char vc_utf_count;
int vc_utf_char;
diff --git a/include/uapi/linux/tiocl.h b/include/uapi/linux/tiocl.h
index b32acc229024..88faba506c3d 100644
--- a/include/uapi/linux/tiocl.h
+++ b/include/uapi/linux/tiocl.h
@@ -36,5 +36,6 @@ struct tiocl_selection {
#define TIOCL_BLANKSCREEN 14 /* keep screen blank even if a key is pressed */
#define TIOCL_BLANKEDSCREEN 15 /* return which vt was blanked */
#define TIOCL_GETKMSGREDIRECT 17 /* get the vt the kernel messages are restricted to */
+#define TIOCL_GETBRACKETEDPASTE 18 /* get whether paste may be bracketed */
#endif /* _LINUX_TIOCL_H */
--
2.49.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position
2025-05-14 1:52 [PATCH 0/2] vt: bracketed paste and cursor position Nicolas Pitre
2025-05-14 1:52 ` [PATCH 1/2] vt: bracketed paste support Nicolas Pitre
@ 2025-05-14 1:52 ` Nicolas Pitre
2025-05-14 5:57 ` Jiri Slaby
1 sibling, 1 reply; 6+ messages in thread
From: Nicolas Pitre @ 2025-05-14 1:52 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby; +Cc: Nicolas Pitre, linux-serial, linux-kernel
From: Nicolas Pitre <npitre@baylibre.com>
The screen cursor position (as well as the screen dimension) is
available through the /dev/vcsa interface already. However the vcsa
header format uses single-byte fields therefore those values are
clamped to 255.
As surprizing as this may seem, some people do use 240-column 67-row
screens (a 1920x1080 monitor with 8x16 pixel fonts) which is getting
close to the limit. Monitors with higher resolution are not uncommon
these days (3840x2160 producing a 480x135 character display) and it is
just a matter of time before someone with, say, a braille display using
the Linux VT console and BRLTTY on such a screen reports a bug about
missing and oddly misaligned screen content.
The screen dimension may already be obtained using TIOCGWINSZ but there
is no such alternatives for obtaining the cursor position. Querying it by
writing "\033[6n" to a tty and reading back the result by anything else
than the actual application using that tty is not possible.
So let's work around this limitation by adding TIOCL_GETCURSORPOS as a
fallback method to get the cursor position when /dev/vcsa reports 255.
Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
---
drivers/tty/vt/vt.c | 22 ++++++++++++++++++++++
include/uapi/linux/tiocl.h | 4 ++++
2 files changed, 26 insertions(+)
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index ed39d9cb4432..60a1fee17198 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -152,6 +152,7 @@ static void con_driver_unregister_callback(struct work_struct *ignored);
static void blank_screen_t(struct timer_list *unused);
static void set_palette(struct vc_data *vc);
static void unblank_screen(void);
+static int get_cursor_pos(struct tty_struct *tty);
#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1)
@@ -3498,6 +3499,8 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
return console_blanked;
case TIOCL_GETBRACKETEDPASTE:
return get_bracketed_paste(tty);
+ case TIOCL_GETCURSORPOS:
+ return get_cursor_pos(tty);
default:
return -EINVAL;
}
@@ -4991,6 +4994,25 @@ void putconsxy(struct vc_data *vc, unsigned char xy[static const 2])
set_cursor(vc);
}
+/* invoked via ioctl(TIOCLINUX) */
+static int get_cursor_pos(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+ unsigned int x, y;
+
+ console_lock();
+ x = vc->state.x;
+ y = vc->state.y;
+ console_unlock();
+
+ /*
+ * Clamp x to 16 bits, y to 15 bits. A display larger than 65535x32767
+ * characters won't be a concern for the foreseeable future.
+ * Bit 31 is reserved to represent negative error codes elsewhere.
+ */
+ return min(x, 0xFFFFu) | (min(y, 0x7FFFu) << 16);
+}
+
u16 vcs_scr_readw(const struct vc_data *vc, const u16 *org)
{
if ((unsigned long)org == vc->vc_pos && softcursor_original != -1)
diff --git a/include/uapi/linux/tiocl.h b/include/uapi/linux/tiocl.h
index 88faba506c3d..51d33d55abf5 100644
--- a/include/uapi/linux/tiocl.h
+++ b/include/uapi/linux/tiocl.h
@@ -38,4 +38,8 @@ struct tiocl_selection {
#define TIOCL_GETKMSGREDIRECT 17 /* get the vt the kernel messages are restricted to */
#define TIOCL_GETBRACKETEDPASTE 18 /* get whether paste may be bracketed */
+#define TIOCL_GETCURSORPOS 20 /* Get screen cursor position */
+ /* If return value >= 0: x = lower 16 bits, y = upper 16 bits. */
+ /* Negative error code otherwise. */
+
#endif /* _LINUX_TIOCL_H */
--
2.49.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] vt: bracketed paste support
2025-05-14 1:52 ` [PATCH 1/2] vt: bracketed paste support Nicolas Pitre
@ 2025-05-14 5:43 ` Jiri Slaby
0 siblings, 0 replies; 6+ messages in thread
From: Jiri Slaby @ 2025-05-14 5:43 UTC (permalink / raw)
To: Nicolas Pitre, Greg Kroah-Hartman
Cc: Nicolas Pitre, linux-serial, linux-kernel
On 14. 05. 25, 3:52, Nicolas Pitre wrote:
> From: Nicolas Pitre <npitre@baylibre.com>
>
> This is comprised of 3 aspects:
>
> - Take note of when applications advertise bracketed paste support via
> "\e[?2004h" and "\e[?2004l".
>
> - Insert bracketed paste markers ("\e[200~" and "\e[201~") around pasted
> content in paste_selection() when bracketed paste is active.
>
> - Add TIOCL_GETBRACKETEDPASTE to return bracketed paste status so user
> space daemons implementing cut-and-paste functionality (e.g. gpm,
> BRLTTY) may know when to insert bracketed paste markers.
>
> Link: https://en.wikipedia.org/wiki/Bracketed-paste
LGTM.
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
In case (and only then) you resend for some reason, I have some nits below.
(And a question at the bottom.)
> Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
> ---
> drivers/tty/vt/selection.c | 35 ++++++++++++++++++++++++++++++----
> drivers/tty/vt/vt.c | 15 +++++++++++++++
> include/linux/console_struct.h | 1 +
> include/uapi/linux/tiocl.h | 1 +
> 4 files changed, 48 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c
> index 791e2f1f7c0b..ac86b82411a8 100644
> --- a/drivers/tty/vt/selection.c
> +++ b/drivers/tty/vt/selection.c
...
> @@ -427,10 +433,31 @@ int paste_selection(struct tty_struct *tty)
> continue;
> }
> __set_current_state(TASK_RUNNING);
> +
> + if (bps) {
> + count = tty_ldisc_receive_buf(ld, bps, NULL, strlen(bps));
> + bps += count;
> + if (*bps == '\0')
> + bps = NULL;
> + else
> + continue;
This could have been simpler:
bps += tty_ldisc_receive_buf(ld, bps, NULL, strlen(bps));
if (*bps != '\0')
continue;
bps = NULL;
> + }
> +
> count = vc_sel.buf_len - pasted;
> - count = tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted, NULL,
> - count);
> - pasted += count;
> + if (count) {
> + count = tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted,
> + NULL, count);
> + pasted += count;
Same here for pasted.
> + if (vc_sel.buf_len > pasted)
> + continue;
> + }
> +
> + if (bpe) {
> + count = tty_ldisc_receive_buf(ld, bpe, NULL, strlen(bpe));
> + bpe += count;
And bpe.
> + if (*bpe == '\0')
> + bpe = NULL;
> + }
> }
> mutex_unlock(&vc_sel.lock);
> remove_wait_queue(&vc->paste_wait, &wait);
> diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
> index efb761454166..ed39d9cb4432 100644
> --- a/drivers/tty/vt/vt.c
> +++ b/drivers/tty/vt/vt.c
> @@ -1870,6 +1870,14 @@ int mouse_reporting(void)
> return vc_cons[fg_console].d->vc_report_mouse;
> }
>
> +/* invoked via ioctl(TIOCLINUX) */
> +static int get_bracketed_paste(struct tty_struct *tty)
vc_bracketed_paste is unsigned. (I understand tioclinux() returns an int.)
> +{
> + struct vc_data *vc = tty->driver_data;
> +
> + return vc->vc_bracketed_paste;
> +}
> +
> enum {
> CSI_DEC_hl_CURSOR_KEYS = 1, /* CKM: cursor keys send ^[Ox/^[[x */
> CSI_DEC_hl_132_COLUMNS = 3, /* COLM: 80/132 mode switch */
...
> --- a/include/uapi/linux/tiocl.h
> +++ b/include/uapi/linux/tiocl.h
> @@ -36,5 +36,6 @@ struct tiocl_selection {
> #define TIOCL_BLANKSCREEN 14 /* keep screen blank even if a key is pressed */
> #define TIOCL_BLANKEDSCREEN 15 /* return which vt was blanked */
> #define TIOCL_GETKMSGREDIRECT 17 /* get the vt the kernel messages are restricted to */
> +#define TIOCL_GETBRACKETEDPASTE 18 /* get whether paste may be bracketed */
Do you plan on updating man-pages too?
https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man/man2const/TIOCLINUX.2const
thanks,
--
js
suse labs
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position
2025-05-14 1:52 ` [PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position Nicolas Pitre
@ 2025-05-14 5:57 ` Jiri Slaby
2025-05-14 13:25 ` Nicolas Pitre
0 siblings, 1 reply; 6+ messages in thread
From: Jiri Slaby @ 2025-05-14 5:57 UTC (permalink / raw)
To: Nicolas Pitre, Greg Kroah-Hartman
Cc: Nicolas Pitre, linux-serial, linux-kernel
On 14. 05. 25, 3:52, Nicolas Pitre wrote:
...
> So let's work around this limitation by adding TIOCL_GETCURSORPOS as a
> fallback method to get the cursor position when /dev/vcsa reports 255.
...
> --- a/drivers/tty/vt/vt.c
> +++ b/drivers/tty/vt/vt.c
...
> +/* invoked via ioctl(TIOCLINUX) */
> +static int get_cursor_pos(struct tty_struct *tty)
> +{
> + struct vc_data *vc = tty->driver_data;
> + unsigned int x, y;
> +
> + console_lock();
> + x = vc->state.x;
> + y = vc->state.y;
> + console_unlock();
> +
> + /*
> + * Clamp x to 16 bits, y to 15 bits. A display larger than 65535x32767
> + * characters won't be a concern for the foreseeable future.
> + * Bit 31 is reserved to represent negative error codes elsewhere.
> + */
> + return min(x, 0xFFFFu) | (min(y, 0x7FFFu) << 16);
Hmm, I would do a proper struct instead. Like winsize.
> --- a/include/uapi/linux/tiocl.h
> +++ b/include/uapi/linux/tiocl.h
> @@ -38,4 +38,8 @@ struct tiocl_selection {
> #define TIOCL_GETKMSGREDIRECT 17 /* get the vt the kernel messages are restricted to */
> #define TIOCL_GETBRACKETEDPASTE 18 /* get whether paste may be bracketed */
>
> +#define TIOCL_GETCURSORPOS 20 /* Get screen cursor position */
The same question about man-pages :).
thanks,
--
js
suse labs
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position
2025-05-14 5:57 ` Jiri Slaby
@ 2025-05-14 13:25 ` Nicolas Pitre
0 siblings, 0 replies; 6+ messages in thread
From: Nicolas Pitre @ 2025-05-14 13:25 UTC (permalink / raw)
To: Jiri Slaby; +Cc: Greg Kroah-Hartman, linux-serial, linux-kernel
On Wed, 14 May 2025, Jiri Slaby wrote:
> On 14. 05. 25, 3:52, Nicolas Pitre wrote:
> ...
> > So let's work around this limitation by adding TIOCL_GETCURSORPOS as a
> > fallback method to get the cursor position when /dev/vcsa reports 255.
> ...
> > --- a/drivers/tty/vt/vt.c
> > +++ b/drivers/tty/vt/vt.c
> ...
> > +/* invoked via ioctl(TIOCLINUX) */
> > +static int get_cursor_pos(struct tty_struct *tty)
> > +{
> > + struct vc_data *vc = tty->driver_data;
> > + unsigned int x, y;
> > +
> > + console_lock();
> > + x = vc->state.x;
> > + y = vc->state.y;
> > + console_unlock();
> > +
> > + /*
> > + * Clamp x to 16 bits, y to 15 bits. A display larger than 65535x32767
> > + * characters won't be a concern for the foreseeable future.
> > + * Bit 31 is reserved to represent negative error codes elsewhere.
> > + */
> > + return min(x, 0xFFFFu) | (min(y, 0x7FFFu) << 16);
>
> Hmm, I would do a proper struct instead. Like winsize.
I did that initially, than I had second thoughts about the extra
overhead implied by such a structure and the separate put_user(). This
coupled with the ambiguity around alignment with argp usage. In that
context is the structure really worth it compared to a simple return?
For the record, TIOCL_SETSEL is defined using:
struct {
char subcode;
short xs, ys, xe, ye;
short sel_mode;
};
but the documentation fails to mention that the structure must be packed
as there must nott be any alignment padding between subcode and the rest
of the structure given the kernel-side implementation.
> > --- a/include/uapi/linux/tiocl.h
> > +++ b/include/uapi/linux/tiocl.h
> > @@ -38,4 +38,8 @@ struct tiocl_selection {
> > #define TIOCL_GETKMSGREDIRECT 17 /* get the vt the kernel
> > messages are restricted to */
> > #define TIOCL_GETBRACKETEDPASTE 18 /* get whether paste may be
> > bracketed */
> >
> > +#define TIOCL_GETCURSORPOS 20 /* Get screen cursor position */
>
> The same question about man-pages :).
Shall do.
Nicolas
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-05-14 13:25 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-14 1:52 [PATCH 0/2] vt: bracketed paste and cursor position Nicolas Pitre
2025-05-14 1:52 ` [PATCH 1/2] vt: bracketed paste support Nicolas Pitre
2025-05-14 5:43 ` Jiri Slaby
2025-05-14 1:52 ` [PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position Nicolas Pitre
2025-05-14 5:57 ` Jiri Slaby
2025-05-14 13:25 ` Nicolas Pitre
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox