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

Nicolas Pitre
2025-05-14T01:56:14Z

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