Implement xterm/256-color palette for ansi()
authorAri Johnson <ari@theari.com>
Fri, 13 Dec 2024 15:23:56 +0000 (10:23 -0500)
committerAri Johnson <ari@theari.com>
Fri, 13 Dec 2024 15:23:56 +0000 (10:23 -0500)
hdrs/dbdefs.h
hdrs/game.h
src/flags.c
src/funstr.c
src/game.c
src/notify.c

index 80d338a59f03163d3184f033edaaa3b2b8578db5..1c80338096c4f4ac7c7cc3b7d5647146dfd4d76a 100644 (file)
@@ -115,6 +115,7 @@ extern dbref first_free;    /* pointer to free list */
 #define Gagged(x)       (IS(Owner(x), TYPE_PLAYER, "GAGGED"))
 #define ShowAnsi(x)     (IS(Owner(x), TYPE_PLAYER, "ANSI"))
 #define ShowAnsiColor(x) (IS(Owner(x), TYPE_PLAYER, "COLOR"))
+#define ShowXtermColor(x) (IS(Owner(x), TYPE_PLAYER, "XTERMCOLOR"))
 #define InProg(x)      (IS(x, TYPE_PLAYER, "INPROGRAM"))
 
 /******* Thing toggles */
index 6dbf85291395bc280f067b1015f8256e6f806ebb..b99bf9b4c2996c77e79258c0411c43c79e3e4196 100644 (file)
@@ -21,6 +21,7 @@ extern void init_flagspaces(void);    /* flags.c */
 extern void init_flag_table(const char *ns);   /* flags.c */
 extern void init_tag_hashtab(void);    /* funstr.c */
 extern void init_pronouns(void);       /* funstr.c */
+extern void init_xterm_colors(void);   /* funstr.c */
 
 /* From bsd.c */
 extern void fcache_init(void);
index 361288251c1765ce973728b845bddf1b7b90e942..4a8650aa834702d2283151812946cad6137a9dc3 100644 (file)
@@ -106,6 +106,7 @@ FLAG flag_table[] = {
   {"VERBOSE", 'v', NOTYPE, VERBOSE, F_ANY, F_ANY},
   {"ANSI", 'A', TYPE_PLAYER, PLAYER_ANSI, F_ANY, F_ANY},
   {"COLOR", 'C', TYPE_PLAYER, PLAYER_COLOR, F_ANY, F_ANY},
+  {"XTERMCOLOR", '\0', TYPE_PLAYER, 0, F_ANY, F_ANY},
   {"MONITOR", 'M', TYPE_PLAYER | TYPE_ROOM | TYPE_THING | TYPE_DIVISION, 0, F_ANY, F_ANY},
   {"NOSPOOF", '"', TYPE_PLAYER, PLAYER_NOSPOOF, F_ANY | F_ODARK,
    F_ANY | F_ODARK},
index e34db221989cf0e996db76c9a4f768dda56b5dcb..6037eb48a20256285f78141b4f71c2495fa93303 100644 (file)
 #define LC_MESSAGES 6
 #endif
 
+struct colormap {
+  char *name;
+  int i_xterm;
+  int i_ansi_fg;
+  int i_ansi_bg;
+};
+
+/* This array maps color names to both the 8-bit 'xterm' color number and the
+ * nearest 4-bit ANSI color. Color names are case-insensitive. Nonstandard
+ * names for colors can be added at the end of the array. */
+static struct colormap xterm_colors[] = {
+  { "Black", 0, 0, 0 },
+  { "Maroon", 1, 1, 1 },
+  { "Green", 2, 2, 2 },
+  { "Olive", 3, 3, 3 },
+  { "Navy", 4, 4, 4 },
+  { "Purple", 5, 5, 5 },
+  { "Teal", 6, 6, 6 },
+  { "Silver", 7, 7, 7 },
+  { "Grey", 8, 8, 3 },
+  { "Red", 9, 9, 1 },
+  { "Lime", 10, 10, 2 },
+  { "Yellow", 11, 11, 3 },
+  { "Blue", 12, 4, 4 },
+  { "Fuchsia", 13, 13, 5 },
+  { "Aqua", 14, 14, 6 },
+  { "White", 15, 15, 7 },
+  { "Grey0", 16, 0, 0 },
+  { "NavyBlue", 17, 0, 0 },
+  { "DarkBlue", 18, 4, 4 },
+  { "Blue3", 19, 4, 4 },
+  { "Blue3", 20, 4, 4 },
+  { "Blue1", 21, 4, 4 },
+  { "DarkGreen", 22, 0, 0 },
+  { "DeepSkyBlue4", 23, 0, 0 },
+  { "DeepSkyBlue4", 24, 6, 6 },
+  { "DeepSkyBlue4", 25, 4, 4 },
+  { "DodgerBlue3", 26, 4, 4 },
+  { "DodgerBlue2", 27, 12, 4 },
+  { "Green4", 28, 2, 2 },
+  { "SpringGreen4", 29, 2, 2 },
+  { "Turquoise4", 30, 6, 6 },
+  { "DeepSkyBlue3", 31, 6, 6 },
+  { "DeepSkyBlue3", 32, 6, 6 },
+  { "DodgerBlue1", 33, 6, 6 },
+  { "Green3", 34, 2, 2 },
+  { "SpringGreen3", 35, 2, 2 },
+  { "DarkCyan", 36, 6, 6 },
+  { "LightSeaGreen", 37, 6, 6 },
+  { "DeepSkyBlue2", 38, 6, 6 },
+  { "DeepSkyBlue1", 39, 6, 6 },
+  { "Green3", 40, 2, 2 },
+  { "SpringGreen3", 41, 2, 2 },
+  { "SpringGreen2", 42, 6, 6 },
+  { "Cyan3", 43, 6, 6 },
+  { "DarkTurquoise", 44, 6, 6 },
+  { "Turquoise2", 45, 14, 6 },
+  { "Green1", 46, 10, 2 },
+  { "SpringGreen2", 47, 10, 2 },
+  { "SpringGreen1", 48, 6, 6 },
+  { "MediumSpringGreen", 49, 6, 6 },
+  { "Cyan2", 50, 14, 6 },
+  { "Cyan1", 51, 14, 6 },
+  { "DarkRed", 52, 0, 0 },
+  { "DeepPink4", 53, 0, 0 },
+  { "Purple4", 54, 5, 5 },
+  { "Purple4", 55, 4, 4 },
+  { "Purple3", 56, 4, 4 },
+  { "BlueViolet", 57, 12, 4 },
+  { "Orange4", 58, 0, 0 },
+  { "Grey37", 59, 8, 0 },
+  { "MediumPurple4", 60, 8, 5 },
+  { "SlateBlue3", 61, 8, 4 },
+  { "SlateBlue3", 62, 12, 4 },
+  { "RoyalBlue1", 63, 12, 4 },
+  { "Chartreuse4", 64, 2, 2 },
+  { "DarkSeaGreen4", 65, 8, 2 },
+  { "PaleTurquoise4", 66, 8, 6 },
+  { "SteelBlue", 67, 8, 6 },
+  { "SteelBlue3", 68, 12, 6 },
+  { "CornflowerBlue", 69, 12, 6 },
+  { "Chartreuse3", 70, 2, 2 },
+  { "DarkSeaGreen4", 71, 8, 2 },
+  { "CadetBlue", 72, 8, 6 },
+  { "CadetBlue", 73, 8, 6 },
+  { "SkyBlue3", 74, 12, 6 },
+  { "SteelBlue1", 75, 12, 6 },
+  { "Chartreuse3", 76, 2, 2 },
+  { "PaleGreen3", 77, 8, 2 },
+  { "SeaGreen3", 78, 8, 6 },
+  { "Aquamarine3", 79, 6, 6 },
+  { "MediumTurquoise", 80, 6, 6 },
+  { "SteelBlue1", 81, 14, 6 },
+  { "Chartreuse2", 82, 10, 2 },
+  { "SeaGreen2", 83, 10, 2 },
+  { "SeaGreen1", 84, 6, 6 },
+  { "SeaGreen1", 85, 6, 6 },
+  { "Aquamarine1", 86, 14, 6 },
+  { "DarkSlateGray2", 87, 14, 6 },
+  { "DarkRed", 88, 1, 1 },
+  { "DeepPink4", 89, 1, 1 },
+  { "DarkMagenta", 90, 5, 5 },
+  { "DarkMagenta", 91, 5, 5 },
+  { "DarkViolet", 92, 5, 5 },
+  { "Purple", 93, 5, 5 },
+  { "Orange4", 94, 1, 1 },
+  { "LightPink4", 95, 8, 1 },
+  { "Plum4", 96, 8, 5 },
+  { "MediumPurple3", 97, 8, 5 },
+  { "MediumPurple3", 98, 12, 5 },
+  { "SlateBlue1", 99, 12, 5 },
+  { "Yellow4", 100, 3, 3 },
+  { "Wheat4", 101, 8, 3 },
+  { "Grey53", 102, 8, 7 },
+  { "LightSlateGrey", 103, 8, 7 },
+  { "MediumPurple", 104, 12, 7 },
+  { "LightSlateBlue", 105, 12, 7 },
+  { "Yellow4", 106, 3, 3 },
+  { "DarkOliveGreen3", 107, 8, 3 },
+  { "DarkSeaGreen", 108, 8, 7 },
+  { "LightSkyBlue3", 109, 8, 7 },
+  { "LightSkyBlue3", 110, 8, 7 },
+  { "SkyBlue2", 111, 12, 7 },
+  { "Chartreuse2", 112, 3, 3 },
+  { "DarkOliveGreen3", 113, 8, 3 },
+  { "PaleGreen3", 114, 8, 7 },
+  { "DarkSeaGreen3", 115, 8, 7 },
+  { "DarkSlateGray3", 116, 7, 7 },
+  { "SkyBlue1", 117, 7, 7 },
+  { "Chartreuse1", 118, 3, 3 },
+  { "LightGreen", 119, 3, 3 },
+  { "LightGreen", 120, 8, 7 },
+  { "PaleGreen1", 121, 7, 7 },
+  { "Aquamarine1", 122, 7, 7 },
+  { "DarkSlateGray1", 123, 7, 7 },
+  { "Red3", 124, 1, 1 },
+  { "DeepPink4", 125, 1, 1 },
+  { "MediumVioletRed", 126, 5, 5 },
+  { "Magenta3", 127, 5, 5 },
+  { "DarkViolet", 128, 5, 5 },
+  { "Purple", 129, 5, 5 },
+  { "DarkOrange3", 130, 1, 1 },
+  { "IndianRed", 131, 8, 1 },
+  { "HotPink3", 132, 8, 5 },
+  { "MediumOrchid3", 133, 8, 5 },
+  { "MediumOrchid", 134, 12, 5 },
+  { "MediumPurple2", 135, 12, 5 },
+  { "DarkGoldenrod", 136, 3, 3 },
+  { "LightSalmon3", 137, 8, 3 },
+  { "RosyBrown", 138, 8, 7 },
+  { "Grey63", 139, 8, 7 },
+  { "MediumPurple2", 140, 8, 7 },
+  { "MediumPurple1", 141, 12, 7 },
+  { "Gold3", 142, 3, 3 },
+  { "DarkKhaki", 143, 8, 3 },
+  { "NavajoWhite3", 144, 8, 7 },
+  { "Grey69", 145, 8, 7 },
+  { "LightSteelBlue3", 146, 7, 7 },
+  { "LightSteelBlue", 147, 7, 7 },
+  { "Yellow3", 148, 3, 3 },
+  { "DarkOliveGreen3", 149, 3, 3 },
+  { "DarkSeaGreen3", 150, 8, 7 },
+  { "DarkSeaGreen2", 151, 7, 7 },
+  { "LightCyan3", 152, 7, 7 },
+  { "LightSkyBlue1", 153, 7, 7 },
+  { "GreenYellow", 154, 3, 3 },
+  { "DarkOliveGreen2", 155, 3, 3 },
+  { "PaleGreen1", 156, 7, 7 },
+  { "DarkSeaGreen2", 157, 7, 7 },
+  { "DarkSeaGreen1", 158, 7, 7 },
+  { "PaleTurquoise1", 159, 7, 7 },
+  { "Red3", 160, 1, 1 },
+  { "DeepPink3", 161, 1, 1 },
+  { "DeepPink3", 162, 5, 5 },
+  { "Magenta3", 163, 5, 5 },
+  { "Magenta3", 164, 5, 5 },
+  { "Magenta2", 165, 13, 5 },
+  { "DarkOrange3", 166, 1, 1 },
+  { "IndianRed", 167, 8, 1 },
+  { "HotPink3", 168, 8, 5 },
+  { "HotPink2", 169, 5, 5 },
+  { "Orchid", 170, 5, 5 },
+  { "MediumOrchid1", 171, 13, 5 },
+  { "Orange3", 172, 3, 3 },
+  { "LightSalmon3", 173, 8, 3 },
+  { "LightPink3", 174, 8, 7 },
+  { "Pink3", 175, 8, 7 },
+  { "Plum3", 176, 7, 7 },
+  { "Violet", 177, 7, 7 },
+  { "Gold3", 178, 3, 3 },
+  { "LightGoldenrod3", 179, 3, 3 },
+  { "Tan", 180, 8, 7 },
+  { "MistyRose3", 181, 7, 7 },
+  { "Thistle3", 182, 7, 7 },
+  { "Plum2", 183, 7, 7 },
+  { "Yellow3", 184, 3, 3 },
+  { "Khaki3", 185, 3, 3 },
+  { "LightGoldenrod2", 186, 7, 7 },
+  { "LightYellow3", 187, 7, 7 },
+  { "Grey84", 188, 7, 7 },
+  { "LightSteelBlue1", 189, 7, 7 },
+  { "Yellow2", 190, 11, 3 },
+  { "DarkOliveGreen1", 191, 11, 3 },
+  { "DarkOliveGreen1", 192, 7, 7 },
+  { "DarkSeaGreen1", 193, 7, 7 },
+  { "Honeydew2", 194, 7, 7 },
+  { "LightCyan1", 195, 7, 7 },
+  { "Red1", 196, 9, 1 },
+  { "DeepPink2", 197, 9, 1 },
+  { "DeepPink1", 198, 5, 5 },
+  { "DeepPink1", 199, 5, 5 },
+  { "Magenta2", 200, 13, 5 },
+  { "Magenta1", 201, 13, 5 },
+  { "OrangeRed1", 202, 9, 1 },
+  { "IndianRed1", 203, 9, 1 },
+  { "IndianRed1", 204, 5, 5 },
+  { "HotPink", 205, 5, 5 },
+  { "HotPink", 206, 13, 5 },
+  { "MediumOrchid1", 207, 13, 5 },
+  { "DarkOrange", 208, 3, 3 },
+  { "Salmon1", 209, 3, 3 },
+  { "LightCoral", 210, 8, 7 },
+  { "PaleVioletRed1", 211, 7, 7 },
+  { "Orchid2", 212, 7, 7 },
+  { "Orchid1", 213, 7, 7 },
+  { "Orange1", 214, 3, 3 },
+  { "SandyBrown", 215, 3, 3 },
+  { "LightSalmon1", 216, 7, 7 },
+  { "LightPink1", 217, 7, 7 },
+  { "Pink1", 218, 7, 7 },
+  { "Plum1", 219, 7, 7 },
+  { "Gold1", 220, 11, 3 },
+  { "LightGoldenrod2", 221, 11, 3 },
+  { "LightGoldenrod2", 222, 7, 7 },
+  { "NavajoWhite1", 223, 7, 7 },
+  { "MistyRose1", 224, 7, 7 },
+  { "Thistle1", 225, 7, 7 },
+  { "Yellow1", 226, 11, 3 },
+  { "LightGoldenrod1", 227, 11, 3 },
+  { "Khaki1", 228, 7, 7 },
+  { "Wheat1", 229, 7, 7 },
+  { "Cornsilk1", 230, 7, 7 },
+  { "Grey100", 231, 15, 7 },
+  { "Grey3", 232, 0, 0 },
+  { "Grey7", 233, 0, 0 },
+  { "Grey11", 234, 0, 0 },
+  { "Grey15", 235, 0, 0 },
+  { "Grey19", 236, 0, 0 },
+  { "Grey23", 237, 0, 0 },
+  { "Grey27", 238, 8, 0 },
+  { "Grey30", 239, 8, 0 },
+  { "Grey35", 240, 8, 0 },
+  { "Grey39", 241, 8, 0 },
+  { "Grey42", 242, 8, 3 },
+  { "Grey46", 243, 8, 3 },
+  { "Grey50", 244, 8, 3 },
+  { "Grey54", 245, 8, 7 },
+  { "Grey58", 246, 8, 7 },
+  { "Grey62", 247, 8, 7 },
+  { "Grey66", 248, 8, 7 },
+  { "Grey70", 249, 7, 7 },
+  { "Grey74", 250, 7, 7 },
+  { "Grey78", 251, 7, 7 },
+  { "Grey82", 252, 7, 7 },
+  { "Grey85", 253, 7, 7 },
+  { "Grey89", 254, 7, 7 },
+  { "Grey93", 255, 7, 7 },
+  { NULL, 0, 0, 0 }
+};
+
+int xterm_to_ansi_fg[256];
+int xterm_to_ansi_bg[256];
+
+HASHTAB htab_xterm_colors;  /**< Hash table of color names */
+
 HASHTAB htab_tag;  /**< Hash table of safe html tags */
 
 #define MAX_COLS 32  /**< Maximum number of columns for align() */
@@ -1472,6 +1747,8 @@ typedef struct {
   char flags;          /**< Ansi text attributes */
   char fore;           /**< Ansi foreground color */
   char back;           /**< Ansi background color */
+  int  forexterm;      /**< Ansi xterm foreground color */
+  int  backxterm;      /**< Ansi xterm background color */
 } ansi_data;
 
 static void dump_ansi_codes(ansi_data * ad, char *buff, char **bp);
@@ -1479,28 +1756,53 @@ static void dump_ansi_codes(ansi_data * ad, char *buff, char **bp);
 /** If we're adding y to x, do we need to add z as well? */
 #define EDGE_UP(x,y,z)  (((y) & (z)) && !((x) & (z)))
 
+#define ANSI_XTERM_BG      "\x1B[48;5;"
+#define ANSI_XTERM_FG      "\x1B[38;5;"
+
 static void
 dump_ansi_codes(ansi_data * ad, char *buff, char **bp)
 {
-  static ansi_data old_ad = { 0, 0, 0 };
+  static ansi_data old_ad = { 0, 0, 0, 0, 0 };
   int f = 0;
 
-  if ((old_ad.fore && !ad->fore)
-      || (old_ad.back && !ad->back)
+  if (    (old_ad.fore && !ad->fore)
+      ||  (old_ad.back && !ad->back)
+      ||  (old_ad.forexterm && !ad->forexterm)
+      ||  (old_ad.backxterm && !ad->backxterm)
       || ((old_ad.flags & ad->flags) != old_ad.flags)) {
     safe_str(ANSI_NORMAL, buff, bp);
     old_ad.flags = 0;
     old_ad.fore = 0;
     old_ad.back = 0;
+    old_ad.forexterm = 0;
+    old_ad.backxterm = 0;
   }
 
-  if ((old_ad.fore == ad->fore)
+  if (   (old_ad.fore == ad->fore)
       && (old_ad.back == ad->back)
+      && (old_ad.forexterm == ad->forexterm)
+      && (old_ad.backxterm == ad->backxterm)
       && (old_ad.flags == ad->flags))
     /* If nothing has changed, don't bother doing anything.
      * This stops the entirely pointless \e[m being generated. */
     return;
 
+  /* XTERM always has priority */
+  if ( ad->forexterm || ad->backxterm ) {
+     if ( ad->forexterm ) {
+        safe_str(ANSI_XTERM_FG, buff, bp);
+        safe_integer(ad->forexterm, buff, bp);
+        safe_str(ANSI_END, buff, bp);
+     }
+     if ( ad->backxterm ) {
+        safe_str(ANSI_XTERM_BG, buff, bp);
+        safe_integer(ad->backxterm, buff, bp);
+        safe_str(ANSI_END, buff, bp);
+     }
+     old_ad = *ad;
+     return;
+  }
+
   safe_str(ANSI_BEGIN, buff, bp);
 
   if (EDGE_UP(old_ad.flags, ad->flags, COL_FLASH)) {
@@ -1545,14 +1847,36 @@ dump_ansi_codes(ansi_data * ad, char *buff, char **bp)
 
 }
 
+void
+init_xterm_colors(void)
+{
+  int i;
+
+  hashinit(&htab_xterm_colors, 512, sizeof(struct colormap *));
+
+  for (i = 0; xterm_colors[i].name; i++) {
+    if (i < 256) {
+      xterm_to_ansi_fg[i] = xterm_colors[i].i_ansi_fg;
+      xterm_to_ansi_bg[i] = xterm_colors[i].i_ansi_bg;
+    }
+
+    hashadd(strlower(xterm_colors[i].name),
+           &xterm_colors[i], &htab_xterm_colors);
+  }
+}
 
 /* ARGSUSED */
 FUNCTION(fun_ansi)
 {
   static char tbuff[BUFFER_LEN];
-  static ansi_data stack[1024] = { {0, 0, 0} }, *sp = stack;
+  static ansi_data stack[1024] = { {0, 0, 0, 0, 0} }, *sp = stack;
   char const *arg0, *arg1;
   char *tbp;
+  char *solidus;
+  char *fgname;
+  char *bgname;
+  int fgnum, bgnum;
+  struct colormap *cm;
 
   tbp = tbuff;
   arg0 = args[0];
@@ -1563,85 +1887,134 @@ FUNCTION(fun_ansi)
   sp[1] = sp[0];
   sp++;
 
-  for (tbp = tbuff; *tbp; tbp++) {
-    switch (*tbp) {
-    case 'n':                  /* normal */
-      sp->flags = 0;
-      sp->fore = 0;
-      sp->back = 0;
-      break;
-    case 'f':                  /* flash */
-      sp->flags |= COL_FLASH;
-      break;
-    case 'h':                  /* hilite */
-      sp->flags |= COL_HILITE;
-      break;
-    case 'i':                  /* inverse */
-      sp->flags |= COL_INVERT;
-      break;
-    case 'u':                  /* underscore */
-      sp->flags |= COL_UNDERSCORE;
-      break;
-    case 'F':                  /* flash */
-      sp->flags &= ~COL_FLASH;
-      break;
-    case 'H':                  /* hilite */
-      sp->flags &= ~COL_HILITE;
-      break;
-    case 'I':                  /* inverse */
-      sp->flags &= ~COL_INVERT;
-      break;
-    case 'U':                  /* underscore */
-      sp->flags &= ~COL_UNDERSCORE;
-      break;
-    case 'b':                  /* blue fg */
-      sp->fore = COL_BLUE;
-      break;
-    case 'c':                  /* cyan fg */
-      sp->fore = COL_CYAN;
-      break;
-    case 'g':                  /* green fg */
-      sp->fore = COL_GREEN;
-      break;
-    case 'm':                  /* magenta fg */
-      sp->fore = COL_MAGENTA;
-      break;
-    case 'r':                  /* red fg */
-      sp->fore = COL_RED;
-      break;
-    case 'w':                  /* white fg */
-      sp->fore = COL_WHITE;
-      break;
-    case 'x':                  /* black fg */
-      sp->fore = COL_BLACK;
-      break;
-    case 'y':                  /* yellow fg */
-      sp->fore = COL_YELLOW;
-      break;
-    case 'B':                  /* blue bg */
-      sp->back = COL_BLUE;
-      break;
-    case 'C':                  /* cyan bg */
-      sp->back = COL_CYAN;
-      break;
-    case 'G':                  /* green bg */
-      sp->back = COL_GREEN;
-      break;
-    case 'M':                  /* magenta bg */
-      sp->back = COL_MAGENTA;
-      break;
-    case 'R':                  /* red bg */
-      sp->back = COL_RED;
-      break;
-    case 'W':                  /* white bg */
-      sp->back = COL_WHITE;
-      break;
-    case 'X':                  /* black bg */
-      sp->back = COL_BLACK;
-      break;
-    case 'Y':                  /* yellow bg */
-      sp->back = COL_YELLOW;
-      break;
+  fgnum = -1;
+  bgnum = -1;
+  solidus = strchr(tbuff, '/');
+  if (solidus) {
+    if (solidus == tbuff) {
+      fgname = NULL;
+      bgname = &solidus[1];
+    } else {
+      fgname = tbuff;
+      solidus[0] = '\0';
+      bgname = &solidus[1];
+    }
+  } else {
+    fgname = tbuff;
+    bgname = NULL;
+  }
+  if (fgname && *fgname) {
+    if (is_integer(fgname)) {
+      fgnum = parse_integer(fgname);
+      if (fgnum < 0 || fgnum > 255)
+        fgnum = -1;
+    }
+    if (fgnum < 0) {
+      cm = hashfind(strlower(fgname), &htab_xterm_colors);
+      if (cm)
+        fgnum = cm->i_xterm;
+    }
+  }
+  if (bgname && *bgname) {
+    if (is_integer(bgname)) {
+      bgnum = parse_integer(bgname);
+      if (bgnum < 0 || bgnum > 255)
+        bgnum = -1;
+    }
+    if (bgnum < 0) {
+      cm = hashfind(strlower(bgname), &htab_xterm_colors);
+      if (cm)
+        bgnum = cm->i_xterm;
+    }
+  }
+
+  if (fgnum != -1)
+    sp->forexterm = fgnum;
+
+  if (bgnum != -1)
+    sp->backxterm = bgnum;
+
+  if (fgnum == -1 && bgnum == -1) {
+    for (tbp = tbuff; *tbp; tbp++) {
+      switch (*tbp) {
+      case 'n':                        /* normal */
+        sp->flags = 0;
+        sp->fore = 0;
+        sp->back = 0;
+        break;
+      case 'f':                        /* flash */
+        sp->flags |= COL_FLASH;
+        break;
+      case 'h':                        /* hilite */
+        sp->flags |= COL_HILITE;
+        break;
+      case 'i':                        /* inverse */
+        sp->flags |= COL_INVERT;
+        break;
+      case 'u':                        /* underscore */
+        sp->flags |= COL_UNDERSCORE;
+        break;
+      case 'F':                        /* flash */
+        sp->flags &= ~COL_FLASH;
+        break;
+      case 'H':                        /* hilite */
+        sp->flags &= ~COL_HILITE;
+        break;
+      case 'I':                        /* inverse */
+        sp->flags &= ~COL_INVERT;
+        break;
+      case 'U':                        /* underscore */
+        sp->flags &= ~COL_UNDERSCORE;
+        break;
+      case 'b':                        /* blue fg */
+        sp->fore = COL_BLUE;
+        break;
+      case 'c':                        /* cyan fg */
+        sp->fore = COL_CYAN;
+        break;
+      case 'g':                        /* green fg */
+        sp->fore = COL_GREEN;
+        break;
+      case 'm':                        /* magenta fg */
+        sp->fore = COL_MAGENTA;
+        break;
+      case 'r':                        /* red fg */
+        sp->fore = COL_RED;
+        break;
+      case 'w':                        /* white fg */
+        sp->fore = COL_WHITE;
+        break;
+      case 'x':                        /* black fg */
+        sp->fore = COL_BLACK;
+        break;
+      case 'y':                        /* yellow fg */
+        sp->fore = COL_YELLOW;
+        break;
+      case 'B':                        /* blue bg */
+        sp->back = COL_BLUE;
+        break;
+      case 'C':                        /* cyan bg */
+        sp->back = COL_CYAN;
+        break;
+      case 'G':                        /* green bg */
+        sp->back = COL_GREEN;
+        break;
+      case 'M':                        /* magenta bg */
+        sp->back = COL_MAGENTA;
+        break;
+      case 'R':                        /* red bg */
+        sp->back = COL_RED;
+        break;
+      case 'W':                        /* white bg */
+        sp->back = COL_WHITE;
+        break;
+      case 'X':                        /* black bg */
+        sp->back = COL_BLACK;
+        break;
+      case 'Y':                        /* yellow bg */
+        sp->back = COL_YELLOW;
+        break;
+      }
     }
   }
 
index ba30f539268fecccb96498f856de6df0524f0cd9..163b35d5cadab74d166f5e37b60e2d91ce5be897 100644 (file)
@@ -796,6 +796,7 @@ init_game_config(const char *conf)
   init_locks();
   init_names();
   init_pronouns();
+  init_xterm_colors();
   init_lmods();
 
   memset(&current_state, 0, sizeof current_state);
index 2882966885b6b5d3306360e320704247ed50c7a9..6296f65d561974dcf7140e3d5dc8ca9bbc617471 100644 (file)
@@ -157,6 +157,9 @@ enum na_type {
   NA_ASCII = 0,                        /**< Plain old ascii. */
   NA_ANSI,                     /**< ANSI flag */
   NA_COLOR,                    /**< ANSI and COLOR flags */
+  NA_XTERMCOLOR,               /**< XTERMCOLOR flag */
+  NA_TXTERMCOLOR,              /**< Like above with telnet-aware client */
+  NA_NXTERMCOLOR,              /**< XTERMCOLOR and NOACCENTS */
   NA_PUEBLO,                   /**< html */
   NA_PASCII,                   /**< Player without any of the above */
   NA_TANSI,                    /**< Like above with telnet-aware client */
@@ -169,7 +172,7 @@ enum na_type {
 };
 
 /** Number of possible message text renderings */
-#define MESSAGE_TYPES 12
+#define MESSAGE_TYPES 15
 
 #define TA_BGC 0       /**< Text attribute background color */
 #define TA_FGC 1       /**< Text attribute foreground color */
@@ -177,6 +180,8 @@ enum na_type {
 #define TA_REV 3       /**< Text attribute reverse/inverse */
 #define TA_BLINK 4     /**< Text attribute blinking/flashing */
 #define TA_ULINE 5     /**< Text attribute underline */
+#define TA_XTERMFG 6   /**< Xterm color, foreground */
+#define TA_XTERMBG 7   /**< Xterm color, background */
 
 static int na_depth = 0;
 
@@ -197,13 +202,15 @@ static unsigned char *notify_makestring(const char *message,
                                        struct notify_strings messages[],
                                        enum na_type type);
 
+extern int xterm_to_ansi_fg[];
+extern int xterm_to_ansi_bg[];
 
 static void
-fillstate(int state[6], const unsigned char **f)
+fillstate(int state[8], const unsigned char **f)
 {
   const unsigned char *p;
   int i;
-  int n;
+  int n, m;
   p = *f;
   p++;
   if (*p != '[') {
@@ -214,11 +221,12 @@ fillstate(int state[6], const unsigned char **f)
     while (*p && *p != 'm') {
       if ((*p > '9') || (*p < '0')) {
        /* Nada */
-      } else if (!(*(p + 1)) || (*(p + 1) == 'm') || (*(p + 1) == ';')) {
+        p++;
+      } else if (!p[1] || p[1] == 'm' || p[1] == ';') {
        /* ShortCode */ ;
        switch (*p) {
        case '0':
-         for (i = 0; i < 6; i++)
+         for (i = 0; i < 8; i++)
            state[i] = 0;
          break;
        case '1':
@@ -234,21 +242,129 @@ fillstate(int state[6], const unsigned char **f)
          state[TA_ULINE] = 1;
          break;
        }
+        p++;
       } else {
        n = (*p - '0') * 10;
        p++;
        n += (*p - '0');
-       if ((n >= 30) && (n <= 37))
+        p++;
+       if ((n >= 30) && (n <= 37)) {
          state[TA_FGC] = n - 29;
-       else if ((n >= 40) && (n <= 47))
+       } else if ((n >= 40) && (n <= 47)) {
          state[TA_BGC] = n - 39;
+        } else if ((n == 38) || (n == 48)) {
+          if (p[0] == ';' && p[1] == '5' && p[2] == ';') {
+            p = &p[3];
+            m = 0;
+            while (*p >= '0' && *p <= '9') {
+              m = m * 10 + *p - '0';
+              p++;
+            }
+            if (n == 38) {
+              state[TA_XTERMFG] = m + 1;
+            } else {
+              state[TA_XTERMBG] = m + 1;
+            }
+          }
+        }
       }
-      p++;
     }
   }
   if ((p != *f) && (*p != 'm'))
     p--;
   *f = p;
+
+  if (state[TA_FGC] && !state[TA_XTERMFG]) {
+    switch (state[TA_FGC]) {
+    case 1: /* Black */
+      if (state[TA_BOLD])
+        state[TA_XTERMFG] = 244; /* Grey50 */
+      else
+        state[TA_XTERMFG] = 0; /* Black */
+      break;
+    case 2: /* Red */
+      if (state[TA_BOLD])
+        state[TA_XTERMFG] = 9; /* Red */
+      else
+        state[TA_XTERMFG] = 160; /* Red3 */
+      break;
+    case 3: /* Green */
+      if (state[TA_BOLD])
+        state[TA_XTERMFG] = 2; /* Green */
+      else
+        state[TA_XTERMFG] = 40; /* Green3 */
+      break;
+    case 4: /* Yellow */
+      if (state[TA_BOLD])
+        state[TA_XTERMFG] = 11; /* Yellow */
+      else
+        state[TA_XTERMFG] = 148; /* Yellow3 */
+      break;
+    case 5: /* Blue */
+      if (state[TA_BOLD])
+        state[TA_XTERMFG] = 63; /* RoyalBlue1 */
+      else
+        state[TA_XTERMFG] = 20; /* Blue3 */
+      break;
+    case 6: /* Magenta */
+      if (state[TA_BOLD])
+        state[TA_XTERMFG] = 201; /* Magenta1 */
+      else
+        state[TA_XTERMFG] = 164; /* Magenta3 */
+      break;
+    case 7: /* Cyan */
+      if (state[TA_BOLD])
+        state[TA_XTERMFG] = 51; /* Cyan1 */
+      else
+        state[TA_XTERMFG] = 43; /* Cyan3 */
+      break;
+    case 8: /* White */
+      if (state[TA_BOLD])
+        state[TA_XTERMFG] = 15; /* White */
+      else
+        state[TA_XTERMFG] = 254; /* Grey89 */
+      break;
+    }
+    state[TA_XTERMFG] += 1;
+  } else if (state[TA_XTERMFG]) {
+    state[TA_FGC] = xterm_to_ansi_fg[state[TA_XTERMFG] - 1] + 1;
+    if (state[TA_FGC] > 8) {
+      state[TA_FGC] -= 8;
+      state[TA_BOLD] = 1;
+    }
+  }
+
+  if (state[TA_BGC] && !state[TA_XTERMBG]) {
+    switch (state[TA_BGC]) {
+    case 1: /* Black */
+      state[TA_XTERMBG] = 0; /* Black */
+      break;
+    case 2: /* Red */
+      state[TA_XTERMBG] = 160; /* Red3 */
+      break;
+    case 3: /* Green */
+      state[TA_XTERMBG] = 40; /* Green3 */
+      break;
+    case 4: /* Yellow */
+      state[TA_XTERMBG] = 148; /* Yellow3 */
+      break;
+    case 5: /* Blue */
+      state[TA_XTERMBG] = 20; /* Blue3 */
+      break;
+    case 6: /* Magenta */
+      state[TA_XTERMBG] = 164; /* Magenta3 */
+      break;
+    case 7: /* Cyan */
+      state[TA_XTERMBG] = 43; /* Cyan3 */
+      break;
+    case 8: /* White */
+      state[TA_XTERMBG] = 254; /* Grey89 */
+      break;
+    }
+    state[TA_XTERMBG] += 1;
+  } else if (state[TA_XTERMBG]) {
+    state[TA_BGC] = xterm_to_ansi_bg[state[TA_XTERMBG] - 1] + 1;
+  }
 }
 
 /** Add an ansi tag if it's needed here */
@@ -270,8 +386,10 @@ ansi_change_state(char *t, char **o, int color, int *state, int *newstate)
       (state[TA_BLINK] && !newstate[TA_BLINK]) ||
       (state[TA_ULINE] && !newstate[TA_ULINE]) ||
       (color && state[TA_FGC] && !newstate[TA_FGC]) ||
-      (color && state[TA_BGC] && !newstate[TA_BGC])) {
-    for (n = 0; n < 6; n++)
+      (color && state[TA_BGC] && !newstate[TA_BGC]) ||
+      (color > 1 && state[TA_XTERMFG] && !newstate[TA_XTERMFG]) ||
+      (color > 1 && state[TA_XTERMBG] && !newstate[TA_XTERMBG])) {
+    for (n = 0; n < 8; n++)
       state[n] = 0;
     safe_str(ANSI_NORMAL, t, o);
   }
@@ -280,7 +398,11 @@ ansi_change_state(char *t, char **o, int color, int *state, int *newstate)
       (newstate[TA_BLINK] && (newstate[TA_BLINK] != state[TA_BLINK])) ||
       (newstate[TA_ULINE] && (newstate[TA_ULINE] != state[TA_ULINE])) ||
       (color && newstate[TA_FGC] && (newstate[TA_FGC] != state[TA_FGC])) ||
-      (color && newstate[TA_BGC] && (newstate[TA_BGC] != state[TA_BGC]))) {
+      (color && newstate[TA_BGC] && (newstate[TA_BGC] != state[TA_BGC])) ||
+      (color > 1 && newstate[TA_XTERMFG] &&
+                (newstate[TA_XTERMFG] != state[TA_XTERMFG])) ||
+      (color > 1 && newstate[TA_XTERMBG] &&
+                (newstate[TA_XTERMBG] != state[TA_XTERMBG]))) {
     safe_chr(ESC_CHAR, t, o);
     safe_chr('[', t, o);
     i = 0;
@@ -288,13 +410,19 @@ ansi_change_state(char *t, char **o, int color, int *state, int *newstate)
     add_ansi_if(TA_REV, "7");
     add_ansi_if(TA_BLINK, "5");
     add_ansi_if(TA_ULINE, "4");
-    if (color) {
+    switch (color) {
+    case 2: /* XTERM */
+      add_ansi_if(TA_XTERMFG, tprintf("38;5;%u", newstate[TA_XTERMFG] - 1));
+      add_ansi_if(TA_XTERMBG, tprintf("48;5;%u", newstate[TA_XTERMBG] - 1));
+      break;
+    case 1: /* ANSI */
       add_ansi_if(TA_FGC, unparse_integer(newstate[TA_FGC] + 29));
       add_ansi_if(TA_BGC, unparse_integer(newstate[TA_BGC] + 39));
+      break;
     }
     safe_chr('m', t, o);
   }
-  for (n = 0; n < 6; n++)
+  for (n = 0; n < 8; n++)
     state[n] = newstate[n];
 }
 
@@ -327,8 +455,8 @@ notify_makestring(const char *message, struct notify_strings messages[],
   char *o;
   const unsigned char *p;
   char *t;
-  int state[6] = { 0, 0, 0, 0, 0, 0 };
-  int newstate[6] = { 0, 0, 0, 0, 0, 0 };
+  int state[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+  int newstate[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
   int changed = 0;
   int color = 0;
   int strip = 0;
@@ -420,12 +548,19 @@ notify_makestring(const char *message, struct notify_strings messages[],
   case NA_COLOR:
   case NA_TCOLOR:
   case NA_NCOLOR:
-    color = 1;
+  case NA_XTERMCOLOR:
+  case NA_TXTERMCOLOR:
+  case NA_NXTERMCOLOR:
+    if (type == NA_XTERMCOLOR)
+      color = 2;
+    else
+      color = 1;
     /* FALLTHROUGH */
   case NA_ANSI:
   case NA_TANSI:
   case NA_NANSI:
-    if (type == NA_NCOLOR || type == NA_NANSI || type == NA_NPUEBLO)
+    if (type == NA_NCOLOR || type == NA_NANSI || type == NA_NPUEBLO ||
+       type == NA_NXTERMCOLOR)
       strip = 1;
     while (*p) {
       switch ((unsigned char) *p) {
@@ -434,7 +569,7 @@ notify_makestring(const char *message, struct notify_strings messages[],
          changed = 0;
          ansi_change_state(t, &o, color, state, newstate);
        }
-       if (type == NA_TANSI || type == NA_TCOLOR)
+       if (type == NA_TANSI || type == NA_TCOLOR || type == NA_TXTERMCOLOR)
          safe_str("\xFF\xFF", t, &o);
        else if (strip && accent_table[IAC].base)
          safe_str(accent_table[IAC].base, t, &o);
@@ -506,7 +641,8 @@ notify_makestring(const char *message, struct notify_strings messages[],
     }
     if (state[TA_BOLD] || state[TA_REV] ||
        state[TA_BLINK] || state[TA_ULINE] ||
-       (color && (state[TA_FGC] || state[TA_BGC])))
+       (color && (state[TA_FGC] || state[TA_BGC]
+                  || state[TA_XTERMFG] || state[TA_XTERMBG])))
       safe_str(ANSI_NORMAL, t, &o);
 
     break;
@@ -693,7 +829,12 @@ notify_type(DESC *d)
   if (d->conn_flags & CONN_HTML) {
     poutput = strip ? NA_NPUEBLO : NA_PUEBLO;
   } else if (ShowAnsi(d->player)) {
-    if (ShowAnsiColor(d->player)) {
+    if (ShowXtermColor(d->player)) {
+      if (strip)
+        poutput = NA_NXTERMCOLOR;
+      else
+        poutput = (d->conn_flags & CONN_TELNET) ? NA_TXTERMCOLOR : NA_XTERMCOLOR;
+    } else if (ShowAnsiColor(d->player)) {
       if (strip)
        poutput = NA_NCOLOR;
       else