linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] console: answer OSC 10 and 11
@ 2022-01-24  5:49 nick black
  2022-01-26 14:04 ` Greg Kroah-Hartman
  0 siblings, 1 reply; 2+ messages in thread
From: nick black @ 2022-01-24  5:49 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Greg Kroah-Hartman, Jiri Slaby, Tetsuo Handa, Daniel Vetter,
	linux-kernel, Thomas Dickey

XTerm and many other terminal emulators implement
Operating System Commands 10 and 11, allowing the
default foreground/background to be set and queried.
Extend the VT control sequence parser to recognize
and support these queries.

The VT already implements two OSCs, for changing
and resetting the palette. In doing so (many years
ago), it broke from the ANSI standard, and did not
require an ST terminator. Read all about it in
xterm(1) (see "brokenLinuxOSC"). I have followed this
grand tradition, and similarly not required ST.
Note that ST can still be safely sent by a client
program, as the VT consumes it. Indeed, my Notcurses
library does exactly this.

"Don't VTs always have black backgrounds?" Nope, you
can change the default background color with any
number of ANSI sequences, and then use the VT's
Private CSI "ESC [ 8 ]" to make the current color pair
the default attributes. Try it at home with, say:

  printf %b '\e[46m' '\e[8]' '\e[H\e[J'

The response follows XTerm's effective lead, using
%02x/%02x/%02x to format the RGB value, rather than
the %02x%02x%02x preferred by the preexisting
P (set palette) OSC. This was done to simplify
client libraries. Note that the spirit of the law,
that the reply is a "control sequence of the same
form which can be used to set", cannot be easily
honored given the semantics of the Linux private CSI
sequence. So it goes.

As a result, notcurses-info now properly detects the
default colors dynamically. Where it used to say:

 no known default fg no known default bg

It now says on boot:

 notcurses 3.0.4 on Linux 5.16.0nlb VT
 ...
 default fg 0xaaaaaa default bg 0x000000

and after a change like that above:

 notcurses 3.0.4 on Linux 5.16.0nlb VT
 ...
 default fg 0xaaaaaa default bg 0xaa5500

This is necessary to produce readable multicolor text
while respecting the user's background choice.

Signed-off-by: nick black <dankamongmen@gmail.com>
---
Changes in v2:
 - Reverse in-kernel BGR to RGB

 drivers/tty/vt/vt.c            | 67 ++++++++++++++++++++++++++++------
 include/linux/console_struct.h |  1 +
 2 files changed, 58 insertions(+), 11 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index f8c87c4d7399..a10629bcaaa1 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1878,6 +1878,31 @@ int mouse_reporting(void)
 	return vc_cons[fg_console].d->vc_report_mouse;
 }
 
+/* handle the OSC query specified by vc->vc_oscmd. we currently handle only 10
+ * and 11 (default foreground and default background, respectively).
+ */
+static void handle_osc_query(struct tty_struct *tty, const struct vc_data *vc)
+{
+	char buf[20];
+	int len, idx;
+	/* response payload conforms to XTerm: %02x/%02x/%02x for R, G, and B. */
+	switch (vc->vc_oscmd) {
+	case 10: /* get default foreground */
+		idx = vc->vc_def_color & 0x0f;
+		break;
+	case 11: /* get default background */
+		idx = (vc->vc_def_color & 0xf0) >> 4;
+		break;
+	default:
+		return;
+	}
+	/* transpose internal BGR to RGB on output */
+	len = snprintf(buf, sizeof(buf), "\x1b]%d;rgb:%02x/%02x/%02x\x1b\\",
+		vc->vc_oscmd, vc->vc_palette[idx * 3 + 2],
+		vc->vc_palette[idx * 3 + 1], vc->vc_palette[idx * 3]);
+	respond_string(buf, len, tty->port);
+}
+
 /* console_lock is held */
 static void set_mode(struct vc_data *vc, int on_off)
 {
@@ -2075,8 +2100,8 @@ static void restore_cur(struct vc_data *vc)
 }
 
 enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey,
-	EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore, ESnonstd,
-	ESpalette, ESosc, ESapc, ESpm, ESdcs };
+	EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore,
+	ESpalette, ESosc, ESoscmd, ESoscparam, ESapc, ESpm, ESdcs };
 
 /* console_lock is held (except via vc_init()) */
 static void reset_terminal(struct vc_data *vc, int do_clear)
@@ -2230,7 +2255,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
 			vc->vc_state = ESsquare;
 			return;
 		case ']':
-			vc->vc_state = ESnonstd;
+			vc->vc_state = ESosc;
 			return;
 		case '_':
 			vc->vc_state = ESapc;
@@ -2287,7 +2312,10 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
 			return;
 		}
 		return;
-	case ESnonstd:
+	case ESosc:
+		/* Operating System Commands are traditionally terminated with an ST
+		 * or a BEL, but Linux historically eschews said terminators.
+		 */
 		if (c=='P') {   /* palette escape sequence */
 			for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
 				vc->vc_par[vc->vc_npar] = 0;
@@ -2297,9 +2325,10 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
 		} else if (c=='R') {   /* reset palette */
 			reset_palette(vc);
 			vc->vc_state = ESnormal;
-		} else if (c>='0' && c<='9')
-			vc->vc_state = ESosc;
-		else
+		} else if (isdigit(c)) {
+			vc->vc_oscmd = c - '0';
+			vc->vc_state = ESoscmd;
+		} else
 			vc->vc_state = ESnormal;
 		return;
 	case ESpalette:
@@ -2348,7 +2377,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
 		if (c == ';' && vc->vc_npar < NPAR - 1) {
 			vc->vc_npar++;
 			return;
-		} else if (c>='0' && c<='9') {
+		} else if (isdigit(c)) {
 			vc->vc_par[vc->vc_npar] *= 10;
 			vc->vc_par[vc->vc_npar] += c - '0';
 			return;
@@ -2556,7 +2585,23 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
 		return;
 	case ESapc:
 		return;
-	case ESosc:
+	case ESoscmd: /* extract the first OSC param, the command */
+		if (isdigit(c)) {
+			vc->vc_oscmd *= 10;
+			vc->vc_oscmd += c - '0';
+		} else if (c == ';') {
+			vc->vc_state = ESoscparam;
+		} else {
+			vc->vc_state = ESnormal;
+		}
+		return;
+	case ESoscparam: /* extract second OSC param */
+		/* All recognized numeric OSC commands take only '?', indicating a query.
+		 * See note above regarding ESosc about lack of OSC terminator ST.
+		 */
+		if (c == '?')
+			handle_osc_query(tty, vc);
+		vc->vc_state = ESnormal;
 		return;
 	case ESpm:
 		return;
@@ -3441,8 +3486,8 @@ static void con_cleanup(struct tty_struct *tty)
 }
 
 static int default_color           = 7; /* white */
-static int default_italic_color    = 2; // green (ASCII)
-static int default_underline_color = 3; // cyan (ASCII)
+static int default_italic_color    = 2; /* green */
+static int default_underline_color = 3; /* cyan */
 module_param_named(color, default_color, int, S_IRUGO | S_IWUSR);
 module_param_named(italic, default_italic_color, int, S_IRUGO | S_IWUSR);
 module_param_named(underline, default_underline_color, int, S_IRUGO | S_IWUSR);
diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
index d5b9c8d40c18..6d4fa51b62de 100644
--- a/include/linux/console_struct.h
+++ b/include/linux/console_struct.h
@@ -128,6 +128,7 @@ struct vc_data {
 	/* VT terminal data */
 	unsigned int	vc_state;		/* Escape sequence parser state */
 	unsigned int	vc_npar,vc_par[NPAR];	/* Parameters of current escape sequence */
+	unsigned int	vc_oscmd; /* Operating System Command selector */
 	/* data for manual vt switching */
 	struct vt_mode	vt_mode;
 	struct pid 	*vt_pid;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH v2] console: answer OSC 10 and 11
  2022-01-24  5:49 [PATCH v2] console: answer OSC 10 and 11 nick black
@ 2022-01-26 14:04 ` Greg Kroah-Hartman
  0 siblings, 0 replies; 2+ messages in thread
From: Greg Kroah-Hartman @ 2022-01-26 14:04 UTC (permalink / raw)
  To: nick black
  Cc: Linus Torvalds, Jiri Slaby, Tetsuo Handa, Daniel Vetter,
	linux-kernel, Thomas Dickey

On Mon, Jan 24, 2022 at 12:49:26AM -0500, nick black wrote:
> XTerm and many other terminal emulators implement
> Operating System Commands 10 and 11, allowing the
> default foreground/background to be set and queried.
> Extend the VT control sequence parser to recognize
> and support these queries.

Nit, can you use the full 72 columns here?

> The VT already implements two OSCs, for changing
> and resetting the palette. In doing so (many years
> ago), it broke from the ANSI standard, and did not
> require an ST terminator. Read all about it in
> xterm(1) (see "brokenLinuxOSC"). I have followed this
> grand tradition, and similarly not required ST.
> Note that ST can still be safely sent by a client
> program, as the VT consumes it. Indeed, my Notcurses
> library does exactly this.
> 
> "Don't VTs always have black backgrounds?" Nope, you
> can change the default background color with any
> number of ANSI sequences, and then use the VT's
> Private CSI "ESC [ 8 ]" to make the current color pair
> the default attributes. Try it at home with, say:
> 
>   printf %b '\e[46m' '\e[8]' '\e[H\e[J'
> 
> The response follows XTerm's effective lead, using
> %02x/%02x/%02x to format the RGB value, rather than
> the %02x%02x%02x preferred by the preexisting
> P (set palette) OSC. This was done to simplify
> client libraries. Note that the spirit of the law,
> that the reply is a "control sequence of the same
> form which can be used to set", cannot be easily
> honored given the semantics of the Linux private CSI
> sequence. So it goes.
> 
> As a result, notcurses-info now properly detects the
> default colors dynamically. Where it used to say:
> 
>  no known default fg no known default bg
> 
> It now says on boot:
> 
>  notcurses 3.0.4 on Linux 5.16.0nlb VT
>  ...
>  default fg 0xaaaaaa default bg 0x000000
> 
> and after a change like that above:
> 
>  notcurses 3.0.4 on Linux 5.16.0nlb VT
>  ...
>  default fg 0xaaaaaa default bg 0xaa5500
> 
> This is necessary to produce readable multicolor text
> while respecting the user's background choice.
> 
> Signed-off-by: nick black <dankamongmen@gmail.com>
> ---
> Changes in v2:
>  - Reverse in-kernel BGR to RGB
> 
>  drivers/tty/vt/vt.c            | 67 ++++++++++++++++++++++++++++------
>  include/linux/console_struct.h |  1 +
>  2 files changed, 58 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
> index f8c87c4d7399..a10629bcaaa1 100644
> --- a/drivers/tty/vt/vt.c
> +++ b/drivers/tty/vt/vt.c
> @@ -1878,6 +1878,31 @@ int mouse_reporting(void)
>  	return vc_cons[fg_console].d->vc_report_mouse;
>  }
>  
> +/* handle the OSC query specified by vc->vc_oscmd. we currently handle only 10
> + * and 11 (default foreground and default background, respectively).
> + */
> +static void handle_osc_query(struct tty_struct *tty, const struct vc_data *vc)
> +{
> +	char buf[20];
> +	int len, idx;
> +	/* response payload conforms to XTerm: %02x/%02x/%02x for R, G, and B. */

Nit, new line needed after variable list.



> +	switch (vc->vc_oscmd) {
> +	case 10: /* get default foreground */
> +		idx = vc->vc_def_color & 0x0f;
> +		break;
> +	case 11: /* get default background */
> +		idx = (vc->vc_def_color & 0xf0) >> 4;
> +		break;

Does 10 and 11 have a #define or enum anywhere?

> +	default:
> +		return;

No need to return an error?  Why not?

> +	}
> +	/* transpose internal BGR to RGB on output */
> +	len = snprintf(buf, sizeof(buf), "\x1b]%d;rgb:%02x/%02x/%02x\x1b\\",
> +		vc->vc_oscmd, vc->vc_palette[idx * 3 + 2],
> +		vc->vc_palette[idx * 3 + 1], vc->vc_palette[idx * 3]);
> +	respond_string(buf, len, tty->port);
> +}
> +
>  /* console_lock is held */
>  static void set_mode(struct vc_data *vc, int on_off)
>  {
> @@ -2075,8 +2100,8 @@ static void restore_cur(struct vc_data *vc)
>  }
>  
>  enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey,
> -	EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore, ESnonstd,
> -	ESpalette, ESosc, ESapc, ESpm, ESdcs };
> +	EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore,
> +	ESpalette, ESosc, ESoscmd, ESoscparam, ESapc, ESpm, ESdcs };
>  
>  /* console_lock is held (except via vc_init()) */
>  static void reset_terminal(struct vc_data *vc, int do_clear)
> @@ -2230,7 +2255,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
>  			vc->vc_state = ESsquare;
>  			return;
>  		case ']':
> -			vc->vc_state = ESnonstd;
> +			vc->vc_state = ESosc;
>  			return;
>  		case '_':
>  			vc->vc_state = ESapc;
> @@ -2287,7 +2312,10 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
>  			return;
>  		}
>  		return;
> -	case ESnonstd:
> +	case ESosc:
> +		/* Operating System Commands are traditionally terminated with an ST
> +		 * or a BEL, but Linux historically eschews said terminators.
> +		 */
>  		if (c=='P') {   /* palette escape sequence */
>  			for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
>  				vc->vc_par[vc->vc_npar] = 0;
> @@ -2297,9 +2325,10 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
>  		} else if (c=='R') {   /* reset palette */
>  			reset_palette(vc);
>  			vc->vc_state = ESnormal;
> -		} else if (c>='0' && c<='9')
> -			vc->vc_state = ESosc;
> -		else
> +		} else if (isdigit(c)) {
> +			vc->vc_oscmd = c - '0';
> +			vc->vc_state = ESoscmd;

We used to jump to ESosc here, what happened to that if the command is
invalid?

> +		} else
>  			vc->vc_state = ESnormal;
>  		return;
>  	case ESpalette:
> @@ -2348,7 +2377,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
>  		if (c == ';' && vc->vc_npar < NPAR - 1) {
>  			vc->vc_npar++;
>  			return;
> -		} else if (c>='0' && c<='9') {
> +		} else if (isdigit(c)) {

Different change, you can put this in a different patch.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2022-01-26 14:04 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-24  5:49 [PATCH v2] console: answer OSC 10 and 11 nick black
2022-01-26 14:04 ` Greg Kroah-Hartman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).