[PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position

Nicolas Pitre posted 2 patches 9 months ago
There is a newer version of this series
[PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position
Posted by Nicolas Pitre 9 months ago
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
Re: [PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position
Posted by Jiri Slaby 9 months ago
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
Re: [PATCH 2/2] vt: add TIOCL_GETCURSORPOS to retrieve the screen cursor position
Posted by Nicolas Pitre 9 months ago
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