From b4b243039598909ebb053f060d227aba8cd1d79c Mon Sep 17 00:00:00 2001
From: Michele Calgaro <michele.calgaro@yahoo.it>
Date: Sat, 11 Jul 2026 17:03:31 +0900
Subject: konsole: refactor previous commit, add documentation and improve
 colorspace tests

Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
---
 konsole/README.moreColors     |  30 ++++++---
 konsole/src/TEmuVt102.cpp     |  88 +++++++++++++++-----------
 konsole/src/TEmuVt102.h       |  11 ++--
 konsole/tests/color-spaces.pl | 139 +++++++++++++++++++++++++++++-------------
 4 files changed, 177 insertions(+), 91 deletions(-)

diff --git a/konsole/README.moreColors b/konsole/README.moreColors
index dcd06fa3a..cded3a832 100644
--- a/konsole/README.moreColors
+++ b/konsole/README.moreColors
@@ -31,16 +31,30 @@ Try the tests/color-spaces.pl to visualize the assignment.
 
 A note on conformance
 
-These ESC codes break the "associativity" of SGR, but after some
-research the result is, that the standard itself is broken here.
+The ISO 8613-6 standard (referenced by ECMA-48) defines colon (':')
+as the sub-parameter separator for SGR 38 and 48. The format is:
 
-The perhaps best codes due to ECMA-48 would probably be e.g.
-38:2:<r>:<g>:<b>, i.e. consistently a colon instead of a semicolon.
-But apparently, this is defined different in ISO-8613 (which is
-included at this place in ECMA-48), actually breaking ECMA-48.
+  ESC[38:2:<i>:<r>:<g>:<b>m
+  ESC[48:2:<i>:<r>:<g>:<b>m
 
-We cannot help this and implemented the codes as above, which
-is a balanced decision.
+where '<i>' is ignored.
+Most modern terminal also support a variant where the
+'<i>' paremeter is not specified at all.
+
+  ESC[38:2:<r>:<g>:<b>m
+  ESC[48:2:<r>:<g>:<b>m
+
+In addition to that, a variant using semicolon (';') as
+sub-parameter separator is very popular and supported.
+
+  ESC[38;2;<r>;<g>;<b>m
+  ESC[48;2;<r>;<g>;<b>m
+
+Konsole accepts all three versions, as in the example below:
+
+  ESC[38:2:<i>:<r>:<g>:<b>m
+  ESC[38:2:<r>:<g>:<b>m
+  ESC[38;2;<r>;<g>;<b>m
 
 For 256 color mode, this is compatible with xterm and perhaps
 with other xterm compatible terminal emulations.
diff --git a/konsole/src/TEmuVt102.cpp b/konsole/src/TEmuVt102.cpp
index a52c0a1f6..c375586b6 100644
--- a/konsole/src/TEmuVt102.cpp
+++ b/konsole/src/TEmuVt102.cpp
@@ -233,8 +233,10 @@ void TEmuVt102::reset()
 void TEmuVt102::resetToken()
 {
   ppos = 0;
-  params.count = 0; params.value[0] = 0; params.value[1] = 0;
-  params.sub[0].value[0] = 0; params.sub[0].count = 0;
+  params.count = 0;
+  params.value[0] = 0;
+  params.sub[0].value[0] = 0;
+  params.sub[0].count = 0;
 }
 
 void TEmuVt102::addDigit(int dig)
@@ -242,8 +244,8 @@ void TEmuVt102::addDigit(int dig)
   if (params.sub[params.count].count == 0) {
     params.value[params.count] = 10*params.value[params.count] + dig;
   } else {
-    struct SubParam *sub = &params.sub[params.count];
-    sub->value[sub->count] = 10*sub->value[sub->count] + dig;
+    SubParam &sub = params.sub[params.count];
+    sub.value[sub.count] = 10*sub.value[sub.count] + dig;
   }
 }
 
@@ -255,11 +257,11 @@ void TEmuVt102::addArgument()
   params.sub[params.count].count = 0;
 }
 
-void TEmuVt102::addSub()
+void TEmuVt102::addSubParam()
 {
-  struct SubParam *sub = &params.sub[params.count];
-  sub->count = TQMIN(sub->count+1,MAXARGS-1);
-  sub->value[sub->count] = 0;
+  SubParam &sub = params.sub[params.count];
+  sub.count = TQMIN(sub.count+1,MAXARGS-1);
+  sub.value[sub.count] = 0;
 }
 
 void TEmuVt102::pushToToken(int cc)
@@ -357,42 +359,56 @@ void TEmuVt102::onRcvChar(int cc)
     if (lec(2,0,ESC)) { tau( TY_ESC(s[1]),    0,   0);          resetToken(); return; }
     if (les(3,1,SCS)) { tau( TY_ESC_CS(s[1],s[2]),    0,   0);  resetToken(); return; }
     if (lec(3,1,'#')) { tau( TY_ESC_DE(s[2]),    0,   0);       resetToken(); return; }
-    if (eps(    CPN)) { tau( TY_CSI_PN(cc), params.value[0],params.value[1]);   resetToken(); return; }
+    if (eps(    CPN)) { tau( TY_CSI_PN(cc), params.value[0],params.value[1]); resetToken(); return; }
 
 // resize = \e[8;<row>;<col>t
     if (eps(    CPS)) { tau( TY_CSI_PS(cc, params.value[0]), params.value[1], params.value[2]);   resetToken(); return; }
 
     if (epe(       )) { tau( TY_CSI_PE(cc),      0,   0);       resetToken(); return; }
     if (ees(    DIG)) { addDigit(cc-'0');                                     return; }
-    if (eec(';'))     {                                addArgument(); return; }
-    if (eec(':'))     {                                addSub();     return; }
+    if (eec(';'))     { addArgument();                                        return; }
+    if (eec(':'))     { addSubParam();                                        return; }
     for (i=0;i<=params.count;i++)
-    if ( epp(     ))  { tau( TY_CSI_PR(cc,params.value[i]),    0,   0); }
-    else if(egt(    ))   { tau( TY_CSI_PG(cc     ),    0,   0); } // spec. case for ESC]>0c or ESC]>c
-    else if (cc == 'm' && params.count - i >= 4 && (params.value[i] == 38 || params.value[i] == 48) && params.value[i+1] == 2)
-    { // ESC[ ... 48;2;<red>;<green>;<blue> ... m -or- ESC[ ... 38;2;<red>;<green>;<blue> ... m
-      i += 2;
-      tau( TY_CSI_PS(cc, params.value[i-2]), COLOR_SPACE_RGB, (params.value[i] << 16) | (params.value[i+1] << 8) | params.value[i+2]);
-      i += 2;
-    }
-    else if (cc == 'm' && params.sub[i].count >= 5 && (params.value[i] == 38 || params.value[i] == 48) && params.sub[i].value[1] == 2)
-    { // ESC[ ... 48:2:<id>:<red>:<green>:<blue> ... m -or- ESC[ ... 38:2:<id>:<red>:<green>:<blue> ... m
-      tau( TY_CSI_PS(cc, params.value[i]), COLOR_SPACE_RGB, (params.sub[i].value[3] << 16) | (params.sub[i].value[4] << 8) | params.sub[i].value[5]);
-    }
-    else if (cc == 'm' && params.sub[i].count == 4 && (params.value[i] == 38 || params.value[i] == 48) && params.sub[i].value[1] == 2)
-    { // ESC[ ... 48:2:<red>:<green>:<blue> ... m -or- ESC[ ... 38:2:<red>:<green>:<blue> ... m
-      tau( TY_CSI_PS(cc, params.value[i]), COLOR_SPACE_RGB, (params.sub[i].value[2] << 16) | (params.sub[i].value[3] << 8) | params.sub[i].value[4]);
-    }
-    else if (cc == 'm' && params.count - i >= 2 && (params.value[i] == 38 || params.value[i] == 48) && params.value[i+1] == 5)
-    { // ESC[ ... 48;5;<index> ... m -or- ESC[ ... 38;5;<index> ... m
-      i += 2;
-      tau( TY_CSI_PS(cc, params.value[i-2]), COLOR_SPACE_256, params.value[i]);
-    }
-    else if (cc == 'm' && params.sub[i].count >= 2 && (params.value[i] == 38 || params.value[i] == 48) && params.sub[i].value[1] == 5)
-    { // ESC[ ... 48:5:<index> ... m -or- ESC[ ... 38:5:<index> ... m
-      tau( TY_CSI_PS(cc, params.value[i]), COLOR_SPACE_256, params.sub[i].value[2]);
+    {
+      if ( epp(     ))  { tau( TY_CSI_PR(cc,params.value[i]),    0,   0); }
+      else if(egt(    ))   { tau( TY_CSI_PG(cc     ),    0,   0); } // spec. case for ESC]>0c or ESC]>c
+      else if (cc == 'm' && (params.value[i] == 38 || params.value[i] == 48))
+      {
+        // CSI SGR 38/48
+        const SubParam &sub = params.sub[i];
+        if (params.count - i >= 4 && params.value[i+1] == 2)
+        {
+          // ESC[ ... {38/48};2;<red>;<green>;<blue> ... m
+          int color = (params.value[i+2] << 16) | (params.value[i+3] << 8) | params.value[i+4];
+          tau(TY_CSI_PS(cc, params.value[i]), COLOR_SPACE_RGB, color);
+          i += 4;
+        }
+        else if (sub.count >= 5 && sub.value[1] == 2)
+        {
+          // ESC[ ... {38/48}:2:<id>:<red>:<green>:<blue> ... m
+          int color = (sub.value[3] << 16) | (sub.value[4] << 8) | sub.value[5];
+          tau(TY_CSI_PS(cc, params.value[i]), COLOR_SPACE_RGB, color);
+        }
+        else if (sub.count == 4 && sub.value[1] == 2)
+        {
+          // ESC[ ... {38/48}:2:<red>:<green>:<blue> ... m
+          int color = (sub.value[2] << 16) | (sub.value[3] << 8) | sub.value[4];
+          tau(TY_CSI_PS(cc, params.value[i]), COLOR_SPACE_RGB, color);
+        }
+        else if (params.count - i >= 2 && params.value[i+1] == 5)
+        {
+          // ESC[ ... {38/48};5;<index> ... m
+          tau(TY_CSI_PS(cc, params.value[i]), COLOR_SPACE_256, params.value[i+2]);
+          i += 2;
+        }
+        else if (sub.count >= 2 && sub.value[1] == 5)
+        {
+          // ESC[ ... {38/48}:5;<index> ... m
+          tau(TY_CSI_PS(cc, params.value[i]), COLOR_SPACE_256, sub.value[2]);
+        }
+      }
+      else { tau( TY_CSI_PS(cc,params.value[i]),    0,   0); }
     }
-    else              { tau( TY_CSI_PS(cc,params.value[i]),    0,   0); }
     resetToken();
   }
   else // mode VT52
diff --git a/konsole/src/TEmuVt102.h b/konsole/src/TEmuVt102.h
index d2c9cc86c..7ba75c955 100644
--- a/konsole/src/TEmuVt102.h
+++ b/konsole/src/TEmuVt102.h
@@ -103,18 +103,21 @@ private:
 #define MAXARGS 16
   void addDigit(int dig);
   void addArgument();
-  void addSub();
+  void addSubParam();
 
-  struct SubParam {
+  struct SubParam
+  {
     int value[MAXARGS];
     int count;
   };
 
-  struct {
+  struct Param
+  {
     int value[MAXARGS];
-    struct SubParam sub[MAXARGS];
     int count;
+    SubParam sub[MAXARGS];
   } params;
+
   void initTokenizer();
   int tbl[256];
 
diff --git a/konsole/tests/color-spaces.pl b/konsole/tests/color-spaces.pl
index 8774c0446..4dd96fa73 100644
--- a/konsole/tests/color-spaces.pl
+++ b/konsole/tests/color-spaces.pl
@@ -8,60 +8,113 @@ print "256 color mode\n\n";
 
 for ($fgbg = 38; $fgbg <= 48; $fgbg +=10) {
 
-# first the system ones:
-print "System colors:\n";
-for ($color = 0; $color < 8; $color++) {
-    print "\x1b[${fgbg};5;${color}m::";
-}
-print "\x1b[0m\n";
-for ($color = 8; $color < 16; $color++) {
-    print "\x1b[${fgbg};5;${color}m::";
-}
-print "\x1b[0m\n\n";
-
-# now the color cube
-print "Color cube, 6x6x6:\n";
-for ($green = 0; $green < 6; $green++) {
-    for ($red = 0; $red < 6; $red++) {
-	for ($blue = 0; $blue < 6; $blue++) {
-	    $color = 16 + ($red * 36) + ($green * 6) + $blue;
-	    print "\x1b[${fgbg};5;${color}m::";
-	}
-	print "\x1b[0m ";
+    # first the system ones:
+    print "System colors:\n";
+    for ($color = 0; $color < 8; $color++) {
+        print "\x1b[${fgbg};5;${color}m::";
     }
-    print "\n";
-}
+    print "\x1b[0m\n";
+    for ($color = 8; $color < 16; $color++) {
+        print "\x1b[${fgbg};5;${color}m::";
+    }
+    print "\x1b[0m\n\n";
 
-# now the grayscale ramp
-print "Grayscale ramp:\n";
-for ($color = 232; $color < 256; $color++) {
-    print "\x1b[${fgbg};5;${color}m::";
-}
-print "\x1b[0m\n\n";
+    # now the color cube
+    print "Color cube, 6x6x6:\n";
+    for ($green = 0; $green < 6; $green++) {
+        for ($red = 0; $red < 6; $red++) {
+            for ($blue = 0; $blue < 6; $blue++) {
+                $color = 16 + ($red * 36) + ($green * 6) + $blue;
+                print "\x1b[${fgbg};5;${color}m::";
+            }
+            print "\x1b[0m ";
+        }
+        print "\n";
+    }
+
+    # now the grayscale ramp
+    print "Grayscale ramp:\n";
+    for ($color = 232; $color < 256; $color++) {
+        print "\x1b[${fgbg};5;${color}m::";
+    }
+    print "\x1b[0m\n\n";
 
 }
 
-print "Examples for the 3-byte color mode\n\n";
+print "------------------------------------------------------------\n";
+print "Examples for the 3-byte color mode (ISO 8613-6 colon syntax)\n\n";
 
 for ($fgbg = 38; $fgbg <= 48; $fgbg +=10) {
 
-# now the color cube
-print "Color cube\n";
-for ($green = 0; $green < 256; $green+=51) {
-    for ($red = 0; $red < 256; $red+=51) {
-	for ($blue = 0; $blue < 256; $blue+=51) {
-            print "\x1b[${fgbg};2;${red};${green};${blue}m::";
-	}
-	print "\x1b[0m ";
+    # now the color cube
+    print "Color cube\n";
+    for ($green = 0; $green < 256; $green+=51) {
+        for ($red = 0; $red < 256; $red+=51) {
+            for ($blue = 0; $blue < 256; $blue+=51) {
+                print "\x1b[${fgbg}:2::${red}:${green}:${blue}m::";
+            }
+            print "\x1b[0m ";
+        }
+        print "\n";
+    }
+
+    # now the grayscale ramp
+    print "Grayscale ramp:\n";
+    for ($gray = 8; $gray < 256; $gray+=10) {
+        print "\x1b[${fgbg}:2::${gray}:${gray}:${gray}m::";
     }
-    print "\n";
+    print "\x1b[0m\n\n";
+
 }
 
-# now the grayscale ramp
-print "Grayscale ramp:\n";
-for ($gray = 8; $gray < 256; $gray+=10) {
-    print "\x1b[${fgbg};2;${gray};${gray};${gray}m::";
+print "-------------------------------------------------------------------\n";
+print "Examples for the 3-byte color mode (colon syntax, no 'i' parameter)\n\n";
+
+for ($fgbg = 38; $fgbg <= 48; $fgbg +=10) {
+
+    # now the color cube
+    print "Color cube\n";
+    for ($green = 0; $green < 256; $green+=51) {
+        for ($red = 0; $red < 256; $red+=51) {
+            for ($blue = 0; $blue < 256; $blue+=51) {
+                print "\x1b[${fgbg}:2:${red}:${green}:${blue}m::";
+            }
+            print "\x1b[0m ";
+        }
+        print "\n";
+    }
+
+    # now the grayscale ramp
+    print "Grayscale ramp:\n";
+    for ($gray = 8; $gray < 256; $gray+=10) {
+        print "\x1b[${fgbg}:2:${gray}:${gray}:${gray}m::";
+    }
+    print "\x1b[0m\n\n";
+
 }
-print "\x1b[0m\n\n";
+
+print "-----------------------------------------------------\n";
+print "Examples for the 3-byte color mode (semicolon syntax)\n\n";
+
+for ($fgbg = 38; $fgbg <= 48; $fgbg +=10) {
+
+    # now the color cube
+    print "Color cube\n";
+    for ($green = 0; $green < 256; $green+=51) {
+        for ($red = 0; $red < 256; $red+=51) {
+            for ($blue = 0; $blue < 256; $blue+=51) {
+                print "\x1b[${fgbg};2;${red};${green};${blue}m::";
+            }
+            print "\x1b[0m ";
+        }
+        print "\n";
+    }
+
+    # now the grayscale ramp
+    print "Grayscale ramp:\n";
+    for ($gray = 8; $gray < 256; $gray+=10) {
+        print "\x1b[${fgbg};2;${gray};${gray};${gray}m::";
+    }
+    print "\x1b[0m\n\n";
 
 }
-- 
cgit v1.2.3

